默认情况下,返回的结果是按照 相关性 进行排序的——最相关的文档排在最前。

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。现在所有你需要知道的是排序发生在索引时建立的平行数据结构中。