しおしお

IntelliJ IDEAのことなんかを書いてます

Elasticsearchで条件にマッチした部分を取得する

Elasticsearchで条件にマッチしたワードをハイライト表示したいなんてことありますよね。 これを実現するために、条件にマッチした部分を検索結果から取得する方法になります。

準備

インデックス内容

フィールドをひとつだけ持つインデックスを作成して確認してみます。

{
  "settings": {
    "number_of_shards": 1,
    "analysis": {
      "analyzer": {
        "kuromoji": {
          "type": "custom",
          "tokenizer": "kuromoji_tokenizer"
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "name": {
          "type": "text",
          "analyzer": "kuromoji"
        }
      }
    }
  }
}

データ投入

JavaのClientを使って、2つのドキュメントを投入します。

fun main() {
    createClient().use { client ->

        val bulkRequest = BulkRequest().apply {
            add(IndexRequest("test", "_doc").apply {
                source(mapOf("name" to "あいうえお かきくけこ"))
            })
            add(IndexRequest("test", "_doc").apply {
                source(mapOf("name" to "さしすせそ たちつてと"))
            })
        }
        val response = client.bulk(bulkRequest, RequestOptions.DEFAULT)
        println(response.status())
    }

}

検索条件にマッチした部分を取得してみる

検索クエリー

検索クエリーを投げる際の、リクエストボディにhighlightを追加してあげます。 また、highlightにはハイライト表示したい(条件にマッチした部分を知りたい)フィールド名を指定してあげます。

{
  "query": {
    "bool": {
      "filter": {
        "terms": {
          "name": ["あい", "たち"]
        }
      }
    }
  },
  "highlight": {
    "fields": {
      "name": {
      }
    }
  }
}

検索結果

検索結果の各ドキュメントごとに、highlightから検索条件にマッチした部分を取得できます。 デフォルトでは、emタグでマッチした部分が囲まれて返ってきます。

{
  "took": 8,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0.0,
    "hits": [
      {
        "_index": "test",
        "_type": "_doc",
        "_id": "_3tmSnoBBFTrBATTDpDa",
        "_score": 0.0,
        "_source": {
          "name": "あいうえお かきくけこ"
        },
        "highlight": {
          "name": [
            "<em>あい</em>うえお かきくけこ"
          ]
        }
      },
      {
        "_index": "test",
        "_type": "_doc",
        "_id": "AHtmSnoBBFTrBATTDpHa",
        "_score": 0.0,
        "_source": {
          "name": "さしすせそ たちつてと"
        },
        "highlight": {
          "name": [
            "さしすせそ <em>たち</em>つてと"
          ]
        }
      }
    ]
  }
}

JavaのClientで試してみる

検索用のリクエスト(SearchRequest)のhighlighterにハイライト表示したいフィールド名を指定してあげます。 検索結果からは、検索にヒットしたドキュメントごとhighlightFieldsからハイライト表示されたフラグメントが取得できます。

コード

fun main() {
    createClient().use { client ->

        val searchRequest = SearchRequest("test").apply {
            types("_doc")
            source(SearchSourceBuilder().apply {
                query(QueryBuilders.boolQuery().apply {
                    filter(QueryBuilders.termsQuery("name", "あい", "かき", "たち"))
                })
                highlighter(HighlightBuilder().apply {
                    field("name")
                })
            })
        }
        val response = client.search(searchRequest, RequestOptions.DEFAULT)
        println(response.status())
        response.hits.hits.forEach { 
            it.highlightFields.forEach {
                println("it = ${it.value}")
            }
        }
    }
}

実行結果

APIを直接呼び出したときと同じように、ハイライト表示対応されたフラグメントが取得できていることが確認できます。

it = [name], fragments[[<em>あい</em>うえお <em>かき</em>くけこ]]
it = [name], fragments[[さしすせそ <em>たち</em>つてと]]