0%

ElasticSearch 搜索操作

ElasticSearch 提供的搜索功能有单条件查询、复合查询、全文查询、地理位置查询四大类,而且提供了搜索建议和一些辅助功能。

查询全部

GET /${index_name}/_search 
{  
	"query": { 
	  	"match_all": {
	    }
	}
} 

单条件查询

Term 查询

term 查询是结构化精准查询的主要查询方式,用于查询待查字段和查询值是否完全匹配。

GET /${index_name}/_search 
{   
	"query": { 
  	"term": { 
    	"${FIELD}": {   //搜索字段名称
      	"value": "${VALUE}"  //搜索值 
      }
    }
  }
}  

FIELD 的数据类型可以是数值型、布尔型、日期型、数组型及关键字等

需要了解的是,term 是包含(contains) 操作,而非 等值(equals)判断。使用 term 要确定的是这个字段是否“被分析”(analyzed),默认的 String 类型是 analyzed,但也可以设置 not_analyzed

例如:现有一个 analyzed 的 String 类型和 not_analyzed 的 String 类型,内容在存储时都为 Quick Foxes!,在 term 查询时,由于 analyzed 类型的 String 类型在被分词后没有 Quick Foxes! 这个词,因此无法查询到数据。

Terms 查询

terms 查询是 term 查询的扩展形式,用于查询一个或多个值与待查字段是否完全匹配。

GET  /${index_name}/_search
{   
	"query": {  
  	"terms": {
  	  //指定查询字段,多个值之间用逗号分隔
    	"FIELD": ["VALUE1","VALUE2", … ]     
    }
  }
} 

Range 查询

range 查询用于范围查询,一般是对数值型和日期型数据的查询。使用 range 进行范围查询时,用户可以按照需求中是否包含边界数值进行选项设置,可供组合的选项如下:

  • gt:大于
  • lt:小于
  • gte:大于或等于
  • lte:小于或等于
GET /${index_name}/_search 
{   
	"query": {    
		"range": {        
	    	"price": {     //指定字段为price,此处包含边界值
		      	"gte": 300,
		        "lte": 500
			}
	    }
	}
} 

Exists 查询

在某些场景下,我们希望找到某个字段不为空的文档,则可以用 exists 搜索。

字段不为空的条件有:

  • 值存在且不是 null
  • 值不是空数组
  • 值是数组,但不是[null]
GET /${index_name}/_search 
{   
	"query": {    
		"exists": {          
			//查询tag字段不为空的文档
		    "field": "tag"     
	    }
	}
} 

Prefix 查询

前缀查询

GET /${index_name}/_search 
{   
	"query": {    
		"prefix": {          
			  //查询field字段的前缀为tag的文档
		    "field": {
		    	"value": "tag"
		    }     
	    }
	}
} 

模糊查询

Wildcard 查询

通配符索引。* 表示全匹配, 表示单一匹配。扫描所有倒排索引,性能较差。

GET /${index_name}/_search
{ 
  "query": { 
    "wildcard": { 
      "companyName": "*京东*" 
    } 
  } 
}

Regexp 查询

正则索引。扫描所有倒排索引,性能较差。

GET /${index_name}/_search
{ 
    "query": { 
        "regexp": { 
            "postcode": "W[0-9].+" 
        } 
    } 
}

Fuzzy 查询

用来模糊查询含有指定关键字的文档。

GET /${index_name}/_search 
{   
	"query": {    
		"fuzzy": {          
			  //查询field字段的前缀为tag的文档
		    "field": "tag" 
	    }
	}
} 

注意:fuzzy 查询最大模糊错误必须在 0-2 之间

搜索关键词长度为 2 不允许存在模糊
搜索关键词长度为 3-5 允许一次模糊
搜索关键词长度大于 5 允许最大 2 模糊

复合查询(Bool 查询)

复合搜索,顾名思义是一种在一个搜索语句中包含一种或多种搜索子句的搜索。

布尔查询是常用的复合查询,它把多个子查询组合成一个布尔表达式,这些子查询之间的逻辑关系是“与”,即所有子查询的结果都为 true 时布尔查询的结果才为真。布尔查询还可以按照各个子查询的具体匹配程度对文档进行打分计算。布尔查询支持的子查询有四种,各子查询的名称和功能如下表所示。

