ES 20 - 查詢Elasticsearch中的資料 (基於DSL查詢, 包括查詢校驗match + bool + term)

瘦風發表於2019-06-27

1 什麼是DSL

DSL: Domain Specific Language, 領域特定語言, 指的是專注於某個應用程式領域的、具有高度針對性的計算機語言.

Query String 與 Query DSL之間的區別:

Query String: 在請求的URL後直接拼接查詢條件;
Query DSL: 在請求的Request Body中攜帶查詢條件.

DSL功能強大, 可以構建複雜的查詢、過濾、聚合條件 —— 最常用的使用方式.

2 DSL校驗 - 定位不合法的查詢語句

對於複雜的查詢, 很有必要在查詢前使用validate API進行驗證, 保證DSL語句的正確有效:

// 要查詢name中包含"java"的文件: 
GET shop/it_book/_validate/query?explain
{
    "query": {
        "math": {            // 錯誤的查詢名稱, 應該是match
            "name": "java"
        }
    }
}

// 校驗結果: 
{
    "valid": false,
    "error": "org.elasticsearch.common.ParsingException: no [query] registered for [math]"
}

// 修改math為match後, 校驗結果為: 
{
    "valid": true,
    "_shards": {
        "total": 1,
        "successful": 1,
        "failed": 0
    },
    "explanations": [
        {
            "index": "shop",
            "valid": true,    // 校驗通過, DSL有效
            "explanation": "+name:java #_type:it_book"  // 查詢條件, +表示必須存在
        }
    ]
}

3 match query的使用

3.1 簡單功能示例

3.1.1 查詢所有文件

GET shop/it_book/_search
{
    "query": {
        "match_all": {}
    }
}

3.1.2 查詢滿足一定條件的文件

查詢name中包含"java"的文件, 同時按照價格升序排序:

GET shop/it_book/_search
{
    "query": {
        "match": {
            "name": "java"
        }
    }, 
    "sort": [
        { 
            "price": {"order": "asc"} 
        }
    ]
}

3.1.3 分頁查詢文件

GET shop/it_book/_search
{
    "query": {
        "match_all": {}
    },
    "from": 0,      // 開始記錄數, 起始數為0
    "size": 1       // 頁大小, 即每頁顯示的記錄數
}

3.1.4 指定返回的結果中包含的欄位

GET shop/it_book/_search
{
    "query": {
        "match_all": {}
    }, 
    "_source": [
        "name",     // 顯示商品名稱
        "price"     // 顯示商品價格
    ]
}

3.2 精確查詢 - match_phrase

不同的資料型別在建立倒排索引時, 有的會作為full text處理, 有的作為exact value處理.

對查詢串分詞時, 使用的分析器(analyzer)必須和建立index時使用的相同, 否則將檢索不到準確的資料.

3.2.1 精確匹配 - exact value

常見的exact value型別有date - 日期型別.

ES檢索時, 不會對String進行分詞, 而是完全根據String的值去精確匹配, 查詢相應的文件.

在DSL中, 通過match_phrase短語匹配達到精確匹配的目的 —— 不會對查詢串進行分詞, 而是直接精確匹配查詢.

示例: 查詢name中包含"thinking in java"的文件, 不會對查詢串進行分詞:

GET shop/_search
{
    "query": {
        "match_phrase": {
            "name": "thinking in java"
        }
    }
}

3.2.2 全文搜尋 - full text

常見的full text型別有: text - 文字串.

ES檢索時, 會對檢索串進行分詞, 包括縮寫、時態、同義詞等轉換手段, 然後根據分詞結果與倒排索引進行匹配, 查詢相應的文件.

索引中只要有任意一個相關field的分詞 匹配拆分後的詞, 這個文件就可以出現在結果中, 只是匹配度越高的排名越靠前.

示例: 查詢name中包含"thinking in java"的文件, 會將查詢串拆分為"think", "in", "java"三個詞:

GET shop/_search
{
    "query": {
        "match": {
            "name": "thinking in java"
        }
    }
}

3.3 控制匹配規則 - operator

operator 操作符, 用來指定ES對分詞後的詞項如何進行檢索過濾. 選項有:

and, 作用 == match_phrase, 即全部匹配;
or, 作用 == match, 即部分匹配.

使用示例:

GET shop/_search
{
    "query": {
        "match": {
            "name": {                   // 要查詢的field 
                "query": "程式設計思想",
                "operator": "or"        // 操作符
            }
        }
    }
}

3.4 指定命中的百分比 - minimum_should_match

minimum_should_match 用來指定最少要匹配多少比例的分詞, 才算符合條件並返回結果.

示例: 搜尋name中包含"併發程式設計的藝術", 被拆分成"併發", "程式設計", "藝術"等詞, 現在要求至少匹配50%的分詞, 可以這樣:

