Sim*_*mon 6 c# mapreduce ravendb
我有两种不同的对象类型存储在RavenDb中,它们是父/子类型关系,在JSON中是这样的:
Account/1
{
"Name": "Acc1",
}
Items/1
{
"Account": "Account/1",
"Value" : "100",
"Tags": [
"tag1",
"tag2"]
}
Items/2
{
"Account": "Account/1",
"Value" : "50",
"Tags": [
"tag2"]
}
Run Code Online (Sandbox Code Playgroud)
请注意,我不想将它们存储在同一文档中,因为一个帐户可能有数千个项目.
我正在尝试编写一个map/reduce索引,它会返回类似于:
{
"Account": "Acc1",
"TagInfo": [
{ "TagName" : "tag1",
"Count" : "1", //Count of all the "tag1" occurrences for acc1
"Value" : "100" //Sum of all the Values for acc1 which are tagged 'tag1'
},
{ "TagName" : "tag2",
"Count" : "2", //Two items are tagged "tag2"
"Value" : "150"
}]
}
Run Code Online (Sandbox Code Playgroud)
即所有不同标签名称的列表,以及每个标签名称的数量及其值.
我想我需要使用多地图将Account和Items集合映射到一起,但我无法找出reduce部分来创建结果的"TagInfo"部分.
这是可能的,还是我在Raven模拟这个错误?
编辑:
我想从这个查询中检索的类看起来像这样:
public class QueryResult
{
public string AccountId {get;set;}
public TagInfo Tags {get;set;}
}
public class TagInfo
{
public string TagName {get;set;}
public int Count {get;set;}
public int TotalSum {get;set;}
}
Run Code Online (Sandbox Code Playgroud)
您不能使用Multi Map/Reduce索引,因为您需要在标签上放置一个地图而在帐户上放置另一个地图.他们没有共同的属性,所以你不能在这里有多个地图/减少.
但是,您可以使用TransformResult.这是怎么做的:
public class Account
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Item
{
public string Id { get; set; }
public string AccountId { get; set; }
public int Value { get; set; }
public List<string> Tags { get; set; }
}
public class TagsWithCountAndValues : AbstractIndexCreationTask<Item, TagsWithCountAndValues.ReduceResult>
{
public class ReduceResult
{
public string AccountId { get; set; }
public string AccountName { get; set; }
public string Tag { get; set; }
public int Count { get; set; }
public int TotalSum { get; set; }
}
public TagsWithCountAndValues()
{
Map = items => from item in items
from tag in item.Tags
select new
{
AccountId = item.AccountId,
Tag = tag,
Count = 1,
TotalSum = item.Value
};
Reduce = results => from result in results
group result by result.Tag
into g
select new
{
AccountId = g.Select(x => x.AccountId).FirstOrDefault(),
Tag = g.Key,
Count = g.Sum(x => x.Count),
TotalSum = g.Sum(x => x.TotalSum)
};
TransformResults = (database, results) => from result in results
let account = database.Load<Account>(result.AccountId)
select new
{
AccountId = result.AccountId,
AccountName = account.Name,
Tag = result.Tag,
Count = result.Count,
TotalSum = result.TotalSum
};
}
}
Run Code Online (Sandbox Code Playgroud)
之后,您可以像这样查询:
var results = session.Query<TagsWithCountAndValues.ReduceResult, TagsWithCountAndValues>()
.Where(x => x.AccountId == "accounts/1")
.ToList();
Run Code Online (Sandbox Code Playgroud)
好的,所以我在丹尼尔的答案的基础上找到了一种以可接受的方式做到这一点的方法,所以我将在这里记录下来以供任何未来的旅行者(可能是我自己!)。
我从尝试为每个帐户返回一个结果更改为每个帐户/标签组合返回一个结果,因此索引必须更改如下(请注意,group by
在reduce
2 个属性上):
public class TagsWithCountAndValues : AbstractIndexCreationTask<Item, TagsWithCountAndValues.ReduceResult>
{
public class ReduceResult
{
public string AccountId { get; set; }
public string AccountName { get; set; }
public string TagName { get; set; }
public int TagCount { get; set; }
public int TagValue { get; set; }
}
public TagsWithCountAndValues()
{
Map = items => from item in items
from tag in item.Tags
select new ReduceResult
{
AccountId = item.AccountId,
TagName = tag,
TagCount = 1,
TagValue = item.Value
};
Reduce = results => from result in results
where result.TagName != null
group result by new {result.AccountId, result.TagName}
into g
select new ReduceResult
{
AccountId = g.Key.AccountId,
TagName = g.Key.TagName,
TagCount = g.Sum(x => x.TagCount),
TagValue = g.Sum(x => x.TagValue),
};
TransformResults = (database, results) => from result in results
let account = database.Load<Account>(result.AccountId)
select new ReduceResult
{
AccountId = result.AccountId,
AccountName = account.Name,
TagName = result.TagName,
TagCount = result.TagCount,
TagValue = result.TagValue,
};
}
}
Run Code Online (Sandbox Code Playgroud)
和以前一样,查询这个只是:
var results = session
.Query<TagsWithCountAndValues.ReduceResult, TagsWithCountAndValues>()
.ToList();
Run Code Online (Sandbox Code Playgroud)
然后,可以通过内存中 LINQ 查询将结果转换为我最初想要的对象。此时可以返回的结果数量相对较少,因此在客户端执行此操作很容易接受。LINQ 语句是:
var hierachicalResult = from result in results
group new {result.TagName, result.TagValue} by result.AccountName
into g
select new
{
Account = g.Key,
TagInfo = g.Select(x => new { x.TagName, x.TagValue, x.TagCount })
};
Run Code Online (Sandbox Code Playgroud)
这为我们提供了每个帐户一个对象,以及一个对象子列表TagInfo
- 每个唯一标签一个。