ElasticSearch查询地图信息(geo-point类型)

最近有个需求是根据经纬度查询地图信息,刚开始有点懵懂,后来了解到es里有个geo-point的类型,这个是存放地理坐标点的信息也就是经纬度的信息,然后通过点位查询距离范围内也就是一个圆内的所有数据。

设置数据格式
地理坐标点(geo-point) 是指地球表面可以用经纬度描述的一个点。地理坐标点可以用来计算两个坐标位置间的距离,或者判断一个点是否在一个区域中。地理坐标点不能被动态映射(dynamic mapping)自动检测,而是需要显式声明对应字段类型为 geo_point ,例子中的es mapping只有location字段

{
  "properties": {
    "location": {
      "type": "geo_point"
    }
  }
}

半角逗号分割的字符串形式 “lat,lon“
明确以 lat 和 lon 作为属性的对象
数组形式表示 [lon,lat]
需要特别注意的就是纬度在前边经度在后边(latitude,longitude),数组表示形式是经度在前纬度在后([longitude,latitude])

这里作者使用的是作者存的值是"location" : "25.004,102.704"的字符串类型,纬度在前,经度在后

geo_distance 找出指定位置在给定距离内的数据,相当于指定圆心和半径找到圆中点
找出distance范围内的所有点位信息
distance:距离 单位/m
location:坐标点 圆心所在位置

 /**
    * 查询地图信息
    * @author Songsong
     * @param latitude 纬度
     * @param longitude 经度
     * @param distance 距离/单位:m
     * @param esIndex es索引
     * @param t 返回类型
    * @return java.util.List<T>
    * @date 2020-11-17 21:27
    */
    public <T> List<T> queryMapData(String latitude, String longitude, Integer distance, String esIndex, Class<T> t) {
        //筛选坐标指定范围内的数据
        GeoDistanceQueryBuilder distanceQueryBuilder =
                QueryBuilders.geoDistanceQuery("location")
                        .point(Double.parseDouble(latitude), Double.parseDouble(longitude))
                        .distance(distance, DistanceUnit.METERS)
                        .geoDistance(GeoDistance.PLANE);
        SearchRequest searchRequest = new SearchRequest();
        //查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .filter(distanceQueryBuilder);
        searchSourceBuilder.query(queryBuilder);
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(10000);
        //按照距离排序
        GeoDistanceSortBuilder sortBuilder = SortBuilders.geoDistanceSort("location",
                new GeoPoint(Double.parseDouble(latitude), Double.parseDouble(longitude)))
                .order(SortOrder.ASC)
                .unit(DistanceUnit.METERS)
                .geoDistance(GeoDistance.PLANE);
        searchSourceBuilder.sort(sortBuilder);
        searchRequest.source(searchSourceBuilder);
        searchRequest.indices(esIndex);
        List<T> result = new ArrayList<>();
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits searchHits = searchResponse.getHits();
            SearchHit[] searchHitArray = searchHits.getHits();
            for (SearchHit searchHit : searchHitArray) {
                //下划线转驼峰
                result.add(CamelAndUnderlineUtil.underlineToCamel(searchHit.getSourceAsString(), t));
            }
        } catch (IOException e) {
            log.error("Method:queryMapData,es查询异常:{}", e.getMessage());
        }
        return result;
    }

CamelAndUnderlineUtil.underlineToCamel这个方法参考作者之前写的
java实用型-驼峰下划线互转

以上java代码是根据经纬度查询distance范围内的数据,返回结果是入参中的对象list,结构可以自己定义。
以上最大查询条数为1w条,并且是按照距离中心点升序后的数据,如果想返回更多的数据怎么办,这时候我们可以考虑用scroll查询,每次根据最新的scroll_id去查询,然后封装到一起再返回,这样可以达到返回>1w的数据的效果。

一、什么是游标查询(Scroll)

顾名思义,相当于用一把游标标记查询的位置.

二.、为什么要使用游标查询

在默认情况下,ES查询每次返回的数量最多只有1W条,且只能是前1W条.

这意味着,在不修改配置的情况下,想通过分页的方式(如下)拿到1W条之后的数据是做不到的,所以自然就有了游标查询。

三、如何使用游标查询

DSL的用法:

复制代码
GET 索引/类型/_search?scroll=1m
{
“size”: 10000,
“query”: {
“match_all”: {}
}
}
复制代码
1m表示:过期时间1分钟

查询结果的第一行会有:

