Elasticsearch でスキーマを指定して空間検索をする

  • 9
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Elasticsearch でスキーマを定義してデータを投入する方法を試してみます。
Elasticsearch のインストールについてはコチラ
kuromoji のインストーラについてはコチラを参考にしてください。

Elasticsearch のスキーマについて

Elasticsearch ではスキーマを マッピング、型を タイプ という概念で扱います。
マッピングを定義していない場合は、投入されたデータの値からタイプを自動で判定して、マッピングされます。
これはとても便利な機能である反面、意図しない・不必要なインデクシングが発生してしまう可能性もあります。
安定した運用が求められるケースでは、しっかりとスキーマを定義したほうが良いですね。

マッピングの指定

今回はランドマークの「名前緯度軽度説明」を投入してみます。

項目 プロパティ名 説明
名前 name ランドマークの名前。全文検索対象外
緯度軽度 coord ランドマークの緯度軽度。空間検索の対象
説明 description ランドマークの説明。全文検索の対象

これをマッピング指定に用いる JSON で表現すると以下のようになります。

mapping.json
{
    "mappings": {
        "sample": {
            "properties": {
                "name": {
                    "type": "string",
                    "index": "not_analyzed"
                },
                "coord": {
                    "type": "geo_point"
                },
                "description": {
                    "type": "string",
                    "analyzer": "kuromoji"
                }
            }
        }
    }
}

この JSON を使って landmark というインデックスを作成します。

$ curl -X PUT http://localhost:9200/landmark --data-binary @mapping.json

意図通り定義されたかを確認してみます。

$ curl -X GET http://localhost:9200/landmark
{
    "landmark": {
        "aliases": {},
        "mappings": {
            "sample": {
                "properties": {
                    "coord": {
                        "type": "geo_point"
                    },
                    "description": {
                        "type": "string",
                        "analyzer": "kuromoji"
                    },
                    "name": {
                        "type": "string",
                        "index": "not_analyzed"
                    }
                }
            }
        },
        ...
    }
}

いい感じですね。

データの投入

前回同様、Bulk API を使ってデータを投入します。
以下の JSON を使います。

landmark.json
{ "index" : {} }
{ "name": "スカイツリー", "coord": { "lat": "35.710063", "lon": "139.8107"}, "description": "東京スカイツリー(とうきょうスカイツリー、英: TOKYO SKYTREE)は東京都墨田区押上一丁目にある電波塔(送信所)である。"}
{ "index" : {} }
{ "name": "BLUE NOTE TOKYO", "coord": { "lat": "35.661198", "lon": "139.716207"}, "description": "N.Y.の「Blue Note」を本店に持つジャズクラブとして、南青山にオープンしたブルーノート東京。ジャズをはじめとする多様な音楽ジャンルのトップアーティストたちが、連夜渾身のプレイを繰り広げている。"}
{ "index" : {} }
{ "name": "東京タワー", "coord": { "lat": "35.65858", "lon": "139.745433"}, "description": "東京タワー(とうきょうタワー、英: Tokyo Tower)は、東京都港区芝公園にある総合電波塔とその愛称である。正式名称は日本電波塔(にっぽんでんぱとう)。"}

API にデータを食わせます。

$ curl -X POST http://localhost:9200/landmark/sample/_bulk --data-binary @landmark.json

データの検索

「ジャズ」で検索してみます。

$ curl -X GET http://localhost:9200/landmark/sample/_search -d '{"query":{"match":{"description":"ジャズ"}}}'
{
    ...
    "hits": {
        "total": 1,
        "max_score": 0.081366636,
        "hits": [
            {
                "_index": "landmark",
                "_type": "sample",
                "_id": "AU071ik4RTPkdK_mzpPE",
                "_score": 0.081366636,
                "_source": {
                    "name": "BLUE NOTE TOKYO",
                    "coord": {
                        "lat": "35.661198",
                        "lon": "139.716207"
                    },
                    "description": "N.Y.の「Blue Note」を本店に持つジャズクラブとして、南青山にオープンしたブルーノート東京。ジャズをはじめとする多様な音楽ジャンルのトップアーティストたちが、連夜渾身のプレイを繰り広げている。"
                }
            }
        ]
    }
}

ちゃんと検索されました。

次に空間検索をしてみます。
「渋谷駅から近い順」に検索してみましょう。
(緯度経度は GoogleMap などから取得すると良いです)

query.json
{
    "sort": [
        {
            "_geo_distance": {
                "coord": {
                    "lat": 35.658517,
                    "lon": 139.701334
                },
                "order": "asc"
            }
        }
    ],
    "query": {
        "filtered": {
            "query": {
                "match_all": {}
            }
        }
    }
}
$ curl -X GET http://localhost:9200/landmark/sample/_search --data-binary @query.json

レスポンスはこんな感じです。

{
    ...
    "hits": {
        "total": 3,
        "max_score": null,
        "hits": [
            {
                ...
                "_source": {
                    "name": "BLUE NOTE TOKYO",
                    "coord": {
                        "lat": "35.661198",
                        "lon": "139.716207"
                    },
                    ...
                },
                "sort": [
                    1376.353778271321
                ]
            },
            {
                ...
                "_source": {
                    "name": "東京タワー",
                    "coord": {
                        "lat": "35.65858",
                        "lon": "139.745433"
                    },
                    ...
                },
                "sort": [
                    3984.1379076620046
                ]
            },
            {
                ...
                "_source": {
                    "name": "スカイツリー",
                    "coord": {
                        "lat": "35.710063",
                        "lon": "139.8107"
                    },
                    ...
                "sort": [
                    11419.980751959736
                ]
            }
        ]
    }
}

地点間の直線距離が計算され、それの照準で返却されています。
もちろんワード指定の検索と組み合わせることも可能ですので、色々と試してみてください。