【问题标题】:Proper Way to Retrieve More than 128 Documents with RavenDB使用 RavenDB 检索超过 128 个文档的正确方法
【发布时间】:2012-04-06 20:34:28
【问题描述】:

我知道这个问题的变体已经被问过(甚至是我自己),但我仍然不明白这件事...

据我了解,这样做可以检索比默认设置 128 更多的文档:

session.Advanced.MaxNumberOfRequestsPerSession = int.MaxValue;

我了解到,WHERE 子句应该是 ExpressionTree 而不是 Func,因此它被视为 Queryable 而不是 Enumerable。所以我认为这应该可行:

public static List<T> GetObjectList<T>(Expression<Func<T, bool>> whereClause)
{
    using (IDocumentSession session = GetRavenSession())
    {
        return session.Query<T>().Where(whereClause).ToList();                
    }
}

但是,这只返回 128 个文档。为什么?

注意,这里是调用上述方法的代码:

RavenDataAccessComponent.GetObjectList<Ccm>(x => x.TimeStamp > lastReadTime);

如果我添加 Take(n),那么我可以得到尽可能多的文档。例如,这将返回 200 个文档:

return session.Query<T>().Where(whereClause).Take(200).ToList();

基于所有这些,检索数千个文档的适当方法似乎是设置 MaxNumberOfRequestsPerSession 并在查询中使用 Take()。那正确吗?如果没有,应该如何完成?

对于我的应用程序,我需要检索数千个文档(其中的数据非常少)。我们将这些文档保存在内存中,并用作图表的数据源。

** 编辑 **

我尝试在我的 Take() 中使用 int.MaxValue:

return session.Query<T>().Where(whereClause).Take(int.MaxValue).ToList();

然后返回 1024。啊。如何获得超过 1024?

** EDIT 2 - 显示数据的示例文档 **

{
  "Header_ID": 3525880,
  "Sub_ID": "120403261139",
  "TimeStamp": "2012-04-05T15:14:13.9870000",
  "Equipment_ID": "PBG11A-CCM",
  "AverageAbsorber1": "284.451",
  "AverageAbsorber2": "108.442",
  "AverageAbsorber3": "886.523",
  "AverageAbsorber4": "176.773"
}

【问题讨论】:

  • 您是否考虑过将所有 10000 个点作为一个集合放在一个文档中?

标签: ravendb


【解决方案1】:

值得注意的是,从 2.5 版开始,RavenDB 有一个“无限结果 API”来允许流式传输。文档中的示例显示了如何使用它:

var query = session.Query<User>("Users/ByActive").Where(x => x.Active);
using (var enumerator = session.Advanced.Stream(query))
{
    while (enumerator.MoveNext())
    {
        User activeUser = enumerator.Current.Document;
    }
}

支持标准 RavenDB 查询、Lucence 查询,还支持异步。

可以在here 找到文档。可以在 here 找到 Ayende 的介绍性博客文章。

【讨论】:

  • 请注意,使用 Streaming API 查询时,索引必须已经存在。如果您通过普通会话 API 运行查询,并且不存在匹配的索引,则会创建动态索引。但是在流式API中,并没有创建动态索引,服务端抱怨找不到索引。
  • Mike - 这是有趣的行为,听起来像一个错误。你在 RavenDB 小组讨论过这个吗?
  • 您可以使用Stream&lt;T&gt;(startsWith) 重载来获取特定集合中的所有文档;如果您不需要执行查询,则无需使用查询。
  • 也许不错的补充是您不必指定一个集合并让它按照约定来计算 (session.Advanced.Stream(documentStore.Conventions.GetTypeTagName(typeof(User))))。可用于存储库。
  • 我们可以在流式查询中使用.Statistics(out stats) 吗?
【解决方案2】:

Take(n) 函数默认最多只能提供 1024 个。但是,您可以在 Raven.Server.exe.config 中更改此默认设置:

<add key="Raven/MaxPageSize" value="5000"/>

欲了解更多信息,请参阅:http://ravendb.net/docs/intro/safe-by-default

【讨论】:

  • 谢谢,迈克。我认为这最终会成为公认的答案,但我想先看看其他人是否对此有不同的看法。
