UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【Elasticsearch】配列のフィールドから指定した要素数だけ取得する方法

結論、あまりよいやり方に思えないが、とりあえずやりたいことはできたので書き残しておく

概要

ElasticSearchの一つのドキュメントの中に画像URLが複数格納された配列のフィールドがあったとする

   ...
   images: [
      "http://some-site/path/to/image/aaaaa.png,
      "http://some-site/path/to/image/bbbb.png,
      "http://some-site/path/to/image/cccc.png,
      "http://some-site/path/to/image/dddd.png,
      "http://some-site/path/to/image/ffff.png,
      ...
   ],

この時、特に上限を設けずに格納(例えば数十件格納)していた場合、このドキュメントを取得しようとするとレスポンスのサイズが膨大になってしまう(画像URLのパスはとても長いことが多いので特に)

しかも、取得したものの、使用する側で2要素分だけでよい場合はそのほとんどが無駄になってしまう

そこで、指定した要素数だけ取得する方法を探ってみた

対応案:script_fields を使う

script_fieldsはフィールドの値をつかった計算結果を別の新しいフィールドとして定義することのできる機能

この機能を使ってimagesの2要素分を抜き出して別のフィールドとして定義すればよいのでは?

と考えたが綺麗にできず、、

以下のようなスクリプトになってしまった

{
  "script_fields": {
    "images": {
      "script": {
        "source": """
        def arr = [];
        def _0 = doc['images'][0];
        def _1 = doc['images'][1];
        if (_0 != "") {
          arr = [_0];
        }
        if (_1 != "") {
          arr = [_0, _1];
        }
        return arr;
        """
      }
    }
  }
}

考えたけどできなかったこと

slice

JavaScriptのsliceのような関数で doc['images'].slice(0, 2)を考えたけどそんな機能はなかった

for文ループ

2要素なら我慢ができるが、要素数が増えたら大変なので for文を考えたが、2つの点であきらめた

  1. ループするごとにインクリメントされる変数 i に対して、
    doc['images'][i]と記述するとシンタックスエラーになる
  2. ループ内で配列に要素を格納する(pushする)方法がわからない
    +=でもダメだった

わかればループを使いたい

まとめ

ここまでして、取得する要素数を絞るべきかは悩ましいのでレスポンスサイズがボトルネックになったら考える

※参考

https://www.elastic.co/guide/en/elasticsearch/reference/7.x/search-request-body.html#request-body-search-script-fields