Elasticsearch中的Term查詢和全文查詢

雙子孤狼發表於2021-07-06

前言

Elasticsearch 中,Term 查詢和全文查詢是兩種完全不同的處理方式,在上一篇我們也簡單對比了 Term 查詢和全文查詢中的 Phrase 中的區別,那麼本文就徹底的來理清這兩種查詢之間的關係。

我們重新建立一個新的索引 index_002,並插入以下資料

POST /_bulk
{"index":{"_index":"index_002"}}
{"id":"1","name":"lonely wolf","address":null,"count":1}
{"index":{"_index":"index_002"}}
{"id":"2","name":"lonely hello wolf","address":[],"count":3}
{"index":{"_index":"index_002"}}
{"id":"3","name":"lonely hello word wolf","address":"[廣東]","count":1}
{"index":{"_index":"index_002"}}
{"id":"4","name":"Lonely Wolf","address":"['廣東','深圳']","count":2}
{"index":{"_index":"index_002"}}
{"id":"5","name":"wolf","address":null,"count":1}

Term 查詢

Term 查詢一般表達的是最小單位查詢,也就是說對我們傳入的關鍵字會作為一個整體進行查詢,而不會進行分詞。

如下查詢,滿足條件的只有第一條資料,需要注意的是對 text 型別欄位需要加上 .keyword

POST index_001/_search
{
  "query": {
    "term": {
      "name.keyword": {
        "value": "lonely wolf"
      }
    }
  }
}

這裡如果不加上 .keyword 則不會返回任何結果,這是因為 text 型別的欄位會被倒排索引進行儲存,倒排索引會利用分析器將文字進行分詞,我們可以利用分詞器來檢視下分詞結果:

POST /_analyze
{
 "analyzer": "standard",
 "text": ["lonely wolf"]
}

可以看到,lonely wolf 被分成了 lonelywolf 兩個單詞,所以我們將 lonely wolf 作為一個進行查詢自然是無法查詢到結果的。

這裡有個地方需要注意,如果我們存入的是大寫單詞,如 Lonely Wolf,分詞器也是一樣的結果,也就是會將大寫字母統一轉化為小寫進行儲存,所以進行全文查詢的時候也是無法查詢出結果。

exists 查詢

用來判定是否存在某一個欄位,返回包含欄位的任何索引值的文件。

GET index_002/_search
{
  "query": {
    "exists": {
      "field": "address"
    }
  }
}

這裡返回的結果就是第三條和第四條資料,像 null 值和空陣列 [] 不會被返回。

如果想要返回 null 值或者空陣列 [] 的資料,那麼可以利用 bool 查詢的 must_not 語句:

GET index_002/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "exists": {"field": "address"}
        }
      ]
    }
  }
}

fuzzy 查詢

用於近似查詢,比如我們有時候在用百度搜尋的時候,輸錯了字會被糾正:

一般情況下有一個單詞錯誤的情況下,fuzzy 查詢可以找到另一個近似的詞來代替,主要有以下場景:

  • 修改一個單詞,如:box--->fox
  • 移除一個單詞,如:black-->lack
  • 插入一個單詞,如:sic-->sick
  • 轉換兩個單詞順序,如:act-->cat

為了可以查詢到這種近似的單詞,fuzzy 查詢需要建立一個所有近似詞的集合,這樣搜尋的時候就可以採用精確查詢找到近似的詞來代替查詢。

比如下面這個查詢就可以查詢出前面四條資料,同樣的,value 修改為 loneyllonelyyloneyle 都能查詢出前面四條資料:

GET index_002/_search
{
  "query": {
    "fuzzy": {
      "name": {
        "value": "lonel"
      }
    }
  }
}

ids 查詢

通過文件 id 進行查詢返回,這裡的 id 為文件中的 _id

GET index_002/_search
{
  "query": {
    "ids": {
      "values": ["id1","id2"]
    }
  }
}

prefix 查詢

通過指定欄位的字首進行查詢。

GET index_002/_search
{
  "query": {
    "prefix": {
      "name": {
        "value": "lo"
      }
    }
  }
}

range 查詢

通過範圍進行查詢。

GET index_002/_search
{
  "query": {
   "range": {
     "id": {
       "gte": 1,
       "lte": 2
     }
   }
  }
}

其中:

  • gt:表示大於。
  • gte:表示大於等於。
  • lt:表示小於。
  • lte:表示小於等於。

這種範圍查詢還可以用於日期的範圍查詢,此時將會對日期進行毫秒數轉換後進行查詢,如下面的例子就是查詢昨天到今天的區間,而且可以通過 time_zone 指定時區:

GET _search
{
    "query": {
        "range" : {
            "timestamp" : {
                "gte" : "now-1d/d",
                "lt" :  "now/d"
            }
        }
    }
}

regexp 查詢

通過正規表示式進行查詢。如下例子可以查詢出 lon 開頭的所有資料:

GET index_002/_search
{
  "query": {
   "regexp": {
     "name": "lon.*"
   }
  }
}

term 查詢

返回一個或者多個單詞精確匹配的文件。

