ElasticSearch实战

本文详细介绍了如何在Springboot项目中利用IkAnalyzer进行中文分词,并通过DSL调用Elasticsearch Restful接口实现精确和模糊搜索,包括聚类和关键字结合的复杂查询。此外,还涵盖了版本升级导致的兼容性问题和自定义Repository的操作技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


es 版本: 6.2.2

实战笔记

1. 利用IkAnalyzer获取中文分词

  1. 在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
    }
  ]
}

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

2. 使用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": []
        }
      }
    ]
  }
}

3. 使用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<EsProduct> esproductlist = productDao.getAllEsProductList(null);
        // 2. 将商品保存至es
        Iterable<EsProduct> esProductIterable = productRepository.saveAll(esproductlist);
        Iterator<EsProduct> iterator = esProductIterable.iterator();
        // 获取保存到ealsticsearch中的商品数量,每保存一个商品数量+1
        int result = 0;
        while (iterator.hasNext()) {
            result++;
            iterator.next();
        }
        return result;
    }

4. 使用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<EsProduct> 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等

conditionfunctionNameelasticsearch asl说明
andfindByNameAndPriceGET_search{"query":{"bool":{"must":[{"query_string":{"query":"小米","fields":["name"]}},{"query_string":{"query":"2499","fields":["price"]}}]}}}用must关键字, 搜索name 为小米并且price为2499的商品。
orfindByNameOrPriceGET_search{"query":{"bool":{"should":[{"query_string":{"query":"小米","fields":["name"]}},{"query_string":{"query":"2499","fields":["price"]}}]}}}用should关键字,搜索name 为小米或price为2499的商品。
isfindByNameGET_search{"query":{"bool":{"must":[{"query_string":{"query":"小米","fields":["name"]}}]}}}根据Name进行搜索。
notfindByNameNotGET_search{"query":{"bool":{"must_not":[{"query_string":{"query":"小米","fields":["name"]}}]}}}使用must_not关键字,查询除了name为小米的商品。
betweenfindByPriceBetweenGET_search{"query":{"bool":{"must":[{"range":{"price":{"gte":1000,"lte":3000}}}]}}}使用range关键字关键字,查询价格在1000-3000的商品。

坑二、升级Springboot版本造成ElasticSearch版本不兼容

如果你的Springboot版本升级到了2.3以上,那么你的ElasticSearch版本要升到 7.2.2。否则会出现 不兼容的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乌托邦钢铁侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值