子查询名称 功能
must 必须匹配该查询条件,等同于 &&
should 可以匹配该查询条件,等同于 ` `
must not 必须不匹配该查询条件,相当于 !
filter 必须匹配过滤条件,不进行打分计算

Must 查询

当查询中包含 must 查询时,相当于逻辑查询中的“与”查询。命中的文档必须匹配该子查询的结果,并且 ES 会将该子查询与文档的匹配程度值加入总得分里。

GET /${index_name}/_search 
{   
	"query": {  
	  	"bool": {
	  		//must查询,数组内可封装各类子查询
	    	"must": [                         
	    		{                     
		        	"term": { 
			          	"city": { "value": "北京" }
			        }
		        },
		        {
		        	"range": { 
			          	"price": {  
			            	"gte": 350,
				            "lte": 400
			            }
			        }
		        }
		    ]
	    }
	}
} 

Should 查询

当查询中包含 should 查询时,表示当前查询为“或”查询。命中的文档可以匹配该查询中的一个或多个子查询的结果,并且 ES 会将该查询与文档的匹配程度加入总得分里。

GET /${index_name}/_search
{ 
	"query": {
	  	"bool": { 
	  		//shoud查询,数组内可封装各类子查询
	    	"should": [      
				{                         
					"term": { 
						"city": { "value": "北京" } 
					}
				},
				{                        
					"term": {
						"city": { "value": "天津" }
					}
				}
			]
	    }
	}
} 

Must Not 查询

当查询中包含 must not 查询时,表示当前查询为“非”查询。命中的文档不能匹配该查询中的一个或多个子查询的结果,ES 会将该查询与文档的匹配程度加入总得分里。

GET /${index_name}/_search
{ 
	"query": {
		"bool": { 
			//must_not查询,数组内可封装各类子查询
			"must_not": [      
				{                         
					"term": { 
						"city": { "value": "北京" } 
					}
				},
				{                        
					"term": {
						"city": { "value": "天津" }
					}
				}
			]
		}
	}
} 

Filter 查询

filter 查询即过滤查询,该查询是布尔查询里非常独特的一种查询。其他布尔查询关注的是查询条件和文档的匹配程度,并按照匹配程度进行打分;而 filter 查询关注的是查询条件和文档是否匹配,不进行相关的打分计算,但是会对部分匹配结果进行缓存。

GET /${index_name}/_search 
{   
	"query": { 
		"bool": {
			// filter查询,数组内可封装各类子查询
			"filter": [                  
				{                
					"term": { "city": "北京" }        
				},
				{                    
					"term": { "full_room": false }
				}
			]
		}
	}
}

Filter 查询原理

假设当前有五个文档。

ES 对 city 字段的倒排索引结构如下表:

关键词 文档
北京 doc1,doc5
天津 doc2,doc4
上海 doc3

ES 对于是否满房字段倒排的索引结构如下表:

关键词 文档
true doc1,doc2,doc5
false doc3,doc4

当 ES 执行过滤条件时,会查询缓存中是否有 city 字段值为“北京”对应的 bitset 数据,如果没有则构建并放入缓存。

城市字段 bitset
北京 1,0,0,0,1
满房字段 bitset
True 1,1,0,0,1

接下来 ES 会遍历查询条件的 bitset 数组,按照文档命中与否进行文档过滤。当一个请求中有多个 filter 查询条件时,ES 会构建多个 bitset 数组。为提升效率,ES 会从最稀疏的数组开始遍历,因为遍历稀疏的数组可以过滤掉更多的文档。

遍历计算完成后,得到匹配所有过滤条件的文档,即 doc1 和 doc5。

filter cache 会跟踪每一个 filter 查询,ES 筛选一部分 filter 查询的 bitset 进行缓存。首先,这些过滤条件要在最近 256 个查询中出现过;其次,这些过滤条件的次数必须超过某个阈值。

另外,filter cache 是有自动更新机制的,即如果有新增文档或者文档被修改过,那么filter cache 对应的过滤条件中的 bitset 将被更新。例如城市为“北京”过滤条件对应的 bitset 为 [1,0,0,0,1],如果文档 4 的城市被修改为“北京”,则“北京”过滤条件对应的 bitset 会被自动更新为 [1,0,0,1,1]。

