好久没有更新文章了,工作之后活成了社畜。想起最近流行的那句话,我想离开浪浪山。。。
累?糊口啊!大哥!
生活仍然要继续。。。
(坚持,努力,奋斗)
ps:以后坚持把一些会的东西写成文档。
之前研究ElasticSearch 8.X的客户端,原本想用SpringBoot的整个方案的,网上搜了一圈没有。GitHub上也说明不支持8.X的版本。所以这边就研究了一下官网给出的Java Client。
ps:先熟悉kibana 的语法,再看接口实现,比较好理解些。
下面给出了一些样例,主要批量的操作。单个的这边就不给出了,大家可以去看官网的文档,或者Github下载Java Client的源码,在Test中会给出demo样例。
(如果写的不好的或者有问题的地方,欢迎大评论)
pom.xml
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.4.3</version>
<dependency>
application.yml
elasticsearch:
cluster-nodes: 192.168.105.29:9200
Configuration配置
@Bean
public ElasticsearchClient elasticsearchClient() {
List<String> clusterNodes = elasticsearchProperties.getClusterNodes();
clusterNodes.forEach(node -> {
try {
String[] parts = StringUtils.split(node, ":");
Assert.notNull(parts, "Must defined");
Assert.state(parts.length == 2, "Must be defined as 'host:port'");
httpHosts.add(new HttpHost(parts[0], Integer.parseInt(parts[1])));
} catch (Exception e) {
throw new IllegalStateException("Invalid ES nodes " + "property '" + node + "'", e);
}
});
RestClient restClient = RestClient.builder(httpHosts.toArray(new HttpHost[0])).build();
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
return new ElasticsearchClient(transport);
}
@Autowired
private ElasticsearchClient elasticsearchClient;
精确查询 + 模糊匹配
public static List<Query> getComposeProceQuery(BillComposePrice composePrice, Bill bill, BillRequest request) {
List<Query> res = new ArrayList<>();
// TermQurey全匹配
res.add(TermQuery.of(m -> m.field(EsField.AREA)
.value(composePrice.getArea()))._toQuery());
res.add(TermQuery.of(m -> m.field(EsField.BILL_NORM)
.value(composePrice.getBillNorm()))._toQuery());
res.add(TermQuery.of(m -> m.field(EsField.BILL_UNIT)
.value(bill.getBillUnit()))._toQuery());
// 过滤已删除的
res.add(TermQuery.of(m -> m.field(EsField.DELETED_FLAG)
.value(0))._toQuery());
// 分词匹配
res.add(MatchQuery.of(m -> m.field(EsField.BILL_PROFEATURE)
.query(bill.getBillProFeature()))._toQuery());
return res;
}
protected List<BillInfo> composePrice(BillRequest request,
String indexName,
BillComposePrice composePrice) throws IOException, ElasticsearchException {
List<BillInfo> res = new ArrayList<>();
List<List<Query>> searchQuerys = new ArrayList<>();
List<Bill> bills = composePrice.getBills();
for (Bill bill : bills) {
// 生成查询条件
searchQuerys.add(getComposeProceQuery(composePrice, bill, request));
}
// 查询条件
List<RequestItem> requestItems = new ArrayList<>();
for (List<Query> searchQuery : searchQuerys) {
RequestItem.Builder requestBuilder = new RequestItem.Builder();
// 设置索引表
requestBuilder.header(h -> h.index(indexName));
requestBuilder.body(b -> b.query(q -> q.bool(co -> {
co.must(searchQuery);
// 业务逻辑,根据不同条件执行不同的查询
if (composePrice.getFindInSelfFlag() == 1 && composePrice.getFindInTradeFlag() == 1) {
co.must(userq -> userq.bool(userB -> userB.should(
TermQuery.of(m -> m.field(EsField.USER)
.value(request.getUser()))._toQuery(),
TermQuery.of(m -> m.field(EsField.USER)
.value(GlobalConstant.TRADE_USER))._toQuery()
)));
}
return co;
})));
requestItems.add(requestBuilder.build());
}
MsearchRequest.Builder builder = new MsearchRequest.Builder();
// 批量查询
List<MultiSearchResponseItem<BillInfo>> responses =
elasticsearchClient.msearch(builder.searches(requestItems).build(), BillInfo.class).responses();
// 解析结果
for (MultiSearchResponseItem<BillInfo> mhit : responses) {
if (!mhit.isFailure() && mhit.result().hits().hits().size() > 0) {
List<Hit<BillInfo>> hits = mhit.result().hits().hits();
// 返回第一个(第一个就是评分最高的那个)
res.add(hits.get(0).source());
} else {
res.add(null);
}
}
return res;
}
批量插入
先检查库中有没有对应数据,有就更新,没有就创建。外加条目数限制。
// 校验库中是否已存在
List<List<Query>> sameQuerys = new ArrayList<>();
for (BillInfo bulkInfo : bulkInfos) {
sameQuerys.add(EsConditionCreate.findSameQuery(bulkInfo));
}
List<MultiSearchResponseItem<BillInfo>> searchResponseItems =
searchBulk(indexName, sameQuerys).responses();
int updateCount = 0;
// 获取相似的ID
for (int i = 0; i < searchResponseItems.size(); i++) {
MultiSearchResponseItem<BillInfo> hit = searchResponseItems.get(i);
if (!hit.isFailure() && hit.result().hits().hits().size() != 0) {
bulkInfos.get(i).setBillUID(hit.result().hits().hits().get(0).id());
updateCount++;
}
}
List<BillInfo> loadBulk;
// 插入条目的限制
if (createLimit != -1 && bulkInfos.size() - updateCount > createLimit) {
loadBulk = new ArrayList<>();
for (BillInfo billInfo : bulkInfos) {
String billUID = billInfo.getBillUID();
if (billUID.isEmpty() && createLimit <= 0) {
// 创建限制不加入
continue;
} else if (billUID.isEmpty()) {
--createLimit;
}
loadBulk.add(billInfo);
}
} else {
loadBulk = bulkInfos;
}
if (loadBulk.size() == 0) {
throw new BillServerException(ResultCode.UPLOAD_OVER_MAX);
}
BulkRequest.Builder br = new BulkRequest.Builder();
for (BillInfo billInfo : loadBulk) {
br.operations(op -> op
.index(idx -> {
idx.index(indexName).document(billInfo);
// 相似的,直接覆盖。设置对应id
if (!billInfo.getBillUID().isEmpty()) {
idx.id(billInfo.getBillUID());
}
return idx;
}));
}
// 批量插入
return elasticsearchClient.bulk(br.build());
批量查询
// 根据条件批量查询
protected MsearchResponse<BillInfo> searchBulk(String indexName, List<List<Query>> searchQuerys)
throws IOException, ElasticsearchException {
List<RequestItem> requestItems = new ArrayList<>();
for (List<Query> searchQuery : searchQuerys) {
RequestItem.Builder requestBuilder = new RequestItem.Builder();
requestBuilder.header(h -> h.index(indexName));
requestBuilder.body(b -> b.query(q -> q.bool(co -> co.must(searchQuery))));
requestItems.add(requestBuilder.build());
}
MsearchRequest.Builder builder = new MsearchRequest.Builder();
return elasticsearchClient.msearch(builder.searches(requestItems).build(), BillInfo.class);
}
/**
* 批量查询
*
* @param searchItems Tuple(id, indexName)
* @return Tuple(id, indexName, value)
*/
protected List<Tuple> searchByIdBulk(List<Tuple> searchItems)
throws IOException, ElasticsearchException {
List<MultiGetOperation> multiGetOperations = new ArrayList<>();
for (Tuple searchItem : searchItems) {
MultiGetOperation multiGetOperation = MultiGetOperation.of(mget -> mget
.index(searchItem.get(0)) // indexName
.id(searchItem.get(1))); // _id
multiGetOperations.add(multiGetOperation);
}
MgetRequest.Builder builder = new MgetRequest.Builder();
builder.docs(multiGetOperations);
List<MultiGetResponseItem<BillInfo>> docs =
elasticsearchClient.mget(builder.build(), BillInfo.class).docs();
return getBillInfos(docs);
}
批量更新
/**
* 批量更新
*
* @param updateList, Tuple(indexName, id, doc)
*/
protected BulkResponse updateBulk(List<Tuple> updateList)
throws IOException, ElasticsearchException {
BulkRequest.Builder br = new BulkRequest.Builder();
for (Tuple updateItem : updateList) {
br.operations(op -> op
.update(up -> up
.index(updateItem.get(0))
.id(updateItem.get(1))
.action(act -> act.doc(updateItem.get(2)))
));
}
return elasticsearchClient.bulk(br.build());
}
多条件查询
代码实在是太长了
Query finalFindByUser = findByUser;
List<Map<Query, List<Query>>> finalFindByProfessions = findByProfessions;
List<Query> finalFindByLockShellNum = findByLockShellNum;
Query finalFindByTime = findByTime;
List<Query> finalFindByWildcard = findByWildcard;
Query finalFindByArea = findByArea;
Query finalFindByBillNorm = findByBillNorm;
Query finalFindBydeleteFlag = findBydeleteFlag;
return elasticsearchClient.search(s -> s
.index(indexName)
.query(q -> q
.bool(b -> {
if (finalFindByUser != null) {
b.must(finalFindByUser);
}
if (finalFindBydeleteFlag != null) {
b.must(finalFindBydeleteFlag);
}
if (isAllSelect) {
return b;
}
if (finalFindByArea != null) {
b.must(finalFindByArea);
}
if (finalFindByBillNorm != null) {
b.must(finalFindByBillNorm);
}
if (finalFindByProfessions != null) {
b.must(pro -> pro.bool(
professionQ -> {
for (Map<Query, List<Query>> findByProfession : finalFindByProfessions) {
for (Map.Entry<Query, List<Query>> entry : findByProfession.entrySet()) {
professionQ.should(professionS -> professionS
.bool(professionB ->
professionB.must(entry.getKey()).should(entry.getValue()))
);
}
}
professionQ.minimumShouldMatch("1");
return professionQ;
}
));
}
if (finalFindByTime != null) {
b.must(finalFindByTime);
}
if (finalFindByLockShellNum != null) {
b.must(loc -> loc.bool(
professionQ -> professionQ.should(finalFindByLockShellNum)
.minimumShouldMatch("1")));
}
if (finalFindByWildcard != null) {
b.must(loc -> loc.bool(
professionQ -> professionQ.should(finalFindByWildcard)
.minimumShouldMatch("1")));
}
return b;
}))
// sort by updatetime by aesc
.sort(sinfo -> sinfo.field(f -> f.field(EsField.UPLOAD_TIME).order(SortOrder.Desc)))
// 分页查询
.from((searchRequest.getSectionNum() - 1) * searchRequest.getPerSectionNum())
.size(searchRequest.getPerSectionNum()), BillInfo.class);