RavenDB 多对多和索引

RavenDB many to many and indexes

需要有关 RavenDB 的帮助。

在我的网页中我想要这样的列表:

还有一个:

我的数据结构:

public class Item
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string CategoryId { get; set; }
}


public class Category
{
    public string Id { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
}

第一个列表的索引:

public class Item_WithCategory : AbstractIndexCreationTask<Item>
{
    public class Result
    {
        public string Name { get; set; }
        public string CategoryName { get; set; }
    }

    public Item_WithCategory()
    {
        Map = items => from item in items
              select new
                  {
                      Name = item.Name,
                      CategoryName = LoadDocument<Category>(item.CategoryId).Name
                  };
    }
}

这个数据结构是否适合我的情况,或者在项目结构中使用 Category 而不是 CategoryId 会更好吗?

我应该使用我的索引还是有更好的解决方案来获取类别名称?

如果我的索引很好,如何编写正确的查询?我目前的尝试:

    Item_WithCategory.Result[] all;
    using (var session = DocumentStoreHolder.Store.OpenSession())
    {
        all = session.Query<Item_WithCategory.Result, Item_WithCategory>().ToArray();
    }

但它抛出异常,指出 return 类型是项目,而不是结果。如何解决?

这里有几个选项。您可以将 CategoryId 和 CategoryName 都存储在 Item 实体上。这当然会导致重复数据(如果你仍然需要存储Category实体),但是"storage is cheap"是一个流行的术语这些days.The缺点是你需要更新一个的每个Item文档如果类别名称更改以保持一致,则给定类别。一个好处是你需要做更少的工作来获得你想要的结果。

如果您也在项目上存储类别名称,则不需要特殊索引来处理第一个列表,只需查询项目和 return 您需要的内容。对于第二个列表,您需要在按类别分组的项目实体上创建一个 Map/Reduce 索引。

但是,如果您需要使用您提供的数据结构,有几种方法可以解决这个问题。首先,确实不建议在索引定义中使用 LoadDocument,尤其是在 select 语句中。这可能会对索引性能产生负面影响。

相反,只需索引您需要查询的属性(或使用自动索引),然后使用结果转换器从相关文档中获取信息:

public class ItemCategoryTransformer : AbstractTransformerCreationTask<Item>
{
    public ItemCategoryTransformer()
    {
        TransformResults = results => from item in results
                                      let category = LoadDocument<Category>(item.CategoryId)
                                      select new ItemCategoryViewModel
                                      {
                                          Name = item.Name,
                                          CategoryName = category.Name
                                      };
    }
}

public class ItemCategoryViewModel
{
    public string Name { get; set; }
    public string CategoryName { get; set; }
}

您可以将此转换器与对项目实体的查询一起使用:

using (var session = documentStore.OpenSession())
{
    var items = session.Query<Item>()
                        .TransformWith<ItemCategoryTransformer, ItemCategoryViewModel>()
                        .ToList();
}

至于第二个列表,仍然使用你的数据结构,你必须使用一些东西。首先,项目的 Map/Reduce 索引,按 CategoryId:

分组
public class Category_Items_Count : AbstractIndexCreationTask<Item, Category_Items_Count.Result>
{
    public class Result
    {
        public string CategoryId { get; set; }
        public int Count { get; set; }
    }

    public Category_Items_Count()
    {
        Map = items => from item in items

            select new Result
            {
                CategoryId = item.CategoryId,
                Count = 1
            };

        Reduce = results => from result in results
            group result by result.CategoryId
            into c
            select new Result
            {
                CategoryId = c.Key,
                Count = c.Sum(x => x.Count)
            };
    }
}

但是由于您在 Item 实体上只有 CategoryId,因此您必须使用与第一个列表中类似的转换器:

public class CategoryItemsCountTransformer : AbstractTransformerCreationTask<Category_Items_Count.Result>
{
    public CategoryItemsCountTransformer()
    {
        TransformResults = results => from result in results
            let category = LoadDocument<Category>(result.CategoryId)
            select new CategoryItemsCountViewModel
            {
                CategoryName = category.Name,
                NumberOfItems = result.Count
            };
    }
}

public class CategoryItemsCountViewModel
{
    public string CategoryName { get; set; }
    public int NumberOfItems { get; set; }
}

最后,像这样查询:

using (var session = documentStore.OpenSession())
{
    var items = session.Query<Category_Items_Count.Result, Category_Items_Count>()
                       .TransformWith<CategoryItemsCountTransformer, CategoryItemsCountViewModel>()
                       .ToList();
}

如您所见,根据您使用的数据结构不同,所需的工作也大不相同。如果您直接将类别名称存储在项目实体上,则不需要任何结果转换器来获得您想要的结果(您只需要一个 Map/Reduce 索引)。

但是,Result Transformers 是在服务器端执行的,并且只在请求时执行,而不是在每次索引发生时执行的索引内部使用 LoadDocument。此外,也许为什么不建议在索引定义中使用 LoadDocuments,索引中使用 LoadDocument 引用的文档的每次更改都将导致必须重写该索引。这可能会导致索引引擎的大量工作。

最后,回答关于为什么在查询时出现异常的最后一个问题:因为索引的实际 return 类型是被索引的文档(在本例中为 Item)。要使用其他东西,您需要将结果投射到其他东西上。这可以通过在查询中使用“.As()”来完成:

Item_WithCategory.Result[] all;
using (var session = DocumentStoreHolder.Store.OpenSession())
{
    all = session.Query<Item_WithCategory.Result, Item_WithCategory>()
        .As<Item_WithCategory.Result>()         
        .ToArray();
}

长post,但希望对您有所帮助!