filter 查询带来的效益远不止这些,使用 filter 查询的子句是不计算分数的,这可以减少不小的时间开销。

全文查询

不同于结构化查询,全文搜索首先对查询词进行分析,然后根据查询词的分词结果构建查询。这里所说的全文指的是文本类型数据(text类型)。结构化搜索关注的是数据是否匹配,全文搜索关注的是匹配的程度;结构化搜索一般用于精确匹配,而全文搜索用于部分匹配。

Match 查询

match 查询是全文搜索的主要代表。对于最基本的 match 搜索来说,只要分词中的一个或者多个在文档中存在即可。

在默认情况下,match 查询使用的是标准分词器。该分词器比较适用于英文,如果是中文则按照字进行切分,因此默认的分词器不适合做中文搜索。例如搜索“金都酒店”,查询词先被分词器切分为“金”“都”“酒”“店”,因此,只要文档中包含这 4 个字中的任何一个字,都会被搜索到。

注意:如果 match 查询数字,日期,布尔值或者 not_analyzed 的字符串时,会精确匹配搜索值,不做分词解析;如果 match 查询全文本,会对查询词做分词解析,然后搜索。

GET /hotel/_search 
{   
	"query": {     
		"match": {            
			"title": {
				"query": "金都酒店"  
			}
		}
	}
}  

我们也可以设置 operator 参数来决定分词后的词集合是进行“与”匹配还是“或”匹配。

“与”匹配

全文必须包含所有分词组合

GET /hotel/_search 
{       
	"query": {     
		"match": {     
			"title":{
				"query": "金都",
				//查询词之间的匹配结果为“与”关系
				//或操作符是 or / 与操作符是 and
				"operator":"and"          
			}
		}
	}
}  

“或”匹配

全文包含分词组合

GET /hotel/_search 
{       
	"query": {     
		"match": {     
			"title":{
				"query": "金都",
				//查询词之间的匹配结果为“或”关系
				"operator":"or"          
			}
		}
	}
}  

有时搜索多个关键字,关键词和文档在某一个比例上匹配即可,如果使用“与”操作过于严苛,如果使用“或”操作又过于宽松。这时可以采用 minimum_should_match 参数,该参数叫作最小匹配参数,其值为一个数值,意义为可以匹配上的词的个数。在一般情况下将其设置为一个百分数,因为在真实场景中并不能精确控制具体的匹配数量。

GET /hotel/_search 

{   
	"query": { 
  	"match": { 
    	"title": {  //match搜索条件
      	"query": "金都", 
        "operator": "or", 
        "minimum_should_match": "80%"  //设置最小匹配度为80%    
      }
    }
  }
} 

MultiMatch 查询

有时用户需要在多个字段中查询关键词,除了使用布尔查询封装多个 match 查询之外,可替代的方案是使用 multi_match。可以在 multi_match 的 query 子句中组织数据匹配规则,并在 fields 子句中指定需要搜索的字段列表。

GET /hotel/_search

{   
	"query": {
		"multi_match": {  
			//匹配关键字为“假日”
			"query": "假日",    
			//设置匹配的字段为title和amenities
			"fields": ["title", "amenities"]
		}
	}
}  

MatchPharse 查询

match_phrase 用于匹配短语,与 match 查询不同的是,match_phrase 用于搜索确切的短语或邻近的词语。

例如搜索“金都酒店”,查询词先被分词器切分为“金都” “酒店”,使用 match_phrase 则要求全文匹配时 “金都” “酒店” 必须按顺序相邻,此时 “金都精选酒店” 不会被查询命中。

GET /hotel/_search 
{  
	"query": {
		"match_phrase": { 
			"title": "文雅酒店" 
		}
	}
}  

match_phrase 也支持设置匹配距离,如果两词间隔 slop 参数定义的长度,也能被命中。

例如将 slop 设置为 2,上述例子可以被命中。

GET /hotel/_search 
{ 
	"query": {  
		"match_phrase": {  
			"title": {  
				"query": "文雅酒店", 
				"slop": 2   //将“文雅”和“酒店”之间的最大匹配距离设置为2       
			}     
		}
	}
}  

