44

My question is similar to this one.

Simply, is there a way to return the geo distance when NOT sorting with _geo_distance?

Update: To clarify, I want the results in random order AND include distance.

Community
  • 1
  • 1
Yeggeps
  • 2,055
  • 2
  • 25
  • 34

4 Answers4

50

Yes you can, by using a script field.

For instance, assuming your doc have a geo-point field called location, you could use the following:

(note the \u0027 is just an escaped single quote, so \u0027location\u0027 is really 'location')

curl -XGET 'http://127.0.0.1:9200/geonames/_search?pretty=1'  -d '
{
   "script_fields" : {
      "distance" : {
         "params" : {
            "lat" : 2.27,
            "lon" : 50.3
         },
         "script" : "doc[\u0027location\u0027].distanceInKm(lat,lon)"
      }
   }
}
'

# [Thu Feb 16 11:20:29 2012] Response:
# {
#    "hits" : {
#       "hits" : [
#          {
#             "_score" : 1,
#             "fields" : {
#                "distance" : 466.844095463887
#             },
#             "_index" : "geonames_1318324623",
#             "_id" : "6436641_en",
#             "_type" : "place"
#          },
... etc

If you want the _source field to be returned as well, then you can specify that as follows:

curl -XGET 'http://127.0.0.1:9200/geonames/_search?pretty=1'  -d '
{
   "fields" : [ "_source" ],
   "script_fields" : {
      "distance" : {
         "params" : {
            "lat" : 2.27,
            "lon" : 50.3
         },
         "script" : "doc[\u0027location\u0027].distanceInKm(lat,lon)"
      }
   }
}
'
DrTech
  • 17,031
  • 5
  • 54
  • 48
  • 1
    However, I'm having trouble getting both _source and distance returned. If I specify _source as "fields" I only get _source, if I don't I only, get distance. Any thoughts? – Yeggeps Feb 28 '12 at 21:36
  • What version of ES are you using? When I wrote this answer, there was a newly introduced bug that meant the source wasn't being returned. However, that has been fixed in the latest RC – DrTech Feb 29 '12 at 08:45
  • Oh, that's probably why, I'm on 0.17.5. – Yeggeps Mar 01 '12 at 11:31
  • Is it possible to run distanceInKm on an object field? for example: place.point, I've also tried _source.place.point, as I get from the docs that this should be possible? Where place is an nested object. Thanks a lot. – Yeggeps Mar 19 '12 at 20:32
  • @DrTech, tried to follow your advice with no success, check out what I see: https://groups.google.com/d/msg/elasticsearch/59jFsrzG2Hc/zSCfNeVKj5sJ – brupm Mar 22 '13 at 02:26
  • unfortunately this fails in the latest version of Elasticsearch (1.2), 1.1 was fine http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/modules-scripting.html – robertzp Jun 10 '14 at 02:28
  • 8
    In elastic 5.x,6.x should using `arcDistance(lat,lon)` or `planeDistance(lat,lon)` methods. – Mehran Prs Jun 06 '18 at 07:10
20

Great answer by DrTech ... here is an updated version for Elasticsearch 5.x with painless as the script language. I also added "store_fields" to include _source in the result:

curl -XGET 'http://127.0.0.1:9200/geonames/_search?pretty=1'  -d '
{
  "stored_fields" : [ "_source" ],
  "script_fields" : {
    "distance" : {
      "script" : {
        "inline": "doc['location'].arcDistance(params.lat,params.lon) * 0.001",
        "lang": "painless",
        "params": {
          "lat": 2.27,
          "lon": 50.3
        }
      }
    }
  }
}'
Wesley Overdijk
  • 1,176
  • 10
  • 18
Jette
  • 2,459
  • 28
  • 37
  • Any reason why this would give me a `Variable [venue] is not defined.` for `doc['venue.coordinates']`? Venue.coordinates is a field in object venue, so I'm not sure what's missing. – Wesley Overdijk Feb 17 '17 at 21:34
  • 1
    this works for 6.x too. You can leave out "lang": "painless" since it's the default. – Someone Special Aug 10 '18 at 10:02
  • 1
    The same code will work with 7.9 version "stored_fields" : [ "_source" ], "script_fields": { "distance": { "script": { "source": "doc['pin.location'].arcDistance(28,77);" } } } – Santhosh Urumese Dec 01 '20 at 07:28
14

To return distance aswel as as all the default fields/source, you could also do this:

To avoid that it sorts by distance (primarily) you just sort by _score (or whatever you want the results sorted by) first.

{
   "sort": [
    "_score",
    {
      "_geo_distance": {
        "location": { 
          "lat":  40.715,
          "lon": -73.998
        },
        "order":         "asc",
        "unit":          "km", 
        "distance_type": "plane" 
      }
    }
  ]
}
Ludo - Off the record
  • 5,153
  • 4
  • 31
  • 23
  • 2
    This solution works also with script support disabled, as is the case on many hosted servers. Thanks! – mporkola Jul 14 '16 at 12:02
4

Since ES 1.3 MVEL is disabled by default so use a query like:

GET some-index/_search
{
  "sort": [
    {
      "_geo_distance": {
        "geo_location": "47.1, 8.1",
        "order": "asc",
        "unit": "m"
      }
    }
  ],
  "query": {
    "match_all": {}
  },
   "script_fields" : {
      "distance" : {
         "lang": "groovy",
         "params" : {
            "lat" : 47.1,
            "lon" : 8.1
         },
         "script" : "doc[\u0027geo_location\u0027].distanceInKm(lat,lon)"
      }
   }
}

see: "lang": "groovy", part

  • can you please tell me how did you enable scripting i mean did you write anything inside your yml file or anything please tell me i am searching this for soo long ?? – Sudhanshu Gaur Sep 17 '15 at 14:44
  • can it be set to one of the source field? – Tun Jan 27 '21 at 22:38