ES7 learning notes GEO location search

The basic content of ES is almost introduced. At last, let's take a look at GEO location search. Now most apps have location-based search functions. For example, when we order takeout, we can sort it according to the distance from us, which can save our delivery fee and delivery time. When we look for work, we can also sort it according to the distance from our home, who I want to find a job close to home, right. These functions are based on GEO search. At present, there are many middleware supporting GEO search functions, such as MySQL, Redis, ES, etc. Let's see how to implement GEO location search in ES.

Creation of GEO fields

Fields of GEO type cannot be generated automatically by dynamic mapping. We need to specify the type of field as GEO when creating index_ point´╝îGEO_ The longitude and latitude stored in the field of point type. Let's see how the longitude and latitude are defined,

english Shorthand Positive number negative
dimension latitude lat north latitude South latitude
longitude longitude lon or lng east longitude Western longitude

There are two abbreviations of longitude, lon is commonly used, and lng is widely used in the open platform of the third-party map. Let's create a Geo_ The index of the point type field is as follows:

PUT /my_geo
{
    "settings":{
        "analysis":{
            "analyzer":{
                "default":{
                    "type":"ik_max_word"
                }
            }
        }
    },
    "mappings":{
        "dynamic_date_formats":[
            "MM/dd/yyyy",
            "yyyy/MM/dd HH:mm:ss",
            "yyyy-MM-dd",
            "yyyy-MM-dd HH:mm:ss"
        ],
        "properties":{
            "location":{
                "type":"geo_point"
            }
        }
    }
}

Created a my_geo index, in which there are some basic configurations, default IK word breaker, dynamic mapping time format. The point is that at the end of the article we added a field location, whose type is geo_point.

After the index is created, let's add two pieces of data. Suppose that passer-by A is in Beijing station and passer-by B is in Chaoyang Park. So how do we measure the latitude and longitude of "Beijing station" and "Chaoyang Park"? When we are doing projects, the front-end will be connected with map control. The information of longitude and latitude can be obtained by calling the API of map control. In our example, the map control is not connected. It's too troublesome to find the coordinates of "Beijing station" and "Chaoyang Park" on the Internet.

We found the coordinates of "Beijing station" as follows:

Then add a piece of data:

POST /my_geo/_doc
{
    "name":"Passerby a",
    "location":{
        "lat": 39.90279998006104,
        "lon": 116.42703999493406
    }
}

Check the coordinates of Chaoyang Park again

Add the information of "passerby B"

POST /my_geo/_doc
{
    "name":"Passerby B",
    "location":{
        "lat": 39.93367367974064,
        "lon": 116.47845257733152
    }
}

Let's use the elastic search head plug-in to look at the data in the index:

GEO query

The information of "passerby a" and "passerby B" is available, but there is no information of location field, because location is a field of feature type, which cannot be displayed here. Let's search. Let's see how to use geo to search. Suppose "I" is located in "work style". First, we need to find the coordinates of "work style",

Then query who is within 5km and send the request as follows:

POST /my_geo/_search
{
    "query":{
        "bool":{
            "filter":{
                "geo_distance":{
                    "distance":"5km",
                    "location":{
                        "lat":39.93031708627304,
                        "lon":116.4470385453491
                    }
                }
            }
        }
    }
}

When querying, we use filter query, and then use geo in filter query_ For distance query, we define the distance as 5km, and then specify the geo type field location. The current coordinates are: 39.93031708627304N, 116.4470385453491E. Check the results:

{
    ......
    "hits":[
        {
            "_index":"my_geo",
            "_type":"_doc",
            "_id":"AtgtXnIBOZNtuLQtIVdD",
            "_score":0,
            "_source":{
                "name":"Passerby a",
                "location":{
                    "lat": 39.90279998006104,
                    "lon": 116.42703999493406
                }
            }
        },
        {
            "_index":"my_geo",
            "_type":"_doc",
            "_id":"ZdguXnIBOZNtuLQtMVfA",
            "_score":0,
            "_source":{
                "name":"Passerby B",
                "location":{
                    "lat": 39.93367367974064,
                    "lon": 116.47845257733152
                }
            }
        }
    ]
}

It seems that we are standing in the "work style", "Beijing station" passerby A and "Chaoyang Park" passerby B are all within 5km. How about shortening the scope a little bit? Change it to 3km. The search request is the same. Just change the distance to 3km and see the results,

{
    ......
    "hits":[
        {
            "_index":"my_geo",
            "_type":"_doc",
            "_id":"ZdguXnIBOZNtuLQtMVfA",
            "_score":0,
            "_source":{
                "name":"Passerby B",
                "location":{
                    "lat": 39.93367367974064,
                    "lon": 116.47845257733152
                }
            }
        }
    ]
}

Only passerby B in Chaoyang Park was found. It's exactly as expected. Let's see how to use GEO search in the program.

JAVA code

When defining entity classes, the corresponding GEO fields should use special types, as follows:

@Setter@Getter
public class MyGeo {

    private String name;
    private GeoPoint location;

}

The location type is GeoPoint. The method of adding data has not changed. It can be converted to Json. Let's see how to use the query,

public void searchGeo() throws IOException {
    SearchRequest searchRequest = new SearchRequest("my_geo");
    SearchSourceBuilder ssb = new SearchSourceBuilder();

    //Coordinates of the work
    GeoPoint geoPoint = new GeoPoint(39.93367367974064d,116.47845257733152d);
    //Geo distance query name=geo field
    QueryBuilder qb = QueryBuilders.geoDistanceQuery("location")
        //Distance 3KM
        .distance(3d, DistanceUnit.KILOMETERS)
        //Coordinate system
        .point(geoPoint);

    ssb.query(qb);
    searchRequest.source(ssb);
    SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

    for (SearchHit hit : response.getHits().getHits()) {
        System.out.println(hit.getSourceAsString());
    }


}
  • SearchRequest specifies index my_geo
  • Create coordinate point GeoPoint of the body
  • Create geo distance query, specify geo field location, distance 3km, coordinate point body
  • Other places have not changed

Run it and see the results,

{"name":"Passerby B","location":{"lat":39.93360786576342,"lon":116.47853840802}}

Only in "Chaoyang Park" passerby B was found out, in line with expectations.

Distance sort

Some small partners may have such a question, I don't want to query by distance, just want to sort the query results by distance from "me". What should I do? Take another look,

public void searchGeoSort() throws IOException {
    SearchRequest searchRequest = new SearchRequest("my_geo");
    SearchSourceBuilder ssb = new SearchSourceBuilder();

    //Coordinates of the work
    GeoPoint geoPoint = new GeoPoint(39.93367367974064d,116.47845257733152d);

    GeoDistanceSortBuilder sortBuilder = SortBuilders
        .geoDistanceSort("location", geoPoint)
        .order(SortOrder.ASC);

    ssb.sort(sortBuilder);
    searchRequest.source(ssb);
    SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

    for (SearchHit hit : response.getHits().getHits()) {
        System.out.println(hit.getSourceAsString());
    }
}

This query does not set query criteria, but creates a geo distance sort. Similarly, specify the geo field location and the current coordinate body first, and then set the sort to ascending. Run it and see the results,

{"name":"Passerby B","location":{"lat":39.93360786576342,"lon":116.47853840802}}
{"name":"Passerby a","location":{"lat":39.902799980059335,"lon":116.42721165631102}}

"Passer-by B", which is close to the "work style", ranks first, which is also in line with expectations. If you have any questions, please leave a message in the comment area~

Tags: Java MySQL Redis JSON

Posted on Fri, 29 May 2020 03:43:29 -0400 by micklerlop