一、前言
之前的學(xué)習(xí)我們已經(jīng)了解了搜索的輔助功能,從這一章開始就是ES真正核心的功能,搜索。針對(duì)不同的數(shù)據(jù)類型,ES提供了很多搜索匹配功能:既有進(jìn)行完全匹配的term搜索,也有按照范圍匹配的range搜索;既有進(jìn)行分詞匹配的match搜索,也有按照前綴匹配的suggesr搜索。我們同樣也會(huì)通過在kibana上進(jìn)行DSL的搜索示例,也會(huì)給出java客戶端的使用代碼。本節(jié)我們將介紹兩個(gè)場(chǎng)景:查詢所有文檔和term級(jí)別的查詢。
二、查詢所有文檔
在關(guān)系型數(shù)據(jù)庫中,當(dāng)需要查詢所有文檔的數(shù)據(jù)時(shí),對(duì)應(yīng)的SQL語句為select * from table_name
.在ES中是否有類似的功能呢?答案是肯定的,使用ES的match_all查詢可以完成類似的功能。使用match_all查詢文檔時(shí),ES不對(duì)文檔進(jìn)行打分計(jì)算,默認(rèn)情況下給每個(gè)文檔賦予1.0的得分。用戶可以通過boost參數(shù)設(shè)定該分值。以下示例使用match_all查詢所有文檔,并設(shè)定所有文檔分值為2.0:
GET /hotel/_search
{
"_source": ["title","city"], //只返回title和city字段
"query": {
"match_all": { //查詢所有文檔
"boost": 2 //設(shè)定所有文檔的分值為2.0
}
}
}
ES返回的數(shù)據(jù)如下:
{
...
"hits" : {
"total" : {
"value" : 6, //命中6個(gè)文檔
"relation" : "eq"
},
"max_score" : 2.0,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "001",
"_score" : 2.0, //最高分為2.0
"_source" : { //命中的文檔集合
"city" : "北京",
"title" : "文雅酒店"
}
},
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "002",
"_score" : 2.0,
"_source" : {
"city" : "北京",
"title" : "京盛酒店"
}
},
...
]
}
}
通過返回的數(shù)據(jù)集可以看到,ES返回所有的文檔,并且所有文檔的得分都為2.0
在Java客戶端進(jìn)行查詢時(shí),可以調(diào)用QueryBuilders.matchAllQuery()
方法新建一個(gè)match_all查詢,并且通過boost()
方法設(shè)置boost值。構(gòu)建完term查詢后,調(diào)用searchSourceBuilder.query()
方法設(shè)置查詢條件。
為方便演示,下面定義一個(gè)打印搜索結(jié)果的方法,該方法接收一個(gè)SearchRequest實(shí)例并將搜索結(jié)果設(shè)置查詢條件。
由于我們的獲取結(jié)果那一塊兒,后面都是共同的,所以我們可以將這塊兒代碼獨(dú)立出來:
private List<Hotel> getQueryResult(SearchRequest searchRequest) throws IOException {
ArrayList<Hotel> resultList = new ArrayList<>();
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
RestStatus status = searchResponse.status();
if (status != RestStatus.OK) {
return Collections.emptyList();
}
SearchHits searchHits = searchResponse.getHits();
for (SearchHit searchHit : searchHits) {
Hotel hotelResult = new Hotel();
hotelResult.setId(searchHit.getId()); //文檔_id
hotelResult.setIndex(searchHit.getIndex()); //索引名稱
hotelResult.setScore(searchHit.getScore()); //文檔得分
//轉(zhuǎn)換為Map
Map<String, Object> dataMap = searchHit.getSourceAsMap();
hotelResult.setTitle((String) dataMap.get("title"));
hotelResult.setCity((String) dataMap.get("city"));
String price = (String) dataMap.get("price");
if (StrUtil.isNotBlank(price)) {
hotelResult.setPrice(Double.valueOf(price));
}
resultList.add(hotelResult);
}
return resultList;
}
然后我們可以在service層使用match_all查詢:
public List<Hotel> matchAllQuery(HotelDocRequest hotelDocRequest) throws IOException {
String indexName = hotelDocRequest.getIndexName();
if (CharSequenceUtil.isBlank(indexName)) {
throw new SearchException("索引名不能為空");
}
//新建搜索請(qǐng)求
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//這里我直接New MatchAllQueryBuilder,不過更推薦QueryBuilders.matchAllQuery().boost(2.0f)
//新建match_all查詢,并設(shè)置boost值為2.0
searchSourceBuilder.query(new MatchAllQueryBuilder().boost(2.0f));
searchRequest.source(searchSourceBuilder);
return getQueryResult(searchRequest);
}
在controller層調(diào)用service:
@PostMapping("/query/match_all")
public FoundationResponse<List<Hotel>> matchAllQuery(@RequestBody HotelDocRequest hotelDocRequest) {
try {
List<Hotel> hotelList = esQueryService.matchAllQuery(hotelDocRequest);
if (CollUtil.isNotEmpty(hotelList)) {
return FoundationResponse.success(hotelList);
} else {
return FoundationResponse.error(100,"no data");
}
} catch (IOException e) {
log.warn("搜索發(fā)生異常,原因?yàn)?{}", e.getMessage());
return FoundationResponse.error(100, e.getMessage());
} catch (Exception e) {
log.error("服務(wù)發(fā)生異常,原因?yàn)?{}", e.getMessage());
return FoundationResponse.error(100, e.getMessage());
}
}
postman調(diào)用該接口:
三、term級(jí)別查詢
3.1、term查詢
term查詢是結(jié)構(gòu)化精準(zhǔn)查詢的主要查詢方式,用于查詢待查字段和查詢值是否完全匹配,其請(qǐng)求形式如下:
GET /hotel/_search
{
"query": {
"term": {
"${FIELD}": { //搜索字段名稱
"value": "${VALUE}" //搜索值
}
}
}
}
其中,F(xiàn)IELD和VALUE分別代表字段名稱和查詢值,F(xiàn)IELD的數(shù)據(jù)類型可以是數(shù)值型,布爾型、日期型、數(shù)組型及關(guān)鍵字等。
例如,下面的例子是查找city為北京的酒店,DSL如下:
GET /hotel/_search
{
"_source": ["title","city"], //希望返回的結(jié)果字段
"from": 0, //分頁
"size": 10001,
"query": {
"term": {
"city": { //搜索字段是city,字段類型為keyword
"value": "北京"
}
}
}
}
返回結(jié)果如下:
對(duì)于日期型的字段查詢,需要按照該字段在mappings中定義的格式進(jìn)行查詢。如果格式不對(duì),那么請(qǐng)求將報(bào)錯(cuò):
例如我這邊create_time的格式是yyyy-MM-dd HH:mm:ss
如果我拿其他格式進(jìn)行請(qǐng)求:
GET /hotel/_search
{
"_source": ["title","city"],
"query": {
"term": {
"create_time": {
"value": "20230121142456"
}
}
}
}
會(huì)發(fā)現(xiàn)報(bào)錯(cuò)
在java客戶端中進(jìn)行查詢時(shí),可以調(diào)用QueryBuilders.termQuery()
方法新建一個(gè)term查詢,可以傳入boolean、double、float、int、long和String等類型的參數(shù),但是沒有日期類型的參數(shù),如下圖所示:
那么如何構(gòu)建日期類型的term查詢呢?可以使用日期格式字符串類型的term查詢來解決。以下為使用日期類型的字符串參數(shù)構(gòu)建的term查詢:我們傳參一般是date類型,這個(gè)時(shí)候我們將傳過來的fate類型通過simpleDateFormat對(duì)傳參的date進(jìn)行轉(zhuǎn)化,即可順利進(jìn)行查詢。
public List<Hotel> getCityByCreateTime(HotelDocRequest hotelDocRequest) throws IOException {
String indexName = hotelDocRequest.getIndexName();
if (CharSequenceUtil.isBlank(indexName)) {
throw new SearchException("索引名不能為空");
}
Hotel hotel = hotelDocRequest.getHotel();
if (ObjectUtil.isEmpty(hotel)) {
throw new SearchException("搜索條件不能為空");
}
SearchRequest searchRequest = new SearchRequest(indexName);
//創(chuàng)建搜索builder
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//構(gòu)建query
String createTimeToSearch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format( hotel.getCreateTime());
searchSourceBuilder.query(QueryBuilders.termQuery("create_time",createTimeToSearch));
//設(shè)定希望返回的字段數(shù)組
searchRequest.source(searchSourceBuilder); //設(shè)置查詢
return getQueryResult(searchRequest);
}
以下是postman調(diào)用例子
3.2、terms查詢
terms查詢是term查詢的擴(kuò)展形式,,用于查詢一個(gè)或多個(gè)值與待查字段是否完全匹配,請(qǐng)求形式如下:
GET /hotel/_search
{
"query": {
"terms": {
"FIELD": [
"VALUE1",
"VALUE2"
]
}
}
}
其中,F(xiàn)IEKD代表待查字段名,VALUE1和VALUE2代表多個(gè)查詢值,F(xiàn)IELD的數(shù)據(jù)類型可以是數(shù)值型,布爾型,日期型,數(shù)組型及關(guān)鍵字等。
以下是搜索城市為北京或者上海的酒店示例:
GET /hotel/_search
{
"_source": ["title","city"],
"from": 0,
"size": 10001,
"query": {
"terms": {
"city": [
"北京",
"上海"
]
}
}
}
在java客戶端中對(duì)應(yīng)terms查詢的類為TermsQuery
,該類的實(shí)例通過QueryBuilders.termsQuery()
生成。在QueryBuilders.termsQuery()
方法中,第一個(gè)參數(shù)為字段名稱,第二個(gè)參數(shù)是一個(gè)集合類型,也可以是一個(gè)單獨(dú)類型,當(dāng)為單獨(dú)類型時(shí),該參數(shù)為可變長度參數(shù)。QueryBuilders.termsQuery()
方法列表如下圖:
以下是使用java進(jìn)行terms查詢城市為北京或者上海的酒店示例:
Service層,我們接收一個(gè)citys數(shù)組參數(shù)
public List<Hotel> termsQuery(HotelDocRequest hotelDocRequest) throws IOException {
//新建搜索請(qǐng)求
String indexName = hotelDocRequest.getIndexName();
if (CharSequenceUtil.isBlank(indexName)) {
throw new SearchException("索引名不能為空");
}
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
List<String> cities = hotelDocRequest.getCities();
searchSourceBuilder.query(QueryBuilders.termsQuery("city", cities));
searchRequest.source(searchSourceBuilder);
return getQueryResult(searchRequest);
}
controller層:文章來源:http://www.zghlxwxcb.cn/news/detail-508645.html
@PostMapping("/query/terms")
public FoundationResponse<List<Hotel>> termsQuery(@RequestBody HotelDocRequest hotelDocRequest) {
try {
List<Hotel> hotelList = esQueryService.termsQuery(hotelDocRequest);
if (CollUtil.isNotEmpty(hotelList)) {
return FoundationResponse.success(hotelList);
} else {
return FoundationResponse.error(100,"no data");
}
} catch (IOException e) {
log.warn("搜索發(fā)生異常,原因?yàn)?{}", e.getMessage());
return FoundationResponse.error(100, e.getMessage());
} catch (Exception e) {
log.error("服務(wù)發(fā)生異常,原因?yàn)?{}", e.getMessage());
return FoundationResponse.error(100, e.getMessage());
}
}
postman執(zhí)行,發(fā)現(xiàn)搜索成功:文章來源地址http://www.zghlxwxcb.cn/news/detail-508645.html
到了這里,關(guān)于Elasticsearch(十)搜索---搜索匹配功能①--查詢所有文檔和term級(jí)別查詢的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!