地理位置查询

ES 为用户提供了基于地理位置的搜索功能。它主要支持两种类型的地理查询:一种是地理点(geo_point),即经纬度查询,另一种是地理形状查询(geo_shape),即支持点、线、圆形和多边形查询等。

GeoDistance 查询

GET /hotel/_search 
{
	"query": {
		"geo_distance": { 
			"distance": "5km",     //设置距离范围为5km
			"location": {          //设置中心点经纬度 
				"lat": "39.915143",  //设置纬度
				"lon": "116.4039"    //设置经度 
			}
		}
	}
}  

GeoBoundingBox 查询

geo_shape 查询提供的是矩形内的搜索,需要用户给出左上角的顶点地理坐标和右下角的顶点地理坐标。

GET /hotel/_search 
{  
	"query": { 
		"geo_bounding_box": { 
			"location": {
				"top_left": {        //设置左上角的顶点坐标   
					"lat": "39.922821", 
					"lon": "116.457044"
				},
				"bottom_right": {    //设置右下角的顶点坐标   
					"lat": "39.907104", 
					"lon": "116.479466" 
				}
			}
		} 
	}
}  

GeoPolygon 查询

geo_polygon 比 geo_shape 提供的地理范围功能更加灵活,它支持多边形内的文档搜索,使用该查询需要提供多边形所有顶点的地理坐标。

GET /hotel/_search 
{  
	"query": { 
		"geo_polygon": {
			"location": { 
				"points": [   
					{   //设置三角形的第1个顶点坐标 
						"lat": "39.959829",
						"lon": "116.417088" 
					},
					{   //设置三角形的第2个顶点坐标    
						"lat": "39.960272",     
						"lon": "116.432035"    
					},        
					{   //设置三角形的第3个顶点坐标         
						"lat": "39.965802",     
						"lon": "116.421399"       
					}        
				]
			}
		}
	}
} 

搜索建议

ES 中 Completion Suggester,其对应的字段类型需要定义为 completion 类型。

PUT /hotel_sug 
{ 
	"mappings": {  
  	"properties": { 
    	"query_word": {
	      	"type": "completion"
      }
    }
  }
}  

使用以下查询方式查询建议内容。

GET /hotel_sug/_search 
{ 
	"suggest": {    
		"hotel_zh_sug": {   //定义搜索建议名称 
			"prefix": "如家",  //设置搜索建议的前缀 
			"completion": {   //设置搜索建议对应的字段名称
				"field": "query_word"
			}
		}
	}
} 

需要注意的是,ES 提供的 Completion Suggester 功能使用的索引结构不是倒排索引,而是在内存中构建 FST(Finite StateTransducers)。构建该数据结构是有比较大的内存存储成本的,因此在生产环境中向索引中添加数据时一定要关注 ES 节点的内存消耗,避免数据量过大造成 ES 节点内存耗尽从而影响集群服务。

匹配打分

Constant Score 查询

如果不想让检索词频率 TF(Term Frequency)对搜索结果排序有影响,只想过滤某个文本字段是否包含某个词,可以使用 Constant Score 将查询语句包装起来。

GET /hotel/_search 
{   
	"query": {    
		"constant_score": {    //满足条件即打分为1       
			"filter": {         
				"match": {        
					//查询设施中包含“停车场”的文档      
					"amenities": "停车场"
				}
			}
		}
	}
} 

通过结果可以看到,使用 Constant Score 搜索时,命中的酒店文档对应的 amenities 字段都包含有“停车场”一词。但是不论该词在文档中出现多少次,这些文档的得分都是一样的,值为 1.0。

在 Constant Score 搜索中,参数 boost 可以控制命中文档的得分,默认值为 1.0。

GET /hotel/_search 
{
	"query": {    
		"constant_score": {
			//设置Constant Score查询命中文档的得分为2.0
			"boost": 2.0,                   
			"filter": {       
				"match": {         
					"amenities": "停车场" 
				}
			}
		}
	}
} 

Function Score 查询

当使用 ES 进行搜索时,命中的文档默认按照相关度进行排序。有些场景下用户需要干预该“相关度”,此时就可以使用 Function Score 查询。使用时,用户必须定义一个查询以及一个或多个函数,这些函数为每个文档计算一个新分数。

