es組合多個條件進行查詢


GET /test_index/_search
{
  "query": {
    "bool": {
      "must": { "match": { "name": "tom" }},
      "should": [
        { "match": { "hired": true }},
        { "bool": {
          "must": { "match": { "personality": "good" }},
          "must_not": { "match": { "rude": true }}
        }}
      ],
      "minimum_should_match": 1
    }
  }
}

 

在es中,使用組合條件查詢是其作為搜索引擎檢索數據的一個強大之處,在前幾篇中,簡單演示了es的查詢語法,但基本的增刪改查功能並不能很好的滿足復雜的查詢場景,比如說我們期望像mysql那樣做到拼接復雜的條件進行查詢該如何做呢?es中有一種語法叫bool,通過在bool里面拼接es特定的語法可以做到大部分場景下復雜條件的拼接查詢,也叫復合查詢

首先簡單介紹es中常用的組合查詢用到的關鍵詞,

filter:過濾,不參與打分
must:如果有多個條件,這些條件都必須滿足 and與
should:如果有多個條件,滿足一個或多個即可 or或
must_not:和must相反,必須都不滿足條件才可以匹配到 !非

發生 描述
must
該條款(查詢)必須出現在匹配的文件,並將有助於得分。

filter
子句(查詢)必須出現在匹配的文檔中。然而不像 must查詢的分數將被忽略。Filter子句在過濾器上下文中執行,這意味着評分被忽略,子句被考慮用於高速緩存。

should
子句(查詢)應該出現在匹配的文檔中。如果 bool查詢位於查詢上下文中並且具有mustor filter子句,則bool即使沒有should查詢匹配,文檔也將匹配該查詢 。在這種情況下,這些條款僅用於影響分數。如果bool查詢是過濾器上下文 或者兩者都不存在,must或者filter至少有一個should查詢必須與文檔相匹配才能與bool查詢匹配。這種行為可以通過設置minimum_should_match參數來顯式控制 。

must_not
子句(查詢)不能出現在匹配的文檔中。子句在過濾器上下文中執行,意味着評分被忽略,子句被考慮用於高速緩存。因為計分被忽略,0所有文件的分數被返回。

下面用實驗演示一下上述查詢的相關語法,

1、首先,我們創建一個索引,並且在索引里添加幾條數據,方便后面使用,
我這里直接批量插入數據,也可以通過PUT的語法單條執行插入,

