もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

ElasticSearchでドキュメントの配列に、追加・削除を行いたい

はじめに

こんばんは。

今回はElasticSearchです。

保存されたドキュメントの配列に、追加・削除を行いたいという場面がありまして、なかなかうまく行かず、試行錯誤した備忘録です。

参考サイト

本題

サンプルデータ

{
  "shop_id": 10000,
  "items": [
    {
      "item_id": 100,
      "name": アイテム100,
      "price": 500,
    },
    {
      "item_id": 200,
      "name": アイテム200,
      "price": 1000,
    },
    {
      "item_id": 300,
      "name": アイテム300,
      "price": 1500,
    },
    {
      "item_id": 400,
      "name": アイテム400,
      "price": 2000,
    },
    {
      "item_id": 500,
      "name": アイテム500,
      "price": 2500,
    },
    {
      "item_id": 600,
      "name": アイテム600,
      "price": 3000,
    },
  ]
},
{
  "shop_id": 20000,
  "items": [
    {
      "item_id": 100,
      "name": アイテム100,
      "price": 500,
    },
    {
      "item_id": 200,
      "name": アイテム200,
      "price": 1000,
    },
    {
      "item_id": 700,
      "name": アイテム700,
      "price": 3500,
    },
  ]
},

削除

shop_id: 10000 のitemsのなかの、item_id: 300item_id: 600 のものだけを消したいということあると思います。

その場合、以下のような形で、削除できました!

POST _update_by_query

"query": {
    "bool": {
        "must": [
            {
                "term": {
                    "shop_id": 10000
                }
            }
        ]
    }
},
"script": {
    "lang": "painless",
    "inline": "for (item_id in params.item_ids) { ctx._source.items.removeIf(item -> item.item_id.toString().contains(item_id)) }",
    "params"': {
        "item_ids" => [
            "300",
            "600"
        ],
    }
}

removeIfでループしながら処理して、itemsに入っているitem_idと合致するかを調べて、合致すれば削除されます。

追加

shop_id: 20000 のitemsに新たに item_id: 600 を追加します。

POST _update_by_query

query": {
    "bool": {
        "must": [
            {
                "term": {
                    "shop_id": 10000
                }
            }
        ]
    }
},
"script": {
    "lang": "painless",
    "inline": "ctx._source.items.addAll(params.items)",
    "params"': {
        "items" => [
            {
              "item_id": 600,
              "name": アイテム600,
              "price": 3000,
            }
        ],
    }
}

本来は、データがあれば更新、データがなければ追加みたいな感じのことをやりたかったので、scriptでループ処理をして対応しようと思っていたのですが、 Painless script loop maximum number of statements limit とエラーがでてしまいまして、それならばということで、存在していれば一旦削除して、その後対象を追加していくという方式にしました。

僕の場合、上記の理由で、1リクエストで削除・追加を行うので、 conflicts=proceed をつけないと、 version_conflict_engine_exception でバージョン違いになります。

また、順番的に削除が先に行われるので、削除の方では、 refresh=wait_for もつけることで今回のやりたいことを実現してます。

終わりに

かなり手こずりましたが、なんとかなりました。

現場からは以上です。