2012-06-29 115 views
15

我有一個需求,我需要從RavenDB中提取整個數據集合Users,並將檢索結果集與另一組數據進行比較。這個特定的收藏中有近4000條記錄。從RavenDB檢索整個數據集合

因爲烏鴉是安全的默認情況下,我不斷收到Number of requests per session exceeded的例外或它返回最多128個記錄。

我不想將屬性Session.Advanced.MaxNumberOfRequestsPerSession設置爲更高的值。

我應該使用什麼查詢來計算所有記錄?處理這種情況的理想方法是什麼?

回答

18

您使用分頁,並一次讀取這1024個項目。

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

我看到上述解決方案的工作,因爲總沒有。的記錄接近4000,所以沒有任何查詢將<30。只是好奇的是,處理類似情況的方法是什麼,其中總記錄數大於30 * 1024,即如果它們多於說31k數? – annantDev

+1

@annantDev您可以跟蹤會話中發出的請求數量。一旦達到30,處置舊會話,創建新會話並繼續閱讀。 –

1

在Ayende答案構建,這裏是一個完整的方法,這並克服每節30次的查詢問題,確實返回提供的類的所有文件:

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; 
    } 

我希望它這樣做並不是很冒險。

+1

Renato Kovarish的答案比這個更有優勢。這個答案使用recurssion。 – BrokeMyLegBiking

+0

@BrokeMyLegBiking確實,這裏的遞歸是不必要的。但是,至少可以處理每個會話限制的30個請求,而其他引用的答案不會。 –

1

我喜歡誠實下面的函數:

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; 
    } 

+0

無論如何,您都可以使用此代碼很快地點擊每個會話的30個請求限制,而不會擴大請求限制。 –

1

隨着@capaj's post一個輕微的扭曲[]。這是將所有文檔ID作爲字符串列表的通用方法。請注意使用Advanced.LuceneQuery<T>(idPropertyName),SelectFields<T>(idPropertyName)GetProperty(idPropertyName)來使事物具有通用性。默認假設"Id"是給定<T>(應該是99.999%的情況)的有效屬性。如果你有其他一些財產作爲你的Id,你也可以通過它。

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; 
} 

的地方/我如何使用,這是使用BulkInsert會議提出在RavenDb一個PatchRequest時的一個例子。在某些情況下,我可能有成千上萬的文檔,並且無法承載將所有文檔加載到內存中,只是爲了修補操作而重新遍歷它們...因此只加載它們的字符串ID以傳入Patch命令。

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); 
     } 
    } 
} 

希望它有幫助。 :)

11

這個問題被張貼此功能是RavenDB可用之前,但在這種情況下任何人絆倒現在......

的鼓勵辦法做到這一點是通過Streaming API。 RavenDB客戶端對流進行批處理,以便它可以自動將頁面請求/響應與服務器進行「尋呼」。如果您選擇使用Streaming API,則客戶端會假設您「知道您在做什麼」,並且不會檢查用於常規查詢的128/1024/30限制。

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

var count = allUsers.Count; 

提示:雖然這是解決問題的方式鼓勵......作爲一般規則,最好避免下手的情況。如果有一百萬條記錄呢?那allUsers列表將會變得巨大。也許可以首先完成索引或轉換,以篩選出實際需要顯示給用戶/進程的數據?這是爲了報告目的嗎?也許RavenDB應該自動導出到一個SQL服務器上並且有報告服務?等...

+0

在v2.5及更舊版本中使用Shards時,Streaming API不可用。 (不確定關於v3.0 +) –

1

我喜歡Al Dass解決方案讓獲得ids操作而不是完整的大對象。也直接從索引獲取ID。然而,遞歸會讓我感到恐懼(儘管我認爲它可能會好起來),並且我刪除了反射。

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; 
} 

也,此更新爲ravendb 3