Java8中使用Opensearch实现搜索功能

简述

​ 本文基于Java8,而OpenSearch全线的2.0版本都是推荐Java11,使用java8连接是会报错,错误信息如下:

java: 无法访问org.opensearch.action.admin.indices.delete.DeleteIndexRequest
  错误的类文件: /路径/mavenRepository/org/opensearch/opensearch/2.5.0/opensearch-2.5.0.jar!/org/opensearch/action/admin/indices/delete/DeleteIndexRequest.class
    类文件具有错误的版本 55.0, 应为 52.0
    请删除该文件或确保该文件位于正确的类路径子目录中。

​ 所以为了适配,此文是基于OpenSearch 1.3.10版本(选择这个版本的原因是因为opensearch-rest-high-level-client的1.3.10依赖引用的人最多版本)所涉及到的API在后续高级版本中可能会失效。

另一点值得提的是,你可以使用低版本的opensearch-rest-high-level-client去连接高版本的OpenSearch,可以连接成功,也可以正确创建对应的Index,但是当查询返回结果式会报错,因为高本版的返回信息低版本无法正确解析

java.io.IOException: Unable to parse response body for Response{requestLine=POST /module-close-avg/_doc?timeout=1m HTTP/1.1, host=http://localhost:9200, response=HTTP/1.1 201 Created}
	at org.opensearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1781)
	at org.opensearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1728)
	at org.opensearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1696)
	at org.opensearch.client.RestHighLevelClient.index(RestHighLevelClient.java:961)
	at VeriousTests.test03(VeriousTests.java:97)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

OpenSearch部署

参考连接:https://blog.csdn.net/abu935009066/article/details/134569603

openSearch

我的部署方式是基于Docker的单节点部署

docker 代码

docker run --name="openSearch1.3.10" -d -p 9200:9200 -p 9600:9600 \
-e "discovery.type=single-node" \
-e "network.host=0.0.0.0" \
-e "plugins.security.disabled=false" \
opensearchproject/opensearch:1.3.10
  • discovery.type=single-node 单节点模式
  • network.host=0.0.0.0 在外部IP也能访问
  • plugins.security.disabled=false 禁用安全套件(为true的话无需密码认证)

关闭https:

#进入容器
docker exec -it openSearch1.3.10 /bin/bash
#编辑opensearch.yml文件
vi /usr/share/opensearch/config/opensearch.yml
#修改 plugins.security.ssl.http.enabled的值为false
plugins.security.ssl.http.enabled: false
#保存后重启docker

测试

openSearch默认的用户名密码都是admin

curl http://localhost:9200 -ku 'admin:admin

返回类似如下信息则代表启动无误

