ElasticSearch DSL 查詢

公號:碼農充電站pro
主頁:https://codeshellme.github.iohtml

DSL(Domain Specific Language)查詢也叫作 Request Body 查詢,它比 URI 查詢更高階,能支持更復雜的查詢。git

1,分頁

默認狀況下,查詢按照算分排序,返回前 10 條記錄。github

ES 也支持分頁,分頁使用 from-size算法

  • from:從第幾個文檔開始返回,默認爲 0。
  • size:返回的文檔數,默認爲 10。

示例:shell

POST /index_name/_search
{
  "from":10,
  "size":20,
  "query":{
    "match_all": {}
  }
}

1.1,深度分頁問題

ES 是一個分佈式系統,數據保存在多個分片中,那麼查詢時就須要查詢多個分片。數組

好比一個查詢 from = 990; size = 10,那麼 ES 須要在每一個分片上都獲取 1000 個文檔:緩存

而後經過 Coordinating 節點彙總結果,最後再經過排序獲取前 1000 個文檔。app

這種方式,當頁數很深的時候,就會佔用不少內存,從而給 ES 集羣帶來很大的開銷,這就是深度分頁問題less

所以,ES 爲了不此類問題帶來的巨大開銷,有個默認的限制 index.max_result_windowfrom + size 必須小於等於 10000,不然就會報錯dom

好比:

POST index_name/_search
{
  "from": 10000,  # 報錯
  "size": 1,
  "query": {
    "match_all": {}
  }
}

POST index_name/_search
{
  "from": 0,  # 報錯
  "size": 10001,
  "query": {
    "match_all": {}
  }
}

爲了解決深度分頁問題,ES 有兩種解決方案:Search AfterScroll

1.2,Search After

Search After 經過實時獲取下一頁的文檔信息來實現,使用方法:

  • 第一步搜索須要指定 sort,而且保證值是惟一的(經過sort by id 來保證)。
  • 隨後的搜索,都使用上一次搜索的最後一個文檔的 sort 值進行搜索。

Search After 的方式不支持指定頁數,只能一頁一頁的往下翻。

Search After 的原理:

在這裏插入圖片描述

示例:

# 插入一些數據
DELETE users

POST users/_doc
{"name":"user1","age":10}

POST users/_doc
{"name":"user2","age":11}

POST users/_doc
{"name":"user2","age":12}

POST users/_doc
{"name":"user2","age":13}

# 第一次搜索
POST users/_search
{
    "size": 1,   # size 值
    "query": {
        "match_all": {}
    },
    "sort": [
        {"age": "desc"} ,
        {"_id": "asc"}  # sort by id  
    ]
}

# 此時返回的文檔中有一個 sort 值
# "sort" : [13, "4dR-IHcB71-f4JZcrL2z"]

# 以後的每一次搜索都須要用到上一次搜索結果的最後一個文檔的 sort 值
POST users/_search
{
    "size": 1,
    "query": {
        "match_all": {}
    },
    "search_after": [ # 上一次搜索結果的最後一個文檔的 sort 值放在這裏
        13, "4dR-IHcB71-f4JZcrL2z"], 
    "sort": [
        {"age": "desc"} ,
        {"_id": "asc"}    
    ]
}

1.3,Scroll

Scroll 經過建立一個快照來實現,方法:

  • 每次查詢時,輸入上一次的 Scroll Id

Scroll 方式的缺點是,當有新的數據寫入時,新寫入的數據沒法被查到(第一次創建快照時有多少數據,就只能查到多少數據)。

示例:

# 寫入測試數據
DELETE users
POST users/_doc
{"name":"user1","age":10}

POST users/_doc
{"name":"user2","age":20}

# 第一次查詢前,先創建快照,快照存在時間爲 5 分鐘,通常不要太長
POST /users/_search?scroll=5m
{
    "size": 1,
    "query": {
        "match_all" : {}
    }
}

# 返回的結果中會有一個 _scroll_id

# 查詢
POST /_search/scroll
{
    "scroll" : "1m", # 快照的生存時間,這裏是 1 分鐘
    "scroll_id" : "xxx==" # 上一次的 _scroll_id 值
}

# 每次的查詢結果都會返回一個 _scroll_id,供下一次查詢使用
# 全部的數據被查完之後,再查詢就得不到數據了

1.4,不一樣分頁方式的使用場景

分頁方式共 4 種:

  • 普通查詢(不使用分頁):須要實時獲取頂部的部分文檔。
  • From-Size(普通分頁):適用於非深度分頁。
  • Search After:須要深度分頁時使用。
  • Scroll:須要所有文檔,好比導出所有數據。

