文末附上项目的下载地址
第五章节中含有一些文档的搜索等操作,这里是基于黑马程序员的搜索做的一次单元测试,单元测试类如下,下一章节将整个项目的java文件贴出
文档搜索RestAPI单元测试
package com.toto.es.hotel;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.DistanceUnit;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.BucketMetricValueAggregate;
import co.elastic.clients.elasticsearch._types.aggregations.Buckets;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggest;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import co.elastic.clients.util.NamedValue;
import co.elastic.clients.util.ObjectBuilder;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.toto.es.hotel.pojo.HotelDoc;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @Description: HotelSearchTest
* @Package: com.toto.es.hotel
* @Author gufanbiao
* @CreateTime 2024-05-04 10:43
*/
@SpringBootTest
@SuppressWarnings("all")
public class HotelSearchTest {
private RestClient restClient;
private ElasticsearchClient client;
private ElasticsearchTransport transport;
/**
* 查询所有文档
*/
@Test
void testSearchAll() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel"), HotelDoc.class);
handleResponse(response);
}
/**
* match 根据字段查询
*/
@Test
void testMatch() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.match(match -> match.field("all").query("如家"))), HotelDoc.class);
handleResponse(response);
}
/**
* 多id查询
* @param response
*/
@Test
void testMultiId() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.ids(ids -> ids.values("728604", "441836", "433576"))), HotelDoc.class);
handleResponse(response);
}
/**
* term 不分词查询(精确)
* @param response
*/
@Test
void testTerm() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.term(term -> term.field("city").value("北京"))), HotelDoc.class);
handleResponse(response);
}
/**
* 范围查询
* @param response
*/
@Test
void testRange() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.range(range -> range.field("price").gte(JsonData.of(200)).lte(JsonData.of(1000)))), HotelDoc.class);
handleResponse(response);
}
/**
* 前缀查询
* @param response
*/
@Test
void testPrefix() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.prefix(prefix -> prefix.field("brand").value("如"))), HotelDoc.class);
handleResponse(response);
}
/**
* 匹配查询
* @param response
*/
@Test
void testMatchPhrase() throws IOException {
//SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.matchPhrase(matchPhrase -> matchPhrase.field("all").query("如家"))), HotelDoc.class);
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.wildcard(w->w.field("name").value("如*"))), HotelDoc.class);
handleResponse(response);
}
/**
* 匹配查询 ?单字符匹配
*/
@Test
void testMatchPhrase2() throws IOException {
//SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.matchPhrase(matchPhrase -> matchPhrase.field("all").query("如家?"))), HotelDoc.class);
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.wildcard(w->w.field("all").value("?家"))), HotelDoc.class);
handleResponse(response);
}
/**
* 模糊查询
* @param response
*/
@Test
void testFuzzy() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.fuzzy(fuzzy -> fuzzy.field("all").value("如家"))), HotelDoc.class);
handleResponse(response);
}
/**
* 多条件查询
* must:所有条件必须同时成立
* must_not:所有条件必须同时不成立
* should:所有条件中成立一个即可
* @param response
*/
@Test
void testMulti() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query ->
query.bool(bool ->
bool.must(must -> must.match(match -> match.field("all").query("如家")))
.filter(filter -> filter.term(term -> term.field("city").value("北京"))))), HotelDoc.class);
handleResponse(response);
}
/**
* 多字段查询-multiMatch
* @param response
*/
@Test
void testMultiMatch() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query ->
query.multiMatch(multiMatch -> multiMatch.fields("all", "brand").query("如家 汉庭"))), HotelDoc.class);
handleResponse(response);
}
/**
* 高亮显示
* @param response
*/
@Test
void testHighlight() throws IOException {
//SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel").query(query -> query.match(match -> match.field("all").query("如家"))).highlight(highlight -> highlight.fields("all", highlightFields -> highlightFields.highlighter(highlighter -> highlighter.type("fvh")))), HotelDoc.class);
SearchResponse<HotelDoc> response = client.search(builder ->
builder.index("hotel")
.query(query -> query.match(match -> match.field("all").query("汉庭")))
.highlight(highlight -> highlight
.fields("name", hf -> hf.requireFieldMatch(false))
.preTags("<span>")
.postTags("</span>")),
HotelDoc.class);
handleResponse(response);
}
/**
* 分页查询
* @param response
*/
@Test
void testPage() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel")
.query(query -> query.match(match -> match.field("all").query("如家")))
.from(0)
.size(200)
.trackTotalHits(t->t.enabled(true)), //使用分页时,最多返回10000条。需要进行设置
HotelDoc.class);
handleResponse(response);
}
/**
* 排序
* @throws IOException
*/
@Test
void testSort() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder -> builder.index("hotel")
.query(query -> query.match(match -> match.field("all").query("如家")))
.sort(sort -> sort.field(fieldSort -> fieldSort.field("price").order(SortOrder.Asc))), HotelDoc.class);
handleResponse(response);
}
/**
* 指定字段查询 filter去指定是include包含或是exclude去除xx字段
* @param response
*/
@Test
void testField() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder ->
builder.index("hotel")
.query(q->q.matchAll(v->v))
.source(s->s.filter(v->v.includes("price","brand", "name"))),
HotelDoc.class);
handleResponse(response);
}
/**
* 地理坐标排序
* @param response
*/
@Test
void testGeoSort() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder ->
builder.index("hotel")
.query(query -> query.matchAll(v->v))
.sort(sort -> sort.geoDistance(geo -> geo.field("location").location(l -> l.latlon(ll -> ll.lat(31.034661).lon(121.612282))).unit(DistanceUnit.Kilometers).order(SortOrder.Asc))),
HotelDoc.class);
handleResponse(response);
}
/**
* 组合查询 function score
* @param response
*/
@Test
void testFunctionScore() throws IOException {
BoolQuery.Builder boolQuery = QueryBuilders.bool();
boolQuery.must(must -> must.match(match -> match.field("name").query("万丽")));
SearchResponse<HotelDoc> response = client.search(builder ->
builder.index("hotel")
//.query(query -> query.bool(bool -> bool.must(must -> must.match(match -> match.field("all").query("如家")))))
.query(query -> query
.functionScore(fs -> fs
// 原始查询,相关性算分的查询
//.query(q -> q.match(match -> match.field("name").query("万丽")))
.query(q -> q.bool(b -> boolQuery))
// 算分函数和过滤条件
.functions(fsFunctions -> fsFunctions.filter(f -> f.term(t -> t.field("isAD").value(true))).weight(10D))
// 加权模式
.boostMode(FunctionBoostMode.Multiply))),
HotelDoc.class);
handleResponse(response);
}
/**
* 聚合查询 对品牌、城市、品牌、价格等维度进行聚合统计
* Buckets<LongTermsBucket> longBuckets = aggregate.lterms().buckets();
* Buckets<StringTermsBucket> stringBuckets = aggregate.sterms().buckets();
* Buckets<DoubleTermsBucket> doubleBuckets = aggregate.dterms().buckets();
* @param response
*/
@Test
void testAgg() throws IOException, InterruptedException {
SearchResponse<Map> response = client.search(builder ->
builder.index("hotel")
.size(0)
.aggregations("brandAgg", a -> a.terms(t -> t.field("brand").size(30))),
Map.class);
Map<String, Aggregate> aggregations = response.aggregations();
Aggregate brandAgg = aggregations.get("brandAgg");
Buckets<StringTermsBucket> buckets = brandAgg.sterms().buckets();
for (StringTermsBucket b : buckets.array()) {
System.err.println(b.key().stringValue() + " : " + b.docCount());
}
}
/**
* Bucket聚合功能,自定义排序规则
* @param response
*/
@Test
void testAggSort() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder ->
builder.index("hotel")
.size(0)
.aggregations("brand_agg", a -> a.terms(t -> t.field("brand").size(2).order(new NamedValue<>("_count", SortOrder.Desc)))),
HotelDoc.class);
System.err.println(response);
handleResponse(response);
}
/**
* Bucket聚合功能,限定聚合范围
* @param response
*/
@Test
void testAggRange() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder ->
builder.index("hotel")
.query(q -> q.range(r -> r.field("price").gte(JsonData.of(0)).lte(JsonData.of(300))))
.size(0)
.aggregations("brand_agg", a -> a.terms(t -> t.field("brand").size(2).order(new NamedValue<>("_count", SortOrder.Desc)))),
HotelDoc.class);
System.err.println(response);
handleResponse(response);
}
/**
* 嵌套Metrics 聚合函数
* @param response
*/
@Test
void testAggMetrics() throws IOException {
SearchResponse<HotelDoc> response = client.search(builder ->
builder.index("hotel")
.size(0)
.aggregations("brand_agg", a -> a.terms(t -> t.field("brand").size(2).order(new NamedValue<>("_count", SortOrder.Desc)))
.aggregations("price_agg", b -> b.avg(c -> c.field("price")))),
HotelDoc.class);
System.err.println(response);
//
// response.aggregations().forEach((k, v) -> {
// System.err.println(k);
// System.err.println(v.get("price_agg"));
// });
}
/**
* 自动补全
* @param response
*/
@Test
void testSuggest() throws IOException {
SearchResponse<Map> response = client.search(builder ->
builder.index("hotel")
.suggest(s -> s.suggesters("suggestions",
suggest -> suggest.prefix("hz").completion(c -> c.field("suggestion").skipDuplicates(true).size(10)))),
Map.class);
// 获取联想建议
Map suggestMap = response.suggest();
if (suggestMap != null) {
List<?> suggestionList = (List<?>) suggestMap.get("suggestions");
if (suggestionList != null) {
for (Object suggestionInfo : suggestionList) {
if (suggestionInfo instanceof Suggestion) {
Suggestion suggestion = (Suggestion) suggestionInfo;
if (suggestion.isCompletion()) {
CompletionSuggest completionSuggest = suggestion.completion();
List<CompletionSuggestOption> options = completionSuggest.options();
for (CompletionSuggestOption option : options) {
System.err.println(option.text());
}
}
}
}
}
}
}
private static void handleResponse(SearchResponse<HotelDoc> response) {
System.err.println(response.hits().total().value());
for (Hit<HotelDoc> doc : response.hits().hits()) {
HotelDoc hotelDoc = doc.source();
//System.err.println(doc);
//System.err.println(doc.highlight());
List<FieldValue> sortList = doc.sort();
if(ObjectUtils.isNotEmpty(sortList)){
hotelDoc.setDistance(sortList.get(0).doubleValue());
}
System.err.println(JSONObject.toJSONString(hotelDoc));
}
}
@BeforeEach
void setup() {
BasicCredentialsProvider credsProv = new BasicCredentialsProvider();
credsProv.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "elastic"));
restClient = RestClient.builder(HttpHost.create("http://127.0.0.1:9200"))
.setHttpClientConfigCallback(hc -> hc.setDefaultCredentialsProvider(credsProv))
.build();
transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
client = new ElasticsearchClient(transport);
}
@AfterEach
void tearDown() throws Exception{
client.shutdown();
transport.close();
restClient.close();
}
}