一、环境准备
springboot与elasticsearch的更新都非常快,为了避免兼容性问题,要注意下选择的版本问题。具体的可参考官网 --> ++springboot与elasticsearch版本兼容性++
1.1导入依赖
XML
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>2.7.18</version>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
1.2application.properties文件配置
javascript
spring.elasticsearch.rest.uris=http://localhost:9200
spring.elasticsearch.rest.username=dev
spring.elasticsearch.rest.password=123456
1.3创建实体对象
java
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 = "employee")
@Data
public class Employee {
@Id
private int id;
@Field(name = "name")
private String name;
@Field(name = "addr", type = FieldType.Keyword)
private String addr;
}
该实体类加上@Document注解之后,程序启动后就会自动向ED发送创建索引的请求(若没有则创建)。
1.4创建Repository接口
java
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface EmployeeRepository extends ElasticsearchRepository<Employee, Integer> {
}
1.5Crud测试案例
使用该接口可方便对索引进行增删查改。例子比较简单,我们直接写个单元测试。代码如下:
java
import gamecontext.admin.elasticsearch.Employee;
import gamecontext.admin.elasticsearch.EmployeeRepository;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest()
@RunWith(SpringRunner.class)
public class ElasticsearchTest {
@Autowired
private EmployeeRepository repository;
@Test
public void testSave() {
Employee p = new Employee();
p.setId(3);
p.setAddr("北京");
p.setName("Tim");
repository.save(p);
Assert.assertEquals("Tim", repository.findById(3).get().getName());
}
@Test
public void testDelete() {
Employee p = new Employee();
p.setId(4);
p.setAddr("北京");
p.setName("Tim");
repository.save(p);
repository.deleteById(4);
Assert.assertFalse(repository.findById(4).isPresent());
}
@Test
public void testUpdate() {
Employee p = repository.findById(3).get();
p.setName("南京");
repository.save(p);
Assert.assertEquals(repository.findById(3).get().getName(), "南京");
}
}
二、使用QueryBuilder进行文档的复杂查询
ElasticsearchRepository只能对文档进行简单的增删查改,如果遇到复杂的全文搜索,则需要借助QueryBuilder和ElasticsearchRestTemplate相关API
2.1数据准备
往索引插入3条文档,数据如下
2.2进行must查询
java
import gamecontext.admin.elasticsearch.Employee;
import gamecontext.admin.elasticsearch.EmployeeRepository;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest()
@RunWith(SpringRunner.class)
public class ElasticsearchTest2 {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@Test
public void testMust() {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("name", "李"))
.must(QueryBuilders.matchQuery("name", "四"));
nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
SearchHits<Employee> hits = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), Employee.class);
Assert.assertTrue(hits.stream().findFirst().isPresent());
Assert.assertEquals(hits.stream().findFirst().get().getContent().getName(), "李四");
}
}
等效的Kibana命令
2.3进行range查询
java
@Test
public void testRange() {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
RangeQueryBuilder builder = QueryBuilders.rangeQuery("age").lte(20).gte(10);
nativeSearchQueryBuilder.withQuery(builder);
SearchHits<Employee> hits = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), Employee.class);
Assert.assertEquals(2, hits.getTotalHits());
}
等效的Kibana命令
2.4进行sort排序
java
@Test
public void testSort() {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("age").order(SortOrder.DESC));
// .withSort(SortBuilders.fieldSort("name").order(SortOrder.ASC));
SearchHits<Employee> hits = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), Employee.class);
Employee p1 = hits.stream().findFirst().get().getContent();
List<SearchHit<Employee>> list = hits.stream().collect(Collectors.toUnmodifiableList());
Assert.assertTrue(list.get(0).getContent().getAge()>list.get(1).getContent().getAge());
Assert.assertTrue(list.get(1).getContent().getAge()>list.get(2).getContent().getAge());
}
等效的Kibana命令
默认情况下,text类型的字段是不能排序的,否则会报以下异常,
bash
Caused by: ElasticsearchException[Elasticsearch exception [type=illegal_argument_exception, reason=Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default. Please use a keyword field instead. Alternatively, set fielddata=true on [name] in order to load field data by uninverting the inverted index. Note that this can use significant memory.]]
翻译如下:
XML
默认情况下,文本字段禁用字段数据。在[region]上设置fielddata=true,以便通过取消反转索引将fielddata加载到内存中。请注意,这可能会占用大量内存。
解决方法:
- 将name字段设置为keyword类型
- 将name字段的fielddata()为true
2.5进行文本高亮
可以使用HighlightBuilder 类来设置查询结果文本高亮
java
@Test
public void testHighlight() {
String PRE_TAG = "<mark>";
String POST_TAG = "</mark>";
HighlightBuilder.Field titleField = new HighlightBuilder.Field("name");
titleField.preTags(PRE_TAG);
titleField.postTags(POST_TAG);
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("name", "李"));
nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
nativeSearchQueryBuilder.withHighlightFields(titleField);
SearchHits<Employee> search = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), Employee.class);
Map<String, List<String>> files = search.getSearchHits().stream().findFirst().get().getHighlightFields();
Assert.assertFalse(CollectionUtils.isEmpty(files));
}