2,排序

ES 默認使用算分進行排序,咱們可使用 sort-processor(不須要再計算算分)來指定排序規則;能夠對某個字段進行排序,最好只對數字型日期型字段排序。

示例:

POST /index_name/_search
{
  "sort":[{"order_date":"desc"}], # 單字段排序
  "query":{
    "match_all": {}
  }
}

POST /index_name/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [ # 多字段排序
    {"order_date": {"order": "desc"}},
    {"_doc":{"order": "asc"}},
    {"_score":{ "order": "desc"}} # 若是不指定 _score,那麼算分爲 null
  ]
}

text 類型的數據進行排序會發生錯誤,能夠經過打開 fielddata 參數(通常不建議這麼作),來對 text 類型進行排序:

# 打開 text的 fielddata
PUT index_name/_mapping
{
  "properties": {
    "customer_full_name" : {       # 字段名稱
          "type" : "text",
          "fielddata": true,       # 打開 fielddata
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
  }
}

3,字段過濾

可使用 _source 設置須要返回哪些字段。示例:

POST /index_name/_search
{
  "_source":["order_date", "xxxxx"],
  "query":{
    "match_all": {}
  }
}

_source 中可使用通配符,好比 ["name*", "abc*"]

4,腳本字段

可使用腳本進行簡單的表達式運算。

POST /index_name/_search
{
  "script_fields": { # 固定寫法
    "new_field": {   # 新的字段名稱
      "script": {    # 固定寫法
        "lang": "painless", # 固定寫法
        "source": "doc['order_date'].value+'hello'" # 腳本語句
      }
    }
  },
  "query": {
    "match_all": {}
  }
}

5,查詢與過濾

查詢會有相關性算分;過濾不須要進行算分,能夠利用緩存,性能更好。

參考這裏

6,全文本查詢

全文本(Full text)查詢會對搜索字符串進行分詞處理

全文本查詢有如下 9 種:

  1. intervals 查詢:能夠對匹配項的順序和接近度進行細粒度控制。
  2. match 查詢:全文本查詢中的標準查詢,包括模糊匹配、短語和近似查詢。
  3. match_bool_prefix 查詢
  4. match_phrase 查詢
  5. match_phrase_prefix 查詢
  6. multi_match 查詢
  7. common terms 查詢
  8. query_string 查詢
  9. simple_query_string 查詢

6.1,Match 查詢

Match 查詢是全文搜索的標準查詢,與下面的幾種查詢相比,更增強大,靈活性也更大,最常使用。

Match 查詢會先對輸入字符串進行分詞,而後對每一個詞項進行底層查詢,最後將結果合併。

例如對字符串 "Matrix reloaded" 進行查詢,會查到包含 "Matrix" 或者 "reloaded" 的全部結果。

在這裏插入圖片描述

示例:

POST index_name/_search
{
  "query": {
    "match": {
      "title": "last christmas" # 表示包含 last 或 christmas
    }
  }
}

POST index_name/_search
{
  "query": {
    "match": {
      "title": { # 表示包含 last 且 包含 christmas,不必定挨着
        "query": "last christmas",
        "operator": "and"
      }
    }
  }
}

6.2,Match Phrase 查詢

使用 match_phrase 關鍵字。示例:

POST index_name/_search
{
  "query": {
    "match_phrase": {
      "title":{
        "query": "one love" # "one love" 至關於一個單詞
      }
    }
  }
}

POST index_name/_search
{
  "query": {
    "match_phrase": {
      "title":{
        "query": "one love",
        "slop": 1 # "one" 和 "love" 之間能夠有 1 個字符
      }
    }
  }
}

6.3,Query String 查詢

使用 query_string 關鍵字。示例:

POST index_name/_search
{
  "query": {
    "query_string": {
      "default_field": "name",   # 默認查詢字段,至關於 URI 查詢中的 df
      "query": "Ruan AND Yiming" # 可使用邏輯運算符
    }
  }
}

# 多 fields 與 分組
POST index_name/_search
{
  "query": {
    "query_string": {
      "fields":["name","about"], # 多個 fields
      "query": "(Ruan AND Yiming) OR (Java AND Elasticsearch)" # 支持分組
    }
  }
}

POST index_name/_search
{
  "query":{
    "query_string":{
      "fields":["title","year"],
      "query": "2012"
     }
   }
}

6.4,Simple Query String 查詢

使用 simple_query_string 關鍵字。

特色:

  • query 字段中不支持 AND OR NOT,會當成普通字符串。
    • AND+ 替代
    • OR| 替代
    • NOT- 替代
  • Term 之間默認的關係是 OR,能夠指定 default_operator 來修改。

示例:

# Simple Query 默認的 operator 是 OR
POST index_name/_search
{
  "query": {
    "simple_query_string": {
      "query": "Ruan AND Yiming", # 這裏的 AND 會當成普通的字符串
      "fields": ["name"]
    }
  }
}

POST index_name/_search
{
  "query": {
    "simple_query_string": {
      "query": "Ruan Yiming",
      "fields": ["name"],
      "default_operator": "AND"
    }
  }
}

GET index_name/_search
{
	"query":{
		"simple_query_string":{
			"query":"Beautiful +mind",
			"fields":["title"]
		}
	}
}

6.5,Multi-match 查詢

一個字符串在多個字段中查詢的狀況,如何匹配最終的結果。(還有一個 dis-max 查詢也是針對這種狀況的)

一個字符串在多個字段中查詢的狀況,Multi-match 有 6 種處理方式,以下:

  • best_fields:最終得分爲分數最高的那個字段,默認的處理方式。
  • most_fields:算分相加。不支持 AND 操做。
  • cross_fields:跨字段搜索,將一個查詢字符串在多個字段(就像一個字段)上搜索。
  • phrase
  • phrase_prefix
  • bool_prefix

示例:

POST index_name/_search
{
  "query": {
    "multi_match" : {              # multi_match 查詢
      "query":      "brown fox",   # 查詢字符串
      "type":       "best_fields", # 處理方式
      "fields":     [ "subject", "message" ], # 在多個字段中查詢,fields 是一個數組
      "tie_breaker": 0.3
    }
  }
}

7,Term 查詢

Term 查詢全文本查詢不一樣的是,Term 查詢不會對查詢字符串進行分詞處理,Term 查詢會在字段匹配精確值

Term 查詢輸入字符串做爲一個總體,在倒排索引中查找匹配的詞項,而且會計算相關性評分

Term 查詢包括如下 11 種:

  1. exists 查詢
  2. fuzzy 查詢
  3. ids 查詢
  4. prefix 查詢
  5. range 查詢
  6. regexp 查詢
  7. term 查詢:若是某個文檔的指定字段包含某個確切值,則返回該文檔。
  8. terms 查詢
  9. terms_set 查詢
  10. type 查詢
  11. wildcard 查詢

7.0,結構化數據與查詢

結構化查詢是對結構化數據的查詢,可使用 Term 語句進行查詢。

結構化數據有着固定的格式,包括:

  • 日期:日期比較,日期範圍運算等。
  • 布爾值:邏輯運算。
  • 數字:數字大小比較,範圍比較等。
  • 某些文本數據:好比標籤數據,關鍵詞等。

結構化查詢是對結構化數據的邏輯運算,運算結果只有「是」和「否」。

7.1,term 查詢

若是某個文檔的指定字段包含某個確切值,則返回該文檔。

1,示例 1 精確匹配

下面舉一個 term 查詢的例子,首先插入一個文檔:

POST /products/_bulk
{ "index": { "_id": 1 }}
{ "productID" : "XHDK-A-1293-#fJ3","desc":"iPhone" }

該文檔插入時,會使用默認的分詞器進行分詞處理。

使用 term 查詢:

POST /products/_search
{
  "query": {
    "term": {
      "desc": {
        # "value": "iPhone" # 會對 iPhone 精確匹配查詢。
                            # 文檔插入時,iPhone 變成了 iphone
                            # 因此查 iPhone 查不到任何內容
        "value":"iphone"    # 查 iphone 能查到
      }
    }
  }
}

keyword 子字段

ES 默認會對 text 類型的數據創建一個 keyword 子字段,用於精確匹配,這稱爲 ES 的多字段屬性

keyword 子字段將原始數據原封不動的存儲了下來。

能夠經過 mapping 查看,以下所示:

"desc" : {          # 字段名稱
  "type" : "text",  # text 數據類型
  "fields" : {
    "keyword" : {   # keyword 子字段
      "type" : "keyword",  # keyword 子類型
      "ignore_above" : 256
    }
  }
}

下面使用 keyword 子字段進行查詢:

POST /products/_search
{
  "query": {
    "term": {
      "desc.keyword": {     # 在 desc 字段的 keyword 子字段中查詢
        "value": "iPhone"   # 能查到
        //"value":"iphone"  # 查不到
      }
    }
  }
}
2,示例 2 查詢布爾值

term 查詢有算分:

POST index_name/_search
{
  "query": {    # 固定寫法
    "term": {   # term 查詢,固定寫法
      "avaliable": true  # 查詢 avaliable 字段的值爲 true 的文檔
    }
  }
}

若是不須要算分,可使用 constant_score 查詢,示例:

POST index_name/_search
{
  "query": {
    "constant_score": {        # constant_score 查詢,固定寫法
      "filter": {              # 固定寫法
        "term": {              # constant_score 包裝一個 term 查詢,就沒有了算分
          "avaliable": true
        }
      }
    }
  }
}

7.2,range 查詢

range 查詢中有幾個經常使用的比較運算:

運算符 含義
gt 大於
gte 大於等於
lt 小於
lte 小於等於
1,數字類型 range 查詢

示例:

POST index_name/_search
{
  "query": {       # 固定寫法
    "range": {     # range 查詢
      "age": {     # 字段名稱
        "gte": 10, # 10 <= age <= 20
        "lte": 20
      }
    }
  }
}
2,日期類型 range 查詢

對於日期類型有幾個經常使用的符號:

符號 含義
y
M
w
d
H / h 小時
m 分鐘
s
now 如今

示例:

POST index_name/_search
{
  "query" : {      # 固定寫法
    "range" : {    # range 查詢
      "date" : {   # 字段名稱
        "gte" : "now-10y"  # 10年以前
      }
    }
  }
}

7.3,exists 查詢

exists 語句能夠判斷文檔是否存在某個字段

搜索存在某個字段的文檔,示例:

POST index_name/_search
{
  "query" : {
    "exists": {  # 存在 date 字段的文檔
        "field": "date"
      }
   }
}

搜索不存在某個字段的文檔,須要使用布爾查詢

示例:

POST index_name/_search
{
  "query": {
    "bool": {       # 布爾查詢
      "must_not": { # 不存在
        "exists": { # 不存在 date 字段的文檔
          "field": "date"
        }
      }
    }
  }
}

7.4,terms 查詢

terms 語句用於處理多值查詢,至關於一個多值版的 term 語句,能夠一次查詢多個值。

示例:

POST index_name/_search
{
  "query": {
    "terms": {  # terms 查詢
      "productID.keyword": [  # 字段名稱
        "QQPX-R-3956-#aD8",   # 多個值
        "JODL-X-1937-#pV7"
      ]
    }
  }
}

8,複合查詢

複合查詢(Compound)可以包裝其餘複合查詢葉查詢,以組合其結果和分數,更改其行爲或者將查詢轉成過濾

複合查詢有如下 5 種:

8.1,bool 查詢

bool 查詢是一個或多個子查詢的組合,共包含如下 4 種子句:

  • must:必須匹配,屬於查詢,貢獻算分。
  • filter:必須匹配,屬於過濾器,不貢獻算分。
  • should:選擇性匹配,只要有一個條件匹配便可,屬於查詢,貢獻算分。
  • must_not:必須不匹配,屬於過濾器,不貢獻算分。

bool 查詢的多個子句之間沒有順序之分,而且能夠嵌套

示例:

POST index_name/_search
{
  "query": {
    "bool" : {
      "must" : {
        "term" : { "user.id" : "kimchy" }
      },
      "filter": {
        "term" : { "tags" : "production" }
      },
      "must_not" : {
        "range" : {
          "age" : { "gte" : 10, "lte" : 20 }
        }
      },
      "should" : [ # 是一個數組
        { "term" : { "tags" : "env1" } },
        { "term" : { "tags" : "deployed" } }
      ],
      "minimum_should_match" : 1,
      "boost" : 1.0
    }
  }
}

8.2,boosting 查詢

boosting 查詢會給不一樣的查詢條件分配不一樣的級別(positive / negative),不一樣的級別對算分有着不一樣的印象,從而影響最終的算分。

positive 級別會對算分有正面影響negative 級別會對算分有負面影響

咱們可使用 boosting 查詢給某些文檔降級(下降算分),而不是將其從搜索結果中排除

示例:

GET index_name/_search
{
  "query": {
    "boosting": {      # boosting 查詢
      "positive": {    # positive 級別
        "term": {      # 匹配 apple 的會對算分有正面影響
          "text": "apple" 
        }
      },
      "negative": {   # negative 級別
        "term": {     # 匹配這個的會對算分有負面影響
          "text": "pie tart fruit crumble tree"
        }
      },
      "negative_boost": 0.5 # 降級的力度
    }
  }
}

8.3,constant_score 查詢

constant_score 查詢能夠將查詢轉成一個過濾,能夠避免算分(下降開銷),並有效利用緩存(提升性能)。

示例:

POST /index_name/_search
{
  "query": {
    "constant_score": {   # constant_score  查詢
      "filter": {         # 過濾器,固定寫法
        "term": {         # 包裝了一個 term 查詢,將 term 查詢轉成了過濾
          "productID.keyword": "XHDK-A-1293-#fJ3"
        }
      }
    }
  }
}

8.4,dis_max 查詢

一個字符串在多個字段中查詢的狀況,如何匹配最終的結果。(還有一個 Multi-match 查詢也是針對這種狀況的)

示例:

POST index_name/_search
{
    "query": {
        "bool": {
            "should": [  # should 語句會綜合全部的字段的分數,最終給出一個綜合分數
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

POST index_name/_search
{
    "query": {
        "dis_max": {     # dis_max 語句不會綜合全部字段的分數,而把每一個字段單獨來看
            "queries": [ # 最終結果是全部的字段中分數最高的
                { "match": { "title": "Quick pets" }},
                { "match": { "body":  "Quick pets" }}
            ]
        }
    }
}

8.5,function_score 查詢

function_score 查詢能夠在查詢結束後,對每個匹配的文檔進行從新算分,而後再根據新的算分進行排序。

它提供瞭如下 5 種算分函數:

  • script_score:自定義腳本。
  • weight:爲文檔設置一個權重。
  • random_score:隨機算分排序。
  • field_value_factor:使用該數值來修改算分。
  • decay functions: gauss, linear, exp:以某個字段爲標準,距離某個值越近,得分越高。
1,field_value_factor 示例

首先插入測試數據:

DELETE blogs
PUT /blogs/_doc/1
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   0
}

PUT /blogs/_doc/2
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   100
}

PUT /blogs/_doc/3
{
  "title":   "About popularity",
  "content": "In this post we will talk about...",
  "votes":   1000000
}

查詢示例1:

新的算分 = 老的算分 * 投票數

POST /blogs/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": { # 該查詢會有一個算分
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {  # 最終的算分要乘以 votes 字段的值
        "field": "votes"
      }
    }
  }
}

