2012-06-29 10 views
15

Ich habe eine Anforderung, wo ich die gesamte Datensammlung Users von RavenDB abrufen und die abgerufene Ergebnismenge mit einem anderen Datensatz vergleichen muss. Es gibt fast 4000 Datensätze in dieser bestimmten Sammlung.Abrufen der gesamten Datensammlung von einer RavenDB

Weil Raven sicher ist Standardmäßig erhalte ich eine Ausnahme für Number of requests per session exceeded oder es gibt die maximal 128 Datensätze zurück.

Ich möchte die Eigenschaft Session.Advanced.MaxNumberOfRequestsPerSession nicht auf einen höheren Wert setzen.

Welche Abfrage soll ich verwenden, um die Anzahl aller Datensätze zu erhalten? Was ist der ideale Ansatz, um mit dieser Situation umzugehen?

Antwort

18

Sie verwenden Paging und lesen diese 1024 Elemente gleichzeitig.

int start = 0; 
while(true) 
{ 
    var current = session.Query<User>().Take(1024).Skip(start).ToList(); 
    if(current.Count == 0) 
      break; 

    start+= current.Count; 
    allUsers.AddRange(current); 

} 
+2

RavenDB aktualisiert Ich sehe, dass oben genannte Lösung funktioniert, weil die Gesamt nicht. der Datensätze ist nahe 4000, so dass keine der Abfragen <30 wäre. Nur neugierig, wie wäre die Art und Weise, ein ähnliches Szenario zu behandeln, in dem Total no of records> 30 * 1024 sind, d.h. wenn sie mehr als sagen 31k zählen? – annantDev

+1

@annantDev Sie können die Anzahl der während der Sitzung gestellten Anfragen verfolgen. Sobald es 30 erreicht hat, verlasse die alte Sitzung, erstelle eine neue Sitzung und lese weiter. –

1

auf der Ayende Antwort Aufbau, hier eine vollständige Methode, die überwunden zurückgibt, das Problem von 30 Abfragen pro Sitzung und in der Tat alle Dokumente des mitgelieferten Klasse:

public static List<T> getAll<T>(DocumentStore docDB) { 
     return getAllFrom(0, new List<T>(), docDB); 
    } 

    public static List<T> getAllFrom<T>(int startFrom, List<T> list, DocumentStore docDB) { 
     var allUsers = list; 

     using (var session = docDB.OpenSession()) 
     { 
      int queryCount = 0; 
      int start = startFrom; 
      while (true) 
      { 
       var current = session.Query<T>().Take(1024).Skip(start).ToList(); 
       queryCount += 1; 
       if (current.Count == 0) 
        break; 

       start += current.Count; 
       allUsers.AddRange(current); 

       if (queryCount >= 30) 
       { 
        return getAllFrom(start, allUsers, docDB); 
       } 
      } 
     } 
     return allUsers; 
    } 

Ich hoffe, dass es ist nicht zu hacky, um es so zu machen.

+1

Die Antwort von Renato Kovarish hat mehrere Vorteile gegenüber diesem. Diese Antwort verwendet Rekursion. – BrokeMyLegBiking

+0

@BrokeMyLegBiking True, die Rekursion ist hier nicht notwendig. Aber this man zumindest behandelt die 30 Anfrage pro Sitzung Limit, dass die andere referenzierte Antwort nicht. –

1

Ich ziehe es ehrlich folgende Funktion:

public IEnumerable<T> GetAll<T>() 
    { 
     List<T> list = new List<T>(); 

     RavenQueryStatistics statistics = new RavenQueryStatistics(); 

     list.AddRange(_session.Query<T>().Statistics(out statistics)); 
     if (statistics.TotalResults > 128) 
     { 
      int toTake = statistics.TotalResults - 128; 
      int taken = 128; 
      while (toTake > 0) 
      { 
       list.AddRange(_session.Query<T>().Skip(taken).Take(toTake > 1024 ? 1024 : toTake)); 
       toTake -= 1024; 
       taken += 1024; 
      } 
     } 

     return list; 
    } 

[] 's

+0

Sie werden die 30 Anfrage pro Sitzung Limit ziemlich schnell mit diesem Code, wie auch immer geschrieben, ohne Erweiterung der Anfrage begrenzen. –

1

Mit einer leichten Drehung auf @capaj's post. Hier ist eine allgemeine Möglichkeit, alle Dokument-IDs als eine Liste von Zeichenfolgen zu erhalten. Beachten Sie die Verwendung von Advanced.LuceneQuery<T>(idPropertyName), und GetProperty(idPropertyName), um Dinge generisch zu machen. Der Standard geht davon aus, dass "Id" eine gültige Eigenschaft auf dem gegebenen <T> ist (was in 99,999% der Fälle der Fall sein sollte). Für den Fall, dass Sie eine andere Eigenschaft als Ihre Id haben, können Sie es auch übergeben.

public static List<string> getAllIds<T>(DocumentStore docDB, string idPropertyName = "Id") { 
    return getAllIdsFrom<T>(0, new List<string>(), docDB, idPropertyName); 
} 

