Dukungan Geospasial di ElasticSearch

1. Pendahuluan

Elasticsearch terkenal karena kemampuan pencarian teks lengkapnya tetapi juga memiliki dukungan geospasial penuh.

Kami dapat menemukan lebih lanjut tentang menyiapkan Elasticsearch dan memulai di artikel sebelumnya ini.

Mari kita lihat bagaimana kita bisa menyimpan geo-data di Elasticsearch dan bagaimana kita bisa mencari data itu menggunakan kueri geo.

2. Jenis Data Geo

Untuk mengaktifkan geo-queries, kita perlu membuat pemetaan indeks secara manual dan secara eksplisit mengatur pemetaan lapangan.

Pemetaan dinamis tidak akan berfungsi saat menyetel pemetaan untuk jenis geografis.

Elasticsearch menawarkan dua cara untuk merepresentasikan geodata:

  1. Pasangan garis lintang-bujur menggunakan jenis bidang titik geografis
  2. Bentuk kompleks yang ditentukan di GeoJSON menggunakan jenis bidang bentuk geografis

Mari kita lihat lebih dalam pada masing-masing kategori di atas:

2.1. Jenis Data Titik Geo

Jenis bidang titik geografis menerima pasangan garis lintang-bujur yang dapat digunakan untuk:

  • Temukan titik dalam jarak tertentu dari titik pusat
  • Temukan titik dalam kotak atau poligon
  • Mengumpulkan dokumen secara geografis atau berdasarkan jarak dari titik pusat
  • Urutkan dokumen berdasarkan jarak

Di bawah ini adalah contoh pemetaan bidang untuk menyimpan data titik geografis:

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

Seperti yang dapat kita lihat dari contoh di atas, tipe untuk bidang lokasi adalah geo_point . Jadi, sekarang kami dapat menyediakan pasangan garis lintang-bujur di lokasi di bidang lokasi.

2.2. Tipe Data Bentuk Geo

Tidak seperti titik geografis , bentuk geografis menyediakan fungsionalitas untuk menyimpan dan mencari bentuk kompleks seperti poligon dan persegi panjang. Tipe data bentuk geografi harus digunakan saat kita ingin mencari dokumen yang berisi bentuk selain geo point.

Mari kita lihat pemetaan untuk tipe data bentuk geografis:

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

Versi terbaru Elasticsearch memecah bentuk geografis yang disediakan menjadi jaring segitiga . Menurut dokumentasi resmi, ini memberikan resolusi spasial yang mendekati sempurna.

3. Berbagai Cara Menyimpan Data Geo Point

3.1. Objek Garis Lintang Bujur

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

Di sini, geo-titik lokasi disimpan sebagai objek dengan lintang dan bujur sebagai kunci.

3.2. Pasangan Garis Bujur

{ "location": "23.02,72.57" }

Di sini, lokasi dinyatakan sebagai pasangan garis lintang-bujur dalam format string biasa. Perlu diketahui, urutan lintang dan bujur dalam format string.

3.3. Geo Hash

{ "location": "tsj4bys" }

Kami juga dapat menyediakan data geo point dalam bentuk geo hash seperti yang ditunjukkan pada contoh di atas. Kita dapat menggunakan alat online untuk mengonversi lintang-bujur menjadi hash geografis.

3.4. Larik Garis Bujur

{ "location": [72.57, 23.02] }

Urutan garis lintang-bujur dibalik ketika garis lintang dan garis bujur disediakan sebagai larik. Awalnya, pasangan lintang-bujur digunakan baik dalam string maupun dalam larik, tetapi kemudian dibalik agar sesuai dengan format yang digunakan oleh GeoJSON.

4. Berbagai Cara Menyimpan Data Bentuk Geo

4.1. Titik

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

