2017-04-06 12 views
0

this question - get unique user within timespanの後に、指定したタイムパン内にのみ出現するユーザーを除外したいと思います。弾性検索ネスト。別のタイムスパンを除いた時間間隔内で集計結果を得る

例では、を2016年にのみ訪問したが、2017年には訪問しなかったユーザーのリストです。これは、ユーザが2017にも表示される可能性があるため、フィルタを2016回だけ使用した場合には当てはまりません。したがって、可能な試行は[2016..2017 users] - [2017 users]を設定することです。

私の裁判は、2つのクエリ([2016..2017 users][2017 users])をESに送信し、アプリケーションでuserList_20162017.Except(userList_2017)を使用して除外しました。

しかし、これは非常に非効率的なアプローチだと思います。私たちはElasticSearch NESTクエリでのみそれを達成できますか?

void Main() 
{ 
    var client = new ElasticClient(connectionSettings); 

    var twoYearsAgo = new DateTime(2016,1,1); 
    var yearAgo = new DateTime(2017,1,1); 

    // get 2016..2017 users 
    var searchResponse20162017 = client.Search<Visitor>(s => s 
     .Size(0) 
     .Query(q => q 
      .DateRange(c => c.Field(p => p.CreationDate) 
       .GreaterThan(twoYearsAgo) 
       .LessThan(DateeTime.UtcNow) 
      ) 
     ) 
     .Aggregations(a => a 
      .Terms("unique_users", c => c 
       .Field(f => f.OwnerUserId) 
       .Size(int.MaxValue) 
      ) 
     ) 
    ); 

    // get 2017 users 
    var searchResponse2017 = client.Search<Visitor>(s => s 
     .Size(0) 
     .Query(q => q 
      .DateRange(c => c.Field(p => p.CreationDate) 
       .GreaterThan(yearAgo) 
       .LessThan(DateeTime.UtcNow) 
      ) 
     ) 
     .Aggregations(a => a 
      .Terms("unique_users", c => c 
       .Field(f => f.OwnerUserId) 
       .Size(int.MaxValue) 
      ) 
     ) 
    ); 

    var uniqueUser20162017 = searchResponse20162017.Aggs.Terms("unique_users").Buckets.Select(b => b.KeyAsString).ToList(); 
    var uniqueUser2017 = searchResponse2017.Aggs.Terms("unique_users").Buckets.Select(b => b.KeyAsString).ToList(); 

    // Final result. seems so naïve and inefficient. 
    var uniqueUser2016Only = searchResponse20162017.Except(searchResponse2017); 

} 

答えて

1

これはfilter sub aggregationで行うことができます。最初にtermsアグリゲーションを使用して範囲2016と2017の一意のIDを取得し、次にこれにサブ集計を実行して2017の範囲にないIDを取得します。termsアグリゲーションのドキュメントカウントがドキュメントフィルタ集約カウント、これが唯一の2016年のIDなく、ここ2017

であることのためにドキュメントカウントとして、

void Main() 
{ 
    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200")); 
    var defaultIndex = "examples"; 
    var connectionSettings = new ConnectionSettings(pool) 
     .DefaultIndex(defaultIndex); 

    var client = new ElasticClient(connectionSettings); 

    if (client.IndexExists(defaultIndex).Exists) 
     client.DeleteIndex(defaultIndex); 

    var examples = new[]{ 
     new Example(1, new DateTime(2016, 01, 01)), 
     new Example(1, new DateTime(2017, 01, 01)), 
     new Example(2, new DateTime(2016, 01, 01)), 
     new Example(3, new DateTime(2017, 01, 01)), 
    }; 

    client.Bulk(b => b 
     .IndexMany(examples) 
     .Refresh(Refresh.WaitFor)); 

    client.Search<Example>(s => s 
     .Size(0) 
     .Query(q => +q 
      .DateRange(c => c.Field(p => p.Date) 
       .GreaterThanOrEquals(new DateTime(2016, 01, 01)) 
       .LessThan(new DateTime(2018, 01, 01)) 
      ) 
     ) 
     .Aggregations(a => a 
      .Terms("ids_in_2016_and_2017", c => c 
       .Field(f => f.ExampleId) 
       .Size(int.MaxValue) 
       .Aggregations(aa => aa 
        .Filter("ids_only_in_2016", f => f 
         .Filter(ff => +!ff 
          .DateRange(d => d 
           .Field(p => p.Date) 
           .GreaterThanOrEquals(new DateTime(2017, 01, 01)) 
           .LessThan(new DateTime(2018, 01, 01)) 
          ) 
         ) 
        ) 
       ) 
      ) 
     ) 
    ); 
} 

public class Example 
{ 
    public Example(int exampleId, DateTime date) 
    { 
     ExampleId = exampleId; 
     Date = date; 
    } 

    public int ExampleId { get; set; } 

    public DateTime Date { get; set; } 
} 

ExampleId 2のみが2016年ではなく2017年にある例です2016年と2017年は2016年のドキュメント数と同じです。

{ 
    "took" : 10, 
    "timed_out" : false, 
    "_shards" : { 
    "total" : 5, 
    "successful" : 5, 
    "failed" : 0 
    }, 
    "hits" : { 
    "total" : 4, 
    "max_score" : 0.0, 
    "hits" : [ ] 
    }, 
    "aggregations" : { 
    "ids_in_2016_and_2017" : { 
     "doc_count_error_upper_bound" : 0, 
     "sum_other_doc_count" : 0, 
     "buckets" : [ 
     { 
      "key" : 1, 
      "doc_count" : 2, 
      "ids_only_in_2016" : { 
      "doc_count" : 1 
      } 
     }, 
     { 
      "key" : 2, 
      "doc_count" : 1, 
      "ids_only_in_2016" : { 
      "doc_count" : 1 
      } 
     }, 
     { 
      "key" : 3, 
      "doc_count" : 1, 
      "ids_only_in_2016" : { 
      "doc_count" : 0 
      } 
     } 
     ] 
    } 
    } 
} 

* OP添付:userIdのリストを取得するための結果。

var list = searchResponse1.Aggs.Terms("ids_in_2016_2017").Buckets 
       .Select(o => new { 
        UserId = o.Key, 
        DocCount = o.DocCount == ((Nest.SingleBucketAggregate)o.Aggregations["ids_only_in_2016"]).DocCount 
       }) 
       .Where(x => x.DocCount == true) 
       .Select(x => x.UserId) 
       .ToList(); 
+0

ありがとうございます! :)クエリで '+ q'や'!+ ff'構文に関するドキュメントが見つかりません。私にそれのためのリンクを与えることができますか? – Youngjae

+1

@Youngjae 'bool'クエリドキュメントを見てください:https://www.elastic.co/guide/en/elasticsearch/client/net-api/5.x/bool-queries.html私は新しい次のような演算子について詳しく説明しているウェブサイトのドキュメントをご覧ください:https://github.com/elastic/elasticsearch-net/blob/enhancement/great-docs-update/docs/query-dsl/bool-dsl/bool-dsl.asciidoc –