-
使用场景
根据用户当前所在的地理位置坐标,按商品关键字查询出附近店铺的相关商品,并按店铺位置远近将搜索结果排序。
-
场景说明
-
按商品关键字搜索,比如关键字为“牛奶”,那么需要搜索出附近店铺发布的带有“牛奶”关键字的商品。
-
商品不会带有位置信息,但是商品所属的店铺是有位置信息的,因此要将店铺的位置信息存放进商品的ES索引中。
-
-
具体实现
-
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(); } }
-
创建商品索引内容
首先可将要放入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); } }
-
搜索商品
@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); } }
相关推荐
5. ES的地理位置处理:如何使用ES的地理位置点和形状,包括对地理位置数据的索引和查询。 6. ES的数据模型设计:如何利用ES的水平扩展性来设计数据模型。 7. ES的集群配置和监控:如何在生产环境中配置和监控集群...
5. **地理位置搜索**:支持地理坐标点的索引和查询,用于地理位置相关的应用。 **四、Elasticsearch的扩展特性** 1. **Kibana**:可视化工具,用于数据可视化和交互式探索。 2. **Logstash**:日志收集和处理工具...
8. **搜索与查询**:Elasticsearch提供丰富的查询语法,包括全文搜索、范围查询、术语查询等。其强大的全文检索能力来源于倒排索引机制。 9. **聚合分析**:除了搜索,Elasticsearch还支持聚合分析,可以进行数据...
同时,由于插件基本支持Elasticsearch的所有查询方式,这意味着你可以利用全文搜索、地理位置搜索、模糊匹配等高级特性,结合SQL语法进行深度数据探索。 压缩包中的"elasticsearch-sql-master"文件可能是一个包含源...
7. **聚合分析**:除了基本的搜索,Elasticsearch还提供丰富的聚合功能,可以进行数据的统计分析,如平均值、最大值、最小值计算,以及桶聚合(例如按时间范围、地理位置等分组)。 8. **安全性**:Elasticsearch ...
6. **搜索功能**:Elasticsearch提供了丰富的搜索功能,包括全文搜索、短语搜索、模糊搜索、范围搜索、地理位置搜索等,支持多字段组合查询和布尔逻辑操作。 7. **聚合分析**:强大的聚合框架允许用户对数据进行...
它的聚合功能强大,能进行复杂的统计分析和数据挖掘,比如求平均值、最大值、最小值,或者进行桶式分组(例如按时间、地理位置等进行分组)。这些功能使得 Elasticsearch 在日志分析、监控、用户行为分析等领域有...
4. **字段数据类型**:Elasticsearch 支持多种数据类型,如文本、数字、日期、地理位置等,便于处理不同类型的数据。 5. **聚合功能**:强大的聚合功能允许用户进行复杂的数据分析,如统计、分组、排序等。 **Head...
例如,可以计算平均值、最大值、最小值,也可以做桶聚合,如按时间区间、地理位置等进行分组,这对于数据探索和可视化非常有用。 ### 五、实时性 由于基于Lucene构建,Elasticsearch 拥有近乎实时的搜索特性。尽管...
同时,它还支持桶聚合,用于分组数据,如按日期范围分组、按地理位置分组等。 5. **分析**:ES内置了多种语言分析器,如英文的Standard Analyzer和中文的IK Analyzer,它们可以将文本拆分为关键词以便索引。用户还...
5. **数据模型**:Elasticsearch支持丰富的数据类型,如文本、数字、日期、地理位置等,允许在一个索引中存储各种类型的数据。 6. **聚合功能**:Elasticsearch提供了强大的聚合功能,用户可以对数据进行复杂分析,...
- **地理位置数据**:Elasticsearch 支持地理坐标点的存储和查询,可以实现基于地理位置的搜索功能。 #### 5. 实时性和可扩展性 Elasticsearch 被设计成实时搜索引擎,对数据的索引和搜索具有非常低的延迟。同时,...
- **复杂查询构建**:掌握构建复杂查询的技术,例如多字段匹配查询、模糊查询、地理位置查询等。 - **聚合分析**:利用Elasticsearch强大的聚合功能进行数据分析,如统计计算、桶分组等,以满足不同业务需求。 - **...
- **地理空间查询**:了解如何对地理位置数据进行索引和查询,适用于地图应用等场景。 #### 5. 安全与监控 - **安全机制**:讲解Elasticsearch的安全框架,包括认证、授权等方面。 - **监控与告警**:介绍X-Pack...
6. **聚合**:允许对数据进行统计分析,如求和、平均值、最大值、最小值,以及分桶聚合(例如按时间或地理位置分组)。 7. **脚本字段**:可以在搜索结果中动态生成字段,基于现有字段的值进行计算或逻辑判断。 8....
此外,Elasticsearch还支持聚合、分组、排序和过滤等高级数据分析操作,能够帮助用户深入挖掘和分析数据。 另外,Elasticsearch具有灵活的数据模型和丰富的数据处理能力。它支持多种数据类型,包括文本、数字、日期...
Elasticsearch支持多种数据类型,包括但不限于文本、数字、日期、地理位置等。这使得用户可以轻松地对不同类型的数据进行搜索和分析,从而增强了Elasticsearch的应用范围和灵活性。 #### 六、高可扩展性 Elastic...