一、前言
繼上一節(jié)學習了ES的搜索的查詢全部和term搜索后,此節(jié)將把搜索匹配功能剩余的2個學習完,分別是range搜索和exists搜索
二、range范圍搜索
range查詢用于范圍查詢,一般是對數值型和日期型數據的查詢。使用range進行范圍查詢時,用戶可以按照需求中是否包含邊界數值進行選項設置,可供組合的選項如下:
- gt:大于;
- lt 小于;
- gte 大于等于;
- lte 小于等于;
其請求形式如下:
GET /hotel/_search
{
"query": {
"range": {
"FIELD": { //需要范圍查詢的列
"gte": "${VALUE1}", //大于等于value1
"lte": "${VALUE2}" //小于等于value2
}
}
}
}
以下是數值類型的查詢示例,查詢住宿價格在500~600(包含邊界值)元的酒店:
GET /hotel/_search
{
"query": {
"range": {
"price": {
"gte": "500",
"lte": "600"
}
}
}
}
ES返回的數據如下:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "001",
"_score" : 1.0,
"_source" : {
"title" : "文雅酒店",
"city" : "北京",
"price" : "558.00",
"create_time" : "2020-03-29 21:00:00",
"amenities" : "浴池,普通停車場/充電停車場",
"full_room" : true,
"location" : {
"lat" : 36.940243,
"lon" : 120.394
},
"praise" : 10
}
},
{
"_index" : "hotel",
"_type" : "_doc",
"_id" : "006",
"_score" : 1.0,
"_source" : {
"title" : "京盛集團精選酒店",
"city" : "上海",
"price" : "500.00",
"create_time" : "2022-01-29 22:50:00",
"full_room" : true,
"location" : {
"lat" : 40.918229,
"lon" : 118.422011
},
"praise" : 20
}
}
]
}
}
如果我需要查詢大于500元(不包含邊界值)的酒店:
GET /hotel/_search
{
"query": {
"range": {
"price": {
"gt": "500"
}
}
}
}
注意,使用range查詢時,查詢值必須符合該字段在mappings中設置的規(guī)范。例如,在酒店索引中,price字段是double類型,則range應該使用數值型或者數值類型的字符串形式,不能使用其他形式。以下示例將導致ES返回錯誤:
GET /hotel/_search
{
"query": {
"range": {
"price": {
"gt": "abc"
}
}
}
}
執(zhí)行上述DSL后,ES返回信息如下:
{
"error" : {
"root_cause" : [
{
"type" : "query_shard_exception",
"reason" : "failed to create query: For input string: \"abc\"",
"index_uuid" : "az-MqIf9QM6asEIfivIBLQ",
"index" : "hotel"
}
],
"type" : "search_phase_execution_exception", //range查詢解析異常
"reason" : "all shards failed",
"phase" : "query",
"grouped" : true,
"failed_shards" : [
{
"shard" : 0,
"index" : "hotel",
"node" : "ER773I31Sx-wJuJwJCh7Ng",
"reason" : {
"type" : "query_shard_exception",
//構建range查詢時出現異常
"reason" : "failed to create query: For input string: \"abc\"",
"index_uuid" : "az-MqIf9QM6asEIfivIBLQ",
"index" : "hotel",
"caused_by" : {
//字符串類型不能轉換為range查詢對應的數值型數據
"type" : "number_format_exception",
"reason" : "For input string: \"abc\""
}
}
}
]
},
"status" : 400
}
和term查詢類似,查詢日期型的字段時,需要遵循該字段在mappings中定義的格式進行查詢。例如create_time使用的格式為"yyyy-MM-dd HH:mm:ss",則range查詢應該使用如下方式:
GET /hotel/_search
{
"query": {
"range": {
"create_time": {
"gte": "2021-02-27 22:00:00",
"lte": "2024-02-27 22:00:00"
}
}
}
}
在Java客戶端上構建range請求是使用QueryBuilders.rangeQuery()
方法實現的,該方法的參數為字段名稱,然后再調用對應的方法即可構建相應的查詢范圍??梢哉{用gt()、lt()、gte()、lte()
等方法分別實現大于、小于、大于等于、小于等于等查詢范圍。在使用時支持鏈式編程,可以連著使用"."操作符,這樣不用拆分語句,也比較容易理解,一下通過range查詢完成一個createTime大于等于輸入的createTimeStart和小于等于createEnd的一個范圍查詢
Service層:
public List<Hotel> rangeQuery(HotelDocRequest hotelDocRequest) throws IOException {
//新建搜索請求
String indexName = hotelDocRequest.getIndexName();
if (CharSequenceUtil.isBlank(indexName)) {
throw new SearchException("索引名不能為空");
}
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
Date createTimeStart = hotelDocRequest.getCreateTimeStart();
String createTimeStartToSearch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createTimeStart);
Date createTimeEnd = hotelDocRequest.getCreateTimeEnd();
String createTimeEndToSearch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createTimeEnd);
searchSourceBuilder.query(QueryBuilders.rangeQuery("create_time").gte(createTimeStartToSearch).lte(createTimeEndToSearch));
searchRequest.source(searchSourceBuilder);
return getQueryResult(searchRequest);
}
Controller層:
@PostMapping("/query/range")
public FoundationResponse<List<Hotel>> rangeQuery(@RequestBody HotelDocRequest hotelDocRequest) {
try {
List<Hotel> hotelList = esQueryService.rangeQuery(hotelDocRequest);
if (CollUtil.isNotEmpty(hotelList)) {
return FoundationResponse.success(hotelList);
} else {
return FoundationResponse.error(100,"no data");
}
} catch (IOException e) {
log.warn("搜索發(fā)生異常,原因為:{}", e.getMessage());
return FoundationResponse.error(100, e.getMessage());
} catch (Exception e) {
log.error("服務發(fā)生異常,原因為:{}", e.getMessage());
return FoundationResponse.error(100, e.getMessage());
}
}
postman調用該接口:
三、exists查詢
在某些場景下,我們希望找到某個字段不為空的文檔,則可以用exists搜索。字段不為空的條件有:
- 值存在且不是null
- 值不是空數組
- 值是數組,但不是[null]
為方便測試,給索引hotel增加tag字段,DSL如下:
POST /hotel/_mapping
{
"properties": {
"tag":{
"type": "keyword"
}
}
}
下面向該索引中分別寫入3條字段為空的數據。
添加tag字段值為null的文檔,DSL如下:
POST /hotel/_create/020
{
"title":"環(huán)球酒店",
"tag":null
}
添加tag字段是空數組的文檔,DSL如下:
POST /hotel/_create/021
{
"title":"環(huán)球酒店2",
"tag":[]
}
添加tag為數組,其中只有一個元素,且該元素為null的文檔,DSL如下:
POST /hotel/_create/022
{
"title":"環(huán)球酒店3",
"tag":[null]
}
上面3種情況的數據使用exists查詢都不命中,查詢的DSL如下:
GET /hotel/_search
{
"query": {
"exists": {
"field": "tag"
}
}
}
返回結果如下:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0, //命中的文檔個數為0
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ] //命中的文檔集合為空
}
}
在java客戶端中進行查詢時,可以調用QueryBuilders.existsQuery(String name)
方法新建一個exists查詢,傳遞的name參數是目標字段名稱。以下是使用Java客戶端構建exists查詢的示例:
service層:
public List<Hotel> existQuery(HotelDocRequest hotelDocRequest) throws IOException {
//新建搜索請求
String indexName = hotelDocRequest.getIndexName();
String propertiesName = hotelDocRequest.getPropertiesName();
if (CharSequenceUtil.isBlank(indexName)) {
throw new SearchException("索引名不能為空");
}
if (CharSequenceUtil.isBlank(propertiesName)) {
throw new SearchException("想要查詢的字段名不能為空");
}
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.from(hotelDocRequest.getOffset());
searchSourceBuilder.size(hotelDocRequest.getLimit());
searchSourceBuilder.query(QueryBuilders.existsQuery(propertiesName));
searchRequest.source(searchSourceBuilder);
return getQueryResult(searchRequest);
}
controller層:文章來源:http://www.zghlxwxcb.cn/news/detail-493873.html
@PostMapping("/query/exist")
public FoundationResponse<List<Hotel>> existQuery(@RequestBody HotelDocRequest hotelDocRequest) {
try {
List<Hotel> hotelList = esQueryService.existQuery(hotelDocRequest);
if (CollUtil.isNotEmpty(hotelList)) {
return FoundationResponse.success(hotelList);
} else {
return FoundationResponse.error(100,"no data");
}
} catch (IOException e) {
log.warn("搜索發(fā)生異常,原因為:{}", e.getMessage());
return FoundationResponse.error(100, e.getMessage());
} catch (Exception e) {
log.error("服務發(fā)生異常,原因為:{}", e.getMessage());
return FoundationResponse.error(100, e.getMessage());
}
}
postman調用即可,比如搜索之前tag字段:
沒有搜到,所以報了no data
如果搜索title,則會將title不為null的值全部搜索出來,由于title不為空的比較多,我這邊只查前3條:文章來源地址http://www.zghlxwxcb.cn/news/detail-493873.html
到了這里,關于Elasticsearch(十一)搜索---搜索匹配功能②--range查詢和exists查詢的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!