SpringBoot集成ElasticSearch实现全文搜索(零基础教学)

目录

背景

摘要

maven依赖

pom文件

代码实现

创建实体

继承 Repository

添加ES数据

方式一:

方式二:

注意:

 修改ES数据

删除ES数据

查询ES数据

Kibana DSL语句

创建索引

查看索引结构

删除索引

增加索引下字段

查看用例数据

查看索引下总数据量

查看某一用例

更改索引最大返回数量


背景

Springboot项目集成ElasticSearch(以下简称“ES”),实现项目中高级/复杂搜索需求,解决传统数据库检索时大SQL语句编写复杂,速度慢,以及难维护等问题。

摘要

本文将介绍如何在SpringBoot项目中集成ElasticSearch,实现高效的全文搜索功能。全文搜索在现代Web应用中越来越重要,ElasticSearch以其强大的搜索能力、高扩展性和易用性成为首选的搜索引擎。

maven依赖

在项目中加入es所需maven依赖包

<!-- es搜索  -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.10.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-elasticsearch</artifactId>
</dependency>

pom文件

连接es服务器

spring.elasticsearch.rest.uris=http://ip:端口
spring.elasticsearch.rest.username=账号
spring.elasticsearch.rest.password=密码

代码实现

以下部分为涉及到ES的JAVA主要代码逻辑,请结合自身项目情况参考使用。

创建实体

创建es实体,项目启动会自动创建索引

@Data
@Document(indexName = "testes",createIndex = true)
public class TestEsVo implements Serializable {

    /**
     * 主键
     */
    @Id
    @Field(type = FieldType.Integer,store = true,index = true)
    Integer id;

    /**
     * 名字
     */
    @Field(type = FieldType.Text,store = true,index = true)
    String name;

    /**
     * 时间
     */
    @Field(type = FieldType.Date, pattern = "yyyy-MM-dd HH:mm:ss")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    Date create_time;

    /**
     * 集合
     */
    @Field(type = FieldType.Text,store = true,index = true)
    List<String> strList;
    
}

继承 Repository

@Repository
public interface TestEsRepository extends ElasticsearchRepository<TestEsVo, Integer> {
    
}

添加ES数据

方式一:

调用ElasticsearchRepository自带方法进行保存

// 批量添加
testEsRepository.saveAll(testEsVoList);

// 单个添加
testEsRepository.save(testEsVo);

方式二:

