1. 程式人生 > >Elasticsearch系列---實戰搜尋語法

Elasticsearch系列---實戰搜尋語法

概要

本篇介紹Query DSL的語法案例,查詢語句的除錯,以及排序的相關內容。

基本語法

空查詢

最簡單的搜尋命令,不指定索引和型別的空搜尋,它將返回叢集下所有索引的所有文件(預設顯示10條):

GET /_search
{}
搜尋多個索引
GET /index1,index2/_doc/_search
{}
指定分頁搜尋
GET /_search
{
  "from": 0,
  "size": 10
}
get帶request body

HTTP協議,GET請求帶body是不規範的做法,但由於ES搜尋的複雜性,加上HTTP協議GET/POST方法表述的語義,GET更適合用來表述查詢的動作,雖然不規範,但還是這麼用了。現在大多數瀏覽器也支援GET+request body,如果遇到不支援的,換成POST即可。瞭解一下就行,不用太慌張。

查詢表示式Query DSL

Query DSL是一種非常靈活、可讀性高的查詢語言,body為JSON格式,絕大部分功能都可以用它來展現,並且這種查詢語句更純粹,讓學習者更專注於本身的功能,避免Client API的干擾。

上一節的空查詢,等價於這個:

GET /_search
{
    "query": {
        "match_all": {}
    }
}
基本語法
# 查詢語句結構
{
    QUERY_NAME: {
        ARGUMENT: VALUE,
        ARGUMENT: VALUE,...
    }
}

# 針對某個欄位的查詢
{
    QUERY_NAME: {
        FIELD_NAME: {
            ARGUMENT: VALUE,
            ARGUMENT: VALUE,...
        }
    }
}
合併查詢語句

再複雜的查詢語句,也是由一個一個的查詢條件疊加而成的,查詢語句有兩種形式:

  • 葉子語句:單個條件組成的語句,如match語句,類似mysql的"id = 1"這種。
  • 複合語句:有多個條件,需要合併在一起才能組成一個完整的語句,需要使用bool進行組合,裡面的條件可以用must必須匹配、must not必須不匹配、should可以匹配修飾,也可以包含過濾器filter。類似mysql的"(status = 1 && language != 'french' && (author = 'John' || author = 'Tom'))"這種。

舉個例子:

{
    "bool": {
        "must":     { "match": { "status": 1 }},
        "must_not": { "match": { "language":  "french" }},
        "should":   { "match": { "author": "John Tom" }},
        "filter":   { "range": { "length" : { "gt" : 30 }} }
    }
}

複合語句可以巢狀,來實現更復雜的查詢需求,在上面的例子上簡單延伸一下:

"bool": {
        "must":     { "match": { "status": 1 }},
        "must_not": { "match": { "language":  "french" }},
        "should":   [
            {"match": { "author": "John Tom" }},
            {"bool": {
                "must":     { "match": { "name": "friend" }},
                "must_not": { "match": { "content":  "star" }}
            }}
        ],
        "filter":   { "range": { "length" : { "gt" : 30 }} }
    }
複合語句相關性分數計算

每一個子查詢都獨自地計算文件的相關性得分。一旦他們的得分被計算出來,bool 查詢就將這些得分進行合併並且返回一個代表整個布林操作的得分,得分高的顯示在前面,filter內的條件不參與分數計算。

過濾器filter

我們還是以英文兒歌的索引為案例,看一個搜尋需求:歌詞內容包含friend,同時歌長大於30秒的記錄

GET /music/children/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "content": "friend"
          }
        }
      ],
      "filter": {
        "range": {
          "length": {
            "gte": 30
          }
        }
      }
    }
  }
}

filter與query

  • 過濾情況filtering context

僅按照搜尋條件把需要的資料篩選出來,不計算相關度分數。

  • 查詢情況query context

匹配條件的資料,會根據搜尋條件的相關度,計算每個document的分數,然後按照分數進行排序,這個才是全文搜尋的情況。

效能差異

filter只做過濾,不作排序,並且會快取結果到記憶體中,效能非常高。
query匹配條件,要做評分,沒有快取,效能要低一些。

