3

私は RavenDB にかなり慣れていないため、次の解決策を見つけるのに苦労しています。

次のような ServiceCalls というコレクションがあります。

public class ServiceCall
    {
        public int ID { get; set; }
        public string IncidentNumber { get; set; }
        public string Category { get; set; }
        public string SubCategory { get; set; }
        public DateTime ReportedDateTime { get; set; }
        public string Block { get; set; }
        public decimal Latitude { get; set; }
        public decimal Longitude { get; set; }
    }

次のような ServiceCalls/CallsByCategory という名前のインデックスがあります。

        Map = docs => from doc in docs
                      select new
                      {
                          Category = doc.Category,
                          CategoryCount = 1,
                          ServiceCalls = doc,
                      };
        Reduce = results => from result in results
                            group result by result.Category into g
                            select new
                            {
                                Category = g.Key,
                                CategoryCount = g.Count(),
                                ServiceCalls = g.Select(i => i.ServiceCalls)
                            };

したがって、出力は次のとおりです。

public class ServiceCallsByCategory
{
    public string Category { get; set; }
    public int CategoryCount { get; set; }
    public IEnumerable<ServiceCall> ServiceCalls { get; set; }
}

このクエリを使用すると、すべてが正常に機能します

var q = from i in session.Query<ServiceCallsByCategory>("ServiceCalls/CallsByCategory") select i

私が絶対に迷っているのは、ReportedDateTime によるクエリを可能にするインデックスを作成することです。これを可能にする何か:

    var q = from i in session.Query<ServiceCallsByCategory>("ServiceCalls/CallsByCategory")
            where i.ServiceCalls.Any(x=>x.ReportedDateTime >= new DateTime(2012,10,1)) 
            select i

どんなガイダンスでも大歓迎です。

4

2 に答える 2

4

いくつかのこと、

  1. .Count()reduce 句にメソッドを含めることはできません。よく見ると、計算が間違っていることがわかります。ビルド 2151 の時点で、これは実際に例外をスローします。代わりに、あなたが望むCategoryCount = g.Sum(x => x.CategoryCount)

  2. マップの構造は、reduce の構造と常に一致する必要があります。もののリストを作成する場合は、それぞれの単一要素配列をマップ.SelectMany()し、reduce ステップで使用する必要があります。あなたが今持っている方法は、おそらくある時点で修正される癖のためにのみ機能します.

  3. 結果を ServiceCalls のリストとして作成することにより、ドキュメント全体をインデックス ストレージにコピーします。それは非効率であるだけでなく、不必要です。IDだけのリストを保持する方がよいでしょう。Raven には、.Include()ドキュメント全体を取得する必要がある場合に使用できるメソッドがあります。ここでの主な利点は、インデックスの結果がまだ古い場合でも、返される各アイテムの最新のデータが保証されることです。

3 つすべてをまとめると、正しいインデックスは次のようになります。

public class ServiceCallsByCategory
{
    public string Category { get; set; }
    public int CategoryCount { get; set; }
    public int[] ServiceCallIds { get; set; }
}

public class ServiceCalls_CallsByCategory : AbstractIndexCreationTask<ServiceCall, ServiceCallsByCategory>
{
    public ServiceCalls_CallsByCategory()
    {
        Map = docs => from doc in docs
                      select new {
                                     Category = doc.Category,
                                     CategoryCount = 1,
                                     ServiceCallIds = new[] { doc.ID },
                                 };
        Reduce = results => from result in results
                            group result by result.Category
                            into g
                            select new {
                                           Category = g.Key,
                                           CategoryCount = g.Sum(x => x.CategoryCount),
                                           ServiceCallIds = g.SelectMany(i => i.ServiceCallIds)
                                       };
    }
}

インクルードを使用してクエリを実行すると、次のようになります。

var q = session.Query<ServiceCallsByCategory, ServiceCalls_CallsByCategory>()
               .Include<ServiceCallsByCategory, ServiceCall>(x => x.ServiceCallIds);

ドキュメントが必要な場合は、それをロードしますsession.Load<ServiceCall>(id)が、Raven はそれを取得するためにサーバーに往復する必要はありません。

NOW - 日付で結果をフィルタリングする方法についての質問には対応していません。そのためには、何を達成しようとしているのかを真剣に考える必要があります。上記のすべては、各カテゴリのすべてのサービス コールを一度に表示する必要があることを前提としています。ほとんどの場合、結果をページ分割する必要があるため、これは実用的ではありません。おそらく、私が上で説明したものを使いたくないでしょう。ここでいくつかの壮大な仮定を立てていますが、ほとんどの場合、グループ化ではなく、カテゴリ別にフィルタリングします。

カテゴリをカウントするだけのインデックスがあるとします (サービス コールのリストがない上記のインデックス)。これを使用して概要画面を表示できます。ただし、いずれかをクリックして詳細画面にドリルダウンするまでは、各カテゴリに含まれるドキュメントに興味を持つことはありません。その時点で、自分がどのカテゴリに属しているかがわかるので、それでフィルタリングして、静的インデックスのない日付範囲に絞り込むことができます。

var q = session.Query<ServiceCall>().Where(x=> x.Category == category && x.ReportedDateTime >= datetime)

私が間違っていて、すべてのカテゴリのすべてのドキュメントを表示し、カテゴリ別にグループ化し、日付でフィルタリングする必要がある場合は、この他の StackOverflow answer で説明したような高度な手法を採用する必要があります。これが本当に必要なものである場合は、コメントでお知らせください。あなたのために書くことができるかどうかを確認します. 動作させるには Raven 2.0 が必要です。

また、ReportedDateTime に保存する内容には十分注意してください。何らかの比較を行う場合は、暦時間瞬間時間の違いを理解する必要があります。カレンダー時間には、夏時間への移行、タイム ゾーンの違いなどの癖があります。瞬間的な時間は、誰が尋ねたとしても、何かが起こった瞬間を追跡します。おそらく、使用には瞬間的な時間が必要です。つまり、 UTC を使用するか、ローカルのコンテキスト値を失うことなく瞬間的な時間を表現できるようにDateTime切り替えることを意味します。DateTimeOffset

アップデート

私が説明した手法を使用して、カテゴリ グループにすべての結果を表示しながら、日付でフィルター処理できるインデックスを構築しようと試みました。残念ながら、それは不可能です。すべての ServiceCalls を元のドキュメントにグループ化し、マップで表現する必要があります。最初に削減する必要がある場合、まったく同じようには機能しません。したがって、特定のカテゴリに入ったら、ServiceCalls の単純なクエリを検討する必要があります。

于 2012-12-07T21:30:18.483 に答える
0

ReportedDateTime を Map に追加して、Reduce で集計していただけますか? カテゴリごとの最大値のみを気にする場合は、このようなもので十分です。

Map = docs => from doc in docs
                      select new
                      {
                          Category = doc.Category,
                          CategoryCount = 1,
                          ServiceCalls = doc,
                          ReportedDateTime
                      };
        Reduce = results => from result in results
                            group result by result.Category into g
                            select new
                            {
                                Category = g.Key,
                                CategoryCount = g.Sum(x => x.CategoryCount),
                                ServiceCalls = g.Select(i => i.ServiceCalls)
                                ReportedDateTime = g.Max(rdt => rdt.ReportedDateTime)
                            };

次に、集計された ReportedDateTime に基づいてクエリを実行できます。

var q = from i in session.Query<ServiceCallsByCategory>("ServiceCalls/CallsByCategory")
            where i.ReportedDateTime >= new DateTime(2012,10,1) 
            select i
于 2012-12-07T00:52:50.287 に答える