【问题标题】:RavenDb map index for additional calculations用于额外计算的 RavenDb 地图索引
【发布时间】:2020-06-17 16:26:55
【问题描述】:

一开始我想提一下,我对任何类型的 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 - 每次插入或修改文档时进行计算(以及将来可能进行聚合)的非常好的方法。

我为索引创建了另一个类

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

令人惊讶的是,它有效。

【问题讨论】:

    标签: c# asp.net-core ravendb


    【解决方案1】:

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

    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

    【讨论】:

    • 我认为将FuelCalculated 指定为第一个泛型类型就足够了,从未真正考虑过投影。谢谢,就像一个魅力!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多