`
javashop
  • 浏览: 46899 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ElasticSearch地理位置查询与分组

 
阅读更多

 

  1. 使用场景

根据用户当前所在的地理位置坐标,按商品关键字查询出附近店铺的相关商品,并按店铺位置远近将搜索结果排序。

  1. 场景说明

    1. 按商品关键字搜索,比如关键字为“牛奶”,那么需要搜索出附近店铺发布的带有“牛奶”关键字的商品。

    2. 商品不会带有位置信息,但是商品所属的店铺是有位置信息的,因此要将店铺的位置信息存放进商品的ES索引中。

  2. 具体实现

    1. ES索引和Mapping的创建

地理坐标点不能被动态映射(dynamic mapping)自动检测,而是需要显式声明对应字段类型为geo-point,如下:

 

 

创建索引和mapping具体代码如下(以下代码为单元测试类,可根据业务需要自定义修改):

 

public class CreateIndex {
    /** 商品索引名称 */
    private static final String INDEX_NAME = "goods_index";
    /** ES  cluster-name*/
    private static final String CLUSTER_NAME = "es_cluster";
    /** ES  cluster-nodes*/
    private static final String CLUSTER_NODES = "127.0.0.1:9300";
    /** ES  用户密码*/
    private static final String USER_PASSWORD = "";
    
    @Test
    public void create() {
        EsTemplateBuilder esTemplateBuilder;
        //如果用户验证
        if(!StringUtil.isEmpty(USER_PASSWORD)){
            esTemplateBuilder = new     DefaultEsTemplateBuilder().setClusterName(CLUSTER_NAME).setClusterNodes(CLUSTER_NODES).setUserPass(USER_PASSWORD);
        }else{
            esTemplateBuilder = new DefaultEsTemplateBuilder().setClusterName(CLUSTER_NAME).setClusterNodes(CLUSTER_NODES);
        }
        ElasticsearchTemplate esTemplate = esTemplateBuilder.build();
        
        //商品索引名称
        String goodsIndexName = INDEX_NAME + "_" + EsSettings.GOODS_INDEX_NAME;
        //先删除商品索引,再创建
        esTemplate.deleteIndex(goodsIndexName);
        esTemplate.createIndex(goodsIndexName);
        //获取商品索引mapping
        Map goodsMapping = createGoodsMapping();
        //创建商品索引mapping
        esTemplate.putMapping(goodsIndexName, EsSettings.GOODS_TYPE_NAME, goodsMapping);
    }
    
    /**
     * 创建商品索引mapping
     * @return
     */
    private Map createGoodsMapping() {
        Map goodsMap = new HashMap();
        goodsMap.put("goodsId", new MyMap().put("type", "long").getMap());
        goodsMap.put("goodsName", new MyMap().put("type", "text").put("analyzer", "ik_max_word").getMap());
        goodsMap.put("sellerId", new MyMap().put("type", "integer").getMap());
        goodsMap.put("location", new MyMap().put("type", "geo_point").getMap());
        //其它字段略...
        return new MyMap().put("properties", goodsMap).getMap();
    }
}
 

 

 

  1. 创建商品索引内容

首先可将要放入ES中的商品信息进行封装,如下:

 

@Document(indexName = "goods_index_"+  EsSettings.GOODS_INDEX_NAME, type = EsSettings.GOODS_TYPE_NAME)
public class GoodsIndex {
    @Id
    private Integer goodsId;
    @Field(type = FieldType.text, analyzer = "ik_max_word")
    private String goodsName;
    @Field(type = FieldType.Integer)
    private Integer sellerId;
    @GeoPointField
    private GeoPoint location;
    //其它字段、get set等内容略...
}
 初始化商品索引内容并放入ES中,具体代码如下:

 

 

@Service
public class GoodsIndexManagerImpl implements GoodsIndexManager {
    @Autowired
    protected ElasticsearchTemplate elasticsearchOperations;
    
    @Override
    public void addIndex(Map goods) {
        GoodsIndex goodsIndex = new GoodsIndex();
        goodsIndex.setGoodsId(StringUtil.toInt(goods.get("goods_id").toString(), 0));
goodsIndex.setGoodsName(goods.get("goods_name").toString());
goodsIndex.setSellerId(StringUtil.toInt(goods.get("seller_id").toString(), 0));
        //获取店铺所在位置纬度
        double latitude = goods.get("latitude") == null ? 0d :     StringUtil.toDouble(goods.get("latitude").toString(), 0d);
        //获取店铺所在位置经度
        double longitude = goods.get("longitude") == null ? 0d : StringUtil.toDouble(goods.get("longitude").toString(), 0d);
        //设置店铺所在位置经纬度
        GeoPoint location = new GeoPoint(latitude, longitude);
        goodsIndex.setLocation(location);
        
        //索引名字
        String indexName = "goods_index_" + EsSettings.GOODS_INDEX_NAME;
        IndexQuery indexQuery = new IndexQuery();
        indexQuery.setIndexName(indexName);
        indexQuery.setType(EsSettings.GOODS_TYPE_NAME);
        indexQuery.setId(goodsIndex.getGoodsId().toString());
        indexQuery.setObject(goodsIndex);
        elasticsearchOperations.index(indexQuery);
    }
}
 

 

  1. 搜索商品

@Service
public class GoodsSearchManagerImpl implements GoodsSearchManager {
    /** 查询范围默认20千米内 */
    private static final double SEARCH_DISTANCE = 20.00;
    
    @Override
    public SearchResult searchAllResult(GoodsSearchParams goodsSearch) {
        //获取搜索页数
        Integer pageNo = goodsSearch.getPageNo();
        //获取搜索每页数量
        Integer pageSize = goodsSearch.getPageSize();
        //返回结果
        SearchResult searchResult = new SearchResult();

        SearchRequestBuilder searchRequestBuilder;
        try {
            //创建查询条件
            searchRequestBuilder = this.createGoodsQuery(goodsSearch);

            //设置分页信息
            searchRequestBuilder.setFrom((pageNo - 1) * pageSize).setSize(pageSize);
            //设置是否按查询匹配度排序
            searchRequestBuilder.setExplain(true);
            //获取查询结果
            SearchResponse response =     searchRequestBuilder.execute().actionGet();
            SearchHits searchHits = response.getHits();

            //新建搜索到的商品结果集合
            List<GoodsSearchResult> resultList = new ArrayList<>();
            //店铺到当前坐标位置的距离
            Map<Integer, Double> shopDistanceMap = new HashMap<>();
            for (SearchHit hit : searchHits) {
                Map<String, Object> map = hit.getSource();
                GoodsSearchResult goodsSearchLine = new GoodsSearchResult();
                //设置商品名称
    goodsSearchLine.setName(map.get("goodsName").toString());
               //设置商品ID
                goodsSearchLine.setGoodsId(map.get("goodsId") == null ? 0 : StringUtil.toInt(map.get("goodsId").toString(), 0));
               //获取商家店铺ID
                Integer sellerId = map.get("sellerId") == null ? 0 : StringUtil.toInt(map.get("sellerId").toString(), 0);
                //设置商家ID
                goodsSearchLine.setSellerId(sellerId);
                //将商品信息放入结果集合中
                resultList.add(goodsSearchLine);

                //获取当前坐标与店铺的距离
                Double distance = StringUtil.toDouble(hit.getSortValues()[0], false);
                shopDistanceMap.put(sellerId, distance);
            }
            Page webPage = new Page<>(pageNo, searchHits.getTotalHits(), pageSize, resultList);
            searchResult.setWebPage(webPage);

            //初始化搜索结果
            this.initSearchResult(searchResult, response, shopDistanceMap);
            return searchResult;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return searchResult;
    }
    
    /**
     * 构建索引查询条件
     * @param goodsSearch 查询条件参数
     * @return
     */
    protected SearchRequestBuilder createGoodsQuery(GoodsSearchParams goodsSearch) {
        //获取查询关键字
        String keyword = goodsSearch.getKeyword();
        //获取店铺ID
        Integer sellerId = goodsSearch.getSellerId();
        //获取用户当前所在位置经度
        double userLng = goodsSearch.getLongitude();
        //获取用户当前所在位置纬度
        double userLat = goodsSearch.getLatitude();
    
        SearchRequestBuilder searchRequestBuilder = elasticsearchTemplate.getClient().prepareSearch("goods_index_"+ EsSettings.GOODS_INDEX_NAME);

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //关键字检索
        if (!StringUtil.isEmpty(keyword)) {
            QueryStringQueryBuilder queryString = new QueryStringQueryBuilder(keyword).field("goodsName");
            queryString.defaultOperator(Operator.AND);
            queryString.analyzer("ik_smart");
            queryString.useDisMax(false);
            boolQueryBuilder.must(queryString);
        }

        //卖家搜索
        if (sellerId != null) {
    boolQueryBuilder.must(QueryBuilders.termQuery("sellerId", sellerId));
        }
        //设置查询条件
        searchRequestBuilder.setQuery(boolQueryBuilder);

        // 以某点为中心,搜索指定范围
        GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("location");
        distanceQueryBuilder.point(userLat, userLng);
        // 定义查询单位:公里
        distanceQueryBuilder.distance(SEARCH_DISTANCE, DistanceUnit.KILOMETERS);
        boolQueryBuilder.filter(distanceQueryBuilder);

        //添加聚合
        this.addAggregation(searchRequestBuilder);
        //添加排序
        this.addSort(searchRequestBuilder, userLng, userLat);
        return searchRequestBuilder;
    }
    
    /**
     * 添加聚合条件
     * @param searchRequestBuilder
     * @return
     */
    protected void addAggregation(SearchRequestBuilder searchRequestBuilder) {
        //sellerId聚合的子聚合:是一个topHits,只显示一条记录,目的是查出这个聚合的店铺信息
        AggregationBuilder shopDetailAgg = AggregationBuilders.topHits("shopDetail").size(1).fetchSource(new String[]{"sellerName","sellerId","shopLogo","shopPraiseRate", "deliveryScope"},new  String[]{});
        //构建按sellerId聚合
        AggregationBuilder shopAgg = AggregationBuilders.terms("shop").field("sellerId").subAggregation(shopDetailAgg);
        searchRequestBuilder.addAggregation(shopAgg);
    }
    
    /**
     * 添加排序条件
     * @param searchRequestBuilder
     * @param sortField 排序字段
     * @param userLng 用户当前所在位置经度
     * @param userLat 用户当前所在位置纬度
     */
    protected void addSort(SearchRequestBuilder searchRequestBuilder, double userLng, double userLat) {
        //以当前的区为基准排序
        SortBuilder locationOrder = SortBuilders.geoDistanceSort("location",userLat,userLng).unit(DistanceUnit.KILOMETERS).order(SortOrder.ASC);
        searchRequestBuilder.addSort(locationOrder);
    }
    
    /**
     * 初始化搜索结果
     * @param searchResult 搜索结果数据
     * @param response 查询结果
     * @param shopDistanceMap 店铺距离数据
     */
    protected void initSearchResult(SearchResult searchResult, SearchResponse response, Map<Integer, Double> shopDistanceMap) {
        //获取聚合结果数据
        Map<String, Aggregation> aggMap = response.getAggregations().asMap();

        //新建店铺信息集合
        List<Map> shopList = new ArrayList<>();
        //获取店铺聚合结果数据
        LongTerms shopTerms = (LongTerms) aggMap.get("shop");
        List<LongTerms.Bucket> bucketList = shopTerms.getBuckets();
        for (LongTerms.Bucket bucket : bucketList) {
            Aggregations shopDetailAggResult = bucket.getAggregations();
            Map<String, Aggregation> detailAggMap = shopDetailAggResult.asMap();
            InternalTopHits hits =(InternalTopHits) detailAggMap.get("shopDetail");
            SearchHit[] detailHit = hits.getHits().getHits();
            if (detailHit != null && detailHit.length >= 1) {
                SearchHit hit = detailHit[0];
                Map shopDetail = hit.getSource();
                double dis = shopDistanceMap.get(shopDetail.get("sellerId"));
                String disStr = "";
                if (dis >= 1) {
                    disStr = CurrencyUtil.round(dis, 2) + "km";
                } else {
                    disStr = CurrencyUtil.round(CurrencyUtil.mul(dis, 1000.00), 0) + "m";
                }
                shopDetail.put("distance", disStr);
                shopList.add(shopDetail);
            }
        }
        //设置店铺信息集合
        searchResult.setShopList(shopList);
    }
}
 
0
1
分享到:
评论

相关推荐

    ElasticSearch原版PDF

    5. ES的地理位置处理:如何使用ES的地理位置点和形状,包括对地理位置数据的索引和查询。 6. ES的数据模型设计:如何利用ES的水平扩展性来设计数据模型。 7. ES的集群配置和监控:如何在生产环境中配置和监控集群...

    Elasticsearch笔记.rar

    5. **地理位置搜索**:支持地理坐标点的索引和查询,用于地理位置相关的应用。 **四、Elasticsearch的扩展特性** 1. **Kibana**:可视化工具,用于数据可视化和交互式探索。 2. **Logstash**:日志收集和处理工具...

    最新版windows elasticsearch-8.3.1-windows-x86_64.zip

    8. **搜索与查询**:Elasticsearch提供丰富的查询语法,包括全文搜索、范围查询、术语查询等。其强大的全文检索能力来源于倒排索引机制。 9. **聚合分析**:除了搜索,Elasticsearch还支持聚合分析,可以进行数据...

    Elasticsearch的sql插件.zip

    同时,由于插件基本支持Elasticsearch的所有查询方式,这意味着你可以利用全文搜索、地理位置搜索、模糊匹配等高级特性,结合SQL语法进行深度数据探索。 压缩包中的"elasticsearch-sql-master"文件可能是一个包含源...

    elasticsearch-7.17.7-windows-x86-64.zip

    7. **聚合分析**:除了基本的搜索,Elasticsearch还提供丰富的聚合功能,可以进行数据的统计分析,如平均值、最大值、最小值计算,以及桶聚合(例如按时间范围、地理位置等分组)。 8. **安全性**:Elasticsearch ...

    elasticsearch-6.8.3.zip

    6. **搜索功能**:Elasticsearch提供了丰富的搜索功能,包括全文搜索、短语搜索、模糊搜索、范围搜索、地理位置搜索等,支持多字段组合查询和布尔逻辑操作。 7. **聚合分析**:强大的聚合框架允许用户对数据进行...

    elasticsearch-6.6.2.tar.gz

    它的聚合功能强大,能进行复杂的统计分析和数据挖掘,比如求平均值、最大值、最小值,或者进行桶式分组(例如按时间、地理位置等进行分组)。这些功能使得 Elasticsearch 在日志分析、监控、用户行为分析等领域有...

    ElasticSearch7.6.1及相关内容

    4. **字段数据类型**:Elasticsearch 支持多种数据类型,如文本、数字、日期、地理位置等,便于处理不同类型的数据。 5. **聚合功能**:强大的聚合功能允许用户进行复杂的数据分析,如统计、分组、排序等。 **Head...

    elasticsearch 2.4.5

    例如,可以计算平均值、最大值、最小值,也可以做桶聚合,如按时间区间、地理位置等进行分组,这对于数据探索和可视化非常有用。 ### 五、实时性 由于基于Lucene构建,Elasticsearch 拥有近乎实时的搜索特性。尽管...

    ElasticSearch的基本功能,Google的protobuf消息方法方式示例

    同时,它还支持桶聚合,用于分组数据,如按日期范围分组、按地理位置分组等。 5. **分析**:ES内置了多种语言分析器,如英文的Standard Analyzer和中文的IK Analyzer,它们可以将文本拆分为关键词以便索引。用户还...

    elasticsearch-7.3.0-linux-x86_64.tar.gz

    5. **数据模型**:Elasticsearch支持丰富的数据类型,如文本、数字、日期、地理位置等,允许在一个索引中存储各种类型的数据。 6. **聚合功能**:Elasticsearch提供了强大的聚合功能,用户可以对数据进行复杂分析,...

    elasticsearch.docx

    - **地理位置数据**:Elasticsearch 支持地理坐标点的存储和查询,可以实现基于地理位置的搜索功能。 #### 5. 实时性和可扩展性 Elasticsearch 被设计成实时搜索引擎,对数据的索引和搜索具有非常低的延迟。同时,...

    2018年最新价值799元Elasticsearch顶尖高手系列:高手进阶篇视频教程

    - **复杂查询构建**:掌握构建复杂查询的技术,例如多字段匹配查询、模糊查询、地理位置查询等。 - **聚合分析**:利用Elasticsearch强大的聚合功能进行数据分析,如统计计算、桶分组等,以满足不同业务需求。 - **...

    【54】2018年最新价值799元Elasticsearch顶尖高手系列:高手进阶篇视频教程 .txt

    - **地理空间查询**:了解如何对地理位置数据进行索引和查询,适用于地图应用等场景。 #### 5. 安全与监控 - **安全机制**:讲解Elasticsearch的安全框架,包括认证、授权等方面。 - **监控与告警**:介绍X-Pack...

    ElasticSearch综合练习题,ES为8版本,使用Kibana运行语句

    6. **聚合**:允许对数据进行统计分析,如求和、平均值、最大值、最小值,以及分桶聚合(例如按时间或地理位置分组)。 7. **脚本字段**:可以在搜索结果中动态生成字段,基于现有字段的值进行计算或逻辑判断。 8....

    Elasticsearch是一个开源的分布式搜索和分析引擎,用于存储、搜索和分析大规模的实时数

    此外,Elasticsearch还支持聚合、分组、排序和过滤等高级数据分析操作,能够帮助用户深入挖掘和分析数据。 另外,Elasticsearch具有灵活的数据模型和丰富的数据处理能力。它支持多种数据类型,包括文本、数字、日期...

    Elasticsearch的核心功能和特点

    Elasticsearch支持多种数据类型,包括但不限于文本、数字、日期、地理位置等。这使得用户可以轻松地对不同类型的数据进行搜索和分析,从而增强了Elasticsearch的应用范围和灵活性。 #### 六、高可扩展性 Elastic...

Global site tag (gtag.js) - Google Analytics