Ok, so I figured out a way to do this in an acceptable way, which is based on Daniel's answer, so I will write it down here for future travelers (maybe myself!).
I gave up trying to return one result for each account, to one result for each combination of accounts / tags, so the index should change as follows (note that group by
in reduce
has 2 properties):
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, }; } }
As before, requesting this is simple:
var results = session .Query<TagsWithCountAndValues.ReduceResult, TagsWithCountAndValues>() .ToList();
The result of this can then be converted to the object that I initially needed in the LINQ query in memory. At this stage, the number of results that can be returned will be relatively small, so doing this on the client side is easily acceptable. LINQ statement:
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 }) };
This gives us one object for each account with a child list of TagInfo
objects - one for each unique tag.