【解决方案3】:

默认情况下,Take(n) 函数最多只能为您提供 1024。但是,您可以将它与 Skip(n) 配对使用以获取所有内容

        var points = new List<T>();
        var nextGroupOfPoints = new List<T>();
        const int ElementTakeCount = 1024;
        int i = 0;
        int skipResults = 0;

        do
        {
            nextGroupOfPoints = session.Query<T>().Statistics(out stats).Where(whereClause).Skip(i * ElementTakeCount + skipResults).Take(ElementTakeCount).ToList();
            i++;
            skipResults += stats.SkippedResults;

            points = points.Concat(nextGroupOfPoints).ToList();
        }
        while (nextGroupOfPoints.Count == ElementTakeCount);

        return points;

RavenDB Paging

【讨论】:

  • 这种方法是更好的方法。
  • 注意服务器请求数的限制。根据 Raven 的“默认安全”设置,它最多只能往返服务器 30 次,因此如果循环需要执行的次数超过此次数,它将失败,因为循环的每次迭代都是另一个服务器请求。
【解决方案4】:

每个会话的请求数是一个单独的概念,然后是每次调用检索到的文档数。会话是短暂的,预计很少有呼叫通过它们发出。

如果您从商店中获得超过 10 份(甚至少于默认 128 份)供人类消费,则说明有问题,或者您的问题需要不同的思维方式,然后从数据存储中加载大量文档。

RavenDB 索引非常复杂。关于索引here 和构面here 的好文章。

如果您需要执行数据聚合,请创建生成聚合数据的 map/reduce 索引,例如:

索引:

    from post in docs.Posts
    select new { post.Author, Count = 1 }

    from result in results
    group result by result.Author into g
    select new
    {
       Author = g.Key,
       Count = g.Sum(x=>x.Count)
    }

查询:

session.Query<AuthorPostStats>("Posts/ByUser/Count")(x=>x.Author)();

【讨论】:

  • 那么你将如何解决这个问题?企业希望查看显示过去 24 小时数据点的图表。每个文档都是一个数据点,过去 24 小时内有 10,000 个。在不带所有数据的情况下如何绘制图表?
  • 我认为您可以通过创建索引或facets 来实现这一点
  • 我刚刚注意到“每个文档都是一个数据点”——你能举一个这个文档的例子吗?
  • 实际上更准确的说法是每个文档包含一个或多个图表的一个或多个数据点。我在上面编辑了我的问题以显示带有数据点的示例文档。另外,感谢您的帮助。我不熟悉方面。我得去看看。最后,我不认为聚合数据会起作用,因为图表单独绘制了每个数据点。
  • 我还没有看到包含 10,000 个数据点的图表,如果被人类查看(但存在奇怪的东西),这些数据点是没有意义的。一种想法是减少粒度。您可以创建一组文档来表示少于 24 小时的聚合数据,例如每小时快照。如果企业想更深入地挖掘,那么打开所有水龙头并带上卡车:)
【解决方案5】:

您还可以通过Stream 方法使用预定义索引。您可以在索引字段上使用 Where 子句。

var query = session.Query<User, MyUserIndex>();
var query = session.Query<User, MyUserIndex>().Where(x => !x.IsDeleted);

using (var enumerator = session.Advanced.Stream<User>(query))
{
    while (enumerator.MoveNext())
    {
        var user = enumerator.Current.Document;
        // do something
    }
}

示例索引:

public class MyUserIndex: AbstractIndexCreationTask<User>
{
    public MyUserIndex()
    {
        this.Map = users =>
            from u in users
            select new
            {
                u.IsDeleted,
                u.Username,
            };
    }
}

文档:What are indexes? Session : Querying : How to stream query results?


重要提示:Stream 方法不会跟踪对象。如果你改变了通过这个方法获得的对象,SaveChanges() 将不会察觉到任何改变。


其他说明:如果不指定要使用的索引,可能会出现以下异常。

InvalidOperationException:StreamQuery 不支持查询动态索引。它被设计用于大型数据集,并且不太可能在 15 秒的索引后返回所有数据集,就像 Query() 一样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多