从RavenDB中检索整个数据集合

时间:2012-06-29 21:06:20

标签: ravendb

我有一个要求,我需要从RavenDB中获取整个数据集合Users,并将检索到的结果集与另一组数据进行比较。此特定系列中有近4000条记录。

因为Raven是默认安全的,我不断获得Number of requests per session exceeded的异常,或者返回最多128条记录。

我不想将属性Session.Advanced.MaxNumberOfRequestsPerSession设置为更高的值。

我应该使用什么查询来获取所有记录的数量?处理这种情况的理想方法是什么?

6 个答案:

答案 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