如何计算评分的html
Lucene 使用布尔模型(Boolean model) 查找匹配文档 并主要的借鉴了 词频/逆向文档频率(term frequency/inverse document frequency) 和 向量空间模型(vector space model),同时加入 协调因子 字段长度归一化 以及词或查询语句权重提高node
词频web
词在文档中出现的频度是多少? 频度越高,权重 越高
tf(t in d) = √frequency 词 t 在文档 d 的词频( tf )是该词在文档中出现次数的平方根算法
逆向文档频率spring
词在集合全部文档里出现的频率是多少?频次越高,权重 越低
vidf(t) = 1 + log ( numDocs / (docFreq + 1)) 词 t 的逆向文档频率( idf )是:索引中文档数量除以全部包含该词的文档数,而后求其对数api
字段长度归一值数组
字段的长度是多少? 字段越短,字段的权重 越高 。
norm(d) = 1 / √numTerms 字段长度归一值( norm )是字段中词数平方根的倒数 session
对于 not_analyzed 字符串字段的归一值默认是禁用的
** 经过实例查询,来看评分是如何计算的**app
GET product/base/_search { "size": 1, "explain": true, "query": { "match": { "name": "上海" } } }
展现出的explanation 内容dom
{ "took": 17, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 19, "max_score": 4.266216, "hits": [ { "_shard": "[product_v3][3]", "_node": "N_gFl4xjTNmIxr9SY9FAQw", "_index": "product_v3", "_type": "base", "_id": "1-134473", "_score": 4.266216, "_source": { "productSource": 1, "departureCitys": [ "上海" ], "keywords": [ "上海", "哈哈" ], "pattern": "101", "buyQuantity": 34, "managerRecommend": "111。", "averageScore": 0, "themes": [ { "category": "游玩景点", "items": [ { "code": 4724, "name": "太原+五台山+大同+平遥" } ] } ], "installmentFlag": 0, "madeType": 0, "bussinessProductId": "1-134473", "attribute": 91, "supplierName": "春秋旅游", "passbyCities": [ "上海" ], "pictureLabels": [], "productId": 134473, "weight": 22, "picture": "http://webresourcetest.springtour.com/Images/gallery/201702/43362c75-91ae-43af-8cfd-85b43ec9199e_201702211501_500_350.jpg", "productThemes": [ "太原+五台山+大同+平遥" ], "brandId": 2, "name": "上海3日2晚上海名牌", "dayNum": 3, "online": 1 }, "_explanation": { "value": 4.266216, "description": "sum of:", "details": [ { "value": 4.266216, "description": "weight(name:上海 in 54) [PerFieldSimilarity], result of:", "details": [ { "value": 4.266216, "description": "score(doc=54,freq=2.0 = termFreq=2.0\n), product of:", "details": [ { "value": 2.730029, "description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:", "details": [ { "value": 4, "description": "docFreq", "details": [] }, { "value": 68, "description": "docCount", "details": [] } ] }, { "value": 1.5626998, "description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:", "details": [ { "value": 2, "description": "termFreq=2.0", "details": [] }, { "value": 1.2, "description": "parameter k1", "details": [] }, { "value": 0.75, "description": "parameter b", "details": [] }, { "value": 12.411765, "description": "avgFieldLength", "details": [] }, { "value": 7.111111, "description": "fieldLength", "details": [] } ] } ] } ] }, { "value": 0, "description": "match on required clause, product of:", "details": [ { "value": 0, "description": "# clause", "details": [] }, { "value": 1, "description": "_type:base, product of:", "details": [ { "value": 1, "description": "boost", "details": [] }, { "value": 1, "description": "queryNorm", "details": [] } ] } ] } ] } } ] } }
因为展现的内容过多,咱们将其简化 就会简化到下面这样的主干
weight(text:上海 in 0) [PerFieldSimilarity]: 0.15342641 // 词 fox 在文档的内部 Lucene doc ID 为 0 ,字段是 text 里的最终评分 result of: fieldWeight in 0 0.15342641 product of: tf(freq=1.0), with freq of 1: 1.0 //词 上海 在该文档 text 字段中只出现了一次。 idf(docFreq=1, maxDocs=1): 0.30685282 //上海 在全部文档 text 字段索引的逆向文档频率。 fieldNorm(doc=0): 0.5 // 该字段的字段长度归一值。
假设咱们有三个文档
咱们为每一个文档建立包含每一个查询此 的权重向量
happy hippopotamus” 查询及文档向量
向量之间是能够比较的,只要测量查询向量和文档向量之间的角度就能够获得每一个文档的相关度
咱们只是利用二维的演示这个原理,其实在现实中 确定有多于两个的。这样的咱们可使用线性代数 ——做为数学中处理向量的一个分支——为咱们提供了计算两个多维向量间角度工具
为何向量之间的角度能够表示他们的相关度 使用了 余弦近似度(cosine similarity)。
在一个多词查询中,ES是如何对这些多词进行处理的呢?
Lucene 使用 布尔模型(Boolean model) 、 TF/IDF 以及 向量空间模型(vector space model) ,而后将它们组合到单个高效的包里以收集匹配文档并进行评分计算
咱们使用例子来看多词查询 是怎么转化为基本查询的
GET /my_index/doc/_search { "query": { "match": { "text": "quick fox" } } } 会在内部被重写为 GET /my_index/doc/_search { "query": { "bool": { "should": [ {"term": { "text": "quick" }}, {"term": { "text": "fox" }} ] } } }
bool 查询实现了布尔模型
实用评分函数 计算公式
score(q,d) = //文档 d 与查询 q 的相关度评分。 queryNorm(q) //查询归一化 因子 · coord(q,d) //协调 因子 · ∑ ( //查询 q 中每一个词 t 对于文档 d 的权重和 tf(t in d) //词 t 在文档 d 中的 词频 。 · idf(t)² //词 t 的 逆向文档频率 · t.getBoost() //查询中使用的 boost( · norm(t,d) //是 字段长度归一值 ,与 索引时字段层 boost (若是存在)的和 ) (t in q)
查询归一因子 queryNorm
查询归一因子 ( queryNorm )试图将查询 归一化 , 这样就能将两个不一样的查询结果相比较。
查询协调
协调因子 ( coord ) 能够为那些查询词包含度高的文档提供奖励,文档里出现的查询词越多,它越有机会成为好的匹配结果。
索引时字段层权重提高
在索引时对这个字段上的数据进行权重的提高
不推荐这样使用
查询脚本
GET /_search { "query": { "bool": { "should": [ { "match": { "title": { "query": "quick brown fox", "boost": 2 } } }, { "match": { "content": "quick brown fox" } } ] } } }
** 将 boost 设置为 2 ,并不表明最终的评分 _score 是原值的两倍;实际的权重值会通过归一化和一些其余内部优化过程**
提高索引权重
当在多个索引中搜索时, 可使用参数 indices_boost 来提高整个索引的权重
GET /docs_2014_*/_search
{
"indices_boost": {
"docs_2014_10": 3,
"docs_2014_09": 2
},
"query": {
"match": {
"text": "quick brown fox"
}
}
}
t.getBoost()
权重提高不会被应用于它在查询表达式中出现的层,而是会被合并下转至每一个词中。 t.getBoost() 始终返回当前词的权重或当前分析链上查询的权重
假如一个业务场景,咱们想搜索Apple,可是咱们可能会返回水果 食谱和公司,咱们可使用must_not语句来排除咱们不想要的,而将结果范围缩小在Apple公司的相关的结果
GET /_search { "query": { "bool": { "must": { "match": { "text": "apple" } }, "must_not": { "match": { "text": "pie tart fruit crumble tree" } } } } }
可是咱们会不会发现,使用这种方法是否是过于严格了?会不会使咱们遗漏一些咱们所须要的数据呢?
咱们乐意使用 权重提高查询 方法
GET /_search { "query": { "boosting": { "positive": { "match": { "text": "apple" } }, "negative": { "match": { "text": "pie tart fruit crumble tree" } }, "negative_boost": 0.5 } } }
操做的过程就是那些匹配 positive 查询的文档罗列出来,对于那些同时还匹配 negative 查询的文档将经过文档的原始 _score 与 negative_boost 相乘的方式降级
下面咱们使用下面的这个查询来演示咱们提高权重的过程
使用function_score
GET product/_search { "query": { "function_score": {//function_score 查询将主查询和函数包括在内 "query": {//先查询主查询 "multi_match": { "query": "上海", "fields": ["name", "keywords"] } }, "field_value_factor": {field_value_factor 函数会被应用到每一个与主 query 匹配的文档 "field": "averageScore"//使用这个字段对score值进行计算 } } } } //每一个文档的最终评分score作了下面的修改 new_score = old_score * number_of_votes
这样的话,咱们的score值会根据成这样的比例增加
带modifyer 参数的请求
GET /blogposts/post/_search { "query": { "function_score": { "query": { "multi_match": { "query": "popularity", "fields": [ "title", "content" ] } }, "field_value_factor": { "field": "votes", "modifier": "log1p" } } } }
修饰词 modifyer 值能够为多种
factor
使用指定字段与factor 的积来调节score的值
咱们经过在factor属性添加值来改变score
GET product/_search
{
"query": {
"function_score": {
"query": {
"multi_match": {
"query": "上海",
"fields": ["name","keywords" ]
}
},
"field_value_factor": {
"field": "buyQuantity",
"modifier": "ln2p",
"factor": 2
}
}
}
}
在函数图表上显示的话,就是这样的
boost_mode
max_boost
经过整合多个函数,来提高权重信息
整合多个函数的查询
GET /_search { "query": { "function_score": { "filter": { //function_score 查询有个 filter 过滤器而不是 query 查询 "term": { "name": "上海" } }, "functions": [ //functions 关键字存储着一个将被应用的函数列表 { "filter": { "term": { "name": "杭州" }}, "weight": 1 }, { "filter": { "term": { "name": "北京" }}, "weight": 1 }, { "filter": { "term": { "name": "三亚" }}, "weight": 2 //三亚 比其余特性更重要,因此它有更高 weight } ], "score_mode": "sum" score_mode 指定各个函数的值进行组合运算的方式。 } } }
random_score 函数的做用
andom_score 函数会输出一个 0 到 1 之间的数, 当种子 seed 值相同时,生成的随机结果是一致的
GET /_search { "query": { "function_score": { "filter": { "term": { "city": "Barcelona" } }, "functions": [ { "filter": { "term": { "features": "wifi" }}, "weight": 1 }, { "filter": { "term": { "features": "garden" }}, "weight": 1 }, { "filter": { "term": { "features": "pool" }}, "weight": 2 }, { "random_score": { "seed": "the users session id" } } ], "score_mode": "sum" } } } // 1 **random_score** 语句没有任何过滤器 filter ,因此会被应用到全部文档 // 2 将用户的会话 ID 做为种子 seed ,让该用户的随机始终保持一致,相同的种子 seed 会产生相同的随机结果。
function_score 查询会提供一组 衰减函数(decay functions) , 让咱们有能力在两个滑动标准,如地点和价格,之间权衡
三种衰减函数
以原点 origin 为中心点,为其设置一个非零的偏移量 offset 覆盖一个范围,而不仅是单个原点。在范围 -offset <= origin <= +offset 内的全部评分 _score 都是 1.0
下面的是衰减函数曲线
gauss 高斯函数 先慢后快最后慢
脚本实例
GET product/base/_search { "query": { "function_score": { "query": {}, "functions": [ { "gauss": { "averageScore": { "origin": "100", "scale": "20", "offset": "50" } } }, { "exp": { "productId": { "origin": "1500", "scale": "100", "offset": "50" } }, "weight": 2 } ] } } }
在作字段映射的时候进行更改类似度
PUT /my_index { "mappings": { "doc": { "properties": { "title": { "type": "string", "similarity": "BM25" // title 字段使用 BM25 类似度算法 }, "body": { "type": "string", "similarity": "default" // body 字段用默认类似度算法 } } } }
Elasticsearch 不支持更改已有字段的类似度算法 similarity 映射
配置BM25类似度
PUT /my_index { "settings": { "similarity": { "my_bm25": { //建立一个基于内置 BM25 ,名为 my_bm25 的自定义类似度算法 "type": "BM25", "b": 0 //禁用字段长度规范化(field-length normalization) } } }, "mappings": { "doc": { "properties": { "title": { "type": "string", "similarity": "my_bm25" //title 字段使用自定义类似度算法 my_bm25 }, "body": { "type": "string", "similarity": "BM25" //字段 body 使用内置类似度算法 BM25 } } } } }