hidekatsu-izuno 日々の記録

プログラミング、経済政策など伊津野英克が興味あることについて適当に語ります(旧サイト:A.R.N [日記])

elasticserch で全件データを取得する

elasticsearch で from/to を指定せずに検索したところ、なぜか10件しか取得できない。調べたところ、なんと to のデフォルト値は無制限ではなく 10 件となっている。

なるほど、それならばこの値をものすごく大きい数にすれば解決できなくはない。しかしながら、それを行うとHTTPのプロトコル的に巨大なJSONが返されるに違いなく一旦メモリに確保されてしまうようでは大問題だし、大きな数を指定するというのは適当すぎる。だからと言ってページ分割して問い合わせすると、結果の整合性が保証できない。本当にそんな方法しかないのだろうかと思っていたところ、やはり解決策が用意されていた。

stackoverflow.com

size defaults to 10, so you may also need &size=BIGNUMBER to get more than 10 items. (where BIGNUMBER equals a number you believe is bigger than your dataset)

BUT, elasticsearch documentation suggests for large result sets, using the scan search type.

ただし、この回答で書かれている search_type=scan という記述は現在では古いようで、最新版では以下のように書く必要がある。

curl -XGET 'localhost:9200/test/kens/_search?scroll=1m'

このクエリを発行すると scroll に指定した時間(上記の例だと1分間)検索結果のスナップショットが取られ、最初のページデータと共にそのIDが返される。

{
  "_scroll_id": "cXVlcnlU...zTUE7MDs=",
  "took":2,
  "timed_out":false,
  "_shards":{
    ...

このIDを使い

curl -XGET  'localhost:9200/_search/scroll'  -d'
{
    "scroll" : "1m", 
    "scroll_id" : "cXVlcnlU...zTUE7MDs=" 
}'

のような問い合わせを次々とおこなうことで、残りのデータを順次取得することができる。

時間が経てばこのスナップショットは自動的に消滅するものの、(仕組みを考えれば想像に固くないけれども)状態を保持するコストが大きいので、明示的に削除すべし、とドキュメントには書かれている。

curl -XDELETE  'localhost:9200/_search/scroll' -d '
{
    "scroll_id" : ["cXVlcnlU...zTUE7MDs="]
}'

HTTPはステートレスなプロトコルなので、SQLのフェッチのようなことを実現するのは難しいことを考えれば、こういう仕組みにならざるを得ないのだよなぁ、と思う反面、RDBMS では当たり前に実現できていたことでも REST インターフェイスだと案外面倒なものだな、と思ってしまった次第。