使用RestHighLevelClient+BulkRequest进行保存

    @Autowired
    private RestHighLevelClient client;
    private void save(List<Testcase> caseAll, List<TestCaseFipLink> caseFipLinkList, List<TestCaseEeaLink> caseEeaLinkList, List<TestCaseVirtual> test_cases_virtual, List<UseCaseVirtual> useCaseVirtuals) {
        try {

            BulkRequest bulkRequest = new BulkRequest();

            for(TestEsVo test : caseAll){

                ObjectMapper objectMapper = new ObjectMapper();
                Map<String, Object> map = objectMapper.convertValue(test, Map.class);

                bulkRequest.add(
                        new IndexRequest("testes")
                                .id(test.getId().toString())
                                .source(map));
            }

            BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            //System.out.println("====" + JSON.toJSONString(bulkResponse));

            List<String> errList = new ArrayList<>();
            if (bulkResponse.hasFailures()) {
                // 有失败的操作
                for (BulkItemResponse itemResponse : bulkResponse) {
                    if (itemResponse.isFailed()) {
                        // 处理失败的操作
                        errList.add(itemResponse.getFailure().getId());
                    }
                }
            } else {
                // 所有操作都成功
                System.out.println("All items were processed successfully");
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

注意:

大数据量,批量插入使用RestHighLevelClient+BulkRequest实现。实测效率比ElasticsearchRepository高很多。

 修改ES数据

1、先从ES查询出文档信息

2、更新文档内容

3、通过save方法重新保存

// 先查询出ES中该文档全部数据
Optional<TestEsVo> test = testCaseEsRepository.findById(2011253);
TestEsVo testEsVo = test.get();

// 修改内容
testEsVo.setName("张三-ES修改");
testEsVo.setDemo1("新增的字段");

// 保存至ES
testEsRepository.save(testEsVo);

删除ES数据

只会删除文档内容,并不会删除索引结构

// 删除该索引下全部文档
testEsRepository.deleteAll();

// 根据id删除文档
testEsRepository.deleteById(123);

查询ES数据

1、keyword不分词 (普通查询),text分词 (模糊查询)

2、若查询条件values为数组,则使用 termsQuery;若查询条件value为字符串,则使用 termQuery

3、“包含(or)” values 中任意一个匹配即可,则使用 termsQuery;

“包含(and)” values 中所有都匹配,则循环使用 termQuery,即先过滤出values中包含元素1的数据,进一步再过滤values中包含元素2的数据

4、mustNot 类似与not in,values中存在的元素都过滤掉

5、“是” 多选字段若想完全匹配,则可将其数组排序后转为String类型进行存储,再使用 termsQuery 进行匹配

6、模糊查询使用text类型,使用matchPhrasePrefixQuery() 构造短语匹配,可避免text类型分词后查询结果不准确

7、es与mysql等存在时区问题,相差八小时(es慢8小时),所以查询是需要先转换时区,或者存入es前先转换时区再查询,但date类型无法设置时区

// 确定查询方式
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
List<String> values = Arrays.asList("A","B","C");

// 包含(or) 查找values中任一元素  例:name = "A"、name = ["A"、"D"]
boolQueryBuilder.filter(QueryBuilders.termsQuery("name.keyword", values));

// 不包含 只要是在values中存在的,则过滤掉 例:name = ["A"、"D"] 既数据中存在A或D都会被过滤掉
boolQueryBuilder.mustNot(QueryBuilders.termsQuery("name.keyword", values));

// 包含(and) 查找values中的所有元素在fip中都存在 values <= fip 例:fip=["A","B","C"]、fip=["A","B","C","D"]
for(String value : values){
    boolQueryBuilder.filter(QueryBuilders.termQuery("fip.keyword", value));
}
// 是 完全匹配  例:name = ["A","B","C"] 显示,其余全不显示
values.sort(Comparator.naturalOrder());
values_str = String.join(",", values);
boolQueryBuilder.filter(QueryBuilders.termQuery("fip.keyword", values_str));

//模糊查询 类似与sql中的like。注意使用text类型,非keyword类型 例:name = "中华人民共和国" ,value = "中华" 或 "中华人民" 或 "中华人民共和国" 
MatchPhrasePrefixQueryBuilder matchPhrasePrefixQueryBuilder = QueryBuilders.matchPhrasePrefixQuery("name", value);
boolQueryBuilder.filter(matchPhrasePrefixQueryBuilder);

//时间类型 注意es与mysql等存在时区问题,相差八小时(es慢8小时),所以查询是需要先转换时区
Integer filterType = testEsDto.getFilterType();
String columnName = testEsDto.getColumnName();
//columnName = columnName + ".keyword";
List<String> valueList = (List<String>) testEsDto.getValue();
//传入es的时间参数
String start_time_str = null;
String ent_time_str = null;
//获取前端的时间参数
Date start_time = null;
Date ent_time = null;

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

if(!CollectionUtils.isEmpty(valueList)){
    try {
        start_time = sdf.parse(valueList.get(0));
        start_time_str = dateFormat.format(start_time);
        if(valueList.size() > 1){
            ent_time = sdf.parse(valueList.get(1));
            ent_time_str = dateFormat.format(ent_time);
        }
    } catch (ParseException e) {
        throw new RuntimeException(e);
    }
}
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(columnName);
            rangeQueryBuilder.gte(start_time_str);
            rangeQueryBuilder.lte(start_time_str);
            
boolQueryBuilder.filter(rangeQueryBuilder);

//分页
Pageable pageable = PageRequest.of(pageNum-1, pageSize);
//排序 text不可排序
FieldSortBuilder sortBuilder = SortBuilders.fieldSort("name.keyword").order(SortOrder.DESC);
//构建查询条件
NativeSearchQuery request = new NativeSearchQueryBuilder()
        .withQuery(boolQueryBuilder)
        .withPageable(pageable)
        .withSort(sortBuilder)
        .build();
//执行,获得检索结果 可使用 ElasticsearchRestTemplate template;        
SearchHits<TestEsVo> search = template.search(request, TestEsVo.class);
//遍历结果,进行处理
for (SearchHit<TestEsVo> hit : search){
    TestEsVo content = hit.getContent();
    TestEsVos.add(content);
}

Page<TestEsVo> page = new PageImpl<>(TestEsVos, pageable, search.getTotalHits());

Kibana DSL语句

创建索引

建议提前维护索引,springboot自动创建的索引格式可能与需求不符。

注意:维护数据前一定要检查ES生成的文档结构是否与以下相符

PUT / testes {
	"mappings": {
		"properties": {
			"_class": {
				"type": "text",
				"fields": {
					"keyword": {
						"type": "keyword",
						"ignore_above": 256
					}
				}
			},
			"id": {
				"type": "long"
			},
			"name": {
				"type": "text",
				"fields": {
					"keyword": {
						"type": "keyword",
						"ignore_above": 256
					}
				}
			},
			"create_time": {
				"type": "date",
				"format": "yyyy-MM-dd HH:mm:ss"
			},
			"ad_str": {
				"type": "text",
				"fields": {
					"keyword": {
						"type": "keyword",
						"ignore_above": 256
					}
				}
			},
			"strList": {
				"type": "text",
				"fields": {
					"keyword": {
						"type": "keyword",
						"ignore_above": 256
					}
				}
			}
		}
	}
}

查看索引结构

GET testes

删除索引

注意:删除用例索引后,索引下的数据也全部被删除。谨慎使用

delete testes

增加索引下字段

两种方法,推荐使用DTL语句提前维护,可避免springboot自动创建的类型不适应需求

1、使用DTL语句添加

PUT /testes/_mapping
{
  "properties": {
    "new_field": {
      "type": "text"
    }
  }
}

2、在Java代码对象中定义字段springboot自动创建

@Field(type = FieldType.Text,store = true,index = true)
String demo1;

查看用例数据

ES限制最多只能查看10000条,不加分页的话最多返回10条

GET testes/_search
{
  "from": 0,
  "size": 10000,
  "query": {
    "match_all": {}
  },
  "track_total_hits": true
}

查看索引下总数据量

GET /_cat/indices/testes?v&s=docs.count:desc&h=index,docs.count,store.size

查看某一用例

GET /{索引名称}/_doc/{用例ID}

GET /testes/_doc/3010168

更改索引最大返回数量

ES限制最多只能查看10000条,可根据实际情况调整,调整过大可能会影响性能

PUT /testes/_settings
{
  "index": {
    "max_result_window": "50000"
  }
}

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要在Spring Boot集成Elasticsearch 7,请按照以下步骤进行操作: 1. 首先,确保你的Spring Boot版本与Elasticsearch版本兼容。根据引用[1],你需要保证Spring Boot集成的版本与安装的Elasticsearch版本一致。在本教程中,Spring Boot的版本为v2.6.2,集成Elasticsearch版本为7.15.2。 2. 然后,在你的Spring Boot项目中添加Elasticsearch的依赖。根据引用,你需要添加如下依赖到你的pom.xml文件中: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> ``` 这个依赖将会自动集成Spring Data Elasticsearch,以便于你使用Elasticsearch作为数据存储。 3. 接下来,你需要配置Elasticsearch的连接信息。你可以在`application.properties`或`application.yml`文件中添加如下配置: ```yaml spring.elasticsearch.rest.uris=http://localhost:9200 ``` 这个配置指定了Elasticsearch的REST API的地址。 4. 现在,你可以在你的代码中使用Spring Data Elasticsearch来访问Elasticsearch了。你可以创建一个Elasticsearch的Repository接口,并且使用它来定义你的数据访问操作。 ```java @Repository public interface YourRepository extends ElasticsearchRepository<YourEntity, String> { // 定义你的数据访问操作 } ``` 这里的`YourEntity`是你的实体类,它需要使用`@Document`注解来指定索引和类型的信息。 至此,你已经成功地将Elasticsearch 7集成到了Spring Boot中。你可以使用Spring Data Elasticsearch提供的丰富的功能来进行数据操作和查询。注意,这只是一个基本的示例,你可能需要根据你的具体需求进行更多的配置和操作。 希望这个回答能够帮助到你!如果你有任何其他问题,请随时提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值