二次打分

Query Rescore 工作的阶段是在原始查询打分之后,它支持对打分后 Top-N 的文档集合执行第二次查询和打分。通过设置 window_size 参数可以控制在每个分片上进行二次打分查询的文档数量,在默认情况下 window_size 的值为 10。在默认情况下,文档的最终得分等于原查询和 rescore 查询的分数之和。当然,还可以使用参数对这两部分的权重进行控制。

GET /hotel/_search 
{
	"query": {  
		"range": { 
			"price": { "gte": 300 }
		}
	}, 
	"rescore": {  
		"query": {
			"rescore_query": {  //对返回的文档进行二次打分  
				"match": {
					"title": "金都"  
				}
			}
		},
		"window_size": 3      //对每个分片的前3个文档进行二次打分  
	}
}  

辅助功能

返回指定字段

在 ES 中,通过 _source 子句可以设定返回结果的字段。_source 指向一个 JSON 数组,数组中的元素是希望返回的字段名称。

GET /hotel/_search 
{   
	"_source": ["title","city"],    //设定只返回title和city字段  
	"query": {     //查询条件     
		"term": {       
			"city": {         
				"value": "北京" 
			}
	    }
	}
}   

结果计数

ES 提供了 _count API功能,在该 API 中,用户提供 query 子句用于结果匹配,ES 会返回匹配的文档条数。

GET /hotel/_count 
{  
	"query": {       //计数的查询条件  
		"term": {
			"city": {
				"value": "北京" 
			}
		}
	}
}  

结果分页

普通分页

在默认情况下,ES 返回前 10 个搜索匹配的文档。用户可以通过设置 from 和 size 来定义搜索位置和每页显示的文档数量,from 表示查询结果的起始下标,默认值为 0,size 表示从起始下标开始返回的文档个数,默认值为 10。

GET /hotel/_search 
{  
	"from": 0,  //设置搜索的起始位置  
	"size": 20, //设置搜索返回的文档个数  
	"query": {  //搜索条件
	  	"term": {
	    	"city": { "value": "北京" }
	    }
	}
}

在默认情况下,用户最多可以取得 10 000 个文档,如果请求超过该值,ES 返回如下报错信息:

{   
  "error" : {     
    "root_cause" : [      
      {//请求数量过多的报错信息         
        "type" : "illegal_argument_exception",         
        "reason" : "Result window is too large, from + size must be less than  or equal to: [10000] but was [10001]. See the scroll api for a more efficient  way to request large data sets. This limit can be set by changing the  [index.max_result_window] index level setting."       
      }     
    ],},   
  "status" : 400 
}

对于普通的搜索应用来说,size 设为 10000 已经足够用了。如果确实需要返回多于 10000 条的数据,可以适当修改 max_result_window 的值。

ES 为什么不适合深分页?

作为一个分布式搜索引擎,一个 ES 索引的数据分布在多个分片中,而这些分片又分配在不同的节点上。一个带有分页的搜索请求往往会跨越多个分片,每个分片必须在内存中构建一个长度为 from+size 的、按照得分排序的有序队列,用以存储命中的文档。然后这些分片对应的队列数据都会传递给协调节点,协调节点将各个队列的数据进行汇总,需要提供一个长度为 number_of_shards *(from+size) 的队列用以进行全局排序,然后再按照用户的请求从 from 位置开始查找,找到 size 个文档后进行返回。

当深翻页的请求过多时会增加各个分片所在节点的内存和 CPU 消耗。尤其是协调节点,随着页码的增加和并发请求的增多,该节点需要对这些请求涉及的分片数据进行汇总和排序,过多的数据会导致协调节点资源耗尽而停止服务。

Scroll 分页

适用于从单个搜索请求中检索大量结果,其方式和传统数据库中的游标 Cursor 类似。在第一次查询时会缓存对应的结果快照并返回客户端读取到的位置参数 scroll_id,后续每次请求都会通过位置参数访问快照实现快速查询。

执行过程

