ElasticSearch实战

5 阅读5分钟

文章目录 实战笔记

  1. 利用IkAnalyzer获取中文分词
  2. 使用DSL来调用ElasticSearch的RestFul接口实现搜索
  3. 使用ElasticSearchRepository将数据导入到ElasticSearch
  4. 使用ElasticSearch做聚类搜索同时满足关键字搜索。 入坑笔记 坑一、简单模糊搜索的方法规范 坑二、升级Springboot版本造成ElasticSearch版本不兼容

es 版本: 6.2.2 实战笔记

  1. 利用IkAnalyzer获取中文分词 在kibana的Dev Tools的console里输入命令,将中文进行分词, 支持的请求方式有五种: GET, POST,PUT,DELETE,HEAD, 使用ik_max_word将中文分词,默认的分词方式为Standard, 此方式只讲中文进行单个拆分,在实际中开发中不太适合用。

将 “小米很不错” 用IKAnalyzer进行拆分: GET /_analyze { "text": "小米很不错", "tokenizer":"ik_max_word" }

打印结果:

{ "tokens": [ { "token": "小米", "start_offset": 0, "end_offset": 2, "type": "CN_WORD", "position": 0 }, { "token": "很不错", "start_offset": 2, "end_offset": 5, "type": "CN_WORD", "position": 1 }, { "token": "很不", "start_offset": 2, "end_offset": 4, "type": "CN_WORD", "position": 2 }, { "token": "不错", "start_offset": 3, "end_offset": 5, "type": "CN_WORD", "position": 3 } ] }

根据打印结果,可以发现字符串以比较友好的形式拆分成了若干个。

  1. 使用DSL来调用ElasticSearch的RestFul接口实现搜索 DSL: 模糊查询所有包含name 、subTitle、keywords关键字中含小米的记录的, 同时根据id来降序排序。

GET /_search { "from":0, "size":20, "query":{ "multi_match": { "query": "小米", "fields": ["name","subTitle","keywords"] } } ,"sort": [ { "id": "desc" } ] }

打印结果:

{ "took": 1, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": 4, "max_score": 1.5881127, "hits": [ { "_index": "pms", "_type": "product", "_id": "33", "_score": 1.5881127, "_source": { "id": 33, "productSn": "4609652", "brandId": 6, "brandName": "小米", "productCategoryId": 35, "productCategoryName": "手机数码", "pic": "", "name": "小米(MI)小米电视4A ", "subTitle": "小米(MI)小米电视4A 55英寸 L55M5-AZ/L55M5-AD 2GB+8GB HDR 4K超高清 人工智能网络液晶平板电视", "keywords": "", "price": 2499, "sale": 0, "newStatus": 0, "recommandStatus": 0, "stock": 100, "promotionType": 0, "sort": 0, "attrValueList": [] } } ] } }

  1. 使用ElasticSearchRepository将数据导入到ElasticSearch   将数据导入到es的原理很简单: 先从数据库select出来,然后再通过ElasticSearchRepository导入到ElasticSearch。在使用ElasticSearchRepository时,需要先定义一个实现类,并给ElasticsearchRepository一个Model类型,该类型为我们导入到es的数据模板类。

public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {

// 继承 ElasticsearchRepository 的方法 findBy 后的属性必须是model所包含的属性,否则会报错。
Page<EsProduct> findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);

}

   然后将数据库中的EsProduct数据给遍历出来,使用ElasticSearchRepository自带的saveAll()方法即可将所有数据导入到ElasticSearch。

@Autowired private EsProductRepository productRepository;

