springboot操作Elasticsearch
Posted fang·up·ad
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot操作Elasticsearch相关的知识,希望对你有一定的参考价值。
一、版本说明
使用版本 ElasticSearch 7.17.3 springboot 2.7 Spring Data Elasticsearch 4.4.8 jdk不需要安装,会使用ES内置的JDK。
安装指定版本的ES,安装指定版本的Spring Boot,安装Spring Boot时选择依赖Spring Data Elasticsearch,spring boot指定了Spring Data Elasticsearch的版本。版本必须对应,否则Spring Data Elasticsearch和ES无法对应起来。
Spring Data Elasticsearch - Reference Documentation 查看版本对应关系。
看Spring Data Elasticsearch官方文档一定注意版本,版本不同,API差距很大,并且分清不同package下的同名类,很容易出错。
&& https://docs.spring.io/spring-data/elasticsearch/docs/4.4.8/reference/html/#preface.metadata && 将url路径中的current或具体版本号修改为Spring Data Elasticsearch版本的版本号就可以查看对应版本文档
下图是查看spring boot中使用Spring Data Elasticsearch的版本号
二、安装ES 创建spring boot项目
Elasticsearch是一个分布式全文搜索引擎,支持单节点模式(Single-Node Mode)和集群模式(Cluster Mode)部署,一 般来说,小公司的业务场景往往使用Single-Node Mode部署即可。以Single-Node Mode部署感受下ES,后续搭建分布式集群深入学习。
安装ES
1. 虚拟机环境准备
操作系统:CentOS 7.x 64 bit
客户端连接工具:SecureCRT关闭虚拟机的防火墙
systemctl stop firewalld.service #停止firewall systemctl disable firewalld.service #禁止firewall开机启动 firewall-cmd --state # 查看防火墙
2. Elasticsearch Single-Node Mode部署
我们在虚拟机上部署Single-Node Mode Elasticsearch
下载Elasticsearch 地址: https://www.elastic.co/cn/downloads/elasticsearch 最新版本 。本课程使用7.3.0版本 。选择Linux版本下载
1.上传、解压tar.gz文件
cd /opt/lagou/software tar -zxvf elasticsearch-7.3.0-linux-x86_64.tar.gz -C ../servers
2.重命名
cd ../servers/ mv elasticsearch-7.3.0/ elasticsearch/
3 配置Elasticsearch
(1)编辑vim /opt/lagou/servers/elasticsearch/confifig/elasticsearch.yml
单机安装请取消注释:node.name: node-1,否则无法正常启动。
修改网络和端口,取消注释master节点,单机只保留一个node
node.name: node-1 network.host: 192.168.1.250 # # Set a custom port for HTTP: # http.port: 9200 cluster.initial_master_nodes: ["node-1"]
(2)按需修改vim /opt/lagou/servers/elasticsearch/confifig/jvm.options内存设置
vim /opt/lagou/servers/elasticsearch/config/jvm.options
根据实际情况修改占用内存,默认都是1G,单机1G内存,启动会占用700m+然后在安装kibana后,基本上无 法运行了,运行了一会就挂了报内存不足。 内存设置超出物理内存,也会无法启动,启动报错。
-Xms2g
-Xmx2g
Exception in thread "main" java.nio.file.AccessDeniedException: /opt/es/elasticsearch-7.17.3/config/jvm.options.d 虚拟机内存设置好,或设置大一点 1G内存肯定不行,至少4G
3.添加es用户,es默认root用户无法启动,需要改为其他用户
useradd estest #修改密码 passwd estest 改变es目录拥有者账号 chown -R estest /opt/lagou/servers/elasticsearch/
4.修改/etc/sysctl.conf
ES因为需要大量的创建索引文件,需要大量的打开系统的文件,所以我们需要解除linux系统当中打开文件最大数目的限制,不然ES启动就会抛错
修改文件句柄数 vim /etc/sysctl.conf 末尾添加:vm.max_map_count=655360 执行sysctl -p 让其生效 sysctl -p
5.修改/etc/security/limits.conf
修改linux系统对文件描述符的限制级别vim /etc/security/limits.conf 末尾添加: * soft nofile 65536 * hard nofile 65536 * soft nproc 4096 * hard nproc 4096
6.启动es
切换刚刚新建的用户 su estest 启动命令 /opt/lagou/servers/elasticsearch/bin/elasticsearch
7.配置完成:浏览器访问测试。 http://192.168.1.250:9200
8.Google商店安装head插件 就可以查看ES的运行状态或者使用Apipost自己调用接口。
我自己写的接口文件: es接口查询
新建 Spring Boot项目
(1)新建spring boot 项目,选择Spring Data Elasticserach
(2)新建repository实体类,映射Es中的index
package com.eb.fri.casebase.beanMappingESIndex; import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; @Document(indexName = "case") @Data public class Case @Id String id; //单位 @Field(type = FieldType.Text, analyzer = "ik_max_word") //发布时间 fielddata允许排序 @Field(type = FieldType.Date,pattern = "yyyy-MM-dd HH:mm:ss") String publish_time; //date类型的数据想要排序,不需要设置fieldata。只需要看下索引是date类型即可。date类型可以进行排序。Test类型可以进行分词。
(3)新建接口继承ElasticsearchRepository
package com.eb.fri.casebase.repository; import com.eb.fri.casebase.beanMappingESIndex.Case; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.annotations.Query; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import java.util.List; import java.util.Optional; public interface ElasticsearchCaseRepository extends ElasticsearchRepository<Case, String> @Override Optional<Case> findById(String s); /** * 模糊查询分页实现 * * @param pageable * @return // */ @Query(value = " \\"fuzzy\\": \\"article\\": \\"?0\\"") Page<Case> findByArticleFussy(String kw, Pageable pageable);
(4)application.yml配置
spring: elasticsearch: uris: 192.168.1.250:9200 connection-timeout: 10
(5)配置spring boot内置支持的logback日志
日志配置文件如果是下面文件名和位置,会被spring boot自动识别。
<logger name="tracer" level="trace"/> 这句是开启Spring Data Elasticsearch的 查询语句日志。
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!--======================================= 本地变量 ======================================== --> <!--在没有定义$LOG_HOME系统变量的时候,可以设置此本地变量。提交测试、上线时,要将其注释掉,使用系统变量。 --> <!-- <property name="LOG_HOME" value="D:/data/logs" /> --> <!-- 应用名称:和统一配置中的项目代码保持一致(小写) --> <property name="APP_NAME" value="base" /> <!--日志文件保留天数 --> <property name="LOG_MAX_HISTORY" value="180" /> <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 --> <!--应用日志文件保存路径 --> <property name="LOG_APP_HOME" value="$catalina.home/$APP_NAME/app" /> <!--=========================== 按照每天生成日志文件:默认配置=================================== --> <!-- 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 --> <pattern>%dyyyy-MM-dd HH:mm:ss.SSS [%thread] %-5level %logger50 - %msg%n</pattern> </encoder> </appender> <!-- 按照每天生成日志文件:主项目日志 --> <appender name="APP" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志文件输出的文件名 --> <FileNamePattern>$LOG_APP_HOME/base.%dyyyy-MM-dd.log </FileNamePattern> <!--日志文件保留天数 --> <MaxHistory>$LOG_MAX_HISTORY</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 --> <pattern>%dyyyy-MM-dd HH:mm:ss.SSS [%thread] %-5level %logger500 - %msg%n</pattern> </encoder> </appender> <!--=============================== 定义各个框架的输出级别 ====================================== --> <root level="DEBUG"> <appender-ref ref="APP" /> <appender-ref ref="STDOUT" /> </root> <!--配置es日志--> <logger name="tracer" level="trace"/> </configuration>
(5)controller调用
简单的CRUD使继承自ElasticsearchRepository接口的接口,复杂查询使用NativeSearchQuery构造查询条件。
package com.eb.fri.casebase.controller; import com.alibaba.fastjson.JSONObject; import com.eb.fri.casebase.beanMappingESIndex.Case; import com.eb.fri.casebase.repository.ElasticsearchCaseRepository; import com.eb.fri.casebase.utils.SnowFlake; import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryStringQueryBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; import java.util.List; /** * @author liguofang * 对ES数据的查询 包括全文搜索、模糊搜索等 */ @RestController @RequestMapping(value = "/es") public class ESController @Autowired ElasticsearchCaseRepository esrepo; @Autowired ElasticsearchOperations op; @ResponseBody @PostMapping(value = "/case/save") public String searchBy(@RequestBody Case caseObj) caseObj.setId(SnowFlake.getID()); Case res = esrepo.save(caseObj); return res.toString(); @GetMapping("/case/delete/id") public String deleteById(@PathVariable("id") Long id) return ""; /** * 查询指定单位的文章,根据作者,文章内容等字段 * * @param pageable * @return */ @GetMapping("/case/searchDeptArticle") public SearchHits<Case> searchDeptArticle(String dept, String fields, @PageableDefault(sort = "publish_time", direction = Sort.Direction.DESC) Pageable pageable) JSONObject props_json = JSONObject.parseObject(fields); BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); boolQuery.must(QueryBuilders.matchQuery("dept", dept)); for (String k : props_json.keySet()) boolQuery.should(QueryBuilders.matchQuery(k, props_json.getString(k))); NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder() //查询条件 .withQuery(boolQuery) //分页 .withPageable(pageable) .build(); SearchHits<Case> searchHits = op.search(nativeSearchQuery, Case.class); return searchHits;
三、DSL操作
查询表达式 | Elasticsearch: 权威指南 | Elastic 官方中文文档版本比较旧,新版本没有中文版
Fuzzy query | Elasticsearch Guide [7.17] | Elastic 指定版本
理解bool组合查询
下面的查询用于查找
title
字段匹配how to make millions
并且不被标识为spam
的文档。那些被标识为starred
或在2014之后的文档,将比另外那些文档拥有更高的排名。如果 两者 都满足。 即使数据中不满足should的数据也会查询出来,只不过评分低,排名靠后。"bool": "must": "match": "title": "how to make millions" , "must_not": "match": "tag": "spam" , "should": [ "match": "tag": "starred" , "range": "date": "gte": "2014-01-01" ]
注意数据是否返回、对评分是否有影响。
must 满足条件才返回
文档 必须 匹配这些条件才能被查询返回。
must_not 不满足条件才返回
文档 必须不 匹配这些条件才能被查询返回。
should 不影响返回,只影响得分
如果满足这些语句中的任意语句,将增加
_score
,否则,无任何影响。它们主要用于修正每个文档的相关性得分。不影响数据是否查询返回。
filter 必须满足条件才返回
必须 匹配条件才会查询返回。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
将查询移到
bool
查询的filter
语句中,这样它就自动的转成一个不评分的 filter 了。"bool": "must": "match": "title": "how to make millions" , "must_not": "match": "tag": "spam" , "should": [ "match": "tag": "starred" ], "filter": "bool": "must": [ "range": "date": "gte": "2014-01-01" , "range": "price": "lte": 29.99 ], "must_not": [ "term": "category": "ebooks" ]
关键区分:输入是否进行分词、索引内字段Test类型一定进行分词、针对全部字段匹配还是单个字段匹配。输入是否进行错别字修改
分词查询:输入单词会进行分词,索引内的字段数据会进行分词。然后进行单个字段匹配或者全部字段匹配。 给定相似度排序结果。 match queryforstring实现
模糊查询:输入单词修改某几个字,然后和索引内分词的单个字段进行匹配。只能单字段,不能全部字段。 fussy regex实现
精确匹配:term实现。输入单词不进行分词,必须和文段分词完全匹配才可以。term实现。
三、操作API
开发流程
主要工作是开启Spring Data Elasticsearch日志后,对比输出的查询dsl和直接调用es的接口的dsl是否相同。 下面是es的官网中文文档
Spring Data Elasticsearch - Reference Documentation 4.4.8版本文档
Elasticsearch学习(7)—— 查询API - 叶枫啦啦的个人空间 - OSCHINA - 中文开源技术交流社区
如何使用Spring Data Elasticsearch
(1) 自定义映射索引的实体类
package com.eb.fri.casebase.beanMappingESIndex; import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; import org.springframework.data.elasticsearch.annotations.FieldType; @Document(indexName = "case") @Data public class Case @Id String id; //单位 @Field(type = FieldType.Text, analyzer = "ik_max_word") //发布时间 fielddata允许排序 @Field(type = FieldType.Date,pattern = "yyyy-MM-dd HH:mm:ss") String publish_time; //date类型的数据想要排序,不需要设置fieldata。只需要看下索引是date类型即可。date类型可以进行排序。Test类型可以进行分词。
注解:@Document用来声明Java对象与ElasticSearch索引的关系 indexName 索引名称(是字母的话必须是小写字母) type 索引类型 shards 主分区数量,默认5 replicas 副本分区数量,默认1 createIndex 索引不存在时,是否自动创建索引,默认true 不建议自动创建索引(自动创建的索引 是按着默认类型和默认分词器) 注解:@Id 表示索引的主键 注解:@Field 用来描述字段的ES数据类型,是否分词等配置,等于Mapping描述,其属性包括如下 index 设置字段是否索引,默认是true,如果是false则该字段不能被查询 store 标记原始字段值是否应该存储在 Elasticsearch 中,默认值为false,以便于快速检索。虽然store占用磁盘空间,但是减少了计算。 type 数据类型(text、keyword、date、object、geo等) analyzer 对字段使用分词器,注意一般如果要使用分词器,字段的type一般是text。 format 定义日期时间格式 注解:@CompletionField 定义关键词索引 要完成补全搜索 analyzer 对字段使用分词器,注意一般如果要使用分词器,字段的type一般是text。 searchAnalyzer 显示指定搜索时分词器,默认是和索引是同一个,保证分词的一致性。 maxInputLength:设置单个输入的长度,默认为50 UTF-16 代码点
具体映射规则查看官网 Spring Data Elasticsearch - Reference Documentation
注意事项
(1) test类型的字段想排序 必须设置fieldata 但是springboot的api设置无效。必须es api执行才可以。
"properties": "publish_time": "type": "text", "fielddata": true
(2)date类型可以进行排序。
(3)排序字段为_score _score才会有相似度分数值,如果排序字段为其他的字段,那么_score值为NaN
(2)自定义接口实现ElasticsearchRepository接口
首先实现映射索引的自定义实体类。
然后实现接口就可以使用一些关键词实现CRUD ES数据。常用来一些简单的操作,不能够实现复杂查询语句。因此,结合ElasticsearchOperations来查询。
package com.eb.fri.casebase.repository; import com.eb.fri.casebase.beanMappingESIndex.Case; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.annotations.Query; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import java.util.List; import java.util.Optional; public interface ElasticsearchCaseRepository extends ElasticsearchRepository<Case, String> //继承来的方法 @Override Optional<Case> findById(String s); //根据关键字实现查询指定的字段 List<SearchHit<Case>> findByAuthor(String kw); /** * 分页实现 * * @param pageable * @return // */ @Query(value = "\\"match\\": \\"article\\": \\"query\\": \\"?0\\"") Page<Case> findByArticle(String kw, Pageable pageable);
(2)使用ElasticsearchOperations 结合Queries实现查询
构造查询语句的三种方式
CriteriaQuery 隔离具体DSL,根据语义进行构造查询语句
StringQuery 使用ES DSL JSON请求体
NativeSearchQuery ES所有原生API都进行了实现
通过 NativeSearchQueryBuilder.withQuery(QueryBuilder1).withFilter(QueryBuilder2).withSort(SortBuilder1).withXXXX().build(); 这样的方式来完成 NativeSearchQuery 的构建。
QueryBuilder 主要用来构建查询条件、过滤条件,SortBuilder 主要是构建排序。
要构建 QueryBuilder,我们可以使用工具类 QueryBuilders,里面有大量的方法用来完成各种各样的 QueryBuilder 的构建,字符串的、Boolean 型的、match 的、地理范围的等等。
要构建 SortBuilder,可以使用 SortBuilders 来完成各种排序。
然后就可以通过 NativeSearchQueryBuilder 来组合这些 QueryBuilder 和 SortBuilder,再组合分页的参数等等,最终就能得到一个 SearchQuery 了。
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery (QueryBuilders.queryStringQuery ("spring boot OR 书籍")).build ();
@GetMapping("/case/searchDeptArticle") public SearchHits<Case> searchDeptArticle(String dept, String props, @PageableDefault(sort = "publish_time", direction = Sort.Direction.DESC) Pageable pageable) JSONObject props_json = JSONObject.parseObject(props); BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); boolQuery.must(QueryBuilders.matchQuery("dept", dept)); for (String k : props_json.keySet()) boolQuery.should(QueryBuilders.matchQuery(k, props_json.getString(k))); NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder() //查询条件 .withQuery(boolQuery) //分页 .withPageable(pageable) .build(); SearchHits<Case> searchHits = op.search(nativeSearchQuery, Case.class); return searchHits;
Query query = new NativeSearchQueryBuilder() .addAggregation(terms("lastnames").field("lastname").size(10)) // .withQuery(QueryBuilders.matchQuery("firstname", firstName)) .build(); SearchHits<Person> searchHits = operations.search(query, Person.class);
ElasticSearch系列(六)springboot中使用QueryBuilders、NativeSearchQuery实现复杂查询_雨剑yyy的博客-CSDN博客
QueryBuilders使用
QueryBuilders是ES中的查询条件构造器。下面结合一些具体的查询场景,分析其常用方法。
假设ES中已经有title为 “总裁关心浦东开发开放” 的数据;
ik_smart分词结果:
"tokens": [ "token": "总裁", "start_offset": 3, "end_offset": 6, "type": "CN_WORD", "position": 1 , "token": "关心", "start_offset": 6, "end_offset": 8, "type": "CN_WORD", "position": 2 , "token": "浦东", "start_offset": 8, "end_offset": 10, "type": "CN_WORD", "position": 3 , "token": "开发", "start_offset": 10, "end_offset": 12, "type": "CN_WORD", "position": 4 , "token": "开放", "start_offset": 12, "end_offset": 14, "type": "CN_WORD", "position": 5 ]
精确查询
精确,指的是查询关键字(或者关键字分词后),必须与目标分词结果完全匹配。1.指定字符串作为关键词查询,关键词支持分词
//查询title字段中,包含 ”开发”、“开放" 这个字符串的document;相当于把"浦东开发开放"分词了,再查询; QueryBuilders.queryStringQuery("开发开放").defaultField("title"); //不指定feild,查询范围为所有feild QueryBuilders.queryStringQuery("青春"); //指定多个feild QueryBuilders.queryStringQuery("青春").field("title").field("content");
2.以关键字“开发开放”,关键字不支持分词
QueryBuilders.termQuery("title", "开发开放") QueryBuilders.termsQuery("fieldName", "fieldlValue1","fieldlValue2...")
3.以关键字“开发开放”,关键字支持分词QueryBuilders.matchQuery("title", "开发开放") QueryBuilders.multiMatchQuery("fieldlValue", "fieldName1", "fieldName2", "fieldName3")
模糊查询
模糊,是指查询关键字与目标关键字可以模糊匹配。1.左右模糊查询,其中fuzziness的参数作用是在查询时,es动态的将查询关键词前后增加或者删除一个词,然后进行匹配 QueryBuilders.fuzzyQuery("title", "开发开放").fuzziness(Fuzziness.ONE) 2.前缀查询,查询title中以“开发开放”为前缀的document; QueryBuilders.prefixQuery("title", "开发开放") 3.通配符查询,支持*和?,?表示单个字符;注意不建议将通配符作为前缀,否则导致查询很慢 QueryBuilders.wildcardQuery("title", "开*放") QueryBuilders.wildcardQuery("title", "开?放")
注意,
在分词的情况下,针对fuzzyQuery、prefixQuery、wildcardQuery不支持分词查询,即使有这种doucment数据,也不一定能查出来,因为分词后,不一定有“开发开放”这个词;查询总结
title为 “总裁关心浦东开发开放” 的数据
查询关键词 开发开放 放 开 queryStringQuery 查询目标中含有开发、开放、开发开放的 无 无 matchQuery 同queryStringQuery 无 无 termQuery 无结果,因为它不支持分词 无 无 prefixQuery 无结果,因为它不支持分词 无 有,目标分词中以”开“开头的 fuzzyQuery 无结果,但是与fuzziness参数有关系 无 无 wildcardQuery 开发开放*无结果 开*,有 放*,无 范围查询
//闭区间查询 QueryBuilders.rangeQuery("fieldName").from("fieldValue1").to("fieldValue2"); //开区间查询,默认是true,也就是包含 QueryBuilders.rangeQuery("fieldName").from("fieldValue1").to("fieldValue2").includeUpper(false).includeLower(false); //大于 QueryBuilders.rangeQuery("fieldName").gt("fieldValue"); //大于等于 QueryBuilders.rangeQuery("fieldName").gte("fieldValue"); //小于 QueryBuilders.rangeQuery("fieldName").lt("fieldValue"); //小于等于 QueryBuilders.rangeQuery("fieldName").lte("fieldValue");
组合查询boolQuery()QueryBuilders.boolQuery() QueryBuilders.boolQuery().must();//文档必须完全匹配条件,相当于and QueryBuilders.boolQuery().mustNot();//文档必须不匹配条件,相当于not QueryBuilders.boolQuery().should();//至少满足一个条件,这个文档就符合should,相当于or
具体demo如下:public void testBoolQuery() NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.boolQuery() .should(QueryBuilders.termQuery("title", "开发")) .should(QueryBuilders.termQuery("title", "青春")) .mustNot(QueryBuilders.termQuery("title", "潮头")) ) .withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC)) .withPageable(PageRequest.of(0, 50)) .build(); List<ArticleEntity> articleEntities = elasticsearchRestTemplate.queryForList(nativeSearchQuery, ArticleEntity.class); articleEntities.forEach(item -> System.out.println(item.toString()));
以上是查询title分词中,包含“开发”或者“青春”,但不能包含“潮头”的document;也可以多个must组合。
SortBuilders排序
上述demo中,我们使用了排序条件://按照id字段降序
.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC))
注意排序时,有个坑,就是在以id排序时,比如降序,结果可能并不是我们想要的。因为根据id排序,es实际上会根据_id进行排序,但是_id是string类型的,排序后的结果会与整型不一致。建议:在创建es的索引mapping时,将es的id和业务的id分开,比如业务id叫做myId:
@Id @Field(type = FieldType.Long, store = true) private Long myId; @Field(type = FieldType.Text, store = true, analyzer = "ik_smart") private String title; @Field(type = FieldType.Text, store = true, analyzer = "ik_smart") private String content;
这样,后续排序可以使用myId进行排序。
分页
使用如下方式分页:@Test public void testPage() NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery("title", "青春")) .withSort(SortBuilders.fieldSort("myId").order(SortOrder.DESC)) .withPageable(PageRequest.of(0, 50)) .build(); AggregatedPage<ArticleEntity> page = elasticsearchRestTemplate.queryForPage(nativeSearchQuery, ArticleEntity.class); List<ArticleEntity> articleEntities = page.getContent(); articleEntities.forEach(item -> System.out.println(item.toString()));
注意,如果不指定分页参数,es默认只显示10条。
高亮显示
查询title字段中的关键字,并高亮显示:@Test public void test() String preTag = "<font color='#dd4b39'>"; String postTag = "</font>"; NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder() .withQuery(QueryBuilders.matchQuery("title", "开发")) .withPageable(PageRequest.of(0, 50)) .withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC)) .withHighlightFields(new HighlightBuilder.Field("title").preTags(preTag).postTags(postTag)) .build(); AggregatedPage<ArticleEntity> page = elasticsearchRestTemplate.queryForPage(nativeSearchQuery, ArticleEntity.class, new SearchResultMapper() @Override public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) List<ArticleEntity> chunk = new ArrayList<>(); for (SearchHit searchHit : response.getHits()) if (response.getHits().getHits().length <= 0) return null; ArticleEntity article = new ArticleEntity(); article.setMyId(Long.valueOf(searchHit.getSourceAsMap().get("id").toString())); article.setContent(searchHit.getSourceAsMap().get("content").toString()); HighlightField title = searchHit.getHighlightFields().get("title"); if (title != null) article.setTitle(title.fragments()[0].toString()); chunk.add(article); if (chunk.size() > 0) return new AggregatedPageImpl<>((List<T>) chunk); return null; @Override public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) return null; ); List<ArticleEntity> articleEntities = page.getContent(); articleEntities.forEach(item -> System.out.println(item.toString()));
结果:
title=勇立潮头——总裁关心浦东<font color='#dd4b39'>开发</font>开放40, content=外交部
以上是关于springboot操作Elasticsearch的主要内容,如果未能解决你的问题,请参考以下文章
五万字长文详解SpringBoot 操作 ElasticSearch
ElasticSearch04_elasticsearch-Rest-Client整合SpringBoot中使用保存数据利用JAVA代码操作es
ElasticSearch04_elasticsearch-Rest-Client整合SpringBoot中使用保存数据利用JAVA代码操作es
ElasticSearch04_elasticsearch-Rest-Client整合SpringBoot中使用保存数据利用JAVA代码操作es