官网连接:
https://www.elastic.co/guide/en/elasticsearch/reference/7.10/query-filter-context.html
Query DSL(Domain Specific Language)
- query string search
- 全文检索-fulltext search
- 精准匹配-term match
- 过滤器-filter
- 组合查询-bool query
1 查询上下文
使用query关键字进行检索,倾向于相关度搜索,故需要计算评分。搜索是Elasticsearch最关键和重要的部分。
GET product/_search
#这两个的查询是等效的
GET product/_search
{
"query": {
"match_all": {}
}
}
2 相关度评分:_score
概念:相关度评分用于对搜索结果排序,评分越高则认为其结果和搜索的预期值相关度越高,即越符合搜索预期值。在7.x之前相关度评分默认使用TF/IDF算法计算而来,7.x之后默认为BM25。在核心知识篇不必关心相关评分的具体原理,只需知晓其概念即可。
排序:相关度评分为搜索结果的排序依据,默认情况下评分越高,则结果越靠前。
GET product/_search
{
"query": {
"match": {
"name": "xiaomi"
}
}
}
结果:可以看到_score是倒序的
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 0.72615415,
"hits" : [
{
"_index" : "product",
"_type" : "_doc",
"_id" : "4",
"_score" : 0.72615415,
"_source" : {
"name" : "xiaomi erji",
"desc" : "erji zhong de huangmenji",
"price" : 999,
"tags" : [
"low",
"bufangshui",
"yinzhicha"
]
}
},
{
"_index" : "product",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.60996956,
"_source" : {
"name" : "xiaomi nfc phone",
"desc" : "zhichi quangongneng nfc,shouji zhong de jianjiji",
"price" : 4999,
"tags" : [
"xingjiabi",
"fashao",
"gongjiaoka"
]
}
}
]
}
}
3 元数据:_source
-
禁用_source:
-
好处:节省存储开销
-
坏处:
- 不支持update、update_by_query和reindex API。
- 不支持高亮。
- 不支持reindex、更改mapping分析器和版本升级。
- 通过查看索引时使用的原始文档来调试查询或聚合的功能。
- 将来有可能自动修复索引损坏。
总结:如果只是为了节省磁盘,可以压缩索引比禁用_source更好。
-
-
数据源过滤器:
Including:结果中返回哪些field
Excluding:结果中不要返回哪些field,不返回的field不代表不能通过该字段进行检索,因为元数据不存在不代表索引不存在
-
在mapping中定义过滤:支持通配符,但是这种方式不推荐,因为mapping不可变
PUT product2 { "mappings": { "_source": { "includes": [ "name", "price" ], "excludes": [ "desc", "tags" ] } } } PUT /product2/_doc/1 { "name": "hongmi erji", "desc": "erji zhong de kendeji", "price": 399, "tags": ["lowbee","xuhangduan","zhiliangx"] } GET product2/_search #结果,只展示了name\price
-
常用过滤规则
- “_source”: “false”,
- “_source”: “obj.*”,
- “_source”: [ “obj1.*”, “obj2.*” ],
- “_source”: {
“includes”: [ “obj1.*”, “obj2.*” ],
“excludes”: [ “*.description” ]
}
DELETE product2 PUT /product2/_doc/1 { "owner":{ "name":"zhangsan", "sex":"男", "age":18 }, "name": "hongmi erji", "desc": "erji zhong de kendeji", "price": 399, "tags": ["lowbee","xuhangduan","zhiliangx"] } GET product2/_search GET product2/_search { "_source": false, #查询结果,不显示_source "_source": "name", "_source": "owner", "_source": "owner.*", "_source": "owner.name", "_source": ["owner.name","owner.age"], #如果includes和excludes没有交集,此时查询结果只显示name\price,查询结果以includes为准,此时owner属性是没有写到excludes里边的,此时excludes不写也是可以的 #如果includes和excludes有了交集,如excludes里边也有name,此时查询结果只显示price "_source": { "includes": [ "name", "price" ], "excludes": [ "desc", "tags" ] }, "query": { "match_all": {} } }
-
4 query string search
#查询所有
GET /product/_search
#带参数,name里边包含xiaomi的
GET /product/_search?q=name:xiaomi
#分页
GET /product/_search?from=0&size=2&sort=price:asc
#在每条数据里边加入date字段
#精准匹配 exact value,只会匹配date为2021-06-01的数据
GET /product/_search?q=date:2021-06-01
#_all搜索 相当于在所有有索引的字段中检索(默认都是创建了倒排索引),会匹配内容包含2021-06-01的数据,结果为date\desc里有2021-06-01的数据
GET /product/_search?q=2021-06-01
#我们不想匹配desc里的怎么办?把它的索引关掉
#1.删掉product
DELETE product
#2.创建索引映射,关掉desc的索引
PUT product
{
"mappings": {
"properties": {
"desc": {
"type": "text",
"index": false
}
}
}
}
#3.插入还是刚才的那些数据
#4.再次查询,结果只有date的数据
GET /product/_search?q=2021-06-01
#恢复原来的数据,删掉product、插入还是刚才的那些数据
#发现查询到了desc里边包含01的数据,而没有date的数据,说明desc的数据被分词了
GET /product/_search?q=01
DELETE product
PUT /product/_doc/1
{
"name" : "xiaomi phone",
"desc" : "shouji zhong de zhandouji",
"date": "2021-06-01",
"price" : 3999,
"tags": [ "xingjiabi", "fashao", "buka" ]
}
PUT /product/_doc/2
{
"name" : "xiaomi nfc phone",
"desc" : "zhichi quangongneng nfc,shouji zhong de jianjiji",
"date": "2021-06-02",
"price" : 4999,
"tags": [ "xingjiabi", "fashao", "gongjiaoka" ]
}
PUT /product/_doc/3
{
"name" : "nfc phone",
"desc" : "shouji zhong de hongzhaji",
"date": "2021-06-03",
"price" : 2999,
"tags": [ "xingjiabi", "fashao", "menjinka" ]
}
PUT /product/_doc/4
{
"name" : "xiaomi erji",
"desc" : "erji zhong de huangmenji",
"date": "2021-04-15",
"price" : 999,
"tags": [ "low", "bufangshui", "yinzhicha" ]
}
PUT /product/_doc/5
{
"name" : "hongmi erji",
"desc" : "erji zhong de kendeji 2021-06-01",
"date": "2021-04-16",
"price" : 399,
"tags": [ "lowbee", "xuhangduan", "zhiliangx" ]
}
5 全文检索-fulltext search
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cSJj1ZUr-1682735356029)(01-核心知识篇.assets/image-20230415123708817.png)]
用户请求会被分词
-
match:匹配包含某个term的子句
-
match_all:匹配所有结果的子句
-
multi_match:多字段条件
-
match_phrase:短语查询,
#DSL
#match ,请求会被分词,分成了xiaomi、phone词相
#包含 xiaomi 或者 phone 的结果都会被命中
GET product/_search
{
"query": {
"match": {
"name": "xiaomi phone"
}
}
}
#match_all,查询所有数据
GET product/_search
{
"query": {
"match_all": {}
}
}
#multi_match,在fields字段里边包含了query
#name中包含了phone 或 huangmenji
#desc中包含了phone 或 huangmenji
#where a=xx or b=xx
GET product/_search
{
"query": {
"multi_match": {
"query": "phone huangmenji",
"fields": ["name","desc"]
}
}
}
#match_phrase
#这里的短语不会被分词,必须包含 xiaomi 和 nfc 两个词项,并且顺序还要相同,并且两个词项之间不能包含其他词项。注意 短语查询并不是直接包含 xiaomi nfc 这个短语
GET product/_search
{
"query": {
"match_phrase": {
"name": "xiaomi nfc"
}
}
}
6 精准匹配-term match
-
term:匹配和搜索词项完全相等的结果
-
term和match_phrase区别:
match_phrase 会将检索关键词分词, match_phrase的分词结果必须在被检索字段的分词中都包含,而且顺序必须相同,而且默认必须都是连续的
term搜索不会将搜索词分词
-
term和keyword区别
term是对于搜索词不分词,
keyword是字段类型,是对于source data中的字段值不分词
-
terms:匹配和搜索词项列表中任意项匹配的结果
-
range:范围查找
#没结果
GET product/_search
{
"query": {
"term": {
"name": "xiaomi phone"
}
}
}
#有结果。被检索字段里必须包含这里的每一个词项,且顺序一致,
GET product/_search
{
"query": {
"match_phrase": {
"name": "xiaomi phone"
}
}
}
#有结果
GET product/_search
{
"query": {
"term": {
"name.keyword": "xiaomi phone"
}
}
}
#查询tags里边包含lowbee\gongjiaoka
GET product/_search
{
"query": {
"terms": {
"tags": [
"lowbee",
"gongjiaoka"
]
}
}
}
#gt是>,gte是>=,lt是<,lte是<=
GET product/_search
{
"query": {
"range": {
"price": {
"gte": 399,
"lte": 2999
}
}
}
}
GET product/_search
{
"query": {
"range": {
"date": {
"gte": "2021-04-15",
"lte": "2021-04-16"
}
}
}
}
#date里边也可以加时区
#now-1d/d 当前时间减一天,单位是天
#now/d 当前时间,单位是天
GET product/_search
{
"query": {
"range": {
"date": {
"gte": "now-1d/d",
"lte": "now/d"
}
}
}
}
7 过滤器-filter
GET _search
{
"query": {
"constant_score": {
"filter": {
"term": {
"status": "active"
}
}
}
}
}
query和filter的主要区别在: filter是结果导向的而query是过程导向。
query倾向于“当前文档和查询的语句的相关度”,而filter倾向于“当前文档和查询的条件是不是相符”。即在查询过程中,query是要对查询的每个结果计算相关性得分的(_score,倒序的),而filter不会。另外filter有相应的缓存机制,可以提高查询效率。
例子,比如查询10万数据,按照age排序,不使用filter的时候,是按照_score倒序的;可以使用filter先把数据过滤出来,之后再排序。
#_score,倒序的
GET product/_search
{
"query": {
"term": {
"name": "phone"
}
}
}
#_score固定的,没有给定的话就是1.0,可以使用boost给定分数
GET product/_search
{
"query": {
"constant_score": {
"filter": {
"term": {
"name": "phone"
}
},
"boost": 1.2
}
}
}
#bool里边也可以嵌套filter
GET product/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"name": "phone"
}
}
]
}
}
}
8 组合查询-bool query
bool:可以组合多个查询条件,bool查询也是采用more_matches_is_better的机制,因此满足must和should子句的文档将会合并起来计算分值
- must:必须满足子句(查询)必须出现在匹配的文档中,并将有助于得分。
- filter:过滤器 不计算相关度分数,cache☆子句(查询)必须出现在匹配的文档中。但是不像 must查询的分数将被忽略。Filter子句在filter上下文中执行,这意味着计分被忽略,并且子句被考虑用于缓存。
- should:可能满足 or子句(查询)应出现在匹配的文档中。
- must_not:必须不满足 ,不计算相关度分数 not子句(查询)不得出现在匹配的文档中。子句在过滤器上下文中执行,这意味着计分被忽略,并且子句被视为用于缓存。由于忽略计分,0因此将返回所有文档的分数。
minimum_should_match:参数指定should返回的文档必须匹配的子句的数量或百分比。如果bool查询包含至少一个should子句,而没有must或 filter子句,则默认值为1。否则,默认值为0
#must计算出来的_score是不一样的,倒序
#查询name包含xiaomi\phone,且desc包含shouji zhong
GET product/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "xiaomi phone"
}
},
{
"match_phrase": {
"desc": "shouji zhong"
}
}
]
}
}
}
#filter计算出来的_score都是0.0
#查询name包含xiaomi\phone,且desc包含shouji zhong
GET product/_search
{
"query": {
"bool": {
"filter": [
{
"match": {
"name": "xiaomi phone"
}
},
{
"match_phrase": {
"desc": "shouji zhong"
}
}
]
}
}
}
#must_not计算出来的_score都是0.0
#查询name包含xiaomi\nfc,且price>=500 的对立面
GET product/_search
{
"query": {
"bool": {
"must_not": [
{
"match": {
"name": "xiaomi nfc"
}
},
{
"range": {
"price": {
"gte": 500
}
}
}
]
}
}
}
#should
#相当于or,查询条件就是可以满足0个,1个,多个
GET product/_search
{
"query": {
"bool": {
"should": [
{
"match_phrase": {
"name": "xiaomi nfc"
}
},
{
"range": {
"price": {
"lte": 500
}
}
}
]
}
}
}
#组合查询
#单独用must结果是1、2、4
#单独用filter结果是4、5
#must+filter结果4(交集是4)
#must与filter是 and 的关系
#为什么不把filter的条件写在must里边呢?
#must是要进行相关度分数计算的。先在filter中进行过滤(此时是不进行相关度分数的计算的),之后再用must。filter减少了计算,而且有缓存
GET product/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "xiaomi"
}
}
],
"filter": [
{
"range": {
"price": {
"lte": 1000
}
}
}
]
}
}
}
#单独用should结果是1、2、3、4、5
#单独用filter结果是2、3
#should+filter结果1、2、3、4、5
#因为此时should子句可以满足的数量是0,
#此时设置minimum_should_match为1,表示should子句可以满足的数量是1,执行结果就是2、3了
#可以多加几个should子句
#should子句里边也可以再包含bool子句
GET product/_search
{
"query": {
"bool": {
"filter": [
{
"range": {
"price": {
"lte": 10000
}
}
}
],
"should": [
{
"match_phrase": {
"name": "nfc phone"
}
}
]
}
}
}