Индекс карты RavenDb для дополнительных вычислений - PullRequest
4 голосов
/ 17 июня 2020

Вначале я хотел бы упомянуть, что я новичок в каких-либо базах данных № Sql. Думаю, я мог неправильно понять концепцию индексов в 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 для полей, не допускающих значения NULL) в каждом вычисляемом поле:

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   <----
      }
   ]
}

Я очень запутался, когда попробовал простой запрос RavenDb в Raven.Studio, и он вернул правильные значения

from index 'FuelCalculatedIndex'
select TotalCost

Так что, похоже, проблема заключается в коде C#. Что я делаю не так?

ОБНОВЛЕНИЕ

Я только что попытался запустить необработанный запрос в C#:

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

Удивительно, но это работает.

1 Ответ

4 голосов
/ 18 июня 2020

Результатом запроса всегда является документ (индекс карты). Таким образом, исходный запрос на самом деле должен быть:

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

...