java客户端有TransportClient和RestClient
elasticsearcElasticsearch计划在Elasticsearch 7.0中弃用TransportClient,在8.0中完全删除它
下面介绍RestClient客户端常用api 官网文档 Java High Level REST Client | Java REST Client [6.8] | Elastic
pom.xml添加依赖
<!-- https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.8.8</version>
</dependency>
装配RestHighLevelClient 实例
package com.wl.elasticsearch.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created by Administrator on 2021/3/15.
*/
@Configuration
public class ElasticSearchConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
return new RestHighLevelClient(
RestClient.builder(
new HttpHost("192.168.92.128", 9200, "http")
));
}
}
The high-level client 内部会创建一个low-level client 用来请求elasticsearch服务端,low-level client维护一个连接池并启动一些线程,因此您应该在完全正确地使用高级客户端后关闭它 clint.close
索引api
1.1创建一个索引
package com.wl.elasticsearch.service;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.common.settings.Settings;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created by Administrator on 2021/3/15.
*/
@Service
public class ElasticSearchService implements DisposableBean {
private RestHighLevelClient client;
@Autowired
public ElasticSearchService(RestHighLevelClient client){
this.client = client;
}
/**
* 增加索引
* @param index 索引名称
* @param shards 每个索引的主分片数,默认值是 5 。这个配置在索引创建后不能修改。
* @param replicas 每个主分片的副本数,默认值是 1 。对于活动的索引库,这个配置可以随时修改。
*/
public CreateIndexResponse createIndex(String index,Integer shards,Integer replicas ) throws Exception{
CreateIndexRequest request = new CreateIndexRequest(index);
request.settings(Settings.builder()
.put("index.number_of_shards", shards)
.put("index.number_of_replicas", replicas)
);
return client.indices().create(request, RequestOptions.DEFAULT);
}
@Override
public void destroy() throws Exception {
client.close();
}
}
测试代码
package com.wl.elasticsearch;
import com.wl.elasticsearch.service.ElasticSearchService;
import org.elasticsearch.client.indices.CreateIndexResponse;
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.SpringJUnit4ClassRunner;
/**
* Created by Administrator on 2021/3/15.
*/
@SpringBootTest(classes = Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ElasticSearchTest {
@Autowired
private ElasticSearchService elasticSearchService;
@Test
public void testCreateIndex() throws Exception{
String index = "test-01";
Integer shards = 5;
Integer replicas = 1;
CreateIndexResponse response = elasticSearchService.createIndex(index,shards,replicas);
System.out.println(response.index());
}
}
使用 curl -X GET "localhost:9200/_cat/indices?v" 查看索引
wl@ubuntu:/usr/local/elastic/elasticsearch-6.8.8$ curl -X GET "localhost:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open test-01 g2fhnaWsRB-FGLN1U1-E8Q 5 1 0 0 1.1kb 1.1kb
wl@ubuntu:/usr/local/elastic/elasticsearch-6.8.8$
1.2删除索引(如果索引不存在会抛出ElaseticsearchException 异常)
/**
* 删除索引
* @param index 索引名称
*/
public AcknowledgedResponse deleteIndex(String index) throws Exception{
DeleteIndexRequest request = new DeleteIndexRequest(index);
return client.indices().delete(request, RequestOptions.DEFAULT);
}
1.3判断索引是否存在
/**
*
* @param index 索引名称数组
*/
public boolean existIndex(String... index) throws Exception{
GetIndexRequest request = new GetIndexRequest(index);
return client.indices().exists(request,RequestOptions.DEFAULT);
}
1.4查询索引
/**
* 查询索引
* @param index 索引名称数组
*/
public GetIndexResponse queryIndex(String... index) throws Exception{
GetIndexRequest request = new GetIndexRequest(index);
return client.indices().get(request,RequestOptions.DEFAULT);
}
1.5关闭和打开索引
/**
* 针对部分索引,我们暂时不需要对其进行读写,可以临时关闭索引,以减少es服务器的开销
* @param index 索引名称
*/
public AcknowledgedResponse closeIndex(String... index) throws Exception{
CloseIndexRequest request = new CloseIndexRequest(index);
return client.indices().close(request,RequestOptions.DEFAULT);
}
/**
* 将关闭的索引打开
* @param index 索引名称
*/
public OpenIndexResponse openIndex(String... index) throws Exception{
OpenIndexRequest request = new OpenIndexRequest(index);
return client.indices().open(request,RequestOptions.DEFAULT);
}
2.document apis
如果将index比作数据库的一张表,那么document 就是表中的数据
2.1增加一条document
/**
* 在索引下新增一个document 文档
* @param index 索引名称 如果索引不存在会自动新增索引
* @param type 文档类型 6.0.0版本之前每个index可以增加多个文档类型,6.0.0-7.0.0只能增加一个类型,7.0之后只能是_doc,8.0版本之后将不支持
* @param documentId 文档id 如果不指定documentId,那么elasticsearch会自动生成一个uuid(建议该id包含其模块类型和其数据库表中的主键id)
* @param source 文档内容 json格式的字符串
*/
public IndexResponse addDocument(String index, String type, String documentId, String source) throws Exception{
IndexRequest request = new IndexRequest(index, type, documentId);
request.source(source, XContentType.JSON);
return client.index(request,RequestOptions.DEFAULT);
}
测试代码
@Test
public void testAddDocument() throws Exception{
String index = "test-01";
String type = "_doc";
String documentId = "test01-0001";
Map<String,Object> map = new HashMap<>();
map.put("user","wl");
map.put("postDate","2021-03-16");
map.put("message","test-01 document message");
String source = new ObjectMapper().writeValueAsString(map);
IndexResponse response = elasticSearchService.addDocument(index,type,documentId,source);
System.out.println(response.toString());
}
执行后查看索引。test-01上已增加一条文档
wl@ubuntu:~$ curl -X GET "localhost:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open test-01 BWioBM5wQdSKFR-xh0OA5Q 5 1 1 0 5.2kb 5.2kb
查询user=wl的文档 curl -X GET 'http://localhost:9200/test-01/_search?q=user:wl&pretty=true'
wl@ubuntu:~$ curl -X GET 'http://localhost:9200/test-01/_search?q=user:wl&pretty=true'
{
"took" : 70,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 0.2876821,
"hits" : [
{
"_index" : "test-01",
"_type" : "_doc",
"_id" : "test01-0001",
"_score" : 0.2876821,
"_source" : {
"postDate" : "2021-03-16",
"message" : "test-01 document message",
"user" : "wl"
}
}
]
}
}
2.2通过document id 查看document文档
/**
* 通过documentId 查询文档
* @param index 索引名称
* @param type 文档类型
* @param documentId 文档id
*/
public GetResponse getDocument(String index, String type, String documentId) throws Exception{
GetRequest getRequest = new GetRequest(index, type, documentId);
return client.get(getRequest,RequestOptions.DEFAULT);
}
2.3 更新文档
/**
* 更新索引下文档id为documentId的文档
* @param index 索引名称
* @param type 文档类型
* @param documentId 文档id
* @param source 文档内容 json格式的字符串
*/
public UpdateResponse updateDocument(String index, String type, String documentId, String source) throws Exception{
UpdateRequest request = new UpdateRequest(index,type,documentId);
request.doc(source, XContentType.JSON);
return client.update(request,RequestOptions.DEFAULT);
}
2.4删除文档
/**
* 删除索引下文档id为documentId的文档
* @param index 索引名称
* @param type 文档类型
* @param documentId 文档id
*/
public DeleteResponse deleteDocument(String index, String type, String documentId) throws Exception{
DeleteRequest request = new DeleteRequest(index, type, documentId);
return client.delete(request,RequestOptions.DEFAULT);
}
3.search api
在介绍search api之前我们先添加一组文章数据用于测试
/**
* 添加文章数据提供search api调用
*/
@Test
public void initArticleData() throws Exception{
String index = "article-index";
String title[] = {"静夜思","小池","赠刘景文","山行","回乡偶书","赠汪伦","望庐山瀑布"};
String author[] = {"李白","杨万里","苏轼","杜牧","贺知章","李白","李白"};
String dynasty[] = {"唐","宋","宋","唐","唐","唐","唐"};
String content[] = {"床前明月光","泉眼无声惜细流","荷尽已无擎雨盖","远上寒山石径斜","少小离家老大回","李白乘舟将欲行","日照香炉生紫烟"};
for(int i=0;i<title.length;i++){
Map<String,Object> map = new HashMap<>();
map.put("title",title[i]);
map.put("author",author[i]);
map.put("dynasty",dynasty[i]);
map.put("content",content[i]);
elasticSearchService.addDocument(index,"_doc","article-" + i,new ObjectMapper().writeValueAsString(map));
}
}
添加成功后 查询作者为李白 的数据
3.1全文搜索
/**
* 全文搜索
* @param word 关键字
* @param from 数据起始位置
* @param size 数据大小
*/
public SearchResponse searchFullText(String word, Integer from, Integer size) throws Exception{
SearchRequest request = new SearchRequest(); //Without arguments this runs against all indices.
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.queryStringQuery(word));
sourceBuilder.from(from);
sourceBuilder.size(size);
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
request.source(sourceBuilder);
return client.search(request,RequestOptions.DEFAULT);
}
测试代码
@Test
public void testSearchFullText() throws Exception{
String word = "李白";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.searchFullText(word,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
测试结果
2021-03-16 17:21:16.886 INFO 5516 --- [ main] com.wl.elasticsearch.ElasticSearchTest : Started ElasticSearchTest in 2.285 seconds (JVM running for 3.064)
[{
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-5",
"_score" : 1.8806726,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "赠汪伦",
"content" : "李白乘舟将欲行"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-6",
"_score" : 1.5098256,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "望庐山瀑布",
"content" : "日照香炉生紫烟"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-0",
"_score" : 0.94000727,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "静夜思",
"content" : "床前明月光"
}
}]
2021-03-16 17:21:17.198 INFO 5516 --- [ Thread-2] o.s.w.c.s.GenericWebApplicationContext : Closing org.springframework.web.context.support.GenericWebApplicationContext@49d3c823: startup date [Tue Mar 16 17:21:14 CST 2021]; root of context hierarchy
Disconnected from the target VM, address: '127.0.0.1:62740', transport: 'socket'
说明:
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); 未指定index表示查询所有的index
sourceBuilder.query(QueryBuilders.queryStringQuery(word));表示所有的字段都去匹配word。可以通过sourceBuilder.query(QueryBuilders.queryStringQuery(word).field(fieldName))指定一个或多个匹配的字段属性
也可以通过QueryBuilders.queryStringQuery(word).anlyzer(anlyzer)来指定分析器。例如 standard(elasticsearch默认)、english、keyword、simple、stop等。
通过sourceBuilder.query(QueryBuilders.queryStringQuery(word).field(fieldName,boost))增加这个属性的匹配权重。boost默认为1,大于1时会提升该字段的权重
通过fuzziness解决单词拼写错误 参考:Fuzziness · elasticsearch-definitive-guide-cn · 看云(上面这种模式测试不支持fuzziness)
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));默认根据_score字段排序。指定字段排序:sourceBuilder.sort(new FieldSortBuilder("fieldName").order(SortOrder.ASC));
使用minimumShouldMatch指定匹配的最小匹配度。minimumShouldMatch必须指定字段才能生效。minimumShouldMatch可以是数字也可以是百分比。例如
sourceBuilder.query(QueryBuilders.queryStringQuery(word).field("author").minimumShouldMatch("3"));
查询时会把word进行分词。eg:李白分为李、白,因为最小匹配必须匹配到三个字(minimumShouldMatch=3),而李白最多只能分两个字且只能匹配到两个字,所以是匹配不到的。 而杨万里、贺知章是可以匹配的。将minmumShouldMatch修改为2则李白或李太白都能匹配到。如果word只有一个字(eg:李)那么minmumShouldMatch的设置是无效的
minmumShouldMatch可以根据word的长度进行调整(英文根据单词的个数调整)。word长度为1时minmumShouldMatch的设置是无效的
该查询为分词匹配的,将word修改为:李太白杜牧 将会查出含有李白和杜牧的所有数据。中文分词默认每一个字为一个词去匹配所有字段的内容(李、太、白、杜、牧5个字分别匹配。英文为以空格做分割为一个单词去匹配)
通配符查询:查询对象word通过 *代替一个或多个字符
指定field匹配和分析器、匹配度测试代码如下
public SearchResponse searchFullText(String word, Integer from, Integer size) throws Exception{
SearchRequest request = new SearchRequest(); //Without arguments this runs against all indices.
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.queryStringQuery(word).field("message",2).field("author").analyzer("standard").minimumShouldMatch("3"));
sourceBuilder.from(from);
sourceBuilder.size(size);
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
request.source(sourceBuilder);
return client.search(request,RequestOptions.DEFAULT);
}
注意:英文句子以空格分割为单词去匹配
@Test
public void testSearchFullText() throws Exception{
String word = "update message document 贺知章李白";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.searchFullText(word,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
执行结果
2021-03-18 14:02:32.837 INFO 13128 --- [ main] com.wl.elasticsearch.ElasticSearchTest : Started ElasticSearchTest in 2.044 seconds (JVM running for 2.753)
[{
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-4",
"_score" : 1.9221728,
"_source" : {
"dynasty" : "唐",
"author" : "贺知章",
"title" : "回乡偶书",
"content" : "少小离家老大回"
}
}, {
"_index" : "test-01",
"_type" : "_doc",
"_id" : "test01-0001",
"_score" : 1.7260926,
"_source" : {
"postDate" : "2021-03-16",
"message" : "test-01 document update message",
"user" : "wl"
}
}]
2021-03-18 14:02:33.172 INFO 13128 --- [ Thread-2] o.s.w.c.s.GenericWebApplicationContext : Closing org.springframework.web.context.support.GenericWebApplicationContext@49d3c823: startup date [Thu Mar 18 14:02:31 CST 2021]; root of context hierarchy
Disconnected from the target VM, address: '127.0.0.1:56829', transport: 'socket'
3.2 termQuery
对某一字段的精确匹配(只有100%匹配才能查询到)
/**
*
* @param index 索引数组
* @param field 字段
* @param value 字段对应要匹配的数据
* @param from 结果集起始位置
* @param size 结果集大小
*/
public SearchResponse termQuery(String[] index,String field,Object value,Integer from,Integer size) throws Exception{
SearchRequest request;
if(index != null && index.length > 0){
request = new SearchRequest(index);
}else {
request = new SearchRequest();
}
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery(field,value));
sourceBuilder.from(from);
sourceBuilder.size(size);
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
request.source(sourceBuilder);
return client.search(request,RequestOptions.DEFAULT);
}
测试代码
@Test
public void testTermQuery() throws Exception{
String[] index = {};
String field = "author";
Object value = "李白";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.termQuery(index,field,value,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
执行发现没有查询到作者为李白的数据
2021-03-17 22:59:35.045 INFO 3380 --- [ main] com.wl.elasticsearch.ElasticSearchTest : Started ElasticSearchTest in 1.999 seconds (JVM running for 2.886)
[]
Disconnected from the target VM, address: '127.0.0.1:53113', transport: 'socket'
这是因为termQuery不支持中文字符串(只支持单个中文),将value修改为李,将会查出所有作者中带李的文章数据。
想要查询多个中文字符串,需要将field修改为 field="author.keyword" 如下
@Test
public void testTermQuery() throws Exception{
String[] index = {};
String field = "author.keyword";
Object value = "李白";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.termQuery(index,field,value,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
结果如下
2021-03-17 23:03:26.200 INFO 10784 --- [ main] com.wl.elasticsearch.ElasticSearchTest : Started ElasticSearchTest in 2.169 seconds (JVM running for 2.94)
[{
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-6",
"_score" : 0.6931472,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "望庐山瀑布",
"content" : "日照香炉生紫烟"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-0",
"_score" : 0.47000363,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "静夜思",
"content" : "床前明月光"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-5",
"_score" : 0.47000363,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "赠汪伦",
"content" : "李白乘舟将欲行"
}
}]
2021-03-17 23:03:26.509 INFO 10784 --- [ Thread-2] o.s.w.c.s.GenericWebApplicationContext : Closing org.springframework.web.context.support.GenericWebApplicationContext@49d3c823: startup date [Wed Mar 17 23:03:24 CST 2021]; root of context hierarchy
Disconnected from the target VM, address: '127.0.0.1:53168', transport: 'socket'
说明:可以使用termsQuery指定多个value 例如
sourceBuilder.query(QueryBuilders.termsQuery(field,"李白","苏轼"));
将会查出作者为李白和苏轼的文章
3.3 matchQuery(不同于termQuery的精确匹配,matchQuery会分词匹配)
public SearchResponse matchQuery(String[] index,String field,Object value,Integer from,Integer size) throws Exception{
SearchRequest request;
if(index != null && index.length > 0){
request = new SearchRequest(index);
}else {
request = new SearchRequest();
}
QueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(field, value)
.fuzziness(Fuzziness.ONE)
.prefixLength(3)
.maxExpansions(10);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(matchQueryBuilder);
sourceBuilder.from(from);
sourceBuilder.size(size);
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
request.source(sourceBuilder);
return client.search(request,RequestOptions.DEFAULT);
}
测试代码
@Test
public void testMatchQuery() throws Exception{
String[] index = {};
String field = "message";
Object value = "update";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.matchQuery(index,field,value,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
测试结果
2021-03-17 23:20:48.507 INFO 14836 --- [ main] com.wl.elasticsearch.ElasticSearchTest : Started ElasticSearchTest in 2.292 seconds (JVM running for 3.059)
[{
"_index" : "test-01",
"_type" : "_doc",
"_id" : "test01-0001",
"_score" : 0.2876821,
"_source" : {
"postDate" : "2021-03-16",
"message" : "test-01 document update message",
"user" : "wl"
}
}]
Disconnected from the target VM, address: '127.0.0.1:53618', transport: 'socket'
2021-03-17 23:20:48.793 INFO 14836 --- [ Thread-2] o.s.w.c.s.GenericWebApplicationContext : Closing org.springframework.web.context.support.GenericWebApplicationContext@49d3c823: startup date [Wed Mar 17 23:20:46 CST 2021]; root of context hierarchy
上面matchQuery使用了fuzziness模糊匹配,fuzziness值为1
将测试代码中的value = "update" 修改为 value="updae" 或"udate"等,也可以查询到这条记录。因为updae 修改为update只需要添加、删除、移位一次
与termsQuery指定多个text不同 QueryBuilders.multiMatchQuery(text,field1,field2)指定多个属性名称
其他用法类似3.1中的searchFullText
3.4 bool查询(联合查询)
如果我们有多个text需要匹配多个field,那么我们可以使用bool查询。一个简单的bool查询允许我们写出非常复杂的逻辑
public SearchResponse boolQuery(String[] index,String field1,String field2,Object value1,Object value2,Integer from,Integer size) throws Exception{
SearchRequest request;
if(index != null && index.length > 0){
request = new SearchRequest(index);
}else {
request = new SearchRequest();
}
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder
.should(QueryBuilders.matchQuery(field1, value1).minimumShouldMatch("2"))
.should(QueryBuilders.matchQuery(field2,value2))
;
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(boolQueryBuilder);
sourceBuilder.from(from);
sourceBuilder.size(size);
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
request.source(sourceBuilder);
return client.search(request,RequestOptions.DEFAULT);
}
测试代码(查询作者为苏轼或者朝代为唐朝的文章)
@Test
public void testBoolQuery() throws Exception{
String[] index = {};
String field1 = "author";
String value1 = "苏轼";
String field2 = "dynasty";
String value2 = "唐";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.boolQuery(index,field1,field2,value1,value2,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
测试结果
2021-03-18 15:46:30.775 INFO 14060 --- [ main] com.wl.elasticsearch.ElasticSearchTest : Started ElasticSearchTest in 2.019 seconds (JVM running for 2.751)
[{
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-2",
"_score" : 0.5753642,
"_source" : {
"dynasty" : "宋",
"author" : "苏轼",
"title" : "赠刘景文",
"content" : "荷尽已无擎雨盖"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-4",
"_score" : 0.18232156,
"_source" : {
"dynasty" : "唐",
"author" : "贺知章",
"title" : "回乡偶书",
"content" : "少小离家老大回"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-6",
"_score" : 0.18232156,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "望庐山瀑布",
"content" : "日照香炉生紫烟"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-0",
"_score" : 0.13353139,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "静夜思",
"content" : "床前明月光"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-3",
"_score" : 0.13353139,
"_source" : {
"dynasty" : "唐",
"author" : "杜牧",
"title" : "山行",
"content" : "远上寒山石径斜"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-5",
"_score" : 0.13353139,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "赠汪伦",
"content" : "李白乘舟将欲行"
}
}]
说明:boolQueryBuilder有should、must等方法。should表示可以匹配也可以不匹配,must表示必须匹配。should、must方法中也可以嵌套boolQuery、matchQuery、termQuery等
我们将上面boolQuery的第二个should条件修改为must 如下
boolQueryBuilder
.should(QueryBuilders.matchQuery(field1, value1).minimumShouldMatch("2"))
.must(QueryBuilders.matchQuery(field2,value2))
;
再次执行test代码(这次表示朝代必须为唐朝,author可以为苏轼也可以不为苏轼)
2021-03-18 15:51:07.733 INFO 18372 --- [ main] com.wl.elasticsearch.ElasticSearchTest : Started ElasticSearchTest in 2.14 seconds (JVM running for 2.879)
[{
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-4",
"_score" : 0.18232156,
"_source" : {
"dynasty" : "唐",
"author" : "贺知章",
"title" : "回乡偶书",
"content" : "少小离家老大回"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-6",
"_score" : 0.18232156,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "望庐山瀑布",
"content" : "日照香炉生紫烟"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-0",
"_score" : 0.13353139,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "静夜思",
"content" : "床前明月光"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-3",
"_score" : 0.13353139,
"_source" : {
"dynasty" : "唐",
"author" : "杜牧",
"title" : "山行",
"content" : "远上寒山石径斜"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-5",
"_score" : 0.13353139,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "赠汪伦",
"content" : "李白乘舟将欲行"
}
}]
可以看到匹配到了朝代为唐朝的所有文章,但是没有author为苏轼的文章。
因为must的优先级是高于should的,elasticsearch先通过must过滤数据,再将must匹配到的数据通过should进行匹配(无论should匹配到还是匹配不到,must条件匹配到的数据都会显示,但是should匹配到的数据的优先级将会提高)
将测试代码中的value1修改为李白 测试结果如下
2021-03-18 15:59:16.912 INFO 12352 --- [ main] com.wl.elasticsearch.ElasticSearchTest : Started ElasticSearchTest in 2.06 seconds (JVM running for 2.841)
[{
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-6",
"_score" : 1.6921471,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "望庐山瀑布",
"content" : "日照香炉生紫烟"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-0",
"_score" : 1.0735387,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "静夜思",
"content" : "床前明月光"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-5",
"_score" : 1.0735387,
"_source" : {
"dynasty" : "唐",
"author" : "李白",
"title" : "赠汪伦",
"content" : "李白乘舟将欲行"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-4",
"_score" : 0.18232156,
"_source" : {
"dynasty" : "唐",
"author" : "贺知章",
"title" : "回乡偶书",
"content" : "少小离家老大回"
}
}, {
"_index" : "article-index",
"_type" : "_doc",
"_id" : "article-3",
"_score" : 0.13353139,
"_source" : {
"dynasty" : "唐",
"author" : "杜牧",
"title" : "山行",
"content" : "远上寒山石径斜"
}
}]
仍然所有朝代为唐朝的文章都被查询出来,但是作者为李白的数据的优先级是高于其他唐朝诗人的。
如果这时将第一个should也修改为must,则只会查询出李白的文章。
本文中的全部代码
package com.wl.elasticsearch.service;
import org.elasticsearch.action.admin.indices.close.CloseIndexRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created by Administrator on 2021/3/15.
*/
@Service
public class ElasticSearchService implements DisposableBean {
private RestHighLevelClient client;
@Autowired
public ElasticSearchService(RestHighLevelClient client){
this.client = client;
}
//######################################index api################################################
/**
* 增加索引
* @param index 索引名称
* @param shards 每个索引的主分片数,默认值是 5 。这个配置在索引创建后不能修改。
* @param replicas 每个主分片的副本数,默认值是 1 。对于活动的索引库,这个配置可以随时修改。
*/
public CreateIndexResponse createIndex(String index,Integer shards,Integer replicas ) throws Exception{
CreateIndexRequest request = new CreateIndexRequest(index);
request.settings(Settings.builder()
.put("index.number_of_shards", shards)
.put("index.number_of_replicas", replicas)
);
return client.indices().create(request, RequestOptions.DEFAULT);
}
// /**
// * 增加分词器
// * @param index 索引名称
// * @param field 字段名称
// * @param text 分词内容
// *
// */
// public AnalyzeResponse addIndexAnalyzer(String index, String field, String text) throws Exception{
// AnalyzeRequest request = new AnalyzeRequest();
// request.index(index);
// request.field(field);
// request.text(text);
// return client.indices().analyze(request,RequestOptions.DEFAULT);
//
// }
/**
* 删除索引
* @param index 索引名称
*/
public AcknowledgedResponse deleteIndex(String index) throws Exception{
DeleteIndexRequest request = new DeleteIndexRequest(index);
return client.indices().delete(request, RequestOptions.DEFAULT);
}
/**
* 判断索引是否存在
* @param index 索引名称数组
*/
public boolean existIndex(String... index) throws Exception{
GetIndexRequest request = new GetIndexRequest(index);
return client.indices().exists(request,RequestOptions.DEFAULT);
}
/**
* 查询索引
* @param index 索引名称数组
*/
public GetIndexResponse queryIndex(String... index) throws Exception{
GetIndexRequest request = new GetIndexRequest(index);
return client.indices().get(request,RequestOptions.DEFAULT);
}
/**
* 针对部分索引,我们暂时不需要对其进行读写,可以临时关闭索引,以减少es服务器的开销
* @param index 索引名称
*/
public AcknowledgedResponse closeIndex(String... index) throws Exception{
CloseIndexRequest request = new CloseIndexRequest(index);
return client.indices().close(request,RequestOptions.DEFAULT);
}
/**
* 将关闭的索引打开
* @param index 索引名称
*/
public OpenIndexResponse openIndex(String... index) throws Exception{
OpenIndexRequest request = new OpenIndexRequest(index);
return client.indices().open(request,RequestOptions.DEFAULT);
}
//################################document api#######################################
/**
* 在索引下新增一个document 文档
* @param index 索引名称 如果索引不存在会自动新增索引
* @param type 文档类型 6.0.0版本之前每个index可以增加多个文档类型,6.0.0-7.0.0只能增加一个类型,7.0之后只能是_doc,8.0版本之后将不支持
* @param documentId 文档id 如果不指定documentId,那么elasticsearch会自动生成一个uuid(建议该id包含其模块类型和其数据库表中的主键id)
* @param source 文档内容 json格式的字符串
*/
public IndexResponse addDocument(String index, String type, String documentId, String source) throws Exception{
IndexRequest request = new IndexRequest(index, type, documentId);
request.source(source, XContentType.JSON);
return client.index(request,RequestOptions.DEFAULT);
}
/**
* 通过documentId 查询文档
* @param index 索引名称
* @param type 文档类型
* @param documentId 文档id
*/
public GetResponse getDocument(String index, String type, String documentId) throws Exception{
GetRequest getRequest = new GetRequest(index, type, documentId);
return client.get(getRequest,RequestOptions.DEFAULT);
}
/**
* 更新索引下文档id为documentId的文档
* @param index 索引名称
* @param type 文档类型
* @param documentId 文档id
* @param source 文档内容 json格式的字符串
*/
public UpdateResponse updateDocument(String index, String type, String documentId, String source) throws Exception{
UpdateRequest request = new UpdateRequest(index,type,documentId);
request.doc(source, XContentType.JSON);
return client.update(request,RequestOptions.DEFAULT);
}
/**
* 删除索引下文档id为documentId的文档
* @param index 索引名称
* @param type 文档类型
* @param documentId 文档id
*/
public DeleteResponse deleteDocument(String index, String type, String documentId) throws Exception{
DeleteRequest request = new DeleteRequest(index, type, documentId);
return client.delete(request,RequestOptions.DEFAULT);
}
//############################################search api####################################################
/**
* 全文搜索
* @param word 关键字
* @param from 数据起始位置 从0开始 默认为0
* @param size 数据大小
*/
public SearchResponse searchFullText(String word, Integer from, Integer size) throws Exception{
SearchRequest request = new SearchRequest(); //Without arguments this runs against all indices.
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// sourceBuilder.query(QueryBuilders.queryStringQuery(word).field("message",2).field("author").analyzer("standard").minimumShouldMatch("3"));
sourceBuilder.query(QueryBuilders.queryStringQuery(word));
sourceBuilder.from(from);
sourceBuilder.size(size);
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
request.source(sourceBuilder);
return client.search(request,RequestOptions.DEFAULT);
}
/**
*
* @param index 索引数组
* @param field 字段
* @param value 字段对应要匹配的数据
* @param from 结果集起始位置
* @param size 结果集大小
*/
public SearchResponse termQuery(String[] index,String field,Object value,Integer from,Integer size) throws Exception{
SearchRequest request;
if(index != null && index.length > 0){
request = new SearchRequest(index);
}else {
request = new SearchRequest();
}
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery(field,value));
// sourceBuilder.query(QueryBuilders.termsQuery(field,"李白","苏轼"));
sourceBuilder.from(from);
sourceBuilder.size(size);
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
request.source(sourceBuilder);
return client.search(request,RequestOptions.DEFAULT);
}
public SearchResponse matchQuery(String[] index,String field,Object value,Integer from,Integer size) throws Exception{
SearchRequest request;
if(index != null && index.length > 0){
request = new SearchRequest(index);
}else {
request = new SearchRequest();
}
QueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(field, value)
.fuzziness(Fuzziness.ONE)
.prefixLength(3)
.maxExpansions(10);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(matchQueryBuilder);
sourceBuilder.from(from);
sourceBuilder.size(size);
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
request.source(sourceBuilder);
return client.search(request,RequestOptions.DEFAULT);
}
public SearchResponse boolQuery(String[] index,String field1,String field2,Object value1,Object value2,Integer from,Integer size) throws Exception{
SearchRequest request;
if(index != null && index.length > 0){
request = new SearchRequest(index);
}else {
request = new SearchRequest();
}
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder
.should(QueryBuilders.matchQuery(field1, value1).minimumShouldMatch("2"))
.should(QueryBuilders.matchQuery(field2,value2))
;
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(boolQueryBuilder);
sourceBuilder.from(from);
sourceBuilder.size(size);
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
request.source(sourceBuilder);
return client.search(request,RequestOptions.DEFAULT);
}
@Override
public void destroy() throws Exception {
client.close();
}
}
测试代码
package com.wl.elasticsearch;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wl.elasticsearch.service.ElasticSearchService;
import org.elasticsearch.action.admin.indices.open.OpenIndexResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexResponse;
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.SpringJUnit4ClassRunner;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Administrator on 2021/3/15.
*/
@SpringBootTest(classes = Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ElasticSearchTest {
@Autowired
private ElasticSearchService elasticSearchService;
@Test
public void testCreateIndex() throws Exception{
String index = "test-01";
Integer shards = 5;
Integer replicas = 1;
CreateIndexResponse response = elasticSearchService.createIndex(index,shards,replicas);
System.out.println(response.index());
}
@Test
public void testDeleteIndex() throws Exception{
String index = "test-01";
AcknowledgedResponse response = elasticSearchService.deleteIndex(index);
System.out.println(response.isAcknowledged());
}
@Test
public void testExistIndex() throws Exception{
String index = "test-01";
boolean exist = elasticSearchService.existIndex(index);
System.out.println(exist);
}
@Test
public void testQueryIndex() throws Exception{
String index = "test-01";
GetIndexResponse response = elasticSearchService.queryIndex(index);
System.out.println(Arrays.toString(response.getIndices()));
}
@Test
public void testCloseIndex() throws Exception{
String index = "test-01";
AcknowledgedResponse response = elasticSearchService.closeIndex(index);
System.out.println(response.isAcknowledged());
}
@Test
public void testOpenIndex() throws Exception{
String index = "test-01";
OpenIndexResponse response = elasticSearchService.openIndex(index);
System.out.println(response.isAcknowledged());
}
@Test
public void testAddDocument() throws Exception{
String index = "test-01";
String type = "_doc";
String documentId = "test01-0001";
Map<String,Object> map = new HashMap<>();
map.put("user","wl");
map.put("postDate","2021-03-16");
map.put("message","test-01 document message");
String source = new ObjectMapper().writeValueAsString(map);
IndexResponse response = elasticSearchService.addDocument(index,type,documentId,source);
System.out.println(response.toString());
}
@Test
public void testGetDocument() throws Exception{
String index = "test-01";
String type = "_doc";
String documentId = "test01-0001";
GetResponse response = elasticSearchService.getDocument(index,type,documentId);
System.out.println(response.getSourceAsString());
}
@Test
public void testUpdateDocument() throws Exception{
String index = "test-01";
String type = "_doc";
String documentId = "test01-0001";
Map<String,Object> map = new HashMap<>();
map.put("user","wl");
map.put("postDate","2021-03-16");
map.put("message","test-01 document update message");
String source = new ObjectMapper().writeValueAsString(map);
UpdateResponse response = elasticSearchService.updateDocument(index,type,documentId,source);
System.out.println(response.toString());
}
/**
* 添加文章数据提供search api调用
*/
@Test
public void initArticleData() throws Exception{
String index = "article-index";
String title[] = {"静夜思","小池","赠刘景文","山行","回乡偶书","赠汪伦","望庐山瀑布"};
String author[] = {"李白","杨万里","苏轼","杜牧","贺知章","李白","李白"};
String dynasty[] = {"唐","宋","宋","唐","唐","唐","唐"};
String content[] = {"床前明月光","泉眼无声惜细流","荷尽已无擎雨盖","远上寒山石径斜","少小离家老大回","李白乘舟将欲行","日照香炉生紫烟"};
for(int i=0;i<title.length;i++){
Map<String,Object> map = new HashMap<>();
map.put("title",title[i]);
map.put("author",author[i]);
map.put("dynasty",dynasty[i]);
map.put("content",content[i]);
elasticSearchService.addDocument(index,"_doc","article-" + i,new ObjectMapper().writeValueAsString(map));
}
}
@Test
public void testSearchFullText() throws Exception{
String word = "李白";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.searchFullText(word,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
@Test
public void testTermQuery() throws Exception{
String[] index = {};
String field = "author.keyword";
Object value = "李太白";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.termQuery(index,field,value,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
@Test
public void testMatchQuery() throws Exception{
String[] index = {};
String field = "author";
Object value = "李太白";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.matchQuery(index,field,value,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
@Test
public void testBoolQuery() throws Exception{
String[] index = {};
String field1 = "author";
String value1 = "李白";
String field2 = "dynasty";
String value2 = "唐";
Integer from = 0;
Integer size = 10;
SearchResponse response = elasticSearchService.boolQuery(index,field1,field2,value1,value2,from,size);
System.out.println(Arrays.toString(response.getHits().getHits()));
}
}
更多查询例子参考:https://www.cnblogs.com/sunfie/p/6653778.html
官网文档参考:Search API | Java REST Client [6.8] | Elastic
分析器参考:
https://www.cnblogs.com/wangzhuxing/p/9502183.html
https://www.cnblogs.com/cjsblog/p/10171695.html
Elasticsearch文本分析之Analyzer使用 - 简书
elasticsearch快速开始:https://www.cnblogs.com/cjsblog/p/9439331.html
elasticsearch基本概念参考:https://www.cnblogs.com/qdhxhz/p/11448451.html