我如何获得 RavenDb 中相关项目的计数?
How can I get counts of related items in RavenDb?
在 RavenDb 中,我正在尝试获取包含计数的项目列表。
使用 Raven 示例数据库作为简化示例,我想获得一个类别列表以及每个类别中的产品数量。
相关类:
public class Category
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public string Id { get; set; }
public string Category { get; set; }
// ...others removed...
}
部分有效的查询(但遗漏 0 个计数)
以下查询似乎有效(我正在为 Linqpad 使用 .Dump();
):
using (var session = docStore.OpenSession()) {
var products = session
.Query<Product>()
.Customize(x => x.Include<Product>(p => p.Category))
.ToArray()
.GroupBy(x => x.Category)
.Select(x => new {
category = session.Load<Category>(x.Key),
numProducts = x.Count()
})
.Dump();
session.Advanced.NumberOfRequests.Dump("NumberOfRequests");
}
问题在于,如果某个类别没有产品,则它不会包含在结果集中。
(此外,这是执行此查询的正确方法吗?对服务器的一个请求提示我至少我没有可怕偏离轨道)
使用非常糟糕的查询更正结果
暴力破解,我可以用代码:
using (var session = docStore.OpenSession()) {
var categories = session
.Query<Category>();
var categoryCounts = new Dictionary<Category,int>();
foreach (var category in categories)
{
if (!categoryCounts.ContainsKey(category)) categoryCounts.Add(category,0);
categoryCounts[category] += session
.Query<Product>()
.Where(p => p.Category == category.Id)
.Count();
}
categoryCounts.Dump();
session.Advanced.NumberOfRequests.Dump("NumberOfRequests");
}
但这显然是一种糟糕的方式,导致 1+n 次请求(其中 n == 类别数)。
如何获取类别+产品数量,包括没有产品的类别,并且不会导致1+n请求?
对于具有数百个类别和数十万(甚至数百万)产品(单独一个类别可能有数十万)的数据库,是否有不同的考虑因素?
执行此类操作的惯用方法是使用索引(即 map - reduce 索引)。虽然我还没有彻底考虑过这一点,所以可能有比使用多地图更简单的方法来实现这一点,但我认为你可以执行以下操作:
public class CategoryUsageCount
{
public string CategoryId { get; set; }
public Category Category { get; set; }
public int UsageCount { get; set; }
}
public class UsageCountByCategory : AbstractMultiMapIndexCreationTask<CategoryUsageCount>
{
public UsageCountByCategory()
{
AddMap<Category>(categories =>
from category in categories
select new {
CategoryId = category.Id,
Category = category,
UsageCount = 0
});
AddMap<Procuct>(products =>
from product in products
select new {
CategoryId = product.Category,
Category = (Category)null,
UsageCount = 1
});
Reduce = results =>
from result in results
group result by result.CategoryId into g
select new {
CategoryId = g.Key,
Category = g.First(x => x != null).Category,
UsageCount = g.Sum(x => x.UsageCount)
};
Index(x => x.CategoryId, FieldIndexing.Analyzed);
}
}
并像这样使用它:
using (var session = docStore.OpenSession()) {
var categoryUsageCounts = session
.Query<CategoryUsageCount, UsageCountByCategory>()
.ToList();
}
在 RavenDb 中,我正在尝试获取包含计数的项目列表。
使用 Raven 示例数据库作为简化示例,我想获得一个类别列表以及每个类别中的产品数量。
相关类:
public class Category
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public string Id { get; set; }
public string Category { get; set; }
// ...others removed...
}
部分有效的查询(但遗漏 0 个计数)
以下查询似乎有效(我正在为 Linqpad 使用 .Dump();
):
using (var session = docStore.OpenSession()) {
var products = session
.Query<Product>()
.Customize(x => x.Include<Product>(p => p.Category))
.ToArray()
.GroupBy(x => x.Category)
.Select(x => new {
category = session.Load<Category>(x.Key),
numProducts = x.Count()
})
.Dump();
session.Advanced.NumberOfRequests.Dump("NumberOfRequests");
}
问题在于,如果某个类别没有产品,则它不会包含在结果集中。
(此外,这是执行此查询的正确方法吗?对服务器的一个请求提示我至少我没有可怕偏离轨道)
使用非常糟糕的查询更正结果
暴力破解,我可以用代码:
using (var session = docStore.OpenSession()) {
var categories = session
.Query<Category>();
var categoryCounts = new Dictionary<Category,int>();
foreach (var category in categories)
{
if (!categoryCounts.ContainsKey(category)) categoryCounts.Add(category,0);
categoryCounts[category] += session
.Query<Product>()
.Where(p => p.Category == category.Id)
.Count();
}
categoryCounts.Dump();
session.Advanced.NumberOfRequests.Dump("NumberOfRequests");
}
但这显然是一种糟糕的方式,导致 1+n 次请求(其中 n == 类别数)。
如何获取类别+产品数量,包括没有产品的类别,并且不会导致1+n请求?
对于具有数百个类别和数十万(甚至数百万)产品(单独一个类别可能有数十万)的数据库,是否有不同的考虑因素?
执行此类操作的惯用方法是使用索引(即 map - reduce 索引)。虽然我还没有彻底考虑过这一点,所以可能有比使用多地图更简单的方法来实现这一点,但我认为你可以执行以下操作:
public class CategoryUsageCount
{
public string CategoryId { get; set; }
public Category Category { get; set; }
public int UsageCount { get; set; }
}
public class UsageCountByCategory : AbstractMultiMapIndexCreationTask<CategoryUsageCount>
{
public UsageCountByCategory()
{
AddMap<Category>(categories =>
from category in categories
select new {
CategoryId = category.Id,
Category = category,
UsageCount = 0
});
AddMap<Procuct>(products =>
from product in products
select new {
CategoryId = product.Category,
Category = (Category)null,
UsageCount = 1
});
Reduce = results =>
from result in results
group result by result.CategoryId into g
select new {
CategoryId = g.Key,
Category = g.First(x => x != null).Category,
UsageCount = g.Sum(x => x.UsageCount)
};
Index(x => x.CategoryId, FieldIndexing.Analyzed);
}
}
并像这样使用它:
using (var session = docStore.OpenSession()) {
var categoryUsageCounts = session
.Query<CategoryUsageCount, UsageCountByCategory>()
.ToList();
}