POST /forum/article/_bulk
{ "index": { "_id": 1 }}
{ "articleID" : "XHDK-A-1293-#fJ3", "userID" : 1, "hidden": false, "postDate": "2019-07-01","title":"java contains hadoop and spark","topic":"java" }
{ "index": { "_id": 2 }}
{ "articleID" : "KDKE-B-9947-#kL5", "userID" : 1, "hidden": false, "postDate": "2019-07-02",title":"php contains admin","topic":"java and php" }
{ "index": { "_id": 3 }}
{ "articleID" : "JODL-X-1937-#pV7", "userID" : 2, "hidden": false, "postDate": "2019-07-03" ,title":"spark is new language","topic":"spark may use java"}
{ "index": { "_id": 4 }}
{ "articleID" : "QQPX-R-3956-#aD8", "userID" : 2, "hidden": true, "postDate": "2019-07-04" ,title":"hadoop may involve java","topic":"big data used"}

1
2
3
4
5
6
7
8
9
10
或者使用put語法

PUT /forum/article/4
{
"articleID": "QQPX-R-3956-#aD8",
"userID": 2,
"hidden": true,
"postDate": "2019-07-04",
"title": "hadoop may involve java",
"topic": "big data used"
}
1
2
3
4
5
6
7
8
9
4條數據插入成功,


2、termQuery,term查詢不分詞,類似於mysql的where filedName = ? 語法,即精准匹配,比如我們查詢articleID = XHDK-A-1293-#fJ3的這條數據,


GET /forum/article/_search
{
"query": {
"term": {
"articleID.keyword":"XHDK-A-1293-#fJ3"
}
}
}
1
2
3
4
5
6
7
8
9


2、must查詢,即查詢的條件中必須匹配的字段,例如,查詢title中必須包含java的數據,


GET /forum/article/_search
{
"query": {
"bool": {
"must": [
{"term":{"title":"hadoop"}}
]
}
}
}
1
2
3
4
5
6
7
8
9
10
11
查出兩條數據


如果是should呢?如下語法,即查詢title中包含hadoop或者topic中包含spark,二者滿足其一即可,

GET /forum/article/_search
{
"query": {
"bool": {
"should": [
{"term":{"title":"hadoop"}},
{"term": {"topic": "spark"}}
]
}
}
}
1
2
3
4
5
6
7
8
9
10
11
查詢出3條數據,


must和should結合使用,


最后再來一個比較復雜的嵌套查詢,我們先看一下這條sql語句,
select *
from forum.article
where article_id=‘XHDK-A-1293-#fJ3’
or (article_id=‘JODL-X-1937-#pV7’ and post_date=‘2017-01-01’),
對應着轉化為es的復合查詢語法是怎樣的呢?拆分來看,就是一個should語句的嵌套,


GET /forum/article/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"articleID.keyword": "XHDK-A-1293-#fJ3"
}
},
{
"bool": {
"must": [
{
"term": {
"articleID.keyword":"JODL-X-1937-#pV7"
}
},
{
"term": {
"postDate":"2019-07-01"
}
}
]
}
}

]
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
查詢到一條結果,按照這種思路,如果我們對一個復雜的查詢不知道如何構建查詢語句時,可以考慮先按照sql的語法進行拆分,然后再組織es查詢語句是個不錯的突破口,

到這里,可能我們會有疑問,復合條件中的term查詢和單純的match區別在哪里呢?既然都是查詢,究竟原理有何不同呢?

我們知道match query是需要全文檢索的,是進行full text的全文檢索,當然如果搜索的字段值做了not_analyzed,match query也相當於是term query了,比如下面這個搜索,由於在插入數據的時候我們沒有對title這個字段進行規定,默認就是text類型的,會被自動分詞,這樣查詢的時候只要title中包含了 hadoop,就可以匹配到,

GET /forum/article/_search
{
"query": {
"match": {
"title": "hadoop"
}
}
}
1
2
3
4
5
6
7
8


2、有些情況下,假如我們直接使用match進行查詢,又希望查出來的結果盡可能是我們期望的包含更多關鍵詞的結果,則在match進行匹配的時候可以添加其他的條件,以便提升結果的匹配精確度,

GET /forum/article/_search
{
"query": {
"match": {
"title": {
"query": "java hadoop",
"operator": "and"
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
這樣匹配出來的結果包含了更多我們期望的關鍵詞,即query中可以指定我們查詢的結果中包含的關鍵詞,


es還有其他的語法達到上述的效果,minimum_should_match ,通過這個語法,可以指定匹配的百分數,就是查詢的關鍵詞至少要達到的百分數,下面這個表示全部匹配,只查詢到一條結果,


假如我們將百分數調低點,比如75%,可以看到查到兩條結果,


3、當然,我們也可以將bool和match結合起來使用,如下,

GET /forum/article/_search
{
"query": {
"bool": {
"must": [
{"match": {"title": "java"}}
],
"must_not": [
{ "match": { "title": "spark"}}
]
, "should": [
{
"match": {
"title": "php"
}
}
]
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
通過這種方式,也可以達到更精准的匹配我們期望的查詢結果,


簡單總結來說,當我們使用match進行查詢的時候,如果查詢的field包含多個詞,比如像下面這個,

{
"match": { "title": "java elasticsearch"}
}
1
2
3
其實es會在底層自動將這個match query轉換為bool的語法bool should,指定多個搜索詞,同時使用term query,則轉化后的語法如下,

{
"bool": {
"should": [
{ "term": { "title": "java" }},
{ "term": { "title": "elasticsearch" }}
]
}
}
1
2
3
4
5
6
7
8
而上面所說的match中加and的查詢,對應於bool查詢,轉化后為 term+must 的語法如下,

{
"match": {
"title": {
"query": "java elasticsearch",
"operator": "and"
}
}
}

{
"bool": {
"must": [
{ "term": { "title": "java" }},
{ "term": { "title": "elasticsearch" }}
]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
對於minimum_should_match這種語法來說,道理類似,

{
"match": {
"title": {
"query": "java elasticsearch hadoop spark",
"minimum_should_match": "75%"
}
}
}

{
"bool": {
"should": [
{ "term": { "title": "java" }},
{ "term": { "title": "elasticsearch" }},
{ "term": { "title": "hadoop" }},
{ "term": { "title": "spark" }}
],
"minimum_should_match": 3
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
我們來看一個具體的操作實例,也就是說必須至少包含3個關鍵詞的數據才會出現在搜索結果中,


3、在搜索中,我們有這樣一種需求,期望搜索的結果中包含java 如果標題中包含hadoop或spark就優先搜索出來,同時呢,如果一個帖子包含java hadoop,一個帖子包含java spark,包含hadoop的帖子要比spark優先搜索出來,

對於這樣的需求,通俗來講,就是需要通過增大某些搜索條件的權重,從而在搜索的結果中,更多符合和滿足我們業務場景的數據靠前搜索出來,在es中可以通過boost關鍵詞來增加搜索條件的權重,

GET /forum/article/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "java"
}
}
],
"should": [
{
"match": {
"title": {
"query": "hadoop"
}
}
},
{
"match": {
"title": {
"query": "spark",
"boost":2
}
}
},
{
"match": {
"title": {
"query": "php"
}
}
},
{
"match": {
"title": {
"query": "hadoop",
"boost": 5
}
}
}
]
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
上面這個例子意思是我們賦予搜索的title中包含hadoop的條件權重更大,hadoop的結果會有限被搜索出來


4、dis_max語法,也叫best_field,在某些情況下,假如我們在bool查詢中用多個字段進行查詢,但是查詢一樣,就可能導致說查詢出來的結果並不是按照我們期望的那個字段將其排在前面,也就是說,我們只需要包含指定字段的內容展示在前面,如下,

GET /forum/article/_search
{
"query": {
"bool": {
"should": [
{ "match": { "title": "java solution" }},
{ "match": { "content": "java solution" }}
]
}
}
}
1
2
3
4
5
6
7
8
9
10
11
title和content的搜索條件相同,但我們希望的是結果中title 包含java solution的靠前展示,但直接這樣查詢可能達不到預期的效果,如果使用dis_max進行拼接就可以了,

GET /forum/article/_search
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "java solution" }},
{ "match": { "content": "java solution" }}
]
}
}
}
1
2
3
4
5
6
7
8
9
10
11
通過這樣的方式,使得查詢的結果更符合預期值,


5、但是使用dis_max,只取某一個query最大的分數,完全不考慮其他query的分數,即假如說某個結果中包title含了java,但topic中沒有包含java,另一卻是相反,還有的結果是兩者都包含java,在dis_max語法下,只會拿到相關度得分最高的那一個,而不會考慮其他的結果,這時,如果需要獲取其他的title或者topic包含java的結果,可以使用tie_breaker進一步包裝,如下,

GET /forum/article/_search
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "spark" }},
{ "match": { "topic": "java"}}
],
"tie_breaker": 0.6
}
}
}
1
2
3
4
5
6
7
8
9
10
11


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM