基于elasticsearch 6.8的地理位置信息查询

基于elasticsearch 6.8的地理位置信息查询

 这篇文章主要有以下组成:

  • 基于es完成类似附近好友查询
  • 基于es完成类似某个点在面内或者相交查询

类似附近好友查询

 话不多说,可能大多数人只需要相关的编码文档,而且网上又有很多的curl命令操作,所以这里有通过项目示例直接进行一个编码查询附近好友查询:

  1. 构建mapping文档

    {
      "point": {
        "_all": {
          "enabled": false
        },
        "properties": {
          "id": {
            "type": "keyword"
          },
          "type": {
            "type": "keyword"
          },
          "location": {
            "type": "geo_point"
          }
        }
      }
    }
    

     如果需要的话可以直接拉过去使用,更改不必要的就可以了,这里是通过spring boot项目构建的,将mapping放置的目录我就在这里不说了.

    • 顺带javaBean

      @Mapping(mappingPath = "/json/poi-mapping.json")
      @Document(
          indexName = "poi",
          type = "point",
          shards = 5,
          replicas = 0,
          refreshInterval = "1s",
          indexStoreType = "fs")
      @NoArgsConstructor
      @AllArgsConstructor
      @Getter
      @Setter
      public class GeoPointBean implements Serializable {
        private static final long serialVersionUID = 2084886055991558779L;
      
        @Id
        @JSONField(ordinal = 1)
        private String id;
      
        @JSONField(ordinal = 2)
        @Field(type = FieldType.Keyword)
        private String type;
      
        @GeoPointField
        @JSONField(ordinal = 3)
        private Object location;
      }
      

      跟着我这里的样子将mapping加上就可以了,在项目启动时候就会自动生成了

      • 上传数据

        // 判断index 是否存在
        if (!esTemplate.indexExists(GeoPointBean.class)) {
            esTemplate.createIndex(GeoPointBean.class);
            esTemplate.putMapping(GeoPointBean.class);
        }
        List<IndexQuery> queries = new ArrayList<>();
        
        if (geoPointBeans.size() != geoPointDao.count()) {
            if (geoPointBeans.size() > 0) {
                for (GeoPointBean bean : geoPointBeans) {
                    IndexQuery indexQuery =
                        new IndexQueryBuilder()
                        .withIndexName("poi")
                        .withType("point")
                        .withId(bean.getId())
                        .withObject(bean)
                        .build();
                    queries.add(indexQuery);
                    // 分批提交索引
                    if (counter % 5000 == 0) {
                        try{
                            esTemplate.bulkIndex(queries);
                        }catch (Exception e){
                            log.error("error------->"+e.getMessage());
                        }
        
                        queries.clear();
                        log.info("bulkIndex counter : " + counter);
                    }
                    counter++;
                }
            }
            // 不足批的索引最后不要忘记提交
            if (queries.size() > 0) {
                esTemplate.bulkIndex(queries);
                counter += queries.size();
            }
            esTemplate.refresh(GeoPointBean.class);
            log.info("bulkIndex  counter : " + counter);
        } else {
            counter = 1;
            log.info("数据无需写入");
        }
        
  2. 进行一个附近好友查询

    //指定 indices和type类型
    SearchRequestBuilder searchRequestBuilderPy = client.prepareSearch("poi").setTypes("point");
    BoolQueryBuilder boolQueryBuilderPy = QueryBuilders.boolQuery();
    GeoDistanceQueryBuilder matchQueryBuilderRegionPy =
    //指定索引字段
    geoDistanceQuery("location")
    //指定查询的点
    .point(point)
    //指定距离,和距离单位
    .distance(distance, DistanceUnit.METERS)
    //排序
    .geoDistance(GeoDistance.ARC);
    boolQueryBuilderPy.must(matchQueryBuilderRegionPy);
    searchRequestBuilderPy.setQuery(boolQueryBuilderPy);
    SearchResponse response = searchRequestBuilderPy.execute().actionGet();
    

     到这里的话就已经完成了附近的人查询.

  3. 基于es完成类似某个点在面内或者相交查询

     如果你看过官方文档的话,应该会知道你按照官网的例子能查询到数据,但是仅限于面或线的实例,那么如果通过一个点来查询,其实很简单就是将一个点构建成一个圆即可,但是这个工作之前还是需要一个改动的,这里贴出来官网一句话,顺便一并贴出如果不加我提到的这个的时候会出现的问题:

    出现的问题
    unsupported_operation_exception: CIRCLE geometry is not supported
    

    其实这个问题是因为,官方例子中对geo_shape类型的实例只是做了一个简单示例,并没有添加对圆的支持.

    其中在官方github中的issue中也提到了这一点,而且也给除了解决方案:

    issue的出处:

    geo_shape query with circle does not work for legacy geo_shape field #49296

    其实官网里也有详细的说明:

    出处

    Indexing approachedit
    GeoShape types are indexed by decomposing the shape into a triangular mesh and indexing each triangle as a 7 dimension point in a BKD tree. This provides near perfect spatial resolution (down to 1e-7 decimal degree precision) since all spatial relations are computed using an encoded vector representation of the original shape instead of a raster-grid representation as used by the Prefix trees indexing approach. Performance of the tessellator primarily depends on the number of vertices that define the polygon/multi-polygon. While this is the default indexing technique prefix trees can still be used by setting the tree or strategy parameters according to the appropriate Mapping Options. Note that these parameters are now deprecated and will be removed in a
    
    

 这里给出一个方案:

{
  "shape": {
    "_all": {
      "enabled": false
    },
    "properties": {
      "id": {
        "type": "keyword"
      },
      "geometry": {
        "type": "geo_shape",
          //以为tree和strategy赋值之后就能正常进行一个点构建成一个圆查询
        "tree": "geohash",
        "strategy": "recursive"
      }
    }
  }
}
查询的接口如下
SearchRequestBuilder searchRequestBuilderPy = client.prepareSearch("wkt").setTypes("shape");
BoolQueryBuilder boolQueryBuilderPy = QueryBuilders.boolQuery();

GeoShapeQueryBuilder matchQueryBuilderRegionPy = null;
try {
    matchQueryBuilderRegionPy =
        geoIntersectionQuery(
        "geometry",
        //将一个点构建成一个圆
        new CircleBuilder()
        //传入点
        .center(point.getLon(), point.getLat())
        //构建半径和单位
        .radius(radius, DistanceUnit.METERS));
} catch (IOException e) {
    e.printStackTrace();
}

你可能感兴趣的:(elasticsearch)