DEV Community

Cover image for ElasticSearch Geo Shape
Taufek Johar
Taufek Johar

Posted on • Originally published at blog.taufek.dev on

ElasticSearch Geo Shape

In previous post, we looked at quick start guide on how to use ElasticSearch with Ruby.

In this post, we will look at how to search based on geo location. Lets say we have food delivery app and we would like to find out the delivery surcharge amount for certain area.

Create Index with Geo Shape Mapping

client = Elasticsearch::Client.new

mapping = {
  surcharge: {
    properties: {
      delivery_area: {
        type: 'geo_shape',
      }
    }
  }
}

client.create(index: :surcharges, type: :surcharge, body: {})

client.indices.put_mapping(index: :surcharges, type: :surcharge, body: mapping)

client.indices.get(index: :surcharges)

# Output
{
  "surcharges"=> {
    "aliases"=>{},
    "mappings"=>{
      "surcharge"=>{
        "properties"=>{
          "delivery_area"=>{"type"=>"geo_shape"}
        }
      }
    },
    "settings"=> {...}
  }
}

Above code, will create an index named surcharges and a document named surcharge. It has property of delivery_area with geo_shape data type.

Index Surcharge Document with Polygon Coordinates

Below shows polygon area that I’ve created with Google Maps. klcc_polygon

Below is the surcharge document which saying within that polygon area, the surcharge amount is $10.

surcharge = {
  id: 1,
  delivery_area: {
    type: 'polygon',
    coordinates: [[
      # longitude, latitude
      [101.710997, 3.157035],
      [101.710997, 3.152954],
      [101.717316, 3.152954],
      [101.717316, 3.157035],
      [101.710997, 3.157035],
    ]]
  },
  amount: 10.0
}

client.index(
  id: surcharge[:id],
  index: :surcharges,
  type: :surcharge,
  body: surcharge
)

# Output
{
  "_index"=>"surcharges",
  "_type"=>"surcharge",
  "_id"=>"1",
  "_version"=>1,
  "result"=>"created",
  "_shards"=>{"total"=>2, "successful"=>1, "failed"=>0},
  "_seq_no"=>1,
  "_primary_term"=>1
}

Search with Geo Point

Once I have my surcharge data indexed, I can start querying based on a coordinate. If my drop off point in somewhere inside the polygon, I’m expecting the query will return the surcharge document. inside_polygon

query = {
  query: {
    bool: {
      filter: {
        geo_shape: {
          'delivery_area': {
            shape: {
              type: :point,
                coordinates: [
                  101.714997, #longitude
                  3.155647 #latitude
                ]
            }
          }
        }
      }
    }
  }
}

client.search(index: 'surcharges', body: query)

# Output
{
  "took"=>9,
  "timed_out"=>false,
  "_shards"=>{
    "total"=>5,
    "successful"=>5,
    "skipped"=>0,
    "failed"=>0
  },
 "hits"=>{
   "total"=>1,
   "max_score"=>1.0,
   "hits"=>[
     {
       "_index"=>"surcharges",
       "_type"=>"surcharge",
       "_id"=>"1",
       "_score"=>1.0,
       "_source"=>{
          "id"=>1,
          "delivery_area"=>{
            "type"=>"polygon",
             "coordinates"=>[[[101.710997, 3.157035], [101.710997, 3.152954], [101.717316, 3.152954], [101.717316, 3.157035], [101.710997, 3.157035]]]
          },
         "amount"=>10.0
        }
      }
    ]
  }
}

Then if my drop off point in somewhere outside the polygon, I’m expecting the query will not return any surcharge document. outside_polygon

query = {
  query: {
    bool: {
      filter: {
        geo_shape: {
          'delivery_area': {
            shape: {
              type: :point,
              coordinates: [
                101.7077343, #longitude
                3.157032 #latitude
              ]
            }
          }
        }
      }
    }
  }
}

client.search(index: 'surcharges', body: query)

# Output
{
  "took"=>0,
  "timed_out"=>false,
  "_shards"=>{
    "total"=>5,
    "successful"=>5,
    "skipped"=>0,
    "failed"=>0
  },
  "hits"=>{
    "total"=>0,
    "max_score"=>nil,
    "hits"=>[]
  }
}

Conclusions

Geo shape is useful when you have data associated with coordinates of land area. Once you indexed those as a polygon in ES index, you could use Geo Shape query to fetch documents that intersect with a particular geo point. There are many other useful features too such as you could set a polygon with a hole in the middle or you could search based on radius instead of point. For more info you can head to ES official documents (geo-shape-datatype and geo-shape-query).

Top comments (0)