GET shop/_search
{
    "query": {
        "match": {
            "name": {
                "query": "併發程式設計的藝術", 
                "minimum_should_match": "50%"
            }
        }
    }
}

當然這種需求也可以用 must、must_not、should 匹配同一個欄位的方式進行組合查詢.

3.5 多欄位的匹配 - multi_match

multi_match 用來對多個欄位同時進行匹配: 任意一個欄位中存在相應的分詞, 就可作為結果返回.

示例 ① : 查詢 name 或 desc 欄位中包含 "面試經典" 的文件 —— 會對查詢串進行分詞:

GET shop/_search
{
    "query": {
        "multi_match": {
            "query": "面試經典", 
            "fields": [
                "name", 
                "desc"
            ]
        }
    }
}

示例 ② : 查詢 name 或 desc 欄位中同時包含 "面試經典" 的文件 —— 不對查詢串進行分詞:

GET shop/_search
{
    "query": {
        "multi_match": {
            "query": "面試經典",
            "type": "cross_fields", // 還有best_fields、most_fields、phrase、phrase_prefix選項
            "operator": "and",      // 全部匹配, or是部分匹配
            "fields": [
                "name", 
                "desc"
            ]
        }
    }
}

4 bool query的使用

bool query, 顧名思義, 就是 真假/有無 查詢. 包括4個子查詢:

① must - 必須匹配, 類似於SQL中的 = ;
② must_not - 必須不匹配, 類似於SQL中的 != ;
③ should - 不強制匹配, 類似於SQL中的 or ;
④ filter - 過濾, 將滿足一定條件的文件篩選出來.

除filter之外, 每個子查詢都會根據自己的條件計算出每個文件的相關度分數, 然後bool綜合所有分數, 合併為一個.

4.1 簡單功能示例

GET shop/_search
{
    "query": {
        "bool": {
            "must":[ 
                { "match": { "name": "Java" } }
            ], 
            "must_not": [
                { "match": { "desc": "程式設計" } }
            ], 
            "should": [
                { "match": { "publisher": "機械工業" } }
            ], 
            "filter": {
                "bool": { 
                    "must": [
                        { "range": { "date": { "gte": "2010-01-01" }}},
                        { "range": { "price": { "lte": 99.00 }}}
                    ]
                }
            }
            
        }
    }
}

4.2 巢狀使用bool query

GET shop/_search
{
    "query": {
        "bool": {
            "should": [
                { "term": { "name.keyword": "Java程式設計思想" } },
                {
                    "bool": {
                        "must": [
                            { "term": { "product_desc": "刷頭" } }
                        ]
                    }
                }
            ]
        }
    }
}

4.3 直接filter操作 - 使用constant_score

如果不指定query條件而直接filter, 將丟擲no [query] registered for [filter], 此時通過constant_score即可實現直接filter.

GET shop/_search 
{
    "query": {
        "constant_score": {
            "filter": {
                "range": { "price": { "gte": 80 } }
            }
        }
    }
}

4.4 指定should的匹配個數 - minimum_should_match

如果組合查詢中沒有must, 就會至少匹配一個should.

可以通過 minimum_should_match 指定匹配的should的個數.

GET shop/_search
{
    "query": {
        "bool": {
            "should": [
                { "match": { "name": "java" } }, 
                { "match": { "desc": "程式設計"} }, 
                { "match": { "price": 109 } }
            ], 
            "minimum_should_match": 2
        }
    }
}

5 term query的使用

5.1 不分詞查詢 - term query

term query: 把查詢串當作一個整體來執行查詢, 即不會對查詢串分詞.

term是完全匹配查詢, 要用在不分詞的欄位上, 如果某個field在對映中被分詞了, term查詢將不起作用.
所以, 不分詞的field, 要在mapping中設定為不分詞.

—— ES 5.x之後, 為每個text型別的欄位新增了名為keyword的子欄位, 是不分詞的, 預設保留256個字元.

—— 可以使用keyword欄位進行term查詢. 示例:

GET shop/_search
{
    "query": {
        "term": {
            "name.keyword": "Java程式設計思想"
        }
    }
}

5.2 in查詢 - terms query

terms, 相當於多個term查詢, 類似於SQL中in關鍵字的用法, 即在某些給定的資料中查詢:

GET shop/_search
{
    "query": {
        "terms": {
            "name.keyword": [
                "Java程式設計思想", "Java併發程式設計的藝術"
            ]
        }
    }
}

參考資料

Elasticsearch DSL 常用語法介紹

版權宣告

作者: 馬瘦風(https://healchow.com)

出處: 部落格園 馬瘦風的部落格(https://www.cnblogs.com/shoufeng)

感謝閱讀, 如果文章有幫助或啟發到你, 點個[好文要頂?] 或 [推薦?] 吧?

本文版權歸博主所有, 歡迎轉載, 但 [必須在文章頁面明顯位置標明原文連結], 否則博主保留追究相關人員法律責任的權利.

相關文章