elasticsearch中的排序与相关性

排序与相关性简介

  1. 默认情况下,返回的结果是按照相关性进行排序的,最相关的文档在最前面,
    后面会解析相关性意味着什么以及如何计算,先来看看sort参数以及如何使用它

排序

  1. 为了按照相关性进行排序,需要将相关性设置为一个数值,es中相关性是由一个浮点数表示,
    并在搜索结果树中通过_score返回,默认排序是_score降序
  2. 有的时候相关性评分并没有意义
    GET /us/_search
{
    "query": {
        "bool": {
            "filter": {
                "term": {
                    "date": "2014-09-20"
                }
            }
        }
    }
}

因为我们使用的是filter过滤,所以没有进行评分,_score默认值为0.0,
如果评分为0.0对你造成困扰,可以使用constant_score

{
    "query": {
        "constant_score": {
            "filter": {
                "term": {
                    "date": "2014-09-20"
                }
            }
        }
    }
}

这将让所有文档应用一个恒定分数(默认为1),它将执行与bool查询相同,性能相同,并且所有文档
像bool一样随机返回,这些文档只是有了一个分数,而不是0

  1. 按照字段的值进行排序
    在这个案例中,通过时间对tweet排序是有意义的,最新的tweet在前面,通过sort参数可以实现
    GET /us/_search
{
    "query": {
        "match_all": {}
    },
    "sort": {
        "date": {
            "order": "desc"
        }
    }
}

结果:
_score不被计算,因为它并没有用于排序
date字段的值表示未毫秒数,通过sort字段返回
_score和max_score字段都是null,计算_score花销巨大,通常仅用于排序,我们并不根据相关性进行排序
所以记录_score是没有意义的,如果你无论如何都要计算_score,可以设置这个参数:track_scores: true
一个简单的方法按照字段升序排序

{
    "query": {
        "match_all": {}
    },
    "sort": "date",  // 按照date字段升序排序
    "track_scores": true  // 始终计算_score
}
  1. 多级排序
    如果我们想要按照date和_score进行排序,并且匹配的结果首先按照日期排序,然后按照相关性进行排序
    GET /us/_search
{
    "query": {
        "match": {
            "name": "john"
        }
    },
    "sort": [
        {
            "date": {
                "order": "desc"
            }
        },
        {
            "_score": {
                "order": "desc"
            }
        }
    ]
}

先按照第一个条件排序,当第一个条件相同时才按照第二个条件排序
多级排序并不一定包含_score,也可以根据不同的字段进行排序,

  • query-string搜索也支持自定义排序,可以在查询字符串中使用sort参数
    GET /us/_search?q=tweet:is&sort=_score:desc
  1. 多值字段的排序
    一种情形是字段有多个值的排序, 需要记住这些值并没有固有的顺序;一个多值的字段仅仅是多个值的包装,这时应该选择哪个进行排序呢?
    对于数字或日期,你可以将多值字段减为单值,这可以通过使用 min 、 max 、 avg 或是 sum 排序模式 。
    例如你可以按照每个 date 字段中的最早日期进行排序,通过以下方法:
"sort": {
    "dates": {
        "order": "asc",
        "mode":  "min"
    }
}

字符串排序与多字段

我们可以按照tweet全文域字段进行搜索,使用tweet.keyword精确值域字段进行排序进行降序排序
GET /us/_search

{
    "query": {
        "match": {
            "tweet": "is"
        }
    },
    "sort": {
        "tweet.keyword": {
            "order": "desc"
        }
    }
}
  • 以全文analyzed字段进行排序,会消耗大量的内存