Scroll 分页方式在 Query 阶段同样也是 Coordinating Node 广播查询请求,获取、合并、排序其他 shard 返回的数据 id 集合,不同的是 Scroll 分页方式会将返回数据 id 的集合生成快照保存到 Coordinating Node 上。Fetch 阶段以游标的方式从生成的快照中获取 size 条数据的 id,并去其他 shard 获取数据详情返回给客户端,同时将下一次游标开始的位置标识 scroll_id 也返回。这样下次客户端发送获取下一页请求时带上 scroll_id 标识,Coordinating Node 会从 scroll_id 标记的位置获取接下来 size 条数据,同时再次返回新的游标位置标识 scroll_id,这样依次类推直到取完所有数据。

优点:

  1. 可以处理大量数据,且结果的一致性优于 Search After
  2. 允许在不重新执行搜索的情况下获取后续的结果

缺点:

  1. 需要保持搜索上下文,因此会对 Elasticsearch 集群产生一定的资源压力
  2. 不适用于实时结果检索(结果可能不是最新的,因为它保持搜索上下文,而在滚动过程中索引可能发生变化)

以下是 Scroll 分页的使用方法:

# 指定检索语句同时设置 scroll 上下文保留时间
POST kibana_sample_data_logs/_search?scroll=3m
{
  "size": 100,
  "query": {
    "match": {
      "host": "elastic"
    }
  }
}

# 向后翻页继续获取数据,直到没有要返回的结果为止
POST _search/scroll                                   
{
  "scroll" : "3m",
  "scroll_id":"FGluY2x1ZGVfY29udGV4dF91d" 
}

PIT 分页

PIT(Point In Time)是基于索引状态的。它通过创建时间点 Point In Time(PIT)来保存搜索过程中的索引状态,允许你在特定时间点上执行搜索操作。这个时间点之后对索引的变化不会影响 PIT。

执行过程

优点:

  1. PIT 占用的资源较少,因为它是基于索引状态的快照,不需要保持资源上下文
  2. 保证结果在创建 PIT 时的索引状态下执行搜索,因此结果一致性较好

缺点:

  1. 数据由于是基于索引快照,因此没有最新数据

以下是 PIT 分页的使用方法:

# 创建 PIT
POST kibana_sample_data_logs/_pit?keep_alive=1m
{
  "id" : "48myAwEXa2liYW5hX3"
}

# 获取数据量 14074
POST kibana_sample_data_logs/_count

# 新增一条数据
POST kibana_sample_data_logs/_doc/14075
{
  "test":"just testing"
}

# 数据总量为 14075
POST kibana_sample_data_logs/_count


# 查询 PIT,数据依然是14074,说明走的是之前时间点的视图的统计。
POST /_search
{
  "track_total_hits": true, 
  "query": {
    "match_all": {}
  }, 
   "pit": {
    "id": "48myAwEXa2liYW5hX3NhbXBs"
  }
}

# 释放 PIT
POST /_pit/PIT_ID/_close

SearchAfter 分页

SearchAfter 是一种基于排序字段的分页方法。它允许你从一个特定的排序字段值开始获取结果,以获得后续的分页数据。

执行过程

Search After 方式也不支持跳页功能,每次查询一页数据。第一次每个 shard 返回一页数据(size 条),Coordinating Node 一共获取到 shard 数 * size 条数据 , 接下来 Coordinating Node 在内存中进行排序,取出前 size 条数据作为第一页搜索结果返回。当拉取第二页时,不同于 Scroll 分页方式,Search After 方式会找到第一页数据被拉取的最大值,作为第二页数据拉取的查询条件。

这样每个 shard 还是返回一页数据(size 条),Coordinating Node 获取到 shard 数 * size 条数据进行内存排序,取得前 size 条数据作为全局的第二页搜索结果。
后续分页查询以此类推…

优点:

  1. 不会对 Elasticsearch 集群产生大量资源压力
  2. 适用于实时结果检索

缺点:

  1. 可能在高并发环境中存在问题,如果有多个文档具有相同的排序字段值,可能会产生重复结果或遗漏结果。

以下是 SearchAfter 分页的使用方法:

POST /your_index/_search
{
  "size": 10,
  "query": {
    "match_all": {}
  },
  "sort": [
    {"your_sort_field": "asc"}
  ],
  "search_after": [last_sort_value]  // 使用上一页结果的排序字段值作为 search_after 参数
}