Di sini, tipe bentuk geografis yang coba kita masukkan adalah sebuah titik . Silakan lihat di bidang lokasi , kami memiliki objek bersarang yang terdiri dari jenis bidang dan koordinat . Bidang meta ini membantu Elasticsearch dalam mengidentifikasi bentuk geografis dan data aktualnya.

4.2. LineString

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

Di sini, kami memasukkan bentuk geo linestring . Koordinat garis garis terdiri dari dua titik yaitu titik awal dan titik akhir. Bentuk geografis LineString sangat membantu untuk kasus penggunaan navigasi.

4.3. Poligon

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] ] ] } }

Here, we're inserting polygon geo shape. Please take a look at the coordinates in above example, first and last coordinates in polygon should always match i.e a closed polygon.

Elasticsearch also supports other GeoJSON structures as well. A complete list of other supported formats is as below:

  • MultiPoint
  • MultiLineString
  • MultiPolygon
  • GeometryCollection
  • Envelope
  • Circle

We can find examples of above-supported formats on the official ES site.

For all structures, the inner type and coordinates are mandatory fields. Also, sorting and retrieving geo shape fields are currently not possible in Elasticsearch due to their complex structure. Thus, the only way to retrieve geo fields is from the source field.

5. ElasticSearch Geo Query

Now, that we know how to insert documents containing geo shapes, let's dive into fetching those records using geo shape queries. But before we start using Geo Queries, we'll need following maven dependencies to support Java API for Geo Queries:

 org.locationtech.spatial4j spatial4j 0.7   com.vividsolutions jts 1.13   xerces xercesImpl   

We can search for the above dependencies in Maven Central repository as well.

Elasticsearch supports different types of geo queries and they are as follow:

5.1. Geo Shape Query

This requires the geo_shape mapping.

Similar to geo_shape type, geo_shape uses GeoJSON structure to query documents.

Below is a sample query to fetch all documents that fall within given top-left and bottom-right coordinates:

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

Here, relation determines spatial relation operators used at search time.

Below is the list of supported operators:

  • INTERSECTS – (default) returns all documents whose geo_shape field intersects the query geometry
  • DISJOINT – retrieves all documents whose geo_shape field has nothing in common with the query geometry
  • WITHIN – gets all documents whose geo_shape field is within the query geometry
  • CONTAINS – returns all documents whose geo_shape field contains the query geometry

Similarly, we can query using different GeoJSON shapes.

Java code for above query is as below:

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

Geo Bounding Box query is used to fetch all the documents based on point location. Below is a sample bounding box query:

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

Java code for above bounding box query is as below:

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

Geo Bounding Box query supports similar formats as we have in geo_point data type. Sample queries for supported formats can be found on the official site.

5.3. Geo Distance Query

Geo distance query is used to filter all documents that come with the specified range of the point.

Here's a sample geo_distance query:

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

And here's the Java code for above query:

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

Similar to geo_point, geo distance query also supports multiple formats for passing location coordinates. More details on supported formats can be found at the official site.

5.4. Geo Polygon Query

Kueri untuk memfilter semua rekaman yang memiliki titik yang termasuk dalam poligon titik tertentu.

Mari kita lihat sekilas contoh kueri:

{ "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} ] } } } } } }

Dan pada kode Java untuk kueri ini:

List allPoints = new ArrayList(); 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 juga mendukung format yang disebutkan di bawah ini:

  • lat-long sebagai larik: [lon, lat]
  • lat-long sebagai string: "lat, lon"
  • geo hash

tipe data geo_point adalah wajib untuk menggunakan kueri ini.

6. Kesimpulan

Pada artikel ini, kami membahas opsi pemetaan yang berbeda untuk mengindeks data geo yaitu geo_point dan geo_shape .

Kami juga menggunakan berbagai cara untuk menyimpan geo-data dan akhirnya, kami mengamati geo-queries dan Java API untuk memfilter hasil menggunakan kueri geo.

Seperti biasa, kode tersedia di proyek GitHub ini.