RavenDB:如何在多地图索引上索引字典键?

RavenDB: How to index dictionary keys on a multi-map index?

我得到了以下有效的 RavenDB MultiMap 索引和 returns 结果。现在,当我想使用查询并尝试过滤数据时,我收到以下消息:

The field 'Stock_Key' is not indexed, cannot query/sort on fields that are not indexed.

我正在尝试获取在某些仓库有库存的所有产品。 _request.Warehouses是可以提供给查询的仓库ID列表。产品库存保存在数据库中的单独集合中,其中包含相同的 SKU。

var query = await _session
    .Query<Products_SearchUniqueBySku.Result, Products_SearchUniqueBySku>()
    .Where(x => x.Stock.Any(y => y.Key.In(_request.Warehouses) && y.Value > 0))
    .ToListAsync();

我一整天都在尝试将键编入索引,但没有成功。将不胜感激一些帮助。我还尝试通过 RavenDB 工作室中的一些 RQL 变体进行查询,但在其中获得了更多相同的消息。不确定 RQL 查询是否正确编写。

from index 'Products/SearchUniqueBySku'
where Stock.589e90c9-09bb-4a04-94fb-cf92bde88f97 > 0

The field 'Stock.589e90c9-09bb-4a04-94fb-cf92bde88f97' is not indexed, cannot query/sort on fields that are not indexed
from index 'Products/SearchUniqueBySku'
where Stock_589e90c9-09bb-4a04-94fb-cf92bde88f97 > 0

The field 'Stock_589e90c9-09bb-4a04-94fb-cf92bde88f97' is not indexed, cannot query/sort on fields that are not indexed

我用过的索引:

using System;
using System.Collections.Generic;
using System.Linq;
using Raven.Client.Documents.Indexes;
using MyProject.Models.Ordering.Entities;
using MyProject.Models.Ordering.Enums;

namespace MyProject.Ordering.Indexes;

public class Products_SearchUniqueBySku : AbstractMultiMapIndexCreationTask<Products_SearchUniqueBySku.Result>
{
    public class Result
    {
        public string Sku { get; set; }
        public ProductTypes Type { get; set; }
        public string Name { get; set; }
        public IDictionary<string, decimal> Stock { get; set; }
    }

    public Products_SearchUniqueBySku()
    {
        AddMap<Product>(
            products => from product in products
                        where product.Type == ProductTypes.Simple
                        where product.Variants.Count == 0
                        select new
                        {
                            product.Sku,
                            product.Type,
                            product.Name,
                            Stock = new Dictionary<string, decimal>()
                        }
        );

        AddMap<Product>(
            products => from product in products
                        where product.Type == ProductTypes.Simple
                        where product.Variants.Count > 0
                        from variant in product.Variants
                        select new
                        {
                            variant.Sku,
                            Type = ProductTypes.Variant,
                            Name = $"{product.Name} ({string.Join("/", variant.Mappings.Select(y => y.Value.Name))})",
                            Stock = new Dictionary<string, decimal>()
                        });

        AddMap<StockItem>(
            items => from item in items
                     group item by item.Sku
                     into grouped
                     select new
                     {
                         Sku = grouped.Key,
                         Type = ProductTypes.Variant,
                         Name = (string) null,
                         Stock = grouped.ToDictionary(x => x.Warehouse.Id, x => x.Stock)
                     }
        );

        Reduce = results => from result in results
                            group result by result.Sku
                            into grouped
                            let product = grouped.Single(x => x.Stock.Count == 0)
                            select new
                            {
                                Sku = grouped.Key,
                                product.Type,
                                product.Name,
                                Stock = grouped.SelectMany(x => x.Stock).ToDictionary(x => x.Key, x => x.Value),
                            };
    }
}

在RavenDB studio上使用的结果(只展示了一部分,你懂的):

from index 'Products/SearchUniqueBySku'
{
    "Sku": "VANS-SH-38",
    "Type": "Variant",
    "Name": "Vans Men's Suede (Maat 38)",
    "Stock": {
        "589e90c9-09bb-4a04-94fb-cf92bde88f97": 10,
        "98304a84-0f44-49ce-8438-8a959ca29b9d": 11
    },
    "@metadata": {
        "@change-vector": null,
        "@index-score": 1
    }
},
{

    "Sku": "889376",
    "Type": "Simple",
    "Name": "Apple Magic Trackpad (2021)",
    "Stock": {
        "589e90c9-09bb-4a04-94fb-cf92bde88f97": 15
    },
    "@metadata": {
        "@change-vector": null,
        "@index-score": 1
    }
}

模型(为简洁起见省略了大部分属性):

public class StockItem
{
    public EntityReference Warehouse { get; set; }
    public string Sku { get; set; }
    public decimal Stock { get; set; }
}

public class EntityReference
{
   public string Id { get; set; }
   public string Name { get; set; }
}

public class Product
{
   public string Id { get; set; }
   public string Name { get; set; }
   public ProductTypes Type { get; set; }
   public List<ProductVariant> Variants { get; set; }
}  

public class ProductVariant
{
   public string Sku { get; set; }
}    

编辑:

基于@Ayende Rahien 的回答。我不得不将 Reduce 更改为以下内容:

  Reduce = results => from result in results
                    group result by result.Sku
                    into grouped
                    let product = grouped.Single(x => x.Stock.Count == 0)
                    let stock = grouped.SelectMany(x => x.Stock).ToDictionary(x => x.Key, x => x.Value)
                    select new
                    {
                        product.Id,
                        ....
                        Stock = stock,
                        _ = stock.Select(x => CreateField("Stock_" + x.Key, x.Value)) <--
                    };

参见索引的动态字段 (docs)。然后我收到以下消息:

Map and Reduce functions of a index must return identical types.

我通过将 _ = (string) null 作为 属性 添加到每个 AddMap 函数来解决这个问题(不确定它是否是完美的解决方案,但是嘿,它起作用了)然后以下查询有效:

from index 'Products/SearchUniqueBySku'
where Stock_589e90c9-09bb-4a04-94fb-cf92bde88f97 > 0

您需要在索引的 Reduce 中执行此操作:

 _ = grouped.SelectMany(x => CreateField("Stock_" +x.Stock.Key, x.Stock.Value))

使用 _ 将字段标记为包含动态字段。 它将发出的字段格式为 Stock_$key