Raven 以特定方式进行分页查询

Raven paging queries in a specific way

我正在使用 RavenDB 3 开发 ASP.NET MVC 应用程序。我对 raven 没有太多经验。

一般情况下,执行查询显示数据时,页面返回前128条。使用分页查询以 "infinite scroll" 方式添加更多记录。

但是,现在我要求在 'groups' 中加载项目。

假设以下 class:

public class Item {
  public string Id { get; set; }
  public int Group { get; set; }
  public string text { get; set; }
}

假设数据库包含 40 个组=“1”的项目、40 个组=“2”的项目和 50 个组=“3”的项目。

共有 130 项。这意味着最后获取的 'group' 将不完整。它将缺少 2 个项目。

我想要一种能够识别这一点的机制,这样它至少可以获取 128 并且如果最后一组未完全包含则可以获取 'extra'。

然后,当我获取下一页时,我希望它从下一组开始。

有什么方法可以让我自己通过多次调用在 'fabricating' 一个页面的情况下完成这项工作吗?

编辑:我不能假设这些组的大小完全相等,但我可以假设大小将是 'simular'

此外,我无法更改设计以将所有项目存储在一个 'group' 对象中。

好的,基本上您需要做的是计算前几页中的结果数和当前页中的结果数。下面是一个快速示例应用程序,可让您了解如何执行此操作。需要注意的是,如果当前组范围的结果数量超过 MaxNumberOfRequestsPerSession,则会抛出错误,因此您可能需要为此进行一些处理。

运行 这个例子的注意事项: 如果您使用的是最新版本的 RavenDB,请确保您的平台在 visual studio 中设置为 x64。否则,此示例将抛出有关 Voron 在 32 位模式下不稳定的错误。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Raven.Client;
using Raven.Client.Embedded;
using Raven.Client.Listeners;

namespace ConsoleApplication
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            using (var gdm = new GroupDataManager())
            {
                Console.WriteLine("Started Seeding");
                gdm.Seed().Wait();
                Console.WriteLine("Finished Seeding");
                Console.WriteLine("===============================================================");
                Console.WriteLine("Get First Page");
                Console.WriteLine("===============================================================");
                var firstPage = gdm.GetPagedGroupResults(1, 3).Result;

                foreach (var item in firstPage)
                {
                    Console.WriteLine(item.Text);
                }
                Console.WriteLine("===============================================================");
                Console.WriteLine("Get Second Page");
                Console.WriteLine("===============================================================");
                var secondPage = gdm.GetPagedGroupResults(2, 3).Result;
                foreach (var item in secondPage)
                {
                    Console.WriteLine(item.Text);
                }
            }
            Console.ReadLine();
        }
    }

    public class GroupDataManager : IDisposable
    {
        private readonly IDocumentStore _documentStore = new EmbeddedRavenDatabase().Store;

        public void Dispose()
        {
            _documentStore.Dispose();
        }

        public async Task Seed()
        {
            var rnd = new Random();
            using (var session = _documentStore.OpenAsyncSession())
            {
                for (var groupNumber = 1; groupNumber < 15; groupNumber++)
                {
                    for (var i = 0; i < rnd.Next(5, 25); i++)
                    {
                        var item = new Item
                        {
                            Group = groupNumber,
                            Text = string.Format("Group: {0} Item:{1}", groupNumber, i)
                        };
                        await session.StoreAsync(item);
                    }
                }

                await session.SaveChangesAsync();
            }
        }

        public async Task<IList<Item>> GetPagedGroupResults(int page, int numberOfGroupsPerPage)
        {
            var startingGroup = ((page - 1) * numberOfGroupsPerPage) + 1;

            using (var session = _documentStore.OpenAsyncSession())
            {
                //  Calculate the number of items that were contained in the previous groups
                var numberToSkip = await session.Query<Item>().CountAsync(item => item.Group < startingGroup);

                var endGroup = startingGroup + numberOfGroupsPerPage;

                //  Calculate the number of items that are in the current range of groups
                var numberToTake = await session.Query<Item>().CountAsync(item => item.Group >= startingGroup && item.Group < endGroup);

                var results = await session.Query<Item>().Skip(numberToSkip).Take(numberToTake).ToListAsync();

                return results;
            }
        }
    }

    public class Item
    {
        public string Id { get; set; }
        public int Group { get; set; }
        public string Text { get; set; }
    }

    /// <summary>
    /// For Testing Only. Prevents stale queries
    /// </summary>
    public class NoStaleQueriesAllowed : IDocumentQueryListener
    {
        public void BeforeQueryExecuted(IDocumentQueryCustomization queryCustomization)
        {
            queryCustomization.WaitForNonStaleResults();
        }
    }

    public class EmbeddedRavenDatabase
    {
        private static bool _configured = false;

        private static readonly Lazy<IDocumentStore> _lazyDocStore = new Lazy<IDocumentStore>(() =>
        {
            var docStore = new EmbeddableDocumentStore
            {
                RunInMemory = true
            };

            docStore.RegisterListener(new NoStaleQueriesAllowed());
            docStore.Initialize();

            return docStore;
        });

        public IDocumentStore Store
        {
            get { return _lazyDocStore.Value; }
        }
    }
}