官方文档: https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/connecting.html
gradle
dependencies {
implementation 'co.elastic.clients:elasticsearch-java:8.1.2'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.10.2'
implementation 'jakarta.json:jakarta.json-api:2.0.1'
}
maven
<project>
<dependencies>
<dependency>
<groupId>co.elastic.clientsgroupId>
<artifactId>elasticsearch-javaartifactId>
<version>8.1.2version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.10.2version>
dependency>
<dependency>
<groupId>jakarta.jsongroupId>
<artifactId>jakarta.json-apiartifactId>
<version>2.0.1version>
dependency>
dependencies>
package com.demo.devops.document.config;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* ES配置
*/
@Configuration
public class ElasticSearchConfig {
@Value("${es.hostname:10.129.129.1}")
private String hostname;
@Value("${es.port:9200}")
private int port;
@Value("${es.username:elastic}")
private String username;
@Value("${es.password:123456}")
private String password;
@Bean
public ElasticsearchClient esRestClient() {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
// Create the low-level client
RestClient restClient = RestClient.builder(new HttpHost(hostname, port)).setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)).build();
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
// And create the API client
return new ElasticsearchClient(transport);
}
}
若无密码,可以使用下面方式:
// Create the low-level client
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
// And create the API client
ElasticsearchClient client = new ElasticsearchClient(transport);
使用es自动设置的mapping
@Autowired
private ElasticsearchClient elasticsearchClient;
----
//创建索引
CreateIndexResponse createIndexResponse = client.indices().create(c -> c.index("newapi"));
设置mappings
@Autowired
private ElasticsearchClient elasticsearchClient;
public void createDocIndex() throws IOException {
log.info("开始新建ES索引");
Map<String, Property> documentMap = new HashMap<>();
documentMap.put("title", Property.of(property ->
property.text(TextProperty.of(p ->
p.index(true)
.analyzer("ik_max_word")
)
)
)
);
documentMap.put("id", Property.of(property ->
property.long_(LongNumberProperty.of(p ->
p.index(true)
)
)
)
);
documentMap.put("content", Property.of(property ->
property.text(TextProperty.of(textProperty ->
textProperty.index(true)
.analyzer("ik_max_word")
)
)
)
);
documentMap.put("createUserId", Property.of(property ->
property.keyword(KeywordProperty.of(p ->
p.index(true)
)
)
)
);
documentMap.put("createTime", Property.of(property ->
property.date(DateProperty.of(p ->
p.index(true)
)
)
)
);
// 创建索引
CreateIndexResponse createIndexResponse = elasticsearchClient.indices().create(c -> {
c.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL)
.mappings(mappings -> mappings.properties(documentMap));
//.aliases(SystemConstant.INDEX_DOC_ALL, aliases -> aliases.isWriteIndex(true));
return c;
});
log.info("结束新建ES索引,res={}", createIndexResponse.acknowledged());
}
public void deleteIndex(String index) throws IOException {
log.info("开始删除索引,index={}", index);
DeleteIndexResponse response = elasticsearchClient.indices().delete(d -> d.index(index));
log.info("开始删除索引,index={},res={}", index, response.acknowledged());
}
Doc是自定义实体类
@Async
public void createDoc(Doc doc) {
log.info("开始新增到es,docNo={}", doc.getDocNo());
// 构建一个创建Doc的请求
try {
String newContent = convertContent(doc.getContent());
doc.setContent(newContent);
elasticsearchClient.index(x -> x.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL).document(doc));
} catch (IOException e) {
log.error("新增es文档异常,docNo=" + doc.getDocNo(), e);
}
log.info("结束新增到es,docNo={}", doc.getDocNo());
}
/**
* 批量新增文档到Es
*
* @throws IOException
*/
public void bulkCreateDocument() throws IOException {
log.info("开始批量新增doc到es");
List<Doc> docList = docService.list().stream().filter(x -> !DocStateEnum.DELETED.getState().equals(x.getState())).collect(Collectors.toList());
log.info("批量新增doc到es,查询到doc数量={}", docList.size());
//构建一个批量操作BulkOperation的集合
List<BulkOperation> bulkOperations = new ArrayList<>();
//向集合添加数据
for (Doc doc : docList) {
bulkOperations.add(new BulkOperation.Builder().create(d -> d.document(doc).index(SystemConstant.ElasticConstants.INDEX_DOC_ALL)).build());
}
//使用bulk方法执行批量操作并获得响应
BulkResponse response = elasticsearchClient.bulk(e -> e.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL).operations(bulkOperations));
//打印结果
log.info("新增完成,耗时={}ms", response.took());
}
@Async
public void deleteDoc(Doc doc) {
log.info("开始删除es文档,docNo={}", doc.getDocNo());
// 构建一个创建Doc的请求
try {
SearchResponse<Doc> response = elasticsearchClient.search(s -> s
.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL)
.query(q -> q.term(
t -> t
.field(SystemConstant.ElasticConstants.FIELD_DOC_NO)
.value(doc.getDocNo())
))
, Doc.class);
if (response.hits().total().value() == 0) {
return;
}
elasticsearchClient.delete(x -> x.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL).id(response.hits().hits().get(0).id()));
} catch (IOException e) {
log.error("删除es文档异常,docNo=" + doc.getDocNo(), e);
}
log.info("结束删除es文档,docNo={}", doc.getDocNo());
}
/**
* 更新文档
*/
@Async
@Override
public void updateDoc(Doc doc) {
log.info("开始修改es文档,docNo={}", doc.getDocNo());
try {
SearchResponse<Doc> response = elasticsearchClient.search(s -> s
.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL)
.query(q -> q.term(
t -> t
.field(SystemConstant.ElasticConstants.FIELD_DOC_NO)
.value(doc.getDocNo())
))
, Doc.class);
assert response.hits().total() != null;
if (response.hits().total().value() == 0) {
//新增
createDoc(doc);
} else {
//更新
String newContent = convertContent(doc.getContent());
doc.setContent(newContent);
elasticsearchClient.update(e -> e.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL).id(response.hits().hits().get(0).id()).doc(doc), Doc.class);
}
} catch (IOException e) {
log.error("更新es文档异常,docNo=" + doc.getDocNo(), e);
}
log.info("结束修改es文档,docNo={}", doc.getDocNo());
}
/**
* 根据文档docNo查询一个文档
*
* @param docNo
* @return
* @throws IOException
*/
@Override
public Doc searchOneDoc(Long docNo) throws IOException {
SearchResponse<Doc> response = elasticsearchClient.search(s -> s
.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL)
.query(q -> q.term(
t -> t
.field(SystemConstant.ElasticConstants.FIELD_DOC_NO)
.value(docNo)
))
, Doc.class);
if (response.hits().total().value() == 0) {
return null;
}
return response.hits().hits().get(0).source();
}
/**
* 文档检索
*
* @param param
* @return
* @throws IOException
*/
@Override
public PageInfo<Doc> docSearch(DocSearchParam param) throws IOException {
// 分页查询
SearchResponse<Doc> response = elasticsearchClient.search(s -> searchConditionBuilder(param, s)
, Doc.class);
log.info("检索完成,耗时={}ms,hit总数={}", response.took(), response.hits().total().value());
List<Doc> docList = response.hits().hits().stream().map(x -> {
Doc doc = x.source();
if (doc == null) {
return null;
}
Map<String, List<String>> highlight = x.highlight();
List<String> titleList = highlight.get(SystemConstant.ElasticConstants.FIELD_TITLE);
List<String> contentList = highlight.get(SystemConstant.ElasticConstants.FIELD_CONTENT);
if (!CollectionUtils.isEmpty(titleList)) {
doc.setTitle(titleList.get(0));
}
if (CollectionUtils.isEmpty(contentList)) {
int length = doc.getContent().length();
doc.setContent(doc.getContent().substring(0, Math.min(length, 300)).replaceAll("\n", ""));
} else {
doc.setContent(contentList.stream().limit(5).collect(Collectors.joining()).replaceAll("\n", ""));
}
return doc;
}).filter(Objects::nonNull).collect(Collectors.toList());
return PageInfo.pageOf(docList, Long.valueOf(response.hits().total().value()).intValue(), param.getPageSize(), param.getPageNum());
}
/**
* doc检索表达式
*
* @param param
* @param s
* @return
*/
private SearchRequest.Builder searchConditionBuilder(DocSearchParam param, SearchRequest.Builder s) {
SearchRequest.Builder builder = s
.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL)
.query(q -> q
//.term(t -> t
// .field("content")
// .value(param.getKw()).
//)
.bool(b -> b.should(should -> should
//.wildcard(m -> m
// .field(SystemConstant.ElasticConstants.FIELD_TITLE)
// .value("*" + param.getKw() + "*")
//)
//字段映射中必须使用分词器,否则查不出来
//.fuzzy(f -> f
// .field(SystemConstant.ElasticConstants.FIELD_TITLE)
// .value(param.getKw())
//)
.match(m -> m
.field(SystemConstant.ElasticConstants.FIELD_TITLE)
.query(param.getKw())
)
)
.should(should -> should
.match(m -> m
.field(SystemConstant.ElasticConstants.FIELD_CONTENT)
.query(param.getKw())
)
)
)
)
//高亮
.highlight(h -> h
.fields(SystemConstant.ElasticConstants.FIELD_CONTENT, f -> f
.preTags("")
.postTags("")
)
.fields(SystemConstant.ElasticConstants.FIELD_TITLE, f -> f
.preTags("")
.postTags("")
)
)
.from(param.getPageNum() * param.getPageSize() - param.getPageSize())
.size(param.getPageSize());
//排序字段为空,则使用默认排序
if (StringUtils.isNotBlank(param.getOrderField())) {
builder.sort(sort -> sort.field(f -> f
.field(Optional.ofNullable(param.getOrderField()).orElse(SystemConstant.ElasticConstants.FIELD_UPDATE_TIME))
.order(SortOrder.Desc.jsonValue().equals(param.getOrderType()) ? SortOrder.Desc : SortOrder.Asc)
)
);
}
return builder;
}
比如 select * from doc where user_id in(1,2,3);
方式一:
ArrayList<String> list = new ArrayList<>();
List<FieldValue> valueList = list.stream().map(x -> FieldValue.of(x)).collect(Collectors.toList());
Query foldersQuery = TermsQuery.of(t -> t.field("userId").terms(new TermsQueryField.Builder()
.value(folderIdValues).build()
))._toQuery();
SearchRequest.Builder builder = s.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL).query(foldersQuery);
方式二:
ArrayList<String> list = new ArrayList<>();
List<FieldValue> valueList = list.stream().map(x -> FieldValue.of(x)).collect(Collectors.toList());
SearchRequest searchRequest = SearchRequest.of(s -> s.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL).query(q ->
q.terms(t ->
t.field("userId")
.terms(new TermsQueryField.Builder()
.value(valueList).build()))));
SearchResponse<Doc> search = elasticsearchClient.search(searchRequest, Doc.class);
方式三:
ArrayList<String> list = new ArrayList<>();
List<FieldValue> valueList = list.stream().map(x -> FieldValue.of(x)).collect(Collectors.toList());
SearchResponse<Doc> response = elasticsearchClient.search(s-> s.index(SystemConstant.ElasticConstants.INDEX_DOC_ALL).query(q ->
q.terms(t ->
t.field("userId")
.terms(new TermsQueryField.Builder()
.value(valueList).build())))
, Doc.class);