Sokongan Geospatial dalam Pencarian Elastik

1. Pengenalan

Elasticsearch terkenal dengan keupayaan carian teks penuh tetapi ia juga mempunyai sokongan geospasial penuh.

Kami dapat mengetahui lebih lanjut mengenai penyediaan Elasticsearch dan memulakan artikel sebelumnya.

Mari kita lihat bagaimana kita dapat menyimpan data geo di Elasticsearch dan bagaimana kita dapat mencari data tersebut menggunakan pertanyaan geo.

2. Jenis Data Geo

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

Pemetaan dinamik tidak akan berfungsi semasa menetapkan pemetaan untuk jenis geo.

Elasticsearch menawarkan dua cara untuk mewakili geodata:

  1. Pasangan garis lintang-bujur menggunakan jenis medan titik geo
  2. Bentuk kompleks yang ditentukan dalam GeoJSON menggunakan jenis bidang bentuk geo

Mari kita perhatikan lebih mendalam setiap kategori di atas:

2.1. Jenis Data Titik Geo

Jenis medan titik geografi menerima pasangan garis lintang-bujur yang dapat digunakan untuk:

  • Cari titik dalam jarak tertentu dari titik pusat
  • Cari titik dalam kotak atau poligon
  • Mengumpulkan dokumen secara geografi atau jarak dari titik pusat
  • Isih dokumen mengikut jarak

Berikut adalah contoh pemetaan untuk bidang untuk menyimpan data titik geo:

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

Seperti yang kita dapat lihat dari contoh di atas, jenis untuk lokasi medan adalah geo_point . Oleh itu, kita sekarang dapat memberikan pasangan garis lintang-bujur di lokasi di medan lokasi.

2.2. Jenis Data Bentuk Geo

Tidak seperti geo-point , bentuk geo menyediakan fungsi untuk menyimpan dan mencari bentuk kompleks seperti poligon dan segi empat tepat. Jenis data bentuk geo mesti digunakan ketika kita ingin mencari dokumen yang mengandungi bentuk selain titik geo.

Mari lihat pemetaan untuk jenis data bentuk geo:

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

Versi terkini Elasticsearch memecah bentuk geo yang disediakan menjadi mesh segitiga . Menurut dokumentasi rasmi, ini memberikan resolusi ruang yang hampir sempurna.

3. Kaedah yang berbeza untuk Menyimpan Data Titik Geo

3.1. Objek Longitud Lintang

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

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

3.2. Pasangan Longitud Lintang

{ "location": "23.02,72.57" }

Di sini, lokasi dinyatakan sebagai pasangan garis lintang-bujur dalam format rentetan biasa. Sila ambil perhatian, urutan garis lintang dan garis bujur dalam format rentetan.

3.3. Geo Hash

{ "location": "tsj4bys" }

Kami juga dapat memberikan data titik geo dalam bentuk geo hash seperti yang ditunjukkan dalam contoh di atas. Kita boleh menggunakan alat dalam talian untuk menukar lintang-bujur menjadi geo hash.

3.4. Array Lintang Bujur

{ "location": [72.57, 23.02] }

Urutan garis lintang-bujur dibalikkan apabila garis lintang dan garis bujur dibekalkan sebagai tatasusunan. Pada mulanya, pasangan garis lintang-bujur digunakan dalam rentetan dan dalam array, tetapi kemudian ia dibalikkan agar sesuai dengan format yang digunakan oleh GeoJSON.

4. Kaedah yang berbeza untuk Menyimpan Data Bentuk Geo

4.1. Titik

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

Di sini, jenis bentuk geo yang ingin kita masukkan adalah intinya . Lihat bidang lokasi , kami mempunyai objek bersarang yang terdiri daripada jenis medan dan koordinat . Medan meta ini membantu Elasticsearch dalam mengenal pasti bentuk geo dan data sebenarnya.

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 untuk linestring terdiri daripada dua titik iaitu titik permulaan dan titik akhir. Bentuk geo LineString sangat membantu untuk kes 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

Pertanyaan untuk menapis semua rekod yang mempunyai titik yang berada dalam poligon titik yang diberikan.

Mari kita lihat contoh pertanyaan dengan cepat:

{ "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 kod Java untuk pertanyaan 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 menyokong format yang disebutkan di bawah:

  • lat-long sebagai array: [lon, lat]
  • lat-long sebagai rentetan: "lat, lon"
  • geo hash

jenis data geo_point adalah wajib untuk menggunakan pertanyaan ini.

6. Kesimpulannya

Dalam artikel ini, kami membincangkan pilihan pemetaan yang berbeza untuk mengindeks data geo iaitu geo_point dan geo_shape .

Kami juga melalui berbagai cara untuk menyimpan data geo dan akhirnya, kami melihat geo-query dan Java API untuk menyaring hasil menggunakan pertanyaan geo.

Seperti biasa, kodnya terdapat dalam projek GitHub ini.