{
  "name" : "969871d1c42d",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "6De147sJSm62dqX0xw5eBw",
  "version" : {
    "distribution" : "opensearch",
    "number" : "1.3.10",
    "build_type" : "tar",
    "build_hash" : "b2150b9f6f6c8a15059ef415d3e24bdedd3e253c",
    "build_date" : "2023-05-16T00:09:23.060284Z",
    "build_snapshot" : false,
    "lucene_version" : "8.10.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

Dashbord

代码

拉去版本对应的dashbord

docker run --name openDash1.3.10 -d -p 5601:5601 \
-e "server.host=0.0.0.0" \
-e 'OPENSEARCH_HOSTS=["http://localhost:9200"]' \
opensearchproject/opensearch-dashboards:1.3.10
  • server.host=0.0.0.0 允许外部IP访问
  • opensearch.hosts=[“http://localhost:9200”] 配置opensearch地址

测试

访问http://localhost:5601,弹出如下界面,用户名密码默认都是admin

在这里插入图片描述

Java连接访问

通过opensearch-rest-high-level-client来连接访问OpenSearch

依赖引入

<dependency>
    <groupId>org.opensearch.client</groupId>
    <artifactId>opensearch-rest-high-level-client</artifactId>
    <version>1.3.10</version>
</dependency>

创建连接

private RestHighLevelClient client;
@Before
public void init(){
    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    credentialsProvider.setCredentials(AuthScope.ANY,
                                       new UsernamePasswordCredentials("admin", "admin"));
    client = new RestHighLevelClient(
        RestClient.builder(new HttpHost("localhost", 9200, "http"))
        .setHttpClientConfigCallback(httpClientBuilder ->
                                     httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)));
}

问题

​ 这里最开始我没有使用 UsernamePasswordCredentials 类创建一个包含用户名和密码的凭据对象,并将其传递给 OpenSearch 客户端,所以导致了报了认证失败的错误

openSearchStatusException[Unable to parse response body
]; nested: ResponseException[method [PUT], host [http://localhost:9200], URI [/module-close-rate?master_timeout=30s&timeout=30s], status line [HTTP/1.1 401 Unauthorized]
Unauthorized];

原始代码:

RestHighLevelClient client = new RestHighLevelClient(
    RestClient.builder(new HttpHost("localhost", 9200, "http")));

​ 到此OpenSearch连接对象就创建完毕了,后续就可以使用client对象来进行数据的CRUD

创建Index

//在 OpenSearch 中添加数据之前,你需要先创建一个索引。你可以使用 CreateIndexRequest 类创建一个索引请求对象,
// 并使用 OpenSearch 客户端对象执行该请求
CreateIndexRequest request = new CreateIndexRequest(indexName);
//这将创建一个名为 indexName 的索引。
client.indices().create(request, RequestOptions.DEFAULT);

添加数据

添加单条数据

//准备要添加到 OpenSearch 中的数据。你可以使用 IndexRequest 类创建一个索引请求对象,并指定要添加到索引中的数据:
IndexRequest indexRequest = new IndexRequest(indexName);
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("age", 99);
jsonMap.put("like", "handsome");
//这将创建一个索引请求对象,并指定要添加到 indexName 索引中的数据。
indexRequest.source(jsonMap);
//执行请求
IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);

批量添加数据

可以使用 OpenSearch 的 Java 客户端 API 提供的 BulkRequestBulkResponse 对象,SimpleTest是一个随机生成名字以及汉字的代码,参考:https://blog.csdn.net/qq_44959735/article/details/131570724

//Json转换
Gson gson = new Gson();
// 创建 BulkRequest 对象
BulkRequest request = new BulkRequest();
Map<String, Object> jsonMap = new HashMap<>();
SimpleTest simpleTest = new SimpleTest();
String[] s = {"male","femal"};
for (int i = 0; i < 500000; i++) {
    jsonMap.put("name", simpleTest.randomName(3));
    jsonMap.put("gender", s[i%2]);
    jsonMap.put("other", simpleTest.randomChar(9));
    //也可以指定document的id,如果不指定,则会默认生成一个
    //request.add(new IndexRequest("your-index-name").id("document-id-1").source("{\"field1\":\"value1\",\"field2\":\"value2\"}", XContentType.JSON))
    request.add(new IndexRequest(indexName).source(gson.toJson(jsonMap), XContentType.JSON));
}
// 执行 BulkRequest 请求
BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);

查询数据

SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 构建精确查询条件
//sourceBuilder.query(QueryBuilders.termQuery("gender", "femal"));
sourceBuilder.query(QueryBuilders.termQuery("name", "唐"));
// 设置排序方式
sourceBuilder.size(1);
searchRequest.source(sourceBuilder);
printResult(client.search(searchRequest, RequestOptions.DEFAULT));

termQuery

​ 精确查询(Term Query):使用精确匹配查询指定字段的值。对中文搜索只支持一个字,也就是如果name为唐佳萱 ,那么是无法正确匹配唐佳萱的,但是如果只给一个,那么会匹配包含的数据,英文支持精确匹配,比如John就会搜索出来名字为John的数据,不会匹配其他数据。

  • 英文匹配:
// 使用精确匹配查询字段 "name" 的值为 "John" 的文档
sourceBuilder.query(QueryBuilders.termQuery("name", "John"));
  • 中文匹配
sourceBuilder.query(QueryBuilders.termQuery("name", "唐"));
sourceBuilder.size(1);
//搜索结果
{"other":"的锹驱脚懊目妖锅呵","gender":"male","name":"唐佳萱"}

sourceBuilder.query(QueryBuilders.termQuery("name", "唐佳萱"));
sourceBuilder.size(1);
//搜索结果为空

matchQuery

​ 全文搜索(Match Query):执行全文搜索查询,可以自动拆分字段查询,比如给个 颜羿娇愿校矿,他会搜索所有包含关键字的数据,优先匹配整体,如果整体匹配不到就会拆分匹配

sourceBuilder.query(QueryBuilders.matchQuery("name", "颜羿娇愿校矿"));
//搜索结果
{"other":"缠臼谷鼠豆双求限眉","gender":"male","name":"颜羿娇"}
{"other":"题涤罚船燥霹逆劣绵","gender":"male","name":"颜羿颜"}
{"other":"祟咏碑保容弦凤猎桥","gender":"male","name":"姚羿娇"}
{"other":"讽锹盲绥耿亲毡瓤糠","gender":"male","name":"柏娇羿"}
{"other":"囚既盖祁悍瞻疑咒德","gender":"femal","name":"舒娇羿"}
{"other":"褪挥弦匠虎颖苏肇轩","gender":"male","name":"柏娇羿"}
{"other":"洼叫清卷冶桂科万瞩","gender":"femal","name":"严娇羿"}
{"other":"旷管巧韭袭硫间铰己","gender":"male","name":"袁娇羿"}
{"other":"数椿疆冯漆躇刹薄坯","gender":"male","name":"禹娇羿"}
{"other":"境极拳扳眉潍穴雾扮","gender":"femal","name":"罗羿娇"}

rangeQuery

​ 范围查询(Range Query):查询指定字段的范围内的值。

// 查询字段 "age" 值在 20 到 30 之间的文档
sourceBuilder.query(QueryBuilders.rangeQuery("age").from(20).to(30));
//搜索结果
{"other":"番锻乳艇济副强芒临","gender":"male","name":"曹辉贵","age":22}
{"other":"凛襄痕仁恩樱炕哉猩","gender":"femal","name":"尹正妹","age":23}
{"other":"窝指肤侧堡驹尸儒掖","gender":"femal","name":"雷悦治","age":26}

boolQuery

​ 逻辑运算查询(Bool Query):使用逻辑运算符组合多个查询条件。

// 使用逻辑运算查询,同时满足字段 "name" 值为 "John" 且字段 "age" 值大于等于 30 的文档
sourceBuilder.query(QueryBuilders.boolQuery()
    .must(QueryBuilders.matchQuery("name", "雷悦"))
    .must(QueryBuilders.rangeQuery("age").gte(30)));
//搜索结果
{"other":"糙娃蛔穷溢潞甚炮锣","gender":"male","name":"雷月悦","age":75}
{"other":"拔辊芬程局楼画响噬","gender":"femal","name":"雷悦昊","age":63}
{"other":"不眼博倍溪赂蓬癣筹","gender":"femal","name":"雷鹏悦","age":61}

wildcardQuery

​ 通配符查询(Wildcard Query):使用通配符模式查询指定字段的值。同样对中文不友好,只能查询一个中文加通配符,*表示匹配多个 ?表示匹配一个

// 使用通配符查询,查询字段 "name" 值以 "雷*" 开头的文档
sourceBuilder.query(QueryBuilders.wildcardQuery("name", "雷*"));
//搜索结果
{"other":"窝指肤侧堡驹尸儒掖","gender":"femal","name":"雷悦治","age":26}
{"other":"龋埃侄疥绝纲隘箱死","gender":"femal","name":"雷玫勇","age":20}
{"other":"狡办绣禹崩圭末贞虑","gender":"male","name":"雷帝观","age":17}
//一下搜索结果为空
sourceBuilder.query(QueryBuilders.wildcardQuery("name", "雷玫*"));
//匹配一个
sourceBuilder.query(QueryBuilders.wildcardQuery("gender", "mal?"));
{"other":"番锻乳艇济副强芒临","gender":"male","name":"曹辉贵","age":22}
{"other":"坡坑哟迁券奄屹眯诞","gender":"male","name":"禹岚梦","age":85}
//下面搜索匹配为空
sourceBuilder.query(QueryBuilders.wildcardQuery("gender", "ma?"));

prefixQuery

prefixQuery方法:使用前缀匹配查询指定字段的值。类似于Wildcard Query中的 keyword*,同样中文匹配不友好

// 使用前缀匹配查询字段 "name" 值以 "Joh" 开头的文档
sourceBuilder.query(QueryBuilders.prefixQuery("name", "Joh"));

regexpQuery

regexpQuery方法:使用正则表达式查询指定字段的值。

// 使用正则表达式查询字段 "name" 值匹配 "J.*n" 的文档
sourceBuilder.query(QueryBuilders.regexpQuery("name", "J.*n"));

fuzzyQuery

fuzzyQuery方法:执行模糊查询,匹配与查询词项相似的文档。

// 执行模糊查询,匹配字段 "name" 值与查询词项 "John" 相似的文档
sourceBuilder.query(QueryBuilders.fuzzyQuery("name", "John"));

existsQuery

existsQuery方法:查询指定字段存在值的文档。

// 查询字段 "name" 存在值的文档
sourceBuilder.query(QueryBuilders.existsQuery("name"));
//搜索结果
{"other":"番锻乳艇济副强芒临","gender":"male","name":"曹辉贵","age":22}
{"other":"凛襄痕仁恩樱炕哉猩","gender":"femal","name":"尹正妹","age":23}
{"other":"坡坑哟迁券奄屹眯诞","gender":"male","name":"禹岚梦","age":85}
sourceBuilder.query(QueryBuilders.existsQuery("nice"));
//搜索结果为空

matchAllQuery

// 匹配所有文档
sourceBuilder.query(QueryBuilders.matchAllQuery());

matchPhraseQuery

​ 执行一个短语匹配查询,它会搜索字段 “other” 中包含短语 “以藏” 的文档。短语匹配查询会确保查询词项按照指定顺序在文档中连续出现。

// 执行短语匹配查询,匹配字段 "other" 中包含短语 "以藏" 的文档
sourceBuilder.query(QueryBuilders.matchPhraseQuery("other", "以藏"));

判断索引是否存在

// 创建 GetIndexRequest 对象
GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);

// 执行 GetIndexRequest 请求
boolean exists = false;
try {
		exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
	} catch (IOException e) {
		System.out.println("索引初始化失败,请检查");
}
System.out.println(exists);

删除

删除索引

DeleteIndexRequest request = new DeleteIndexRequest(indexName);
// 执行 DeleteIndexRequest 请求
AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);

删除数据

​ 需要先查询数据,获取doc的id

// 创建 DeleteRequest 对象
DeleteRequest request = new DeleteRequest("module-close-avg", "B8sXhowB-h7jQIqxdjJG");

// 执行 DeleteRequest 请求
DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值