public static List<string> getAllIdsFrom<T>(int startFrom, List<string> list, DocumentStore docDB, string idPropertyName) { 
    var allUsers = list; 

    using (var session = docDB.OpenSession()) 
    { 
     int queryCount = 0; 
     int start = startFrom; 
     while (true) 
     { 
      var current = session.Advanced.LuceneQuery<T>().Take(1024).Skip(start).SelectFields<T>(idPropertyName).ToList(); 
      queryCount += 1; 
      if (current.Count == 0) 
       break; 

      start += current.Count; 
      allUsers.AddRange(current.Select(t => (t.GetType().GetProperty(idPropertyName).GetValue(t, null)).ToString())); 

      if (queryCount >= 28) 
      { 
       return getAllIdsFrom<T>(start, allUsers, docDB, idPropertyName); 
      } 
     } 
    } 
    return allUsers; 
} 

Ein Beispiel, wo/wie ich das ist, wenn ein PatchRequest in RavenDB mit der BulkInsert Sitzung zu machen. In einigen Fällen habe ich vielleicht Hunderttausende von Dokumenten und kann es mir nicht leisten, alle Dokumente im Speicher zu laden, nur um sie erneut für die Patch-Operation zu wiederholen ... also das Laden nur ihrer String-IDs in die Patch Befehl.

void PatchRavenDocs() 
{ 
    var store = new DocumentStore 
    { 
     Url = "http://localhost:8080", 
     DefaultDatabase = "SoMeDaTaBaSeNaMe" 
    }; 

    store.Initialize(); 

    // >>>here is where I get all the doc IDs for a given type<<< 
    var allIds = getAllIds<SoMeDoCuMeNtTyPe>(store);  

    // create a new patch to ADD a new int property to my documents 
    var patches = new[]{ new PatchRequest { Type = PatchCommandType.Set, Name = "SoMeNeWPrOpeRtY" ,Value = 0 }}; 

    using (var s = store.BulkInsert()){ 
     int cntr = 0; 
     Console.WriteLine("ID Count " + allIds.Count); 
     foreach(string id in allIds) 
     { 
      // apply the patch to my document 
      s.DatabaseCommands.Patch(id, patches); 

      // spit out a record every 2048 rows as a basic sanity check 
      if ((cntr++ % 2048) == 0) 
       Console.WriteLine(cntr + " " + id); 
     } 
    } 
} 

Ich hoffe, es hilft. :)

11

Diese Frage wurde geschrieben, bevor diese Funktion in RavenDB verfügbar war, aber falls jemand stolpert dies jetzt ...

Der ermunterte Weg, dies über die Streaming API zu tun ist. Der RavenDB-Client legt den Stream so ab, dass er die Anforderungen/Antworten automatisch an den/vom Server senden kann. Wenn Sie sich für die Verwendung der Streaming API entscheiden, geht der Client davon aus, dass Sie "wissen, was Sie tun", und überprüft nicht die 128/1024/30 Limits, die für reguläre Abfragen verwendet werden.

var query = session.Query<User>(); 
  
using (var enumerator = session.Advanced.Stream(query)) { 
    while (enumerator.MoveNext()) { 
        allUsers.Add(enumerator.Current.Document); 
    } 
} 

var count = allUsers.Count; 

Tipps: Obwohl dies der Weg ist, ermutigt das Problem ... Als allgemeine Regel zu lösen ist es am besten, die Situation zu vermeiden, mit zu beginnen. Was ist, wenn es eine Million Datensätze gibt? Diese allUsers Liste wird riesig werden.Vielleicht könnte zuerst ein Index oder eine Transformation durchgeführt werden, um herauszufiltern, welche Daten Sie dem Benutzer/Prozess tatsächlich anzeigen müssen? Ist dies für die Berichterstattung? Vielleicht sollte RavenDB automatisch zu einem SQL-Server mit Reporting Services exportieren? Etc ...

+0

Die Streaming-API ist nicht verfügbar, wenn Shards in Version 2.5 und älter verwendet werden. (Nicht sicher über v3.0 +) –

1

Ich mag Al-Dass-Lösung, um IDs anstelle von vollständigen großen Objekten zu bearbeiten. Auch die IDs direkt aus dem Index holen. Allerdings macht mir die Rekursion etwas Angst (obwohl ich denke, dass es in Ordnung sein könnte) und ich entfernte die Reflexion.

public List<string> GetAllIds<T>() 
{ 
var allIds = new List<string>(); 
IDocumentSession session = null; 

try 
{ 
    session = documentStore.OpenSession(); 
    int queryCount = 0; 
    int start = 0; 
    while (true) 
    { 
     var current = session.Advanced.DocumentQuery<T>() 
      .Take(1024) 
      .Skip(start) 
      .SelectFields<string>("__document_id") 
      .AddOrder("__document_id") 
      .ToList(); 

     if (current.Count == 0) 
      break; 
     allIds.AddRange(current); 

     queryCount += 1; 
     start += current.Count; 

     if (queryCount == 30) 
     { 
      queryCount = 0; 
      session.Dispose(); 
      session = documentStore.OpenSession(); 
     } 
    } 
} 
finally 
{ 
    if (session != null) 
    { 
     session.Dispose(); 
    } 
} 

return allIds; 
} 

auch, ist dieses 3

Verwandte Themen