1. 序章

Elasticsearchは、全文検索機能で最もよく知られていますが、完全な地理空間サポートも備えています。

Elasticsearchのセットアップと開始について詳しくは、この前の記事をご覧ください。

Elasticsearchで地理データを保存する方法と、地理クエリを使用してそれらのデータを検索する方法を見てみましょう。

2. 地理データタイプ

ジオクエリを有効にするには、インデックスのマッピングを手動で作成し、フィールドマッピングを明示的に設定する必要があります。

地理タイプのマッピングを設定している間、動的マッピングは機能しません。

Elasticsearchには、地理データを表す2つの方法があります。

  1. geo-pointフィールドタイプを使用した緯度と経度のペア
  2. geo-shapeフィールドタイプを使用してGeoJSONで定義された複雑な形状

上記の各カテゴリをさらに詳しく見ていきましょう。

2.1. ジオポイントデータタイプ

ジオポイントフィールドタイプは、次の目的で使用できる緯度と経度のペアを受け入れます。

  • 中心点から一定の距離内にある点を見つける
  • ボックスまたはポリゴン内のポイントを検索します
  • 地理的に、または中心点からの距離によってドキュメントを集約します
  • 距離でドキュメントを並べ替える

以下は、ジオポイントデータを保存するためのフィールドのマッピング例です。

PUT /index_name
{
    "mappings": {
        "TYPE_NAME": {
            "properties": {
                "location": { 
                    "type": "geo_point" 
                } 
            } 
        } 
    } 
}

上記の例からわかるように、 タイプ 為に 位置 フィールドは geo_point。 したがって、緯度と経度のペアを 位置 場所フィールドに。

2.2. 地理形状データ型

geo-point とは異なり、 geo shape は、ポリゴンや長方形などの複雑な形状を保存および検索する機能を提供します。 Geo shape データ型は、ジオポイント以外の形状を含むドキュメントを検索する場合に使用する必要があります。

地理形状データ型のマッピングを見てみましょう。

PUT /index_name
{
    "mappings": {
        "TYPE_NAME": {
            "properties": {
                "location": {
                    "type": "geo_shape"
                }
            }
        }
    }
}

Elasticsearchの最近のバージョンは、提供された地理的形状を三角形メッシュに分解します。 関係者によるとドキュメンテーション 、これはほぼ完璧な空間分解能を提供します。

3. ジオポイントデータを保存するさまざまな方法

3.1. 緯度経度オブジェクト

PUT index_name/index_type/1
{
    "location": { 
        "lat": 23.02,
        "lon": 72.57
    }
}

ここで、ジオポイント 位置 でオブジェクトとして保存されます 緯度経度 キーとして。

3.2. 緯度経度ペア

{
    "location": "23.02,72.57"
}

ここ、 位置 プレーンな文字列形式では、緯度と経度のペアとして表されます。 文字列形式の緯度と経度のシーケンスに注意してください。

3.3. ジオハッシュ

{
    "location": "tsj4bys"
}

上記の例に示すように、ジオハッシュの形式でジオポイントデータを提供することもできます。 オンラインツールを使用して、緯度経度をジオハッシュに変換できます。

3.4. 経度緯度配列

{
    "location": [72.57, 23.02]
}

緯度と経度が配列として指定されている場合、緯度と経度の順序が逆になります。 当初、緯度と経度のペアは文字列と配列の両方で使用されていましたが、GeoJSONで使用される形式に一致させるために後で逆になりました。

4. 地理形状データを保存するさまざまな方法

4.1. ポイント

POST /index/type
{
    "location" : {
        "type" : "point",
        "coordinates" : [72.57, 23.02]
    }
}

ここで、挿入しようとしている地理形状タイプはポイントです。 locationフィールドをご覧ください。フィールドtypecoordinatesで構成されるネストされたオブジェクトがあります。 これらのメタフィールドは、Elasticsearchが地理的形状とその実際のデータを識別するのに役立ちます。

4.2. LineString

POST /index/type
{
    "location" : {
        "type" : "linestring",
        "coordinates" : [[77.57, 23.02], [77.59, 23.05]]
    }
}

ここでは、linestringジオシェイプを挿入しています。 linestring の座標は、2つのポイントで構成されています。 開始と終了。 LineString geo shapeは、ナビゲーションのユースケースに非常に役立ちます。

4.3. ポリゴン

POST /index/type
{
    "location" : {
        "type" : "polygon",
        "coordinates" : [
            [ [10.0, 0.0], [11.0, 0.0], [11.0, 1.0], [10.0, 1.0], [10.0, 0.0] ]
        ]
    }
}

ここでは、ポリゴンジオシェイプを挿入しています。 上記の例の座標を見てください。ポリゴンの最初の座標と最後の座標は常に一致する必要があります。つまり、閉じたポリゴンです。

Elasticsearchは他のGeoJSON構造もサポートしています。 サポートされている他の形式の完全なリストは次のとおりです。

  • マルチポイント
  • MultiLineString
  • マルチポリゴン
  • GeometryCollection
  • 封筒
  • サークル

上記でサポートされているフォーマットの例は、公式のESサイトにあります。

すべての構造で、内部のtypeおよびcoordinatesは必須フィールドです。 また、Elasticsearchでは構造が複雑なため、現在、地理形状フィールドの並べ替えと取得はできません。 したがって、ジオフィールドを取得する唯一の方法は、ソースフィールドからです。

5. ElasticSearchジオクエリ

ジオシェイプを含むドキュメントを挿入する方法がわかったので、ジオシェイプクエリを使用してそれらのレコードをフェッチする方法を見ていきましょう。 ただし、Geo Queriesの使用を開始する前に、GeoQueriesのJavaAPIをサポートするために、Mavenの依存関係に従う必要があります。

