17

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

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

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

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

4

6 回答 6

21

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

}
于 2012-06-30T08:21:30.170 回答
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 服务器?ETC...

于 2014-10-12T22:55:54.227 回答
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;
    }

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

于 2012-12-26T12:27:45.807 回答
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;
    }

[]的

于 2013-04-16T17:58:48.950 回答
1

@capaj 的帖子略有变化。这是将所有文档 ID 作为字符串列表获取的通用方法。注意 , 和 的使用,Advanced.LuceneQuery<T>(idPropertyName)以使事情通用。默认假设是给定的有效属性(99.999% 的时间应该是这种情况)。如果您有其他财产,您也可以将其传递。SelectFields<T>(idPropertyName)GetProperty(idPropertyName)"Id"<T>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);
        }
    }
}

希望能帮助到你。:)

于 2014-08-22T22:11:24.240 回答
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

于 2017-01-25T00:40:00.280 回答