4

いくつかの作業項目を表す一連のドキュメントがあります。

public class WorkItem
{
    public string Id {get;set;
    public string DocumentId { get; set; }
    public string FieldId { get; set; }
    public bool IsValidated { get; set; }
}

public class ExtractionUser
{
    public string Id {get;set;}
    public string Name {get;set;}
    public string[] AssignedFields {get;set;}
}

ユーザーは一連の FieldId にアクセスできます。この一連のフィールドに基づいて WorkItem をクエリし、ドキュメントごとにステータスを取得する必要があります。

public class UserWorkItems
{
    public string DocumentId { get; set; }
    public int Validated { get; set; }
    public int Total { get; set; }
}

私が後にしているクエリは次のとおりです。

using (var session = RavenDb.OpenSession())
{
    string[] userFields = session.Load<User>("users/1").Fields;
    session.Query<WorkItem>()
        .Where(w => w.FieldId.In(userFields))
        .GroupBy(w => w.DocumentId)
        .Select(g => new
        {
            DocumentId = g.Key,
            Validated = g.Where(w => w.IsValidated).Count(),
            Total = g.Count()
        }).Skip(page * perPage).Take(perPage)
        .ToArray();
}

Map/Reduce インデックスを作成しようとしましたが、主な問題は、カウントされるプロパティであるため、Reduce 出力に含まれない FieldId にフィルターを適用できるようにする必要があることでした。

また、クエリ部分の FieldId と TransformResults で単純な Map インデックスを実行して GroupBy を実行しようとしましたが、TransformResults の前にページングが適用されるため、グループ化前のドキュメントがページと合計に反映されますが、これは良くありません。

次に、ユーザーとそのフィールド コレクションをマップし、ワークアイテムとフィールドをマップするマルチ マップ インデックスを使用して、結果を希望どおりに縮小しようとしました。インデックス定義で要点を作成しました。reduce 部分には、group by フィールド、複数の SelectMany、および最後の GroupBy と Select が含まれます。インデックスは raven によって受け入れられましたが、結果が返されません。実際にデバッグする方法がわからないので、マルチ マップ インデックスに少し行き詰まっています。

最終的に、私の問題は「削減された」フィールドでクエリを実行する方法に削減される可能性があると思います。

このような機能を実現する方法はありますか? Map/MultiMap/Reduce/TransformResults 以外に探索できるオプションはありますか?

更新: Ayende の Map Reduce の投稿を読んでいるときに、mapreduce に間違ってアプローチしていることに気付きました。まだ解決策を探しています...

UPDATE 2:もう少し調査した後、私はやりたいことのように見えるがデータを返さないこのインデックスになりました(インデックスはスタジオで直接定義されました):

地図:

from user in docs
where user["@metadata"]["Raven-Entity-Name"] == "ExtractionUsers"
from field in user.AssignedFields
from item in docs
where item["@metadata"]["Raven-Entity-Name"] == "WorkItems" && item.FieldId == field
select new {
    UserId = user.Id,
    DocumentId = item.DocumentId,
    Validated = item.Status=="Validated"? 1: 0,
    Count = 1
}

減らす:

from r in results
group r by new { r.UserId , r.DocumentId } into g
select new {
    UserId = g.Key.UserId,
    DocumentId = g.Key.DocumentId,
    Validated = g.Sum(d => d.Validated),
    Count = g.Sum(d => d.Count),
}

アイデアは、すべてのドキュメントをインデックスにマップし、Users から Fields および WorkItems にリンクしようとすることです。

4

2 に答える 2

3

1週間後、私は問題を解決することができました。私は、シンプルでうまく機能しているように見える、少し異なる (あまり関係のない) アプローチを採用しました。他の誰かがこの種の問題を抱えている場合の詳細は次のとおりです。

WorkItem を DocumentId でグループ化し、Validated フィールドと NonValidated フィールドをコレクションに入れます。map reduce の結果は次のようになります。

public class Result
{
    public string DocumentId { get; set; }
    public string[] ValidatedFields { get; set; }
    public string[] ReadyFields { get; set; }
}

Map関数は次のようになります。

Map = items => items.Select(i => new
{
    DocumentId = i.DocumentId,
    ValidatedFields = i.IsValidated ? new string[] { i.FieldId } : new string[0],
    ReadyFields = !i.IsValidated ? new string[] { i.FieldId } : new string[0]
});

そしてReduce :

Reduce = result => result
    .GroupBy(i => i.DocumentId)
    .Select(g => new
    {
        DocumentId = g.Key,
        ValidatedFields = g.SelectMany(i => i.ValidatedFields),
        ReadyFields = g.SelectMany(i => i.ReadyFields)
    });

インデックスをクエリするには、次の式を使用します。

User user = session.Load<User>("users/1");
var result = session.Query<WorkItem, UserWorkItemIndex>()
    .As<UserWorkItemIndex.Result>()
    .Where(d => d.ValidatedFields.Any(f => f.In(user.AssignedFields)))
    .ToArray();

クライアント側で行う必要があるのは、ユーザーに属するフィールドのみをカウントすることだけです。

解決策の要点もあります。

于 2012-11-10T15:27:21.520 に答える
0

まず、免責事項:実際のシステムで RavenDB を使用したことはありませんが、いくつかの記事を読み、いくつかのビデオを見て、その背後にあるアイデアが本当に好きです。私はこの問題を面白い練習問題だと思いました。したがって、このアプローチは理想的ではない可能性があります。コメントと改善を歓迎します。

私の考えではWorkItems、これらのフィールドを含めるには、コレクションにインデックスを作成する必要があります。