# 返回前面四條資料
GET index_002/_search
{
  "query": {
   "term": {
     "name": {
       "value": "lonely"
     }
   }
  }
}
# 只返回第一條資料
GET index_002/_search
{
  "query": {
   "term": {
     "name.keyword": {
       "value": "lonely wolf"
     }
   }
  }
}

terms 查詢

terms 查詢和 term 查詢是一個含義,區別只是 terms 可以一次精確匹配多個詞。

# 返回全部五條資料
GET index_002/_search
{
  "query": {
   "terms": {
     "name": [
       "lonely",
       "wolf"
     ]
   }
  }
}

terms_set 查詢

terms_set 查詢和 terms 查詢是一樣的查詢規則,不同的是 terms_set 查詢可以定義匹配詞項的數量,定義的數量只能從文件中的某一列中進行獲取或者使用指令碼進行配置:

# 這裡只能查詢第一和第三兩條資料,因為 `Wolf` 中的首字母大寫,無法被精確匹配上,count列不能是text型別
GET index_002/_search
{
  "query": {
    "terms_set": {
      "name": {
        "terms": [
          "lonely",
          "Wolf"
        ],
        "minimum_should_match_field": "count"
      }
    }
  }
}

type 查詢

指定型別查詢,type 型別在 7.0 版本已經標註為過期,8.0 版本已經被廢棄。

wildcard 查詢

通過萬用字元進行查詢,這個可以理解為是簡易版本的正規表示式查詢:

GET index_002/_search
{
  "query": {
   "wildcard": {
     "name": {
       "value": "lone*"
     }
   }
  }
}

全文查詢

高階全文查詢通常用於對全文欄位 text 型別(比如電子郵件的正文)進行全文查詢。全文查詢在搜尋和索引時,都會對欄位進行分詞處理,查詢之前會先對輸入的詞進行分詞處理,然後對每個詞項進行查詢,最後將結果進行合併,並根據算分結果將結果進行返回。

全文查詢也包括很多種,在這裡我們主要介紹 match 查詢和 match_phrase 查詢。

match 查詢

match 查詢是執行全文搜尋的標準查詢,包括模糊匹配選項。如下就是一個標準的 match 查詢語句:

# 返回全部5條資料
POST index_002/_search
{
  "query": {
    "match": {
      "name": "lonely wolf"
    }
  }
}

對比 term 查詢:

# 沒有滿足條件的結果
POST index_002/_search
{
  "query": {
    "term": {
      "name": "lonely wolf"
    }
  }
}
# 返回第一條資料
POST index_002/_search
{
  "query": {
    "term": {
      "name.keyword": "lonely wolf"
    }
  }
}

根據上面幾個查詢的結果我們可以得出 term 查詢和全文 match 查詢的區別:

  • term 查詢會將搜尋關鍵字作為一個整體進行查詢。
  • match 查詢會將搜尋關鍵字進行分詞,且分詞後預設是 or 的關係。

根據這兩個結論,也可以很明顯知道,一般不對 text 型別欄位採用 term 查詢,因為 text 型別欄位會被分詞索引,可能會導致無法被 term 查詢匹配出結果。

再看下面這個例子,會返回第二和第三兩條資料(分詞後的搜尋和順序無關):

# 查詢出最少匹配中3個詞項的結果
POST index_002/_search
{
  "query": {
    "match": {
      "name": {
        "query": "hello wolf lonely",
        "operator": "or",
        "minimum_should_match": 3
      }
    }
  }
}

match_phrase 查詢

match_phrase 會將輸入的搜尋關鍵字作為一個短語進行查詢,這點看來類似於 term 查詢,但是 match_phrase 查詢內嵌了一個引數 slot 用來定義短語中允許的空隙,預設是 0 表示中間不允許有其他詞:

POST index_002/_search
{
  "query": {
    "match_phrase": {
      "name": {
        "query": "lonely wolf"
      }
    }
  }
}

這條語句的結果就能查詢出第一和第四條資料,注意,雖然第四條資料中的 lonely wolf 是大寫字母開頭,但是索引的時候會將其轉為小寫進行索引,所以也能查詢出結果。

此時我們加入 slot=1 條件進行查詢,表示允許短語之間存在一個間隙,所以此時能查詢出第二條資料:

POST index_002/_search
{
  "query": {
    "match_phrase": {
      "name": {
        "query": "hello wolf lonely",
        "slop": 1
      }
    }
  }
}

總結

本文主要講述了 Term 查詢和全文查詢中 match 查詢的區別,總結起來主要有以下幾點:

  1. Term 查詢對搜尋關鍵字不會進行分詞處理,而是作為一個整體進行查詢。
  2. 全文查詢如 match 等查詢,會對搜尋關鍵字進行分詞,並對每個詞項進行搜尋,預設 or 的關係進行合併,並最終演算法返回結果。
  3. Text 型別欄位,索引時會進行分詞,大寫字母會轉成小寫,所以如果用 Term 或者 match_phrase 查詢時要注意因分詞而對查詢結果產生的影響。

相關文章