详解SpringCloud微服务技术栈:DSL查询ES文档高级语法、相关性算分数学原理总结

‍作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
上期文章:详解SpringCloud微服务技术栈:ElasticSearch实践1——RestClient操作索引库与文档
订阅专栏:微服务技术全家桶
希望文章对你们有所帮助

之前已经使用了DSL实现了索引的增删改查以及文档的增删改,并且通过RestClient进行实现。
但是文档的查询操作很复杂,并且分类比较多,所以先用DSL语句进行各种查询操作的实现,再用RestClient实现各类查询。

DSL查询ElasticSearch文档

  • DSL查询分类和基本语法
  • 全文检索查询
  • 精确查询
  • 地理查询
  • 复合查询
    • 相关性算分
    • Function Score Query
    • Boolean Query

DSL查询分类和基本语法

ElasticSearch提供了基于JSON的DSL来定义查询,常见查询的分类:

1、查询所有:查询出所有数据,一般测试的时候用,例如match_all
2、全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配,例如:
(1)match_query
(2)multi_match_query
3、精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段,例如:
(1)ids
(2)range
(3)term
4、地理(geo)查询:根据经纬度查询,例如:
(1)geo_distance
(2)geo_bounding_box
5、复合(compound)查询:将上述各种查询条件组合起来,合并查询条件,例如:
(1)bool
(2)function_score

基本语法:

GET /indexName/_search
{
	"query": {
		"查询类型": {
			"查询条件": "条件值"
		}
	}
}

如果是查询所有,那么就没有查询的条件,例如:

GET /hotel/_search
{
	"query": {
		"match_all": {
		}
	}
}

不过这种查询方法,最终显示的结果可能不会全部出来,避免压力过大,至于如何全部搜索出来,后续演示一下分页查询就好了。

全文检索查询

全文检索查询,会对用户输入的内容分词,常用于搜索框搜索:
match查询是全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索,例如:

# 将外滩如家分词为外滩和如家进行检索,都包含的酒店排名最高
GET /hotel/_search
{
	"query": {
		"match": {
			"all": "外滩如家"
		}
	}
}

multi_match与match相似,但是允许查询多个字段:

# 查询酒店的品牌和名称是否与“外滩如家”匹配
GET /hotel/_search
{
	"query": {
		"multi_match": {
			"query": "外滩如家",
			"fields": ["brand", "name"]
		}
	}
}

更推荐使用match,要查询的字段之前就已经全部放在all里面了。而multi_match要查询的字段太多,效率会更低下。

精确查询

精确查询一般是查找keyword、数值、日期、boolean等类型字段,不会对搜索条件做分词。常见的2种:
(1)term:根据词条精确值查询

GET /hotel/_search
{
	"query": {
		"term": {
			"city": {
				"value": "上海"
			}
		}
	}
}

(2)range:根据值的范围查询

# 查询 100<价格<=300 的酒店
GET /hotel/_search
{
	"query": {
		"range": {
			"price": {
				"gt": 100,
				"lte": 300
			}
		}
	}
}

地理查询

根据经纬度查询。常见应用场景包括携程、滴滴、微信等。
geo_bounding_box:选取两个经纬度坐标,并且根据这两个点做矩形,在矩形之内的酒店即为查询结果,比较适合用来查询一定范围内的信息。
geo_distance:查询到指定中心点小于某个距离值的所有文档。

GET /hotel/_search
{
	"query": {
		"geo_distance": {
			"distance": "15km",
			"location": "31.21, 121.5"
		}
	}
}

复合查询

复合查询就是把其它查询组合起来,实现更复杂的搜素逻辑

相关性算分