上面這種算法當出現這兩種狀況的時候,會出現問題:

  • 投票數爲 0
  • 投票數特別大

查詢示例2,引入平滑函數

新的算分 = 老的算分 * 平滑函數(投票數)

POST /blogs/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field": "votes",
        "modifier": "log1p"  # 在原來的基礎上加了一個平滑函數
      }                      # 新的算分 = 老的算分 * log(1 + 投票數)
    }
  }
}

平滑函數有下面這些:

在這裏插入圖片描述

查詢示例3,引入 factor

新的算分 = 老的算分 * 平滑函數(factor * 投票數)

POST /blogs/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field": "votes",
        "modifier": "log1p" ,
        "factor": 0.1
      }
    }
  }
}

引入 factor 以後的算分曲線:

在這裏插入圖片描述

2,Boost Mode 和 Max Boost 參數

Boost Mode:

  • Multiply:算分與函數值的乘積。
  • Sum:算分與函數值的和。
  • Min / Max:算分與函數值的最小/最大值。
  • Replace:使用函數值替代算分。

Max Boost 能夠將算分控制在一個最大值。

示例:

POST /blogs/_search
{
  "query": {
    "function_score": {
      "query": {
        "multi_match": {
          "query":    "popularity",
          "fields": [ "title", "content" ]
        }
      },
      "field_value_factor": {
        "field": "votes",
        "modifier": "log1p" ,
        "factor": 0.1
      },
      "boost_mode": "sum",
      "max_boost": 3
    }
  }
}
3,random_score 示例

示例:

POST /blogs/_search
{
  "query": {
    "function_score": {
      "random_score": {  # 將原來的查詢結果隨機排序
        "seed": 911119   # 隨機種子
      }
    }
  }
}

(本節完。)


推薦閱讀:

ElasticSearch 查詢

ElasticSearch URI 查詢

ElasticSearch 文檔及操做

ElasticSearch 分詞器

ElasticSearch 搜索引擎概念簡介


歡迎關注做者公衆號,獲取更多技術乾貨。

碼農充電站pro

相關文章
相關標籤/搜索