應用場景

filter一個非常重要的作用就是減少不相關資料對query的影響,提升query的效能,二者常常搭配在一起使用。
組合使用的時候,把期望符合條件的document的搜尋條件放在query裡,把要濾掉的條件放在filter裡。

constant_score查詢

如果一個查詢只有filter過濾條件,可以用constant_score來替代bool查詢,這樣的查詢語句更簡潔、更清晰,只是沒有評分,示例如下:

GET /music/children/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": { "content": "gymbo"}
      }
    }
  }
}

filter內不支援terms語法,注意一下。

最常用的查詢

再複雜的查詢語句,也是由最基礎的查詢變化而來的,而最常用的查詢其實也就那麼幾個。

  1. match_all查詢

查詢簡單的匹配所有文件

GET /_search
{
    "query": {
        "match_all": {}
    }
}
  1. match查詢

無論是全文搜尋還是精確查詢,match查詢是最基本的標準

# 全文搜尋例子
{ "match": { "content": "loves smile" }}

# 精確搜尋
{ "match": { "likes":    15           }}
{ "match": { "date":   "2019-12-05" }}
{ "match": { "isOwner": true         }}
{ "match": { "keyword":    "love you"  }}

對於精確值的查詢,我們可以使用filter來替代,filter有快取的效果。

  1. multi_match查詢

可以在多個欄位上執行相同的match查詢

{
    "multi_match": {
        "query":    "my sunshine",
        "fields":   [ "name", "content" ]
    }
}
  1. range查詢

查詢指定區間內的數字或時間,query和filter都支援,一般是filter用得多,允許的操作符如下:

  • gt 大於
  • gte 大於或等於
  • lt 小於
  • lte 小於或等於
{
    "range": {
        "length": {
            "gte":  45,
            "lt":   60
        }
    }
}
  1. term查詢

用於精確值匹配,精確值可以是數字,日期,boolean或keyword型別的字串

{ "term": { "likes":    15           }}
{ "term": { "date":   "2019-12-05" }}
{ "term": { "isOwner": true         }}
{ "term": { "keyword":    "love you"  }}

建立索引時mapping設定為not_analyzed時,match等同於term,用得多的是match和range。

  1. terms查詢

跟term類似,只是允許一次指定多個值進行匹配,只要有任何一個匹配上,都滿足條件

{ "terms": { "content": [ "love", "gymbo", "sunshine" ] }}

查詢語句除錯

複雜的查詢語句,可能會有幾百行,可以先使用除錯工具檢測一下查詢語句,定位不合法的搜尋及原因,完整語法如下:

GET /index/type/_validate/query?explain
{
  "query": {
    ...
  }
}

explain引數可以提供更詳細的查詢不合法的資訊,便於問題定位。寫一個錯誤的例子,比如使用中文標點符號:

GET /music/children/_validate/query?explain
{
  "query": {
    "terms": { "content“: [ "love", "gymbo", "sunshine" ] }
  }
}

錯誤提示如下:

{
  "valid": false,
  "error": """
ParsingException[Failed to parse]; nested: JsonParseException[Unexpected character ('l' (code 108)): was expecting a colon to separate field name and value
 at [Source: org.elasticsearch.transport.netty4.ByteBufStreamInput@5e57280e; line: 3, column: 33]];; com.fasterxml.jackson.core.JsonParseException: Unexpected character ('l' (code 108)): was expecting a colon to separate field name and value
 at [Source: org.elasticsearch.transport.netty4.ByteBufStreamInput@5e57280e; line: 3, column: 33]
"""
}

valid關鍵字,true為驗證通過,false為不通過,如上提示資訊,會指明3行33列錯誤,原因是使用了中文的引號。將語法修正後,得到的正確響應如下:

{
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "valid": true,
  "explanations": [
    {
      "index": "music",
      "valid": true,
      "explanation": "+content:(gymbo love sunshine) #*:*"
    }
  ]
}

排序

查詢請求得到的結果,預設排序是相關性得分降序。如果我們只使用filter過濾,符合filter條件的文件,評分都是一樣的(bool的filter得分是null,constant_score得分是1),結果文件還是隨機返回,顯然這樣的排序不符合我們的預期。

sort排序規則

為此,我們可以使用sort屬性,對文件進行排序,sort的用法與mysql如出一轍,示例如下:

GET /music/children/_search
{
  "query": {
    "bool": {
        "filter":   { "range": { "length" : { "gt" : 30 }} }
    }
  },
  "sort": [
    {
      "length": {
        "order": "desc"
      }
    }
  ]
}

sort內可以同時指定多個排序欄位,一旦使用sort排序後,_score得分將變成null,因為我們指定了排序規則,_score沒有實際意義了,就不用耗費精力再去計算它。

字串排序問題

我們知道text型別的欄位,會有關鍵詞分詞處理,對這樣的欄位進行排序,結果往往都不準確,6.x版本以後的text型別,會再自動建立一個keyword型別的欄位,這個欄位是不分詞的,所以這樣就有了分工,text型別的負責搜尋,keyword型別則負責排序。
我們回顧一下music索引的mapping資訊(節選):

{
  "music": {
    "mappings": {
      "children": {
        "properties": {
          "content": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}

例如name欄位,有一個text型別的,裡面fields還有一個型別為keyword,名稱也為keyword的欄位,所以在排序的場景中,我們應該使用name.keyword,示例如下:

GET /music/children/_search
{
  "sort": [
    {
      "name.keyword": {
        "order": "asc"
      }
    }
  ]
}

小結

本篇介紹Query DSL的語法及基礎實戰內容,順帶點了一下filter與query的區別,面對複雜查詢語句時,建議先用驗證工具進行排查,最後介紹了一下排序方面的知識,基礎語法、上機案例多實踐即可。

專注Java高併發、分散式架構,更多技術乾貨分享與心得,請關注公眾號:Java架構社群

相關推薦

Elasticsearch系列---實戰搜尋語法

概要 本篇介紹Query DSL的語法案例,查詢語句的除錯,以及排序的相關內容。 基本語法 空查詢 最簡單的搜尋命令,不指定索引和型別的空搜尋,它將返回叢集下所有索引的所有文件(預設顯示10條): GET /_search {} 搜尋多個索引 GET /index1,index2/_doc/_search {

Elasticsearch系列---初識搜尋

概要 本篇主要介紹搜尋的報文結構含義、搜尋超時時間的處理過程,提及了一下多索引搜尋和輕量搜尋,最後將精確搜尋與全文搜尋做了簡單的對比。 空搜尋 搜尋API最簡單的形式是不指定索引和型別的空搜尋,它將返回叢集下所有索引的所有文件(預設顯示10條): GET /_search 響應的結果示例(有篩選,只取了一條d

ElasticSearch最佳入門實踐(四十九)各種query搜尋語法

1、match all 查詢所有 GET /_search { "query": { "match_all": {} } } 2、match 匹配某一個filed是否包含文字 GET /_search {

搜尋推薦一——Centos搭建Elasticsearch單機實戰

環境 jdk: 1.8 centos: 7 elasticsearch: 5.3.0 一、JDK安裝 $: tar -zxvf jdk-8u181-linux-x64.tar.gz $: mkdir /usr/local/java $: cp jdk1.8

ElasticSearch最佳入門實踐(四十七)query DSL搜尋語法

1、一個例子讓你明白什麼是Query DSL GET /_search { "query": { "match_all": {} } } 2、Query DSL的基本語

ElasticSearch36:初識搜尋引擎_快速上機動手實戰Query DSL搜尋語法

1.match allGET /website/article/_search { "query": { "match_all": {} } } 執行結果:{ "took": 38, "timed_out": false, "_shards":

elasticsearch核心知識--38.Query DSL搜尋語法和bool多條件查詢

1、一個例子讓你明白什麼是Query DSLGET /_search{    "query": {        "match_all": {}    }}2、Query DSL的基本語法{    QUERY_NAME: {        ARGUMENT: VALUE, 

elasticsearch系列四:搜尋詳解(搜尋API、Query DSL)

{  "took": 60,  "timed_out": false,  "_shards": {    "total": 5,    "successful": 5,    "skipped": 0,    "failed": 0  },  "hits": {    "total": 1000,    "m

第十三篇 elasticsearch的Query DSL搜尋語法

Query DSL基本語法 { QUERY_NAME: { ARGUMENT: VALUE, ARGUMENT: VALUE,... } } { QUERY_NAME: { FIELD_

elasticsearch-驗證搜尋語法是否正確

【問題描述】:驗證拼寫的查詢語句是否正確,在/索引/型別後使用_validate/query?explain:GET /test_index/test_type/_validate/query?explain{  "query": {    "math": {      "t

Elasticsearch系列---搜尋執行過程及scroll遊標查詢

概要 本篇主要介紹一下分散式環境中搜索的兩階段執行過程。 兩階段搜尋過程 回顧我們之前的CRUD操作,因為只對單個文件進行處理,文件的唯一性很容易確定,並且很容易知道是此文件在哪個node,哪個shard中。 但搜尋比CRUD複雜,符合搜尋條件的文件,可能散落在各個node、各個shard中,我們需要找到匹配

Elasticsearch系列---結構化搜尋

概要 結構化搜尋針對日期、時間、數字等結構化資料的搜尋,它們有自己的格式,我們可以對它們進行範圍,比較大小等邏輯操作,這些邏輯操作得到的結果非黑即白,要麼符合條件在結果集裡,要麼不符合條件在結果集之外,沒有那種相似的概念。 前言 結構化搜尋將會有大量的搜尋例項,我們將"音樂APP"作為主要的案例背景,去開發一

Elasticsearch系列---深入全文搜尋

概要 本篇介紹怎樣在全文欄位中搜索到最相關的文件,包含手動控制搜尋的精準度,搜尋條件權重控制。 手動控制搜尋的精準度 搜尋的兩個重要維度:相關性(Relevance)和分析(Analysis)。 相關性是評價查詢條件與結果的相關程度,並對相關程度進行排序,一般使用TF/IDF方法。 分析是指將索引文件與查詢條

Elasticsearch系列---多欄位搜尋

### 概要 本篇介紹一下multi_match的best_fields、most_fields和cross_fields三種語法的場景和簡單示例。 ### 最佳欄位 bool查詢採取"more-matches-is-better"匹配越多分越高的方式,所以每條match語句的評分結果會被加在一起,從而

elasticsearch系列(四)部署

linux .tar.gz ast 官方 hup bin arc 分享 quest linux環境 centOS6.8 本文采用tar包的方式部署es 準備jdk8的環境 5.4.0的es依賴jdk8及以上版本 下載linux版的jdk jdk-8u121-linux-x6

搜索引擎ElasticSearch系列(四): ElasticSearch2.4.4 sql插件安裝

china code als 插件 技術分享 -s fun nlp 4.0 一:ElasticSearch sql插件簡介   With this plugin you can query elasticsearch using familiar SQL syntax.

elasticsearch系列(六)備份

indices stat 必須 tor 信息 操作 accepted gui 配置 快照備份 1.創建文件倉庫 1.1 在$ELASTICSEARCH_HOME/config/elasticsearch.yaml中增加配置 #這個路徑elasticsearch必須有權限訪問

elasticsearch系列(七)java定義score

集群 scrip image search 支持 name dsr 計算方法 dynamic 概述 ES支持groovy 和 java兩種語言自定義score的計算方法,groovy甚至可以嵌套在請求的參數中,有點厲害,不過不在本篇討論範圍。 如何用自定義的java代碼來定

java開發實戰語法

java11.271,錯誤信息與標準信息的輸出標準信息輸出流out(黑色)錯誤信息輸出流err(紅色)package conn.cev.yufa;public class yufa { public static void main(String[] args) { System.out.println(&

ElasticSearch 系列隨筆

www 錯誤 問題 day last del home AI 插入 1.ElasticSearch 常用設置 2.ElasticSearch 從2.2升級到6.2.4後在Kibana註意問題 (Validation Failed: 1: an id must be pro