使用RavenDB检索超过128个文档的正确方法

27

我知道类似的问题之前已经被问过了(包括我自己),但是我仍然不理解其中一两件事...

我的理解是,通过这种方法可以检索到比默认设置的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的结果呢?

**编辑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个点作为一个集合放在一个文档中吗? - Peter Willis
5个回答

37
值得注意的是,自 2.5 版本以来,RavenDB 具有“无界结果 API”,允许流式传输。文档中的示例展示了如何使用该 API:

自从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查询和Lucene查询,同时也支持异步操作。

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


3
请注意,在使用流式 API 进行查询时,索引必须已经存在。如果您通过普通会话 API 运行查询,且没有匹配的索引存在,则会创建一个动态索引。但在流式 API 中,不会创建动态索引,服务器会抱怨找不到索引。 - Mike Schenk
Mike - 这个行为很有趣,听起来像是一个 bug。你在 RavenDB 群组中讨论过这个问题吗? - Sean Kearon
1
您可以使用Stream<T>(startsWith)重载来获取特定集合中的所有文档;如果不需要执行查询,则无需使用查询。 - kamranicus
也许值得一提的是,您不必指定集合,而是让它按照约定自行解决(session.Advanced.Stream(documentStore.Conventions.GetTypeTagName(typeof(User))))。这对于存储库可能很有用。 - Caramiriel
我们可以在流式查询中使用.Statistics(out stats)吗? - Rudey

25

Take(n) 函数默认最多只会返回1024条记录。不过你可以在 Raven.Server.exe.config 文件中修改这个默认值:

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

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


谢谢,迈克。我认为这将最终成为被接受的答案,但我想先看看是否有其他人对此有不同的看法。 - Bob Horn

16

默认情况下,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分页


1
这种方法是迄今为止更好的方法。 - Matt
4
请注意服务器请求次数的限制。根据Raven的“默认安全”设置,它最多只会向服务器发送30次请求,因此如果循环需要执行超过该次数,则会失败,因为每次循环迭代都是另一个服务器请求。 - Mike Schenk

5

每个会话请求次数是一个独立的概念,与每次检索的文档数量不同。会话的生命周期很短,预期在其上发出的调用很少。

如果您从商店购买了超过10件任何人类消费品(甚至少于默认128件),则表示存在问题,或者您的问题需要与来自数据存储的大量文档有所不同。

RavenDB索引非常复杂。有关索引的良好文章在这里,有关facets的文章在这里

如果您需要执行数据聚合,请创建映射/归约索引,该索引将得到聚合数据,例如:

索引:

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

1
那么你会如何解决这个问题?业务方希望看到一张图表,显示过去24小时的数据点。每个文档都是一个数据点,在过去的24小时内有10,000个这样的数据点。如何在不传输所有数据的情况下绘制该图表? - Bob Horn
我认为你可以通过创建索引或分面搜索来实现这一点。 - Petar Vučetin
我刚刚注意到“每个文档都是一个数据点” - 你能展示一个这样的文档的例子吗? - Petar Vučetin
实际上,更准确的说法是每个文档包含一个或多个图表的一个或多个数据点。我编辑了我的问题,展示了一个带有其数据点的样本文档。另外,感谢您的帮助。我不熟悉facets。我得去查一下。最后,我认为聚合数据行不通,因为图表会单独绘制每个数据点。 - Bob Horn
我从未见过一张包含10,000个数据点的图表,如果由人类查看,它就没有任何意义(但奇怪的事情确实存在)。一个想法是减少细节。您可以创建一组文档,代表在不到24小时的时间段内聚合的数据,例如每小时拍摄一次快照。如果业务需要深入挖掘,那么我们将打开所有水龙头并带来卡车 :) - Petar Vučetin
我同意你的观点。而且,我认为你的快照想法很好。然而,我只涉及 RavenDB 方面的这个项目。在 1 版完成时做出了以这种方式处理的决定。虽然我/我们可能能够改变架构,但也可能无法这样做。谢谢! - Bob Horn

1
你可以使用预定义的索引和 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,
            };
    }
}

文档:什么是索引?会话:查询:如何流式查询结果?


重要提示:`Stream` 方法不会追踪对象。如果您更改从该方法获取的对象,则 `SaveChanges()` 将不会注意到任何更改。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接