  • DocumentId(これは最終的にグループ化するためです)
  • FieldId(これがフィルター処理するフィールドであるため)
  • ValidatedCountIsValidated( = trueを持つレコードの数)
  • TotalCount

このインデックスを作成した後、.Where(x => x.FieldId.In(userFields))フィルターを使用してクエリを実行し、上記の構造を持つ一連の結果を取得できます。

最終結果を得るには、これらの結果をさらにグループ化する必要がありDocumentIdます。

私が思いついたコードはこれです:

インデックスの定義

public class WorkItems_ValidationStatistics :
    AbstractIndexCreationTask<WorkItem, WorkItems_ValidationStatistics.ReduceResult>
{
    public class ReduceResult
    {
        public string DocumentId { get; set; }
        public string FieldId { get; set; }
        public int ValidatedCount { get; set; }
        public int TotalCount { get; set; }
    }

    public WorkItems_ValidationStatistics()
    {
        Map = workItems =>
              from workItem in workItems
              select new
                    {
                        workItem.DocumentId,
                        workItem.FieldId,
                        ValidatedCount = workItem.IsValidated ? 1 : 0,
                        TotalCount = 1
                    };

        Reduce = results =>
                 from result in results
                 group result by new { result.FieldId, result.DocumentId }
                     into g
                     select new
                        {
                            g.Key.DocumentId,
                            g.Key.FieldId,
                            ValidatedCount = g.Sum(x => x.ValidatedCount),
                            TotalCount = g.Sum(x => x.TotalCount)
                        };
    }
}

データベースにインデックスを作成するコード:

public void CreateIndex()
{
    using (var store = CreateDocumentStore())
    {
        IndexCreation.CreateIndexes(
            typeof(WorkItems_ValidationStatistics).Assembly, store);
    }
}

注:または、RavenDB Management Studio でインデックスを直接作成することもできます。

インデックスをクエリし、最終的な集計を行うコード:

public void GetWorkItemStatisticsGroupedByDocumentId()
{
    using (var store = CreateDocumentStore())
    using (var documentSession = store.OpenSession())
    {
        var userFields = new[] { "fields/11", "fields/13" };

        var items = documentSession
            .Query<WorkItems_ValidationStatistics.ReduceResult, WorkItems_ValidationStatistics>()
            .Where(x => x.FieldId.In(userFields))
            .ToList();

        var results = items
            .GroupBy(x => x.DocumentId)
            .Select(g => new
                {
                    DocumentId = g.Key,
                    ValidatedCount = g.Sum(x => x.ValidatedCount),
                    TotalCount = g.Sum(x => x.TotalCount)
                });

        foreach (var r in results)
        {
            Console.WriteLine("DocId={0}: validated: {1}/{2}", 
                r.DocumentId, r.ValidatedCount, r.TotalCount);
        }
    }
}
于 2012-11-06T16:45:00.440 に答える