需要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();
}
但它会抛出异常,说明返回类型是项目,而不是结果。如何解决?
答案 0 :(得分:1)
你有几个选择。您可以在Item实体上存储CategoryId和CategoryName。这当然会导致重复的数据(如果你仍然需要存储类别实体),但“存储很便宜”这些天是一个流行的术语。这样做的缺点是你需要更新给定类别的每个Item文档如果类别名称更改以保持一致。好处是您需要做更少的工作才能获得理想的结果。
如果您在项目上存储类别名称,则不需要特殊索引来处理第一个列表,只需查询项目并返回所需内容。对于第二个列表,您需要在类别上分组的Item实体上创建Map / Reduce索引。
但是,如果您需要使用您提供的数据结构,有几种方法可以解决这个问题。首先,我们不建议在索引定义中使用LoadDocument,尤其是在select语句中。这可能会以负面的方式影响索引性能。
相反,只需索引您需要查询的属性(或使用自动索引),然后使用Result Transformer从相关文档中获取信息:
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; }
}
您可以将此Transformer与Item实体的查询一起使用:
using (var session = documentStore.OpenSession())
{
var items = session.Query<Item>()
.TransformWith<ItemCategoryTransformer, ItemCategoryViewModel>()
.ToList();
}
至于第二个列表,仍在使用您的数据结构,您必须使用一些东西。首先,对Items进行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();
}
正如您所看到的,根据您使用的数据结构,所需的工作存在很大差异。如果您直接在Item实体上存储了Category Name,则不需要任何Result Transformers来实现您所获得的结果(您只需要Map / Reduce索引)。
但是,结果转换器是在服务器端执行的,只在请求时执行,而不是在每次索引发生时执行的索引中使用LoadDocument。此外,也许为什么不建议在索引定义中使用LoadDocuments,对索引中使用LoadDocument引用的文档的每次更改都将导致必须重写该索引。这可能会导致索引引擎的大量工作。
最后,回答你关于为什么在查询时遇到异常的最后一个问题:因为索引的实际返回类型是被索引的文档(在本例中为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();
}
很长的帖子,但希望它有所帮助!