"_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBAAAAAAABO-dFmRFSU9NM1VNU2JxNG9UUlNnSmpXMVEAAAAAAL7J_hYxT0dJOVJVMVFxU2I0N2xCR2IyVzJnAAAAAAC-    SmQWWk1aN0sxUmRSQmFNS3EwVFh0R0luUQAAAAAAvkplFlpNWjdLMVJkUkJhTUtxMFRYdEdJblE=",

这个_scroll_id就相当于书签,之后的查询带着这个书签,就能根据size不断拿到之后的数据,但是前提是在过期时间之内

之后的查询DSL:

GET _search/scroll
{
  "scroll":"1m",
  "scroll_id":"DnF1ZXJ5VGhlbkZldGNoBAAAAAAABPP1FmRFSU9NM1VNU2JxNG9UUlNnSmpXMVEAAAAAAL7OTxYxT0dJOVJVMVFxU2I0N2xCR2IyVzJnAAAAAAC-j70WVVlOZkxQRzJRLXlMRlVMbEQtalBfUQAAAAAAyWm-Fk9HdGx1b3VsUXRLZHV4c1E1OExja0E="
}

将获取的scroll_id作为条件继续查询即可,这里不需要再指定索引和类型,因为scroll_id的唯一性.

在过期时间内,之后的查询的scroll_id是不变的.
  
示例:比如返回5w条数据,每次查询5000条,也就是说总共需要查10次,不多说,上java代码:

/**
    * 查询地图信息
    * @author Songsong
     * @param latitude 纬度
     * @param longitude 经度
     * @param distance 距离/单位:m
     * @param esIndex es索引
     * @param t 返回类型
    * @return java.util.List<T>
    * @date 2020-11-17 21:27
    */
public <T> List<T> queryMapData(String latitude, String longitude, Integer distance, String esIndex, Class<T> t) {
        //筛选坐标指定范围内的数据
        GeoDistanceQueryBuilder distanceQueryBuilder =
                QueryBuilders.geoDistanceQuery("location")
                        .point(Double.parseDouble(latitude), Double.parseDouble(longitude))
                        .distance(distance, DistanceUnit.METERS)
                        .geoDistance(GeoDistance.PLANE);
        SearchRequest searchRequest = new SearchRequest();
        //查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .filter(distanceQueryBuilder);
        searchSourceBuilder.query(queryBuilder);
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(5000);
        //scoll滚动查询
        Scroll scroll = new Scroll((TimeValue.timeValueMinutes(1L)));
        //按照距离排序
        GeoDistanceSortBuilder sortBuilder = SortBuilders.geoDistanceSort("location",
                new GeoPoint(Double.parseDouble(latitude), Double.parseDouble(longitude)))
                .order(SortOrder.ASC)
                .unit(DistanceUnit.METERS)
                .geoDistance(GeoDistance.PLANE);
        searchSourceBuilder.sort(sortBuilder);
        searchRequest.source(searchSourceBuilder);
        searchRequest.indices(esIndex);
        searchRequest.scroll(scroll);
        List<T> result = new ArrayList<>();
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits searchHits = searchResponse.getHits();
            SearchHit[] searchHitArray = searchHits.getHits();
            String scrollId = searchResponse.getScrollId();
            //限制返回最大数量
            while (null != searchHitArray && searchHitArray.length > 0 && result.size() < 50000) {
                for (SearchHit searchHit : searchHitArray) {
                    //下划线转驼峰
                    result.add(CamelAndUnderlineUtil.underlineToCamel(searchHit.getSourceAsString(), t));
                }
                //构造滚动查询条件
                SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
                searchScrollRequest.scroll(scroll);
                searchResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
                scrollId = searchResponse.getScrollId();
                searchHitArray = searchResponse.getHits().getHits();
            }
            //处理完成后清除滚动
            ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
            clearScrollRequest.addScrollId(scrollId);
            restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("Method:queryMapData,es查询异常:{}", e.getMessage());
        }
        return result;
    }

CamelAndUnderlineUtil.underlineToCamel这个方法参考作者之前写的
java实用型-驼峰下划线互转

以上就是根据最新的scroll_id查询下一页的数据,此查询有个弊端,就是不能查询指定的页数,每次只能查询下一页的数据,所以具体得看使用场景。

所有数据获取完毕之后,需要手动清理掉 scroll_id 。虽然es 会有自动清理机制,但是 srcoll_id 的存在会耗费大量的资源来保存一份当前查询结果集映像,并且会占用文件描述符。所以用完之后要及时清理。所以就有了这段代码:

//处理完成后清除滚动
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
clearScrollRequest.addScrollId(scrollId);
restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);

进行手动清理scroll_id的操作。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值