结果排序

ES 提供了 sort 子句可以对数据进行排序。使用 sort 子句一般是按照字段信息进行排序,不受相关性影响,而且打分步骤需要耗费一定的硬件资源和时间,因此默认情况下,不对文档进行打分。使用 sort 排序分为两种类别,一种是按照字段值的大小进行排序,另一种是按照给定地理坐标的距离远近进行排序。

按普通字段值排序

使用 sort 子句对字段值进行排序时需要指定排序的字段。ES 默认是按照字段值进行升序排序,可以设置 order 参数为 asc 或 desc,指定按照字段值进行升序或者降序排序。

GET /hotel/_search 
{  
	"query": {              
		"match": { "title": "金都" }
	}, 
	"sort": [   
		{                 
			//按照价格降序排列  
			"price": { "order": "desc" }
		}
	]
} 

使用 sort 对搜索结果排序后,在每个文档的 _source 信息下面多出了一个 sort 信息,该信息中显示了当前文档排序字段的值。另外,文档的 _score 值和 max_score 都为 null,这说明在默认情况下 ES 查询时使用 sort 对结果排序是不计算分数的。

按地理距离排序

ES 提供的基于地理位置的查询功能,使用 geo_distance 查询,配合 sort 可以指定另一种排序规则,即按照文档坐标与指定坐标的距离对结果进行排序。使用时,需要在 sort 内部指定排序名称为 geo_distanc,并指定目的地坐标。除了可以指定升序或者降序排列外,还可以指定排序结果中 sort 子句中的距离的计量单位,默认值为 km 即千米。在进行距离计算时,系统默认使用的算法为 arc,该算法的特点是计算精准但是耗费时间较长,用户可以使用 distance_type 参数选择另一种计算速度快但经度略差的算法,名称为 plane。

GET /hotel/_search 
{  
	"query": {     
		"geo_distance": {  
			"distance": "5km",
			"location": {          
				"lat": "39.915143",
				"lon": "116.4039"
			}
		}
	},  
	"sort": [                           
		//设置排序逻辑     
		{ 
			"_geo_distance": { 
				"location": {         
					//设置排序的中心点坐标 
					"lat": "39.915143",
					"lon": "116.4039" 
				},
				"order": "asc",     //按距离由近到远进行排序
				"unit": "km",       //排序所使用的距离的计量单位  
				"distance_type": "plane"  //排序所使用的距离计算算法  
			}
		}
	]
}   

查询时设置权重

boost

在 ES 中可以通过查询的 boost 值对某个查询设定其权重。在默认情况下,所有查询的 boost 值为 1。但是当设置某个查询的 boost 为 2 时,不代表匹配该查询的文档评分是原来的 2 倍,而是代表匹配该查询的文档得分相对于其他文档得分被提升了。

boost 值的设置只限定在 term 查询和类 match 查询中,其他类型的查询不能使用 boost 设置。boost 值没有特别约束,因为它代表的是一个相对值。当该值在 0~1 时表示对权重起负向作用,当该值大于 1 时表示对权重起正向作用。

GET /hotel/_search 
{
	"query": { 
		"bool": { 
			"should": [   
				{
					"match": { 
						"title":{     
							"query": "金都", 
							"boost": 2                  
						}
					}
				},
				{ 
					"match": {
						"title": { 
							"query": "文雅" 
						}
					}
				}
			]
		}
	}
}

boosting

boosting 给 range 查询提供了设置权重的能力。

ES 的 boosting 查询分为两部分,一部分是 positive 查询,代表正向查询,另一部分是 negative 查询,代表负向查询。可以通过 negative_boost 参数设置负向查询的权重系数,该值的范围为 0~1。最终的文档得分为:正向匹配值 + 负向匹配值 × negative_boost

GET /hotel/_search 
{
	"query": { 
	  	"boosting": {
	    	"positive": {
		      	"match": {  
		        	"title": { "query": "金都" }
		        }
			},
		    "negative": {           
		      	"range": { 
		        	"price": { 
			          	"lte": 200
		        	} 
		        }
		    },
			"negative_boost": 0.2   //设置降低的权重值   
	    }
	}
}