- 更新日: 2014年5月17日
- Elasticsearch
Elasticsearchのクエリとフィルターで簡単な検索を試す例
昨日、ElasticsearchのインストールとCSVからのデータ挿入 を行い、Elasticsearch で検索を行えるところまで作業しました。その続きで、簡単な検索を試してみます。
Elasticsearch の検索を試すには、Marvel の Sense を使うなど GUI から操作する方法と、ターミナルからコマンドで行う方法が利用できます。また Elasticsearch で検索を実行する方法として、Queries と Filters の2種類を使う方法が用意されています。
イメージを掴むために、さっそく Queries を使って検索を行ってみることにします。まずは、elasticsearch を起動。
1 2 3 |
$ elasticsearch |
コマンドでの検索
ターミナルから以下の REST API に curl コマンドで検索を実行できます。クエリもレスポンスも json 形式です。
1 2 3 4 5 6 7 8 9 10 11 12 |
$ curl -XGET 'http://localhost:9200/ldgourmet/restaurant/_search?pretty=true' -d ' { "query" : { "simple_query_string" : { "fields": ["name"], "query": "熊本 焼肉", "default_operator": "and" } } }' |
name フィールドを対象に「熊本 焼肉」で検索してみました。default_operator を and としていますので、「熊本 and 焼肉」の検索となり、name フィールドに「熊本」と「焼肉」の両方を含む結果を返します。
レスポンスは以下のようになります。2件ヒットしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
{ "took" : 8, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 2, "max_score" : 2.591806, "hits" : [ { "_index" : "ldgourmet", "_type" : "restaurant", "_id" : "466431", "_score" : 2.591806, "_source" : { "id": "466431", "name": "焼肉きんぐ 熊本近見店", "property": "", "alphabet": "", "name_kana": "やきにくきんぐ くまもとちかみてん", "pref_id": "43", "area_id": "531", ... |
Marvel Sense を使って GUI で検索
Marvel Sense を利用する場合は、ブラウザから以下の URL にアクセス。
http://localhost:9200/_plugin/marvel/sense/index.html
左側のボックスにクエリを次のように入力し、三角ボタンを押してクエリ実行。
1 2 3 4 5 6 7 8 9 10 11 12 |
GET /ldgourmet/_search { "query": { "simple_query_string": { "fields": ["_all"], "query": "熊本 焼肉", "default_operator": "and" } } } |
今回は、全てのフィールドを対象に検索するために fields に _all を指定しました。レスポンスは以下の通り。21件ヒットしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
{ "took": 10, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 21, "max_score": 1.1293696, "hits": [ { "_index": "ldgourmet", "_type": "restaurant", "_id": "326302", "_score": 1.1293696, "_source": { "id": "326302", "name": "焼肉 なべしま", "property": "熊本インター店(他多数)", "alphabet": "", "name_kana": "やきにくなべしま", "pref_id": "43", "area_id": "531", ... |
Queries と Filters
Elasticsearch で検索を実行する方法として、Queries(クエリ)と Filters(フィルター)の2種類が用意されています。それぞれ公式ドキュメントより翻訳して特徴を列挙しておきます。
・クエリは全文検索用に利用する。
・関連性のスコアに依存する結果を得たい時に利用する。
・フィルターはキャッシュされ、たいしてメモリも使わない。
・他のクエリが同じフィルターを利用する場合は爆速である。
・term, terms, prefix, range などのフィルターはデフォルトでキャッシュされ、同じフィルターが複数の異なるクエリで利用されるようなケースで推奨される。”age higher than 10″ のような range フィルターのケースなど。
・他の geo, scripts などのフィルターはフィールド値とともにメモリーにロードされ、デフォルトではキャッシュされない。これらのフィルターは元々速いし、キャッシュするのに単に実行するより余計な処理が必要なため。
・残りの and, not, or などのフィルターは他のフィルターを操作するので、基本的にキャッシュされない。
・全てのフィルターは _cache 要素と _cache_key 要素を指定して明示的にキャッシュを操作できる。大きなフィルター作成時などに便利。
以上を読んだところ、次のようなケースではフィルターを使って検索条件を組み立てたほうが良いかと思われます。
・同じ検索条件を複数の異なるクエリで使いまわすようなケース
・単純にフィールド値の大小などで絞り込み検索を行うようなケース
・スコアリングに関係ない検索を行うようなケース
Marvel Sense でフィルターを試す例
address フィールドに対して「熊本」でフィルターをかけます。
1 2 3 4 5 6 7 8 9 10 |
GET /ldgourmet/restaurant/_search { "filter": { "term": { "address": "熊本" } } } |
レスポンス。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
{ "took": 50, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 821, "max_score": 1, "hits": [ { "_index": "ldgourmet", "_type": "restaurant", "_id": "216228", "_score": 1, "_source": { "id": "216228", "name": "地球旨いもの居酒屋 ブエノスアイレス", "property": "", "alphabet": "", "name_kana": "ちきゅううまいものいざかやぶえのすあいれす", "pref_id": "43", "area_id": "531", "station_id1": "9408", "station_time1": "4", "station_distance1": "301", "station_id2": "9409", "station_time2": "5", "station_distance2": "373", "station_id3": "9406", "station_time3": "6", "station_distance3": "456", "category_id1": "505", "category_id2": "0", "category_id3": "0", "category_id4": "0", "category_id5": "0", "zip": "〒860-0801", "address": "熊本市安政町5-8ラテンクラブビル1~4F", "north_latitude": "32.47.53.434", "east_longitude": "130.42.43.114", |
821件がヒットしました。ちなみにデフォルトでは、検索ヒットしたうちの10件の結果を返します。20件返したければ、size パラメータで指定します。
1 2 3 4 5 6 |
GET /ldgourmet/restaurant/_search { "size": 20, ... |
terms フィルターで “熊本” または “福岡” でフィルターする検索例。
1 2 3 4 5 6 7 8 9 10 |
GET /ldgourmet/restaurant/_search { "filter": { "terms": { "address": ["熊本", "福岡"] } } } |
レスポンス。4137件ヒットしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
{ "took": 15, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 4137, "max_score": 1, "hits": [ { "_index": "ldgourmet", "_type": "restaurant", "_id": "20084", "_score": 1, "_source": { "id": "20084", "name": "よし川", "property": "", "alphabet": "", ... |
フィルターとクエリを組み合わせて検索
では、最後に熊本の焼肉屋さんで、ランチ営業を行っているお店を検索してみます。熊本、ランチ営業の2つの検索条件は filter で and を使いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
GET /ldgourmet/restaurant/_search { "query" : { "term" : { "name" : "焼肉" } }, "filter": { "and" : [ { "term": { "address": "熊本" } }, { "term": { "open_lunch": 1 } } ] } } |
レスポンス。5件ヒットしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
{ "took": 5, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 5, "max_score": 1.878558, "hits": [ { "_index": "ldgourmet", "_type": "restaurant", "_id": "463353", "_score": 1.878558, "_source": { "id": "463353", "name": "焼肉 さぼたん", "property": "", "alphabet": "", "name_kana": "やきにく さぼたん", "pref_id": "43", "area_id": "531", ... |
クエリとフィルターは以上のような使い方となります。実践ではもっと沢山フィルターを複数組み合わせたり、様々なクエリとともに用いることになると思います。クエリもフィルターも数十種類ずつ用意されていますので、柔軟に検索条件を組み立てることができるはずです。詳細は公式ドキュメントをご参照お願いします。親子テーブルの検索や、関連性スコアを使った検索・ソートについては末尾の参考リンクが詳しいです。
簡単ではありますが、Elasticsearch のクエリとフィルターを使って検索を行う例でした。以上で Elasticsearch の基本は押さえられたと思います。次回は、MySQL データベースから Elasticesearch へデータをインポートするために、JDBC River Plugin を使ってみたいと思います。
- – 参考リンク –
- Elasticsearchチュートリアル – 不可視点
- 実践!Elasticsearch – Wantedly Engineer Blog
- ElasticSearch 検索方法まとめ – Qiita
- Elasticsearch の関連記事
- CentOS6にElasticsearchをインストールしMySQLからデータをインポート
- Rails で jQuery を使って Elasticsearch 全文検索による検索文字をハイライトさせる
- elasticsearch-ruby でトークナイザーを指定してトークン分割
- elasticsearch-ruby で外部入力から検索時の json 用文字列のエスケープ処理
- Elasticsearch を Ruby から使う
- ElasticsearchにMySQLからデータ挿入、JDBC River Pluginのインストールと使い方
- ElasticsearchのインストールとCSVからのデータ挿入
Leave Your Message!