ES(Elasticsearch)排序与相关性
默认情况下,返回的结果是按照 相关性 进行排序的——最相关的文档排在最前。
1.按照字段的值排序
在这个案例中,通过时间来对 tweets 进行排序是有意义的,最新的 tweets 排在最前。 我们可以使用 sort
参数进行实现:
GET /_search
{
"query" : {
"bool" : {
"filter" : { "term" : { "user_id" : 1 }}
}
},
"sort": { "date": { "order": "desc" }}
}
2.多级排序
假定我们想要结合使用 date
和 _score
进行查询,并且匹配的结果首先按照日期排序,然后按照相关性排序:
GET /_search
{
"query" : {
"bool" : {
"must": { "match": { "tweet": "manage text search" }},
"filter" : { "term" : { "user_id" : 2 }}
}
},
"sort": [
{ "date": { "order": "desc" }},
{ "_score": { "order": "desc" }}
]
}
3.多值字段的排序
一种情形是字段有多个值的排序, 需要记住这些值并没有固有的顺序;一个多值的字段仅仅是多个值的包装,这时应该选择哪个进行排序呢?
对于数字或日期,你可以将多值字段减为单值,这可以通过使用 min
、 max
、 avg
或是 sum
排序模式 。 例如你可以按照每个 date
字段中的最早日期进行排序,通过以下方法:
"sort": {
"dates": {
"order": "asc",
"mode": "min"
}
}
4.字符串排序与多字段
被解析的字符串字段也是多值字段, 但是很少会按照你想要的方式进行排序。如果你想分析一个字符串,如 fine old art
, 这包含 3 项。我们很可能想要按第一项的字母排序,然后按第二项的字母排序,诸如此类,但是 Elasticsearch 在排序过程中没有这样的信息。
你可以使用 min
和 max
排序模式(默认是 min
),但是这会导致排序以 art
或是 old
,任何一个都不是所希望的。
我们真正想要做的是传递一个 单字段 但是却用两种方式索引它。所有的 _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 主字段与之前的一样: 是一个 analyzed 全文字段。
新的 tweet.raw 子字段是 not_analyzed.
现在,至少只要我们重新索引了我们的数据,使用 tweet
字段用于搜索,tweet.raw
字段用于排序:
GET /_search
{
"query": {
"match": {
"tweet": "elasticsearch"
}
},
"sort": "tweet.raw"
}
以全文 analyzed
字段排序会消耗大量的内存。
5.什么是相关性?
我们曾经讲过,默认情况下,返回结果是按相关性倒序排列的。 但是什么是相关性? 相关性如何计算?
每个文档都有相关性评分,用一个正浮点数字段 _score
来表示 。 _score
的评分越高,相关性越高。
Elasticsearch 的相似度算法被定义为检索词频率/反向文档频率, TF/IDF ,包括以下内容:
检索词频率
检索词在该字段出现的频率?出现频率越高,相关性也越高。 字段中出现过 5 次要比只出现过 1 次的相关性高。
反向文档频率
每个检索词在索引中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低。
字段长度准则
字段的长度是多少?长度越长,相关性越低。 检索词出现在一个短的 title 要比同样的词出现在一个长的 content 字段权重更大。
单个查询可以联合使用 TF/IDF 和其他方式,比如短语查询中检索词的距离或模糊查询里的检索词相似度。
5.1理解评分标准
当调试一条复杂的查询语句时,想要理解 _score
究竟是如何计算是比较困难的。Elasticsearch 在 每个查询语句中都有一个 explain 参数,将 explain
设为 true
就可以得到更详细的信息。
GET /_search?explain
{
"query" : { "match" : { "tweet" : "honeymoon" }}
}
"_explanation": { //honeymoon 相关性评分计算的总结
"description": "weight(tweet:honeymoon in 0)
[PerFieldSimilarity], result of:",
"value": 0.076713204,
"details": [
{ //honeymoon 相关性评分计算的总结
"description": "fieldWeight in 0, product of:",
"value": 0.076713204,
"details": [
{ //检索词频率
"description": "tf(freq=1.0), with freq of:",
"value": 1,
"details": [
{
"description": "termFreq=1.0",
"value": 1
}
]
},
{ //反向文档频率
"description": "idf(docFreq=1, maxDocs=1)",
"value": 0.30685282
},
{ //字段长度准则
"description": "fieldNorm(doc=0)",
"value": 0.25,
}
]
}
]
}
输出 explain
结果代价是十分昂贵的,它只能用作调试工具 。千万不要用于生产环境。
第一部分是关于计算的总结。告诉了我们 honeymoon
在 tweet
字段中的检索词频率/反向文档频率或TF/IDF, (这里的文档 0
是一个内部的 ID,跟我们没有关系,可以忽略。)
然后它提供了权重是如何计算的细节:
检索词频率:
检索词 `honeymoon` 在这个文档的 `tweet` 字段中的出现次数。
反向文档频率:
检索词 `honeymoon` 在索引上所有文档的 `tweet` 字段中出现的次数。
字段长度准则:
在这个文档中, `tweet` 字段内容的长度 -- 内容越长,值越小。
复杂的查询语句解释也非常复杂,但是包含的内容与上面例子大致相同。 通过这段信息我们可以了解搜索结果是如何产生的。
6.Doc Values 介绍
本章的最后一个话题是关于 Elasticsearch
内部的一些运行情况。在这里我们先不介绍新的知识点,所以我们应该意识到,Doc Values
是我们需要反复提到的一个重要话题。
当你对一个字段进行排序时,Elasticsearch
需要访问每个匹配到的文档得到相关的值。倒排索引的检索性能是非常快的,但是在字段值排序时却不是理想的结构。
- 在搜索的时候,我们能通过搜索关键词快速得到结果集。
- 当排序的时候,我们需要倒排索引里面某个字段值的集合。换句话说,我们需要
转置
倒排索引。
转置
结构在其他系统中经常被称作 列存储
。实质上,它将所有单字段的值存储在单数据列中,这使得对其进行操作是十分高效的,例如排序。
在 Elasticsearch
中,Doc Values
就是一种列式存储结构,默认情况下每个字段的 Doc Values
都是**的,Doc Values
是在索引时创建的,当字段索引时,Elasticsearch
为了能够快速检索,会把字段的值加入倒排索引中,同时它也会存储该字段的 Doc Values
。
Elasticsearch
中的 Doc Values
常被应用到以下场景:
- 对一个字段进行排序
- 对一个字段进行聚合
- 某些过滤,比如地理位置过滤
- 某些与字段相关的脚本计算
因为文档值被序列化到磁盘,我们可以依靠操作系统的帮助来快速访问。当 working set
远小于节点的可用内存,系统会自动将所有的文档值保存在内存中,使得其读写十分高速; 当其远大于可用内存,操作系统会自动把 Doc Values
加载到系统的页缓存中,从而避免了 jvm
堆内存溢出异常。
我们稍后会深入讨论 Doc Values
。现在所有你需要知道的是排序发生在索引时建立的平行数据结构中。
备注:文章参考Elasticsearch: 权威指南
https://www.elastic.co/guide/cn/elasticsearch/guide/current/sorting.html
上一篇: Redis集群方案(高可用)之哨兵模式(一主二从三哨兵)
下一篇: 考研复习(7)树的基本操作