@Override public int importAllProduct() { // 1. 获取所有商品列表 List esproductlist = productDao.getAllEsProductList(null); // 2. 将商品保存至es Iterable esProductIterable = productRepository.saveAll(esproductlist); Iterator iterator = esProductIterable.iterator(); // 获取保存到ealsticsearch中的商品数量,每保存一个商品数量+1 int result = 0; while (iterator.hasNext()) { result++; iterator.next(); } return result; }

  1. 使用ElasticSearch做聚类搜索同时满足关键字搜索。    举个栗子,例如我们需要根据某个商品的商标和商品类型来查询,比如我要查询商标为小米、类型为手机数码,关键字为手机。

然后点击搜索,就能够搜索出我们想要的商品了。 具体实现思路:    参照Elasticsearch的ASL写法,可以发现这里有条件过滤MUST和模糊匹配MUTI_MATCH,使用ElastichSearchRepository的search()方法就能够完成搜索,只不过我们需要先将这些条件和模糊匹配的Builder先组成起来。 条件查询 asl:

{ "query": { "bool": { "must": [{ "query_string": { "query": "小米", "fields": ["name"] } }, { "query_string": { "query": "2499", "fields": ["price"] } }] } } }

模糊匹配 asl:

{ "from":0, "size":3, "query":{ "multi_match": { "query": "小米", "fields": ["name","subTitle","keywords"] } } ,"sort": [ { "id": "desc" } ] }

NativeSearchQueryBuilder: 可以用来搜索.withQuery()和条件搜索.withFilter()。 BoolQueryBuilder : 相当于ASL里的MUST,必须一样才算匹配成功。 FunctionScoreQueryBuilder: 相当于ASL里的大query。

@Override public Page search(String keyword, Long brandId, Long productCategoryId, Integer pageNum, Integer pageSize) { Pageable pageable = PageRequest.of(pageNum, pageSize); NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); //分页 nativeSearchQueryBuilder.withPageable(pageable); //过滤 if (brandId != null || productCategoryId != null) { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); if (brandId != null) { boolQueryBuilder.must(QueryBuilders.termQuery("brandId", brandId)); } if (productCategoryId != null) { boolQueryBuilder.must(QueryBuilders.termQuery("productCategoryId", productCategoryId)); } nativeSearchQueryBuilder.withFilter(boolQueryBuilder); } //搜索 if (StringUtils.isEmpty(keyword)) { nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery()); } else { List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>(); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("name", keyword), ScoreFunctionBuilders.weightFactorFunction(10))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("subTitle", keyword), ScoreFunctionBuilders.weightFactorFunction(5))); filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("keywords", keyword), ScoreFunctionBuilders.weightFactorFunction(2))); FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()]; filterFunctionBuilders.toArray(builders); FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders) .scoreMode(FunctionScoreQuery.ScoreMode.SUM) .setMinScore(2); nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder); } NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build(); LOGGER.info("DSL:{}", searchQuery.getQuery().toString()); return productRepository.search(searchQuery); }

入坑笔记 坑一、简单模糊搜索的方法规范 继承 ElasticsearchRepository 的方法 findBy 后的属性必须是model所包含的属性,否则会报错: Caused by: org.springframework.data.mapping.PropertyReferenceException: No property keyword found for type EsProduct! Did you mean ‘keywords’? 举个栗子: 你的model 要查询的分词字段为: name , subTitle , keywords, 需要注意的是这些字段要用@Field(analyzer=“ik_max_word”)标记,这样数据导入给elasticsearch后,elasticsearch才能把这些字段拆分成分词来匹配。

import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType;

@Field(analyzer = "ik_max_word",type = FieldType.Text) private String name; @Field(analyzer = "ik_max_word",type = FieldType.Text) private String subTitle; @Field(analyzer = "ik_max_word",type = FieldType.Text) private String keywords;

那么在写自定义方法时应该定义为: findByNameOrSubTitleOrKeywords , 方法名的字段在by后面必须是驼峰命名,by后面有几个字段,传的参数就有几个。

package com.example.shop.nosql.elasticsearch.repository;

import com.example.shop.nosql.elasticsearch.document.EsProduct; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**

  • 商品操作es */

public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {

// 继承 ElasticsearchRepository 的方法 findBy 后的属性必须是model所包含的属性,否则会报错。
Page<EsProduct> findByNameOrSubTitleOrKeywords(String name, String subTitle, String keywords, Pageable page);

}

另外ElasticSearchRepository支持关键字加条件的方式,如and, or, not等

condition functionName elasticsearch asl 说明 and findByNameAndPrice GET_search{"query":{"bool":{"must":[{"query_string":{"query":"小米","fields":["name"]}},{"query_string":{"query":"2499","fields":["price"]}}]}}} 用must关键字, 搜索name 为小米并且price为2499的商品。 or findByNameOrPrice GET_search{"query":{"bool":{"should":[{"query_string":{"query":"小米","fields":["name"]}},{"query_string":{"query":"2499","fields":["price"]}}]}}} 用should关键字,搜索name 为小米或price为2499的商品。 is findByName GET_search{"query":{"bool":{"must":[{"query_string":{"query":"小米","fields":["name"]}}]}}} 根据Name进行搜索。 not findByNameNot GET_search{"query":{"bool":{"must_not":[{"query_string":{"query":"小米","fields":["name"]}}]}}} 使用must_not关键字,查询除了name为小米的商品。 between findByPriceBetween GET_search{"query":{"bool":{"must":[{"range":{"price":{"gte":1000,"lte":3000}}}]}}} 使用range关键字关键字,查询价格在1000-3000的商品。 坑二、升级Springboot版本造成ElasticSearch版本不兼容 如果你的Springboot版本升级到了2.3以上,那么你的ElasticSearch版本要升到 7.2.2。否则会出现 不兼容的问题。

原文链接:blog.csdn.net/qq_33036061…