我有一个要求,我需要从RavenDB中获取整个数据集合Users
,并将检索到的结果集与另一组数据进行比较。此特定系列中有近4000条记录。
因为Raven是默认安全的,我不断获得Number of requests per session exceeded
的异常,或者返回最多128条记录。
我不想将属性Session.Advanced.MaxNumberOfRequestsPerSession
设置为更高的值。
我应该使用什么查询来获取所有记录的数量?处理这种情况的理想方法是什么?
答案 0 :(得分:20)
您使用分页,一次读取这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);
}
答案 1 :(得分:13)
此问题是在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服务器上?等...
答案 2 :(得分: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;
}
我希望这样做并不太苛刻。
答案 3 :(得分: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;
}
[]的
答案 4 :(得分: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;
}
使用PatchRequest
会话在RavenDb中创建BulkInsert
时,我在何处/如何使用此示例。在某些情况下,我可能有成千上万的文档,并且不能在内存中加载所有文档,只是为了补丁操作再次重复它们...因此只加载它们的字符串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);
}
}
}
希望它有所帮助。 :)
答案 5 :(得分:1)
我喜欢Al Dass解决方案,即使用id来操作而不是完整的大型对象。也直接从索引获取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