用于额外计算的 RavenDb 映射索引

RavenDb map index for additional calculations

一开始我想说我对任何一种 NoSql 数据库都很陌生。我想我可能误解了 RavenDb 中索引的概念。

在我的应用程序中,我收集了代表汽车加油历史的文件(包括在此加油时所采取的路线的子集合)。简化版如下所示:

public class Fuel
{
    public decimal? VolumeUsed { get; set; }
    public decimal CostPerLitre { get; set; }
    public decimal? TotalDistance { get; set; }
    public IList<Route> Routes { get; set; }
}

public class Route
{
    public string StartingAddress { get; set; }
    public IList<Stop> Stops { get; set; }
}

集合中的文档不会经常修改,所以我决定将所有计算都放入数据库中,而不是在每次请求时即时计算所有内容。但我不认为额外的计算字段应该在本文档中,因为它们将仅取决于现有值而不是由用户提供。我想到了 Map index - 每次插入或修改文档时进行计算(以及将来可能的聚合)的非常好的方法。

我为索引

创建了另一个class
public class FuelCalculated
{
    public decimal? VolumeUsed { get; set; }
    public decimal CostPerLitre { get; set; }
    public decimal? TotalDistance { get; set; }
    public decimal? AverageFuelConsumption { get; set; }
    public decimal? TotalCost { get; set; }
    public IList<RouteCalculated> Routes { get; set; }
}

public class RouteCalculated
{
    public string StartingAddress { get; set; }
    public IList<Stop> Stops { get; set; }
    public decimal TotalDistance { get; set; }
    public decimal AverageFuelConsumption { get; set; }
}

和索引定义:

public class FuelCalculatedIndex : AbstractIndexCreationTask<Fuel, FuelCalculated>
{
    public FuelCalculatedIndex()
    {
        Map = fuels =>
            fuels.Select(f => new FuelCalculated()
            {
                AverageFuelConsumption = (f.VolumeUsed * 100) / f.TotalDistance,
                Routes = f.Routes.Select(r => new RouteCalculated()
                {
                    StartingAddress = r.StartingAddress,
                    Stops = r.Stops,
                    TotalDistance = r.Stops.Sum(s => s.Distance),
                    AverageFuelConsumption = r.Stops.Sum(s => s.AverageFuelConsumption * s.Distance) / r.Stops.Sum(s => s.Distance),
                }).ToList(),
                TotalCost = f.VolumeUsed * f.CostPerLitre,
                TotalDistance = f.TotalDistance,
                VolumeUsed = f.VolumeUsed,
            });

        StoreAllFields(FieldStorage.Yes);
    }
}

现在我不关心索引的数量 - 我想要显示所需的一切,因此查询不必跳转到原始文档。

现在,当我在代码中查询文档时,我在每个计算字段中得到空结果(或 0 对于不可空字段):

using (var session = documentStore.OpenAsyncSession())
{
    return await session
        .Query<FuelCalculated, FuelCalculatedIndex>()
        .ToListAsync();
}

返回 JSON:

{
   "volumeUsed":28.04,
   "costPerLitre":4.93,
   "totalDistance":467.3,
   "totalCost":null,                       <----
   "averageFuelConsumption":null,          <----
   "routes":[
      {
         "startingAddress":"Address 1",
         "stops":[
            {
               "address":"Address 2",
               "distance":351.0,
               "averageFuelConsumption":6.0
            }
         ],
         "totalDistance":0,                <----
         "totalAverageFuelConsumption":0   <----
      },
      {
         "startingAddress":"Address 3",
         "stops":[
            {
               "address":"Address 4",
               "distance":116.3,
               "averageFuelConsumption":7.0
            }
         ],
         "totalDistance":0,                <----
         "totalAverageFuelConsumption":0   <----
      }
   ]
}

当我在 Raven.Studio 中尝试简单的 RavenDb 查询并返回正确的值时,我真的很困惑

from index 'FuelCalculatedIndex'
select TotalCost

看来问题出在C#代码上。我做错了什么?

更新

我刚刚尝试在 C# 中运行原始查询:

await session
    .Advanced.AsyncRawQuery<FuelCalculated>("from index 'FuelCalculatedIndex' select TotalCost")
    .ToListAsync();

令人惊讶的是,它有效。

查询的结果始终是文档(地图索引)。 所以原来的查询实际上应该是:

using (var session = documentStore.OpenAsyncSession())
{
    return await session
        .Query<FuelCalculated, FuelCalculatedIndex>()
        .As<Fuel>
        .ToListAsync();
}

您需要使用 ProjectInto 才能获取存储的字段:

using (var session = documentStore.OpenAsyncSession())
{
    return await session
        .Query<FuelCalculated, FuelCalculatedIndex>()
        .ProjectInto<FuelCalculated>
        .ToListAsync();
}

https://ravendb.net/docs/article-page/4.2/Csharp/client-api/session/querying/how-to-project-query-results#projectinto