function score:算分函数查询,可以控制文档相关性算分,控制文档排名。如百度竞价。
简单的算分可以直接算词条的频率:
T F (词条频率) = 词条出现次数 文档中词条总数 TF(词条频率)=\frac{词条出现次数}{文档中词条总数} TF(词条频率)=文档中词条总数词条出现次数
但是当搜索内容里面包括多个词条时,词条就有权重之分了,使用TF-IDF算法
I D F (逆文档频率) = L o g ( 文档总数 包含词条的文档总数 ) s c o r e = ∑ i n T F (词条频率) ∗ I D F (逆文档频率) IDF(逆文档频率)=Log(\frac{文档总数}{包含词条的文档总数})\\ score=\sum_i^nTF(词条频率) * IDF(逆文档频率) IDF(逆文档频率)=Log(包含词条的文档总数文档总数)score=inTF(词条频率)IDF(逆文档频率)
也就是说,这个词条在全部文档中出现的越多,它就越不值钱
当然,现在的ES已经不使用这种算法了,而采用了BM25算法
S c o r e ( Q , d ) = ∑ i n l o g ( 1 + N − n + 0.5 n + 0.5 ) ⋅ f i f i + k 1 ⋅ ( 1 − b + b ∗ d l a v g d l ) Score(Q,d) = \sum_i^nlog(1+\frac{N-n+0.5}{n+0.5}) · \frac{f_i}{f_i+k_1 · (1-b+b*\frac{dl}{avgdl})} Score(Q,d)=inlog(1+n+0.5Nn+0.5)fi+k1(1b+bavgdldl)fi
这个算法具体是怎么样更优秀的大家可以自行推导,看起来复杂但是其实也没有特别的复杂,比起TF-IDF算法,BM25算法受大量词频的影响会小很多,在大量词频的情况下也相对更平滑:
详解SpringCloud微服务技术栈:DSL查询ES文档高级语法、相关性算分数学原理总结_第1张图片
因此需要记住ES中的相关性打分的算法有哪些,我看一些大厂面试也是会考的,甚至上面的公式也是要会的。

TF-IDF:在ES5.0之前,会随着词频增加而越来越大
BM25:在ES5.0之后,会随着词频的增加而增大,但增长曲线会趋于水平

Function Score Query

使用function score query,可以修改文档的相关性算分(从原有的相关性算法基础上修改),根据新得到的算分排序。
这里直接用例子来进行说明,假设用户要搜索带着“外滩”的酒店,但是因为品牌方为“如家”的酒店跟我是合作关系,我当然就希望查询结果中带“如家”的排名会靠前一点:
详解SpringCloud微服务技术栈:DSL查询ES文档高级语法、相关性算分数学原理总结_第2张图片
这只是个例子,其使用还有多种重新算分的方法,需要掌握如下几点:

1、query:原始查询,搜索文档并根据相关性来打分(BM25算法),得到query score
2、filter:过滤条件,符合条件的文档才会被重新算分
3、weight:算法函数,算分函数的结果成为function score,将来会与query score运算,得到新算分,常见算分函数:
(1)weight:给一个常量值,作为function score
(2)field_value_factor:将文档中的某个字段值,作为function score
(3)random_score:随机生成一个值,作为function score
(4)script_score:自定义计算公式,公式结果作为function score
4、boost_mode:加权模式,定义query scorefunction score的运算方式:
(1)multiply:默认方式,相乘
(2)replace:用function score替换query score
(3)其他:sum、avg、max、min

总结:

function score query定义的三要素:
(1)过滤条件:决定哪些文档要加分
(2)算法函数:如何计算function score
(3)加权方式:function score与query score如何运算

Boolean Query

布尔查询与function score query不一样,function score query是在原来的算分基础上修改算分。而Boolean Query不会修改算分是一个或多个查询子句的组合。子查询的组合方式有:

must:必须匹配每个子查询,类似“与”
should:选择性匹配子查询,类似“或”
must_not:必须不匹配,不参与算分,类似“非”
filter:必须匹配,不参与算分

must_not和filter可以解决一个问题,子查询过多的时候,参与算分太多就会影响性能,而must_not和filter只会返回匹配和不匹配,却不去具体算分,同时因为几乎没有啥运算,所以ES底层会将其放到缓存中去,因此这两种方法会大大提升性能。例如用户要过滤出酒店价格低于500的,那么只需要将符合条件的呈现就行,没必要去再给价格算分后排个序。

这样的方法常见于搜索中下的筛选按钮,比如筛选出品牌、酒店的星级等(filter或must_not),高效率的先筛选出一部分符合条件的酒店,然后再去搜索框中搜索(must),从而提高整体效率。

例如:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。
详解SpringCloud微服务技术栈:DSL查询ES文档高级语法、相关性算分数学原理总结_第3张图片

你可能感兴趣的:(微服务技术全家桶,搜索,算法,数学,微服务,spring,cloud,elasticsearch)