springboot 使用 Spring Data Elasticsearch 地理查询、经纬度查询(GeoDistanceQueryBuilder)

说明

给定圆心点坐标、半径,查询落在该圆的点

实体类

package es.entity;

import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.GeoPointField;

import org.elasticsearch.common.geo.GeoPoint;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.GeoPointField;


@Document(indexName = "meituan_v1", shards = 1, replicas = 0)
public class Meituan {
    @Id
    private String id;
    @Field(type = FieldType.Keyword)
    private String name;
    @GeoPointField
    private GeoPoint location;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public GeoPoint getLocation() {
        return location;
    }

    public void setLocation(GeoPoint location) {
        this.location = location;
    }

    @Override
    public String toString() {
        return "Meituan{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", location=" + location +
                '}';
    }
}

地理范围查询

service类

package day1.service;

import day1.entity.Meituan;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;

@Service
public class ESMeituanSrvImpl {

    @Resource
    private ElasticsearchRestTemplate template;
    /**
     * 地理范围查询
     *
     * @param lat
     * @param lon
     * @param distance
     * @return
     */
    public SearchHits<Meituan> geo(double lat, double  lon, double distance){
        //构建地理查询条件
        GeoDistanceQueryBuilder builder =
                QueryBuilders
                        .geoDistanceQuery("location")//查询字段
                        .point(lat, lon)//设置圆心点经纬度
                        .distance(distance, DistanceUnit.KILOMETERS)//设置半径距离和单位(千米)
                        .geoDistance(GeoDistance.PLANE);// 使用平面算法
        //构建排序
        GeoDistanceSortBuilder sortBuilder =
                SortBuilders.geoDistanceSort("location",lat,lon)//设置参照点经纬度
                        .point(lat, lon)
                        .unit(DistanceUnit.KILOMETERS) // 返回结果有sort,表示距离,这个单位就是距离单位
                        .order(SortOrder.ASC);//排序方式
        //构造查询条件
        NativeSearchQuery nativeSearchQuery =
                new NativeSearchQueryBuilder()
                        .withFilter(builder)
                        .withSort(sortBuilder)
                        .build();
        SearchHits<Meituan> shs = template.search(nativeSearchQuery, Meituan.class);
        return shs;
    }
}

测试类

在 test 目录下创建类,如下图:

package test;

import day1.Main;
import day1.entity.Meituan;
import day1.service.ESMeituanSrvImpl;
import org.elasticsearch.common.unit.DistanceUnit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Main.class)
public class TestESMeituanSrvImpl {

    @Resource
    private ESMeituanSrvImpl srv;

    @Test
    public void geo(){
        SearchHits<Meituan> shs = srv.geo(77, 35, 200);
        for(SearchHit<Meituan> item:shs){
            Meituan meituan=item.getContent();
            List<Object> sortValues = item.getSortValues();
            if(sortValues!=null && sortValues.size()>0){
                double d=(Double)sortValues.get(0);//距离
                System.out.print("距离:"+d+" "+ DistanceUnit.KILOMETERS+ "--");
            }
            System.out.println(meituan);
        }
    }
}

模糊条件 + 地理查询

通配符实现模糊查询店家名称,并在距离圆心点200km的范围的店家

service

在上面 service 类中加入下面代码:

public SearchHits<Meituan> geo2(String keyword, double lat, double  lon, double distance){
    //通配符模糊查询店家名称
    WildcardQueryBuilder nameQuery = QueryBuilders.wildcardQuery("name", "*" + keyword + "*");

    //构建地理查询条件
    GeoDistanceQueryBuilder geoBuilder =
            QueryBuilders.geoDistanceQuery("location")//查询字段
                    .point(lat, lon)//设置圆心点经纬度
                    .distance(distance, DistanceUnit.KILOMETERS)//设置半径距离和单位(千米)
                    .geoDistance(GeoDistance.PLANE);// 使用平面算法
    //构建bool查询
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
                .must(nameQuery)
                .must(geoBuilder);

    //构建排序
    GeoDistanceSortBuilder sortBuilder =
            SortBuilders.geoDistanceSort("location",lat,lon)//设置参照点经纬度
                    .point(lat, lon)
                    .unit(DistanceUnit.KILOMETERS) // 返回结果有sort,表示距离,这个单位就是距离单位
                    .order(SortOrder.ASC);//排序方式
    //构造查询条件
    NativeSearchQueryBuilder nativeSearchQueryBuilder =
            new NativeSearchQueryBuilder()
                    .withFilter(boolQuery)
                    .withSort(sortBuilder);
    SearchHits<Meituan> shs = template.search(nativeSearchQueryBuilder.build(), Meituan.class);
    return shs;
}

测试

在上面的测试类中增加下面代码:

@Test
public void geo2(){
    SearchHits<Meituan> shs = srv.geo2("面",77,35,120);
//        SearchHits<Meituan> shs = srv.geo2("面",77,35,200);
    for(SearchHit<Meituan> item:shs){
        Meituan meituan=item.getContent();
        List<Object> sortValues = item.getSortValues();
        if(sortValues!=null && sortValues.size()>0){
            double d=(Double)sortValues.get(0);//距离
            System.out.print("距离:"+d+" "+DistanceUnit.KILOMETERS+"--");
        }
        System.out.println(meituan);
    }
}

半径不同,查询出的店家也不同