🦁

mappingにてとあるfieldにmulti fieldを追加をした後、partial reindexではなぜ期待する挙動が得られないか

2023/12/24に公開

はじめに

こんにちは!GMOペパボでwebアプリケーションエンジニアをしている、kazuです。

本記事は、GMOペパボの2023年度のアドベントカレンダー21日目の記事です。
https://adventar.org/calendars/8634

結論

Elasticsearchにおいて、既存のmappingにあるfieldに対して、multi fieldを追加した場合、documentについて再index(bulk_indexなど)する必要がある。updateではmappingの変更は反映されない。

https://github.com/ankane/searchkick/blob/74fd743f47eea99bfb33cf9339dccfc8ca4f5fc0/lib/searchkick/index.rb#L158-L164

前提

を使って検索機能を実装しているECサイトを前提とします。

そして、当ECサイトにおいて、商品のデータを格納するindexがあるとして、もともと以下のようなmapping定義があったとします。

{
  "mappings": {
    "properties": {
      "id": {
        "type": "integer"
      },
    }
  }
}

そこで、新たに、このidというfieldに対して、multi fieldを追加して、検索時にはtextとして検索できるようにしたいとなった時には、以下のようなmapping定義を追加します。(multi fieldの追加にはupdate mapping APIが便利です)

これは、idとしてはinteger型で格納するが、id.analyzedとしてはtext型で格納するという定義です。

{
  "mappings": {
    "properties": {
      "id": {
        "type": "integer",
        "fields": {
          "analyzed": {
            "type": "text"
          }
        }
      },
    }
  }
}

起こっていた問題

mapping定義を追加した後、partial reindexを用いて、idfieldについてのみ、index内の全てのdocumentを更新しようとしました。これは、特定のfieldについてのみ、documentを更新することができる機能です。

class Product < ApplicationRecord
  def search_data
    {
      name: name,
      category: category
    }.merge(prices_data)
  end

  def prices_data
    {
      price: price,
      sale_price: sale_price
    }
  end
end
Product.reindex(:prices_data)

私は、mapping更新後、idfieldについてのみ、partial reindexを使って、すべてのdocumentを更新すれば、期待通り、検索時にはtextとして検索できるようになると考えていました。しかし、実際には、検索は機能しなかったのです。

なぜpartial reindexではmapping更新後のmapping定義が反映されないのか

これは、partial reindexでは、searchkickの内部実装として、updateが最終的に実行されるためです。

# 例えば、以下のようにして、idについてのみ、partial reindexを実行した場合
Product.reindex(:id)

https://github.com/ankane/searchkick/blob/74fd743f47eea99bfb33cf9339dccfc8ca4f5fc0/lib/searchkick/model.rb#L30C9-L32C50

https://github.com/ankane/searchkick/blob/74fd743f47eea99bfb33cf9339dccfc8ca4f5fc0/lib/searchkick/index.rb#L232-L239

https://github.com/ankane/searchkick/blob/74fd743f47eea99bfb33cf9339dccfc8ca4f5fc0/lib/searchkick/index.rb#L265-L267

https://github.com/ankane/searchkick/blob/74fd743f47eea99bfb33cf9339dccfc8ca4f5fc0/lib/searchkick/relation_indexer.rb#L44-L46

https://github.com/ankane/searchkick/blob/74fd743f47eea99bfb33cf9339dccfc8ca4f5fc0/lib/searchkick/record_indexer.rb#L52-L54

https://github.com/ankane/searchkick/blob/74fd743f47eea99bfb33cf9339dccfc8ca4f5fc0/lib/searchkick/record_indexer.rb#L88-L105

つまり、partial reindexとはつまり、method_nameを指定して、documentをreindexしていくものであり、method nameがあるとき、index.bulk_updateが実行されるということが上からわかります。

では、bulk_updateとbulk_indexの差異

ドキュメントに書いてあるが、updateは、documentの単なる更新を行うものであり、indexは、documentの追加(既にあれば更新し、バージョンをインクリメントする)を行うものである。

mappingを更新した後は、単なるupdateでは、変更前のmappingで更新されてしまう?ようで、変更後のmappingで更新するためには、indexを使う必要があるようです。

GitHubで編集を提案

Discussion