子Typeの集約結果を条件に親Typeの検索


(Masatoshi Hiraoka) #1

お世話になります。

Elasticsearchの2.0がリリースされて、Pipeline Aggregationなどが使えるようになり、
Elasticsearch単体でかなりできることの幅が増えましたね!
先日2.1がcurrentになっていたのに今日見たら既に2.2になっておりました。。
追いつくのが大変です。。

さて、掲題の件ですが、お知恵をお借りしたく投稿しました。

あるIndex内にTypeが2つあり、それぞれ親子の関係を持っているとします。
親子間のリレーションとしては0 to manyであるとします。

PUT my-index
{
  "mappings": {
    "parent": {
      "properties": {
        "user_id": {
          "type": "string"
        }
      }
    },
    "child": {
      "_parent": {
        "type": "user"
      }, 
      "properties": {
        "value": {
          "type": "string"
        }
      }
    }
  }
}

上記のようなスキーマに対して

curl -XPUT localhost:9200/_bulk --data-binary "@request"; echo
{ "index": { "_index": "my-index", "_type": "parent", "_id": "1" }
{ "user_id": "hogehoge" }
{ "index": { "_index": "my-index", "_type": "parent", "_id": "2" }
{ "user_id": "fugahuga" }
{ "index": { "_index": "my-index", "_type": "child", "_id": "1", "_parent": "1", "_routing": "1" }
{ "value": 300 }
{ "index": { "_index": "my-index", "_type": "child", "_id": "2", "_parent": "1", "_routing": "1" }
{ "value": 100 }
{ "index": { "_index": "my-index", "_type": "child", "_id": "3", "_parent": "1", "_routing": "1" }
{ "value": 600 }
{ "index": { "_index": "my-index", "_type": "child", "_id": "1", "_parent": "2", "_routing": "2" }
{ "value": 300 }
{ "index": { "_index": "my-index", "_type": "child", "_id": "2", "_parent": "2", "_routing": "2" }
{ "value": 50 }
{ "index": { "_index": "my-index", "_type": "child", "_id": "3", "_parent": "2", "_routing": "2" }
{ "value": 100 }

このようなデータを入れたとき、同じ親Typeに紐づく子Typeのvalue値の合計が一定値以上の、
親TypeのDocumentを取得したい、といった場合、何か良い方法はありますでしょうか?

例えば、child.valueのsumが1000以上のparent.user_id(もしくは_idでも可)を取得したいなど。

has_child内でAggregationは使えませんし、子TypeでAggregationをかけた結果を検索に使いたくとも、
基本的にaggregationの結果はDocument Countしか返ってこず。。

もし何か良い解決方法等あれば、ご教示頂けますと幸いです。

どうぞ宜しくお願い致します。


(Jun Ohtani) #2

例えばですが、親のidがとれるこんな感じはどうでしょう?
mappingのvalueをstringからlongに変えましたが。

GET my-index/child/_search
{
  "size": 0, 
  "aggs": {
    "type_of_parent": {
      "terms": {
        "field": "_parent",
        "size": 0
      },
      "aggs": {
        "sum_of_values": {
          "sum": {
            "field": "value"
          }
        },
        "filtered_values": {
          "bucket_selector": {
            "buckets_path": {
              "sumValues": "sum_of_values"
            },
            "script": "sumValues >= 1000 "
          }
        }
      }
    }
  }
}

(Masatoshi Hiraoka) #3

@johtani

早速のご回答ありがとうございます!
すみません、毎度大変助かります。。

と、いただいた内容を拝見致しました!
_parentでaggregationをかける発想はなく、勉強になりました!

ご指摘の通り、一定の条件を満たす子をもつ親が取得できたのですが、
今回ユーザー毎のイベントトラッキング的なことを考えており、
例で挙げたparentがuser、childがeventになる形を想定しております。

その状態から、ある一定のイベントを発生させているユーザーを探したい、
というのはそもそものやりたいことでして、言葉が足らず、申し訳ありません。

上記から、userTypeに格納されるドキュメント数もかなりの数になることが見込まれ、
結果として返却されるBucketの数もかなり多くなってしまいます。
そうなると結果を処理するプログラム側などに無理が出てきそうな気がしていて、
何か良い方法がないかと悩んでいたところでした。。

aggregationの結果がページングできたらまだなんとかなりそうなのですがw

ともあれ、大変参考になりました!
ありがとうございます!


(Jun Ohtani) #4

例えば、ユーザもしくはイベントをある程度絞り込む条件を検索条件に加えるとかすれば、いいかと思いますがどうですか?
そうすれば、バケット数も小さくなり、メモリの使用量とかも減るかと。
数回投げる必要はあるかもしれませんが。


(Masatoshi Hiraoka) #5

@johtani

ご回答ありがとうございます!

そうですね、今できることとして、根本的に対応方法を変更するか、
絞り込みの条件をもう少し細かくした上で、複数回投げる 形で対応するか、
になりそうです。

諸々試してみます!

ご教示ありがとうございました!


(system) #6