Bug in geo distance search in Java API v1.7.2


(Ivan Batura) #1

There are some similar topic, but I have decided to open new one, as I have found the exact reason of this bug.

While use org.elasticsearch.index.query.FilterBuilders#geoDistanceFilter

FilterBuilders.geoDistanceFilter("locations").distance("100mi")
      .lat(-70).lon(40);

It builds incorrect filter (query) according to the documentation, should be [lon, lat], however builder builds [lat,lon]:

...
"geo_distance" : {
                             "points" : [ -70, 40 ],
                             "distance" : "100mi"
                           }                            
... 

Reason:

org.elasticsearch.index.query.GeoDistanceFilterBuilder (lines: 124-126)

} else {
    builder.startArray(name).value(lon).value(lat).endArray();
}

Thus it builds array with "name" and values FIRST - lon, SECOND - lat.

org.elasticsearch.common.geo.GeoPoint#resetFromString (lines: 65-74)

public GeoPoint resetFromString(String value) {
    int comma = value.indexOf(',');
    if (comma != -1) {
        lat = Double.parseDouble(value.substring(0, comma).trim());
        lon = Double.parseDouble(value.substring(comma + 1).trim());
    } else {
        resetFromGeoHash(value);
    }
    return this;
}

It parse string(array) and take values FIRST - lat, SECOND - lon.

Therefore it use incorrect order, as it writes there lon, lat, but reads lat,lon


(Mark Walkom) #2

I've asked the dev team to check and comment on this :slight_smile:


(Nick Knize) #3

This often causes confusion but the logic is correct. They're two different geo-point representations and parsed in different logic blocks. The first is a GeoJSON lon, lat array - an array of two double field values required in lon, lat order. The second is a human-friendly comma separated string in lat, lon order. The parsing logic for the array:

org.elasticsearch.index.query.GeoDistanceFilterParser (lines: 84 - 87)

else if (token == XContentParser.Token.START_ARRAY) {
    fieldName = currentFieldName;
    GeoUtils.parseGeoPoint(parser, point);
}

and from org.elasticsearch.common.geo.GeoUtils (lines: 398 - 402)

if(element == 1) {
    lon = parser.doubleValue();
} else if(element == 2) {
    lat = parser.doubleValue();
} 

The parsing logic for the comma separated string:

org.elasticsearch.index.query.GeoDistanceFilterParser.java (lines: 137 - 140)

} else {
    point.resetFromString(parser.text());
    fieldName = currentFieldName;
}

(system) #4