<dependency>
    <groupId>org.locationtech.spatial4j</groupId>
    <artifactId>spatial4j</artifactId>
    <version>0.7</version> 
</dependency>
<dependency>
    <groupId>com.vividsolutions</groupId>
    <artifactId>jts</artifactId>
    <version>1.13</version>
    <exclusions>
        <exclusion>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
        </exclusion>
    </exclusions>
</dependency>

上記の依存関係は、MavenCentralリポジトリでも検索できます。

Elasticsearchはさまざまなタイプのジオクエリをサポートしており、それらは次のとおりです。

5.1. 地理形状クエリ

これには、geo_shapeマッピングが必要です。

geo_shape タイプと同様に、geo_shapeはGeoJSON構造を使用してドキュメントをクエリします。

以下は、左上と右下の座標を指定してに該当するすべてのドキュメントをフェッチするためのサンプルクエリです。

{
    "query":{
        "bool": {
            "must": {
                "match_all": {}
            },
            "filter": {
                "geo_shape": {
                    "region": {
                        "shape": {
                            "type": "envelope",
                            "coordinates" : [[75.00, 25.0], [80.1, 30.2]]
                        },
                        "relation": "within"
                    }
                }
            }
        }
    }
}

ここで、関係は、検索時に使用される空間関係演算子を決定します。

以下は、サポートされている演算子のリストです。

  • INTERSECTS –(デフォルト)geo_shapeフィールドがクエリジオメトリと交差するすべてのドキュメントを返します
  • DISJOINT geo_shapeフィールドにクエリジオメトリとの共通点がないすべてのドキュメントを取得します
  • WITHIN geo_shapeフィールドがクエリジオメトリ内にあるすべてのドキュメントを取得します
  • CONTAINS geo_shapeフィールドにクエリジオメトリが含まれているすべてのドキュメントを返します

同様に、さまざまなGeoJSONシェイプを使用してクエリを実行できます。

上記のクエリのJavaコードは次のとおりです。

Coordinate topLeft = new Coordinate(74, 31.2);
Coordinate bottomRight = new Coordinate(81.1, 24);

GeoShapeQueryBuilder qb = QueryBuilders.geoShapeQuery("region",
    new EnvelopeBuilder(topLeft, bottomRight).buildGeometry());
qb.relation(ShapeRelation.INTERSECTS);

5.2. ジオバウンディングボックスクエリ

Geo Bounding Boxクエリは、ポイントの場所に基づいてすべてのドキュメントをフェッチするために使用されます。 以下は、バウンディングボックスクエリの例です。

{
    "query": {
        "bool" : {
            "must" : {
                "match_all" : {}
            },
            "filter" : {
                "geo_bounding_box" : {
                    "location" : {
                        "bottom_left" : [28.3, 30.5],
                        "top_right" : [31.8, 32.12]
                    }
                }
            }
        }
    }
}

上記のバウンディングボックスクエリのJavaコードは次のとおりです。

QueryBuilders
  .geoBoundingBoxQuery("location").setCorners(31.8, 30.5, 28.3, 32.12);

Geo Bounding Boxクエリは、geo_pointデータ型と同様の形式をサポートしています。 サポートされている形式のサンプルクエリは、公式サイトにあります。

5.3. 地理距離クエリ

地理距離クエリは、ポイントの指定された範囲に含まれるすべてのドキュメントをフィルタリングするために使用されます。

次に、geo_distanceクエリのサンプルを示します。

{
    "query": {
        "bool" : {
            "must" : {
                "match_all" : {}
            },
            "filter" : {
                "geo_distance" : {
                    "distance" : "10miles",
                    "location" : [31.131,29.976]
                }
            }
        }
    }
}

上記のクエリのJavaコードは次のとおりです。

QueryBuilders
  .geoDistanceQuery("location")
  .point(29.976, 31.131)
  .distance(10, DistanceUnit.MILES);

geo_pointと同様に、地理距離クエリも位置座標を渡すための複数の形式をサポートしています。 サポートされているフォーマットの詳細については、公式サイトをご覧ください。

5.4. ジオポリゴンクエリ

指定されたポイントのポリゴン内にあるポイントを持つすべてのレコードをフィルタリングするためのクエリ。

サンプルクエリを簡単に見てみましょう。

{
    "query": {
        "bool" : {
            "must" : {
                "match_all" : {}
            },
            "filter" : {
                "geo_polygon" : {
                    "location" : {
                        "points" : [
                        {"lat" : 22.733, "lon" : 68.859},
                        {"lat" : 24.733, "lon" : 68.859},
                        {"lat" : 23, "lon" : 70.859}
                        ]
                    }
                }
            }
        }
    }
}

そして、このクエリのJavaコードで:

List<GeoPoint> allPoints = new ArrayList<GeoPoint>(); 
allPoints.add(new GeoPoint(22.733, 68.859)); 
allPoints.add(new GeoPoint(24.733, 68.859)); 
allPoints.add(new GeoPoint(23, 70.859));

QueryBuilders.geoPolygonQuery("location", allPoints);

Geo Polygon Queryは、以下のフォーマットもサポートしています。

  • lat-配列としての長さ:[lon、lat]
  • 文字列としてのlat-long:「lat、lon」
  • ジオハッシュ

このクエリを使用するには、geo_pointデータ型が必須です。

6. 結論

この記事では、地理データにインデックスを付けるためのさまざまなマッピングオプション、つまりgeo_pointgeo_shapeについて説明しました。

また、 geo-data を保存するさまざまな方法を試し、最後に、地理クエリとJava APIを観察して、地理クエリを使用して結果をフィルタリングしました。

いつものように、コードはこのGitHubプロジェクトで利用できます