1.加入依赖
java
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.12.2</version>
</dependency>
2.配置类
java
@Slf4j
@Configuration
public class ElasticSearchConfig {
@Value("${elasticsearch.hosts}")
private String hosts;
@Value("${elasticsearch.port}")
private int port;
@Value("${elasticsearch.username}")
private String username;
@Value("${elasticsearch.password}")
private String password;
@Value("${elasticsearch.apikey:''}")
private String apikey;
/**
* 单节点没密码连接
*
* @return
*/
@Bean
public ElasticsearchClient elasticsearchClient() {
String[] servers = hosts.split(",");
int len = servers.length;
if (0 == len) {
log.error("ElasticsearchClient 配置错误!");
}
ElasticsearchTransport transport = null;
// 不是集群时
if (1 == len) {
// 无账号、密码
if (StringUtils.isEmpty(username) && StringUtils.isEmpty(password)) {
RestClient client = RestClient.builder(new HttpHost(servers[0], port, "http"))
.setHttpClientConfigCallback(httpClientBuilder
->httpClientBuilder.setDefaultHeaders(
Collections.singletonList(new BasicHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())))
.addInterceptorLast((HttpResponseInterceptor) (response, context)
-> response.addHeader("X-Elastic-Product", "Elasticsearch")))
.build();
transport = new RestClientTransport(client, new JacksonJsonpMapper());
} else {
// 账号密码的配置
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
// 自签证书的设置,并且还包含了账号密码
RestClientBuilder.HttpClientConfigCallback callback = httpAsyncClientBuilder -> httpAsyncClientBuilder
.setDefaultCredentialsProvider(credentialsProvider)
.addInterceptorLast(
(HttpResponseInterceptor)
(response, context) ->
response.addHeader("X-Elastic-Product", "Elasticsearch"));
RestClient client = RestClient.builder(new HttpHost(servers[0], port, "http"))
.setHttpClientConfigCallback(callback)
.build();
transport = new RestClientTransport(client, new JacksonJsonpMapper());
}
} else {
// 集群时无账号、密码
if (StringUtils.isEmpty(username) && StringUtils.isEmpty(password)) {
transport = getElasticsearchTransport(toHttpHost());
} else {
transport = getElasticsearchTransport(username, password, toHttpHost());
}
}
return new ElasticsearchClient(transport);
}
private HttpHost[] toHttpHost() {
if (hosts.split(",").length == 0) {
throw new RuntimeException("invalid elasticsearch configuration");
}
String[] hostArray = hosts.split(",");
HttpHost[] httpHosts = new HttpHost[hostArray.length];
HttpHost httpHost;
for (int i = 0; i < hostArray.length; i++) {
String[] strings = hostArray[i].split(":");
httpHost = new HttpHost(strings[0], Integer.parseInt(strings[1]), "http");
httpHosts[i] = httpHost;
}
return httpHosts;
}
private static ElasticsearchTransport getElasticsearchTransport(String username, String password, HttpHost... hosts) {
// 账号密码的配置
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
// 自签证书的设置,并且还包含了账号密码
RestClientBuilder.HttpClientConfigCallback callback = httpAsyncClientBuilder -> httpAsyncClientBuilder
.setSSLContext(buildSSLContext())
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.setDefaultCredentialsProvider(credentialsProvider)
.setDefaultHeaders(
Stream.of(new BasicHeader(
HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString())).collect(toList())
).addInterceptorLast(
(HttpResponseInterceptor)
(response, context) ->
response.addHeader("X-Elastic-Product", "Elasticsearch"))
.addInterceptorLast((HttpResponseInterceptor) (response, context)
-> response.addHeader("X-Elastic-Product", "Elasticsearch"));
// 用builder创建RestClient对象
RestClient client = RestClient
.builder(hosts)
.setHttpClientConfigCallback(callback)
.build();
return new RestClientTransport(client, new JacksonJsonpMapper());
}
private static ElasticsearchTransport getElasticsearchTransport(HttpHost... hosts) {
// 用builder创建RestClient对象
RestClient client = RestClient
.builder(hosts)
.build();
return new RestClientTransport(client, new JacksonJsonpMapper());
}
private static SSLContext buildSSLContext() {
ClassPathResource resource = new ClassPathResource("es01.crt");
SSLContext sslContext = null;
try {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate trustedCa;
try (InputStream is = resource.getInputStream()) {
trustedCa = factory.generateCertificate(is);
}
KeyStore trustStore = KeyStore.getInstance("pkcs12");
trustStore.load(null, null);
trustStore.setCertificateEntry("ca", trustedCa);
SSLContextBuilder sslContextBuilder = SSLContexts.custom().loadTrustMaterial(trustStore, null);
sslContext = sslContextBuilder.build();
} catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException |
KeyManagementException e) {
log.error("ES连接认证失败", e);
}
return sslContext;
}
}
3.相关api
前提:
注入ElasticsearchClient
java
@Autowired
private ElasticsearchClient client;
3.1 创建索引
java
public void createIndex() throws IOException {
ElasticsearchIndicesClient indices = client.indices();
//是否存在
//1.lambda
boolean isExit = indices.exists(req -> req.index("ttt")).value();
//2.builder,of
ExistsRequest existsRequestOf = ExistsRequest.of(req -> req.index("ttt"));
ExistsRequest existsRequest = new ExistsRequest.Builder().index("ttt").build();
boolean builderExist = indices.exists(existsRequest).value();
if (isExit){
log.info("已经存在ttt");
}else {
//1.lambda
boolean isSuccess = indices.create(req -> req.index("ttt")).acknowledged();
//2.builder,of
CreateIndexRequest createIndexRequestOf = CreateIndexRequest.of(req -> req.index("ttt"));
CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index("ttt").build();
boolean buildIsSuccess = indices.create(createIndexRequest).acknowledged();
if (isSuccess){
log.info("创建成功");
}else {
log.info("创建失败");
}
}
}
3.2 删除索引
java
public void deleteIndex() throws IOException {
//1.lambda
boolean isSuccess = client.indices().delete(req -> req.index("ttt")).acknowledged();
//2.builder,of
DeleteIndexRequest deleteRequestOf = DeleteIndexRequest.of(req -> req.index("ttt"));
DeleteIndexRequest deleteRequest = new DeleteIndexRequest.Builder().index("ttt").build();
boolean buildDeleteRequest = client.indices().delete(deleteRequest).acknowledged();
}
3.3查询索引
java
public void queryIndex() throws IOException {
//1.lambda
//查询单个
Map<String, IndexState> tttIndex = client.indices().get(req -> req.index("ttt")).result();
//查询索引
Set<String> all = client.indices().get(req -> req.index("*")).result().keySet();
//2.builder
GetIndexRequest getIndexRequestOf = GetIndexRequest.of(req -> req.index("ttt"));
GetIndexRequest getIndexRequest = new GetIndexRequest.Builder().index("ttt").build();
Map<String, IndexState> result = client.indices().get(getIndexRequest).result();
}
3.4 插入文档
java
public void addDoc() throws IOException {
Goods goods = new Goods("3212334","华为mate60", 9999.0);
//1.lambda
client.index(i->i
.index("goods")
.id(goods.getSku())
.document(goods));
//2.builder-of
IndexRequest<Goods> addIndexRequestOf = IndexRequest.of(t -> t.id(goods.getSku()).document(goods).index("goods"));
IndexRequest<Goods> addIndexRequest = new IndexRequest<>.Builder()
.index("goods")
.id(goods.getSku())
.document(goods).build();
IndexResponse index = client.index(addIndexRequest);
}
3.5 批量插入文档
java
public void addBatchDoc() throws IOException {
List<Goods> goodsList = List.of(new Goods("1","手机",999.0));
BulkRequest.Builder br = new BulkRequest.Builder();
for (Goods goods : goodsList) {
br.operations(op->op.
index(idx->idx.index("goods")
.id(goods.getSku())
.document(goods)));
}
BulkResponse result = client.bulk(br.build());
// Log errors, if any
if (result.errors()) {
log.error("Bulk had errors");
for (BulkResponseItem item: result.items()) {
if (item.error() != null) {
log.error(item.error().reason());
}
}
}
}
3.6查询文档
java
public void queryDoc() throws IOException {
//1.lambda
GetResponse<Goods> response = client.get(g -> g
.index("goods")
.id("00000")
, Goods.class);
//2.builder,of
GetRequest request = GetRequest.of(g -> g.index("goods").id("00000"));
GetRequest ofRequest = new GetRequest.Builder().index("goods").id("00000").build();
GetResponse<Goods> response1 = client.get(request, Goods.class);
if (response.found()){
Goods source = response.source();
System.out.println(source);
}
}
3.7修改文档
java
public void updateDoc() throws IOException {
//全量
Goods goods = new Goods("1","2",3.0);
UpdateResponse<Goods> update = client.update(u -> u
.doc(goods)
.id(goods.getSku())
, Goods.class);
//of
UpdateRequest<Object, Object> of = UpdateRequest.of(u -> u.id(goods.getSku()).doc(goods));
UpdateResponse<Object> update1 = client.update(of, Goods.class);
}
3.8删除文档
java
*/
public void deleteDoc() throws IOException {
DeleteResponse goods = client.delete(d -> d
.index("goods")
.id("0000"));
//of
DeleteRequest deleteRequest = DeleteRequest.of(d -> d.index("goods").id("0000"));
client.delete(deleteRequest);
}
3.9 DSL 匹配查询
java
public void query() throws IOException {
String text = "华为mate60";
SearchResponse<Goods> response = client.search(s -> s
.index("goods")
.query(q -> q
.match(m -> m
.field("name")
.query(text))
)
, Goods.class);
//of
SearchRequest request = SearchRequest.of(s -> s.index("goods")
.query(q -> q
.match(m ->
m.field("").field(""))
));
SearchResponse<Goods> search = client.search(request, Goods.class);
//结果
//总数
TotalHits total = response.hits().total();
assert total != null;
boolean isExactResult = total.relation() == TotalHitsRelation.Eq;
if (isExactResult) {
log.info("找到 " + total.value() + " 个结果");
} else {
log.info("找到超过 " + total.value() + " 个结果");
}
List<Hit<Goods>> hits = response.hits().hits();
for (Hit<Goods> hit : hits) {
Goods goods = hit.source();
System.out.println(goods);
}
}
3.10 多精确terms
java
public void terms(){
List<FieldValue> v = new ArrayList<>();
FieldValue tag2 = FieldValue.of("tag2");
FieldValue tag1 = FieldValue.of("tag1");
v.add(tag1);v.add(tag2);
TermsQuery tags = TermsQuery.of(t -> t.field("tags").terms(tm -> tm.value(v)));
SearchRequest of = SearchRequest.of(s -> s.index("goods")
.query(q -> q
.bool(b -> b
.must(m -> m
.terms(tags)
)
.should(sh -> sh
.match(ma -> ma
.field("")
.query("")
)
)
)
)
.from(0)
.size(10)
);
}
3.11 布尔查询
java
@Slf4j
@Service
public class EsTest {
@Autowired
private ElasticsearchClient client;
/**
* bool
*/
public void boolQuery() throws IOException {
Map<String, HighlightField> map = new HashMap<>();
map.put("title", HighlightField.of(hf -> hf.preTags("<em>").postTags("<em/>")));
map.put("description", HighlightField.of(hf -> hf.preTags("<em>").postTags("<em/>")
.numberOfFragments(4).fragmentSize(50)));
//numberOfFragments(4),表示将字段分割为最多4个片段,并设置 fragmentSize(50),表示每个片段的大小为50个字符。
Highlight highlight = Highlight.of(
h -> h.type(HighlighterType.Unified)
.fields(map)
.fragmentSize(50)//设置默认的高亮片段大小为50个字符,如果字段没有单独设置,则使用此默认值。
.numberOfFragments(5)//设置每个字段的最大高亮片段数为5个。
);
String text = "华为mate60";
SearchResponse<Goods> search = client.search(s -> s
.index("goods")
.query(q -> q
.bool(b -> b
.must(m -> m
.term(t -> t
.field("品牌")
.value("华为")
)
)
.should(sd -> sd
.match(mh -> mh
.field("name")
.query("mate60")))
)
)
.from(0)
.size(10)
.highlight(highlight)
, Goods.class);
//of
SearchRequest of = SearchRequest.of(s -> s.index("goods")
.query(q -> q
.bool(b -> b
.must(m -> m
.term(t -> t.field("品牌")
.value("华为")
)
)
.should(sh -> sh
.match(ma -> ma
.field("")
.query("")
)
)
)
)
.from(0)
.size(10)
.highlight(highlight)
);
SearchResponse<Goods> response = client.search(of, Goods.class);
List<Hit<Goods>> hits = response.hits().hits();
for (Hit<Goods> hit : hits) {
Goods goods = hit.source();
if (goods == null){
continue;
}
Map<String, List<String>> highlightList = hit.highlight();
highlightList.forEach((key, Value)->{
if (Objects.equals(key,goods.getName())){
//存入
}else {
//无高亮
}
}
);
System.out.println(goods);
}
}
}
3.12构建排序
java
/**
* 构建排序
*/
private List<SortOptions> buildSort(SearchDTO dto) {
if (dto.getTimeSort() != null){
SortOptions sortOptions ;
if (dto.getTimeSort() == 0){
sortOptions = SortOptions.of(s -> s.field(FieldSort.of(f -> f.field(SearchConstants.TIMESTAMP).order(SortOrder.Asc))));
}else {
sortOptions = SortOptions.of(s -> s.field(FieldSort.of(f -> f.field(SearchConstants.TIMESTAMP).order(SortOrder.Desc))));
}
return List.of(sortOptions);
}else {
return Collections.emptyList();
}
}
...........