前言
学习需要先打好基础,最后才能逐步深入。今天来重新探索下ElasticSearch
的全文搜索。
搜索
ElasticSearch中存在两种搜索方式。一种是Request Body Search
和Request URL Search
方式。Request URL Search
是通过将参数放在URL
之后来达到传递查询语句的目的,比如
GET /GET /kibana_sample_data_ecommerce/_search?q=customer_first_name:Eddie
另外一种是Request Body Search
是通过将参数以json
串的方式传递,相对于Request URL Search
来说更加清晰,更够传递更加复杂的参数。一般在实际的使用中,Request Body Search
会使用的多一些。下面主要讲一讲Request Body Search
的方式来查询ElasticSearch
。
Query and filter context
本文使用的数据均为kibana
中的kibana_sample_data_ecommerce
数据。
在ElasticSearch
的查询API中存在两种主要的过滤方式。一种是Query
,由query
引导。另外一种是filter
,由filter
来引导。两者之间的区别是在query
之后的查询子句,会对文档的分数产生影响。而filter
之后的查询子句不会对文档的分数产生影响。
举个例子。
GET /kibana_sample_data_ecommerce/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"customer_first_name": "Eddie"
}
}
],
"filter": {
"term": {
"customer_full_name.keyword": "Eddie Underwood"
}
}
}
}
}
可以看到该bool
查询存在match
和term
子查询。因为term
在filter
对象内,所以不会对最后的排序分数产生影响。而match
子查询中的customer_first_name
会对最后的排序分数产生影响。
Match All Query
match_all
match_all
作为最简单的查询。一般不会在生产环境中使用,在测试环境中使用的会较多。match_all
会查询出所有的文档。
举个例子。
GET /kibana_sample_data_ecommerce/_search
{
"query": {
"match_all": {}
}
}
match_none
今天才发现居然存在这么个搜索。match_none
是match_all
的反面,不会搜索到任何的文档。
GET /kibana_sample_data_ecommerce/_search
{
"query": {
"match_none": {}
}
}
Full text queries
作为一个全文搜索引擎的意义,对于text
字段使用全文搜索。最典型的应用是可以用来搜索文章(比如各大博客网站),用来搜索代码(比如Github)。
match Query
match
查询是最典型的全文搜索的查询子句。match
查询子句会将查询语句根据字段的分析器分析为多个单词。然后逐个查询文档中该字段是否存在。
举个例子。
GET /kibana_sample_data_ecommerce/_search
{
"query": {
"match": {
"customer_full_name": "Eddie Weber"
}
}
}
可以得到
"hits" : {
"total" : 115,
"hits" : [
{
"customer_full_name" : "Eddie Weber"
}
]
customer_full_name
会被分析为eddie
和weber
两个单词。之后在文档中的customer_full_name
中查询是否包含这两个字段。如果包含了这两个单词其中一个,那么就算是匹配成功。如果一个都没有包含,那么就表示匹配不成功。在示例数据中eddie
或者weber
匹配到了115个文档。
默认情况下匹配的算法的确跟上面一样,只要包含了一个单词就可以匹配上文档。但是,也可以通过参数operator
来修改这个算法。比如下面指定and
操作符。
GET /kibana_sample_data_ecommerce/_search
{
"query": {
"match": {
"customer_full_name": {
"query": "Eddie Weber",
"operator": "and"
}
}
}
}
只能匹配上两个文档。从原来的匹配115个文档降到了2个文档。
"hits" : {
"total" : 2,
"hits" : [
"_source" : {
"customer_full_name" : "Eddie Weber",
这里表示必须customer_full_name
必须既包含eddie
也要包含weber
才可以匹配上。
match Phrase Query
match Phrase
表示短语搜索。当我们想要搜索一个短语的时候,可以使用match Phrase Query
,比如我们还是想要搜索customer_full_name
为Eddie Webber
的人。但是,我们不想搜索出来只叫做Eddie
,也不想搜索出来叫做Eddie xxx Webber
的人。这个时候可以使用match Phrase
,因为match Phrase
不仅会要求字段内容中均包含eddie
和webber
也要求这两个单词的顺序是一致的,中间不能有其他单词插入。
比如我们再插入一条数据,一个叫做Eddie fake Weber
的人。
POST /kibana_sample_data_ecommerce/_doc/1
{
"customer_full_name":"Eddie fake Weber"
}
这个时候,使用match
和and
来进行匹配会发现有三条数据。
"hits" : {
"total" : 3,
"max_score" : 9.586426,
"hits" : [
{
"customer_full_name" : "Eddie Weber"
},
{
"customer_full_name":"Eddie Weber"
},
{
"customer_full_name":"Eddie fake Weber"
}
我们使用match Phrase Query
来进行搜索。
GET /kibana_sample_data_ecommerce/_search
{
"query": {
"match_phrase": {
"customer_full_name": "Eddie Weber"
}
}
}
就只会得到只有名字叫做Eddie Weber
的人。
"hits" : {
"total" : 2,
"max_score" : 9.586426,
"hits" : [
{
"customer_full_name" : "Eddie Weber"
},
{
"customer_full_name" : "Eddie Weber"
}
ElasticSearch
中还提供了一个叫做slop
的参数。使用slop
参数可以指定单词之间的最大间隔是多少。间隔指短语中单词与单词之间间隔了多少单词。比如上面的Eddie Weber
中的Eddie
和Weber
没有间隔任何的单词,所以间隔为0。而新插入的数据Eddie fake Weber
中的Eddie
和Weber
间隔为1,因为间隔了fake
这个单词。
默认的match phrase
的slop
的值为0。可以设置slop
值为1来匹配上Eddie fake Weber
GET /kibana_sample_data_ecommerce/_search
{
"query": {
"match_phrase": {
"customer_full_name": {
"query": "Eddie Weber",
"slop": 1
}
}
}
}
可以匹配上三个文档。
"hits" : {
"total" : 3,
"max_score" : 9.586426,
"hits" : [
{
"customer_full_name" : "Eddie Weber"
},
{
"customer_full_name":"Eddie Weber"
},
{
"customer_full_name":"Eddie fake Weber"
}
和match
搜索比较下可以发现match phrase
类似于match
加上and
条件再加上顺序和间隔为0的条件。
match Phrase Prefix Query
match phrase prefix
跟match phrase
之间的区别是match phrase prefix
可以让你在短语的最后一个单词上进行前缀匹配。比如还是想要搜索customer_full_name
,这个时候我可能已经忘记了Eddie Weber
的最后一个单词Weber
的全部内容了。那么我可以使用match phrase prefix
来搜索Eddie We
,因为最后一个单词We
会进行前缀匹配。所以,我还是能够搜索到Eddie Weber
这个人。
比如
GET /kibana_sample_data_ecommerce/_search
{
"query": {
"match_phrase_prefix": {
"customer_full_name": "Eddie We"
}
}
}
可以匹配上
"hits" : {
"total" : 2,
"max_score" : 9.586426,
"hits" : [
{
"customer_full_name" : "Eddie Weber"
},
{
"customer_full_name" : "Eddie Weber"
}
那么不是最后一个单词而是在之前的单词会进行前缀匹配吗?
比如我搜索Edd We
GET /kibana_sample_data_ecommerce/_search
{
"query": {
"match_phrase_prefix": {
"customer_full_name": "Edd We"
}
}
}
匹配不上任何的文档。因为只会对最后一个单词进行前缀匹配。
"hits" : {
"total" : 0,
"max_score" : null,
"hits" : [ ]
}
另外ElasticSearch
还提供了max_expansions
来控制最后一个前缀单词和需要匹配的单词之间的距离。什么意思?比如上面使用We
来前缀匹配Weber
。那么这两个单词之间的距离是ber
,也就是3个字母的距离。
加入我们使用max_expansions
来控制匹配的距离为2,看一下会不会匹配上Weber
?
关于写作
"百天"写作计划下半部。
每周更新一篇碎碎念。
每周三、周六更新一篇尽量全面,详细的文章。
可能时长突破了百天,但是又有什么关系呢?提高写作水平,形成写作的习惯才是最终的目的。
如果这篇文章给你带来了一些帮助,可以动动手指点个赞,顺便关注一波就更好了。
如果上面都没有,那么写下读完之后最想说的话?有效的反馈和你的鼓励是对我最大的帮助。
另外打算把博客给重新捡起来了。欢迎大家来访问吃西瓜。
我是shane。今天是2019年9月18日。"百天"写作计划下半部,52/100。