什么是相关性

  1. 默认情况下,返回的搜索结果是按照相关性倒叙排序的,但是什么是相关性?如何计算相关性?
    每一个文档都有一个相关性评分,用一个正浮点数_score表示,_score的评分越高,相关性越高
    查询语句会为每一个文档生成一个_score字段,
  • es的相似度算法被定义为检索词频率/反向文档频率,TF/IDF
  • 检索词频率TF(term frequency)
    检索词在该字段出现的频率,出现频率越高,相关性也越高,字段中出现过5次要出现过1次的相关性高
  • 反向文档频率IDF(document frequency)
    每个检索词在索引中的出现频率,频率越高,相关性月底,检索词出现在多数文档中会比出现在少数文档中权重更低
  • 字段长度准则
    字段的长度是多少?长度越长,相关性越低,检索词在一个短的title要比一个长的content权重更大
    单个查询可以联合使用TF/IDF和其它方式
  • 相关性并不只是全文本检索的专利,也适用于yes|no子句,匹配的子句越多,相关性评分越高
    如果多条子句被合并为一条复合的查询语句,例如bool查询,则每个查询子句计算出的评分会被合并到总的相关评分当中去
  1. 理解评分标准
    当调试一条比较复杂的查询语句时,想理解如何计算是比较困难的,es在每个查询语句中都有一个explain参数,
    将explain参数设置为true就可以获取更多详细的信息
    GET /us/_search?explain=true
{
    "query": {
        "match_phrase": {
            "tweet": "it"
        }
    }
}

explain参数可以让返回结果添加一个_score评分结果得来依据

    "_shard" :      1,
    "_node" :       "mzIVYCsqSWCG_M_ZffSs9Q",

注意返回值加入了该文档来自于哪个节点哪个分片上的信息,因为检索词频率和反向文档频率
是在每个分片中计算出来的,而不是每个索引中

  • 返回的结果中有TF检索词频率,IDF反向文档频率的相关计算
  • 输出explain代价是十分昂贵的,只能用于调试,不能用于生产
第一部分是关于计算的总结。告诉了我们 honeymoon 在 tweet 字段中的检索词频率/反向文档频率或TF/IDF, (这里的文档 0 是一个内部的 ID,跟我们没有关系,可以忽略。)

然后它提供了权重是如何计算的细节:

检索词频率:

检索词 `honeymoon` 在这个文档的 `tweet` 字段中的出现次数。
反向文档频率:

检索词 `honeymoon` 在索引上所有文档的 `tweet` 字段中出现的次数。
字段长度准则:

在这个文档中, `tweet` 字段内容的长度 -- 内容越长,值越小。
复杂的查询语句解释也非常复杂,但是包含的内容与上面例子大致相同。 通过这段信息我们可以了解搜索结果是如何产生的。

json形式的explain描述是难以阅读的,可以加上一个参数format=yaml,以yaml格式显示
GET /us/_search?explain=true&format=yaml

{
    "query": {
        "match_phrase": {
            "tweet": "it"
        }
    }
}

Doc Values介绍

  1. 当你对一个字段进行排序时,es需要访问每一个文档得到相关的值,倒排索引的检索性能是非常快的
    但是在字段值排序时却不是理想的结构
  • 在搜索的时候,我们能通过搜索关键词快速得到结果集
  • 在排序的时候,我们需要倒排索引里面每个字段值的集合,换句话说,我们需要转置倒排索引
    转置结构在其它系统中称为列存储,实际上,它将所有单字段的值存在单数据列中,这使的对其进行操作是十分高效的,例如排序
    在es中,Doc Values就是一种列式存储结构,默认情况下,每个字段的Doc Values都是激活的,
    Doc Values是在索引时创建的,当字段索引时,es为了能够快速检索,会把字段的值加入到倒排索引中,
    同时它也会存储该字段的Doc Values
  • es中的Doc Value通常被用于一下场景
    对一个字段进行排序
    对一个字段进行聚合
    某些过滤,比如地理位置过滤
    某些与字段相关的脚本计算

因为文档值被序列化到磁盘,我们可以依靠操作系统的帮助来快速访问。当 working set 远小于节点的可用内存
系统会自动将所有的文档值保存在内存中,使得其读写十分高速; 当其远大于可用内存
,操作系统会自动把 Doc Values 加载到系统的页缓存中,从而避免了 jvm 堆内存溢出异常。

  • 重点:排序发生在索引时建立的平行数据结构中
posted @ 2022-04-20 18:31  专职  阅读(603)  评论(0编辑  收藏  举报