快捷搜索:  汽车  科技

elasticsearch 分组查询(ElasticSearch地理位置查询与分组)

elasticsearch 分组查询(ElasticSearch地理位置查询与分组)@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; //其它字段、

  1. 使用场景

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

  1. 场景说明按商品关键字搜索,比如关键字为“牛奶”,那么需要搜索出附近店铺发布的带有“牛奶”关键字的商品。商品不会带有位置信息,但是商品所属的店铺是有位置信息的,因此要将店铺的位置信息存放进商品的ES索引中。
  2. 具体实现ES索引和Mapping的创建

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

elasticsearch 分组查询(ElasticSearch地理位置查询与分组)(1)

创建索引和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); } }

猜您喜欢: