6

JSON で次のように、RavenDb に格納されている 2 つの異なるオブジェクト タイプがあります。これらは親子タイプの関係です。

Account/1
{        
    "Name": "Acc1",
}

Items/1
{
    "Account": "Account/1",
    "Value" : "100",
    "Tags": [
       "tag1",
       "tag2"]
}

Items/2
{
    "Account": "Account/1",
    "Value" : "50",
    "Tags": [
       "tag2"]
}

アカウントには何千ものアイテムがある可能性があるため、これらを同じドキュメントに保存したくないことに注意してください。

次のようなものを返す 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"
        }]
}

つまり、すべての個別のタグ名とそれぞれの数およびその値のリストです。

アカウントとアイテムのコレクションを一緒にマップするには、マルチマップを使用する必要があると思いますが、結果の「TagInfo」部分を作成するための縮小部分がわかりません。

これは可能ですか、それともレイヴンでこれをすべて間違ってモデリングしていますか?

編集:

このクエリから取得したいクラスは次のようになります。

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;}
}
4

2 に答える 2

6

1つのマップをタグに、もう1つをアカウントに配置する必要があるため、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
                                                    };
    }
}

その後、次のようにクエリを実行できます。

var results = session.Query<TagsWithCountAndValues.ReduceResult, TagsWithCountAndValues>()
    .Where(x => x.AccountId == "accounts/1")                        
    .ToList();
于 2012-05-02T19:14:32.367 に答える
2

OK、それで私はダニエルの答えに基づいて許容できる方法でこれを行う方法を考え出したので、将来の旅行者(おそらく私自身!)のためにここに記録します。

アカウントごとに1つの結果を返すように変更したので、アカウントとタグの組み合わせごとに1つの結果を返すように変更したため、インデックスを次のように変更する必要がありました(group byreduceは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,
                                                             };
    }
}

前と同じように、これをクエリするのは次のとおりです。

var results = session
    .Query<TagsWithCountAndValues.ReduceResult, TagsWithCountAndValues>()
    .ToList();

この結果は、メモリ内の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 })
                        };

これにより、アカウントごとに1つのオブジェクトが提供され、TagInfoオブジェクトの子リスト(一意のタグごとに1つ)が提供されます。

于 2012-05-10T19:27:32.303 に答える