【问题标题】:Retrieving entire data collection from a RavenDB从 RavenDB 检索整个数据集合
【发布时间】:2012-06-29 21:06:20
【问题描述】:

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

因为 Raven 默认情况下是安全的,所以我不断收到 Number of requests per session exceeded 的异常,或者它返回最多 128 条记录。

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

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

【问题讨论】:

  • 如果有人只回答第一个答案,乔恩亚当斯的答案是一个好方法。这是介绍“新”功能的文章的链接。 ayende.com/blog/161249/…

标签: ravendb


【解决方案1】:

您使用分页,一次读取这 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);

}

【讨论】:

  • 我看到上述解决方案有效,因为总数。记录数接近 4000,因此查询数将小于 30。只是好奇,处理总记录数 > 30*1024 的类似情况的方法是什么,即如果它们超过 31k 计数?
  • @annantDev 您可以跟踪会话中发出的请求数。一旦达到 30,处理旧会话,创建新会话并继续阅读。
【解决方案2】:

这个问题是在 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 服务器?等等……

【讨论】:

  • 在 v2.5 及更早版本中使用 Shards 时,Streaming API 不可用。 (不确定 v3.0+)
  • StreamQuery 不支持查询动态索引。所以要使用这种方法,你需要先建立一个索引来查询。
【解决方案3】:

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

我希望这样做不会太笨拙。

【讨论】:

  • Renato Kovarish 的答案比这个有几个优点。这个答案使用递归。
  • @BrokeMyLegBiking 没错,这里的递归是不必要的。但是这个至少可以处理每个会话 30 个请求的限制,而另一个引用的答案没有。
【解决方案4】:

老实说,我更喜欢以下功能:

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

[]的

【讨论】:

  • 使用这段代码,您将很快达到每个会话 30 个请求的限制,无论如何都编写,而无需扩展请求限制。
【解决方案5】:

@capaj's post 略有改动。这是将所有文档 ID 作为字符串列表获取的通用方法。请注意使用Advanced.LuceneQuery&lt;T&gt;(idPropertyName)SelectFields&lt;T&gt;(idPropertyName)GetProperty(idPropertyName) 使事情变得通用。默认假设"Id" 是给定&lt;T&gt; 上的有效属性(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);
        }
    }
}

希望对您有所帮助。 :)

【讨论】:

    【解决方案6】:

    我喜欢 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-02
      • 2016-05-21
      • 1970-01-01
      相关资源
      最近更新 更多