Table of Contents generated with DocToc
在 Elasticsearch 中, 相关性得分 由一个浮点数进行表示,并在搜索结果中通过 _score
参数返回, 默认排序是 _score
降序。
在这个案例中,通过时间来对 tweets 进行排序是有意义的,最新的 tweets 排在最前。 我们可以使用 sort
参数进行实现:
GET /_search
{
"query" : {
"bool" : {
"filter" : { "term" : { "user_id" : 1 }}
}
},
"sort": { "date": { "order": "desc" }}
}
首先我们在每个结果中有一个新的名为 sort
的元素,它包含了我们用于排序的值。
其次 _score
和 max_score
字段都是 null
。 计算 _score
的花销巨大,通常仅用于排序; 我们并不根据相关性排序,所以记录 _score
是没有意义的。如果无论如何你都要计算 _score
, 你可以将track_scores
参数设置为 true
。
假定我们想要结合使用 date
和 _score
进行查询,并且匹配的结果首先按照日期排序,然后按照相关性排序:
GET /_search
{
"query" : {
"bool" : {
"must": { "match": { "tweet": "manage text search" }},
"filter" : { "term" : { "user_id" : 2 }}
}
},
"sort": [
{ "date": { "order": "desc" }},
{ "_score": { "order": "desc" }}
]
}
多级排序并不一定包含 _score
。
对于数字或日期,你可以将多值字段减为单值,这可以通过使用 min
、 max
、 avg
或是 sum
排序模式。 例如你可以按照每个 date
字段中的最早日期进行排序,通过以下方法:
"sort": {
"dates": {
"order": "asc",
"mode": "min"
}
}
被解析的字符串字段也是多值字段。为了以字符串字段进行排序,这个字段应仅包含一项: 整个 not_analyzed
字符串。 但是我们仍需要 analyzed
字段,这样才能以全文进行查询
一个简单的方法是用两种方式对同一个字符串进行索引,这将在文档中包括两个字段: analyzed
用于搜索, not_analyzed
用于排序。
但是保存相同的字符串两次在 _source
字段是浪费空间的。 我们真正想要做的是传递一个 单字段 但是却用两种方式索引它。所有的 _core_field
类型 (strings, numbers, Booleans, dates) 接收一个 fields
参数
该参数允许你转化一个简单的映射如:
"tweet": {
"type": "string",
"analyzer": "english"
}
为一个多字段映射如:
"tweet": {
"type": "string",
"analyzer": "english",
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed"
}
}
}
现在,至少只要我们重新索引了我们的数据,使用 tweet
字段用于搜索,tweet.raw
字段用于排序。
查询语句会为每个文档生成一个 _score
字段。评分的计算方式取决于查询类型 不同的查询语句用于不同的目的: fuzzy
查询会计算与关键词的拼写相似程度,terms
查询会计算 找到的内容与关键词组成部分匹配的百分比,但是通常我们说的 relevance 是我们用来计算全文本字段的值相对于全文本检索词相似程度的算法。
Elasticsearch 的相似度算法 被定义为 检索词频率/反向文档频率, TF/IDF ,包括以下内容:
-
检索词频率
检索词在该字段出现的频率?出现频率越高,相关性也越高。 字段中出现过 5 次要比只出现过 1 次的相关性高。
-
反向文档频率
每个检索词在索引中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低。
-
字段长度准则
字段的长度是多少?长度越长,相关性越低。 检索词出现在一个短的 title 要比同样的词出现在一个长的 content 字段权重更大。
当调试一条复杂的查询语句时, 想要理解 _score
究竟是如何计算是比较困难的。Elasticsearch 在 每个查询语句中都有一个 explain 参数,将 explain
设为 true
就可以得到更详细的信息。
GET /_search?explain
{
"query" : { "match" : { "tweet" : "honeymoon" }}
}
它提供了 _explanation
。每个 入口都包含一个 description
、 value
、 details
字段,它分别告诉你计算的类型、计算结果和任何我们需要的计算细节
当 explain
选项加到某一文档上时, explain
api 会帮助你理解为何这个文档会被匹配,更重要的是,一个文档为何没有被匹配。
请求路径为 /index/type/id/_explain
,如下所示:
GET /us/tweet/12/_explain
{
"query" : {
"bool" : {
"filter" : { "term" : { "user_id" : 2 }},
"must" : { "match" : { "tweet" : "honeymoon" }}
}
}
}
当你对一个字段进行排序时,Elasticsearch 需要访问每个匹配到的文档得到相关的值。倒排索引的检索性能是非常快的,但是在字段值排序时却不是理想的结构。
- 在搜索的时候,我们能通过搜索关键词快速得到结果集。
- 当排序的时候,我们需要倒排索引里面某个字段值的集合。换句话说,我们需要
倒置
倒排索引。
倒置
结构在其他系统中经常被称作 列存储
。实质上,它将所有单字段的值存储在单数据列中,这使得对其进行操作是十分高效的,例如排序。
在 Elasticsearch 中,doc values 就是一种列式存储结构,默认情况下每个字段的 doc values 都是激活的,doc values 是在索引时创建的,当字段索引时,Elasticsearch 为了能够快速检索,会把字段的值加入倒排索引中,同时它也会存储该字段的 doc values。
Elasticsearch 中的 doc vaules 常被应用到以下场景:
- 对一个字段进行排序
- 对一个字段进行聚合
- 某些过滤,比如地理位置过滤
- 某些与字段相关的脚本计算
因为文档值被序列化到磁盘,我们可以依靠操作系统的帮助来快速访问。当 working set
远小于节点的可用内存,系统会自动将所有的文档值保存在内存中,使得其读写十分高速; 当其远大于可用内存,操作系统会自动把 doc values 加载到系统的页缓存中,从而避免了 jvm 堆内存溢出异常。
上一章:7、请求体查询
下一章:9、执行分布式检索