前言
這篇文章是出自於線上課程 Complete Guide to Elasticsearch 的所記錄的筆記。
這一篇文章要來比較 ElasticSearch term & match 之間的差異。
本文
Term
term 在進行搜尋時,不會先對字句做分析
通常使用在精確比對中,像是數字、日期及關鍵字。
二話不說,直接看範例
E.g.
1 2 3 4 5 6 7 8 9 10
| GET /analyzer_test/_search { "query": { "term": { "description": { "value": "dog" } } } }
|
上面這種寫法,可以把 value: ...
簡化成下面這樣子
1 2 3 4 5 6 7 8
| GET /analyzer_test/_search { "query": { "term": { "description": "dog" } } }
|
上面兩種得到的結果會是相同的。
terms
透過 term 搜尋,欄位加上 keyword 的效果會更好
1 2 3 4 5 6 7 8 9 10 11
| GET /analyzer_test/_search { "query": { "terms": { "test.keyword": [ "Brunch", "Dinner" ] } } }
|
ids
透過 id 取得特定資料,做法相當單純,而且可以一次使用多筆 id 來搜尋。
E.g.
1 2 3 4 5 6 7 8
| GET /analyzer_test/_search { "query": { "ids": { "values": ["EQlV-30BeiHXdjTuKITq", "eBhV-30BH9MdJOJuGEIo"] } } }
|
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
| { "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "eBhV-30BH9MdJOJuGEIo", "_score" : 1.0, "_source" : { "test" : "Brunch" } }, { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "EQlV-30BeiHXdjTuKITq", "_score" : 1.0, "_source" : { "test" : "Dinner" } } ] } }
|
gte, gt, lte, lt
透過指定條件來取得特定範圍內的資料。
numeric
1 2 3 4 5 6 7 8 9 10 11
| GET /analyzer_test/_search { "query": { "range": { "in_stock": { "gte": 10, "lte": 20 } } } }
|
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
| { "took" : 858, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "wBgL_30BH9MdJOJuw0Km", "_score" : 1.0, "_source" : { "in_stock" : 20 } }, { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "wRgL_30BH9MdJOJu0kJr", "_score" : 1.0, "_source" : { "in_stock" : 13 } } ] } }
|
Date
也可以搜尋特定範圍日期的資料,Elasticsearch 預設使用的日期格式為 yyyy/mm/dd
。
1 2 3 4 5 6 7 8 9 10 11
| GET /analyzer_test/_search { "query": { "range": { "created": { "gte": "2021/01/01", "lte": "2021/12/31" } } } }
|
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
| { "took" : 538, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 3, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "EgkQ_30BeiHXdjTudIQr", "_score" : 1.0, "_source" : { "created" : "2021/01/02" } }, { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "EwkQ_30BeiHXdjTuhISx", "_score" : 1.0, "_source" : { "created" : "2021/02/05" } }, { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "wxgQ_30BH9MdJOJuk0Kw", "_score" : 1.0, "_source" : { "created" : "2021/10/05" } } ] } }
|
若要修改搜尋日期的格式,可以加上欄位 format
E.g.
1 2 3 4 5 6 7 8 9 10 11 12
| GET /analyzer_test/_search { "query": { "range": { "created": { "gte": "01/01/2021", "lte": "31/12/2021", "format": "dd/MM/yyyy||yyyy" } } } }
|
也可以透過 anchor ||
的方式來表達相對應的時間
<operator>: <date>||<relative_method>
E.g.
1 2 3 4 5 6 7 8 9 10
| GET /analyzer_test/_search { "query": { "range": { "created": { "gte": "2021/01/01||-1y", } } } }
|
-1y: 相對於前面的日期,再減少一年
-1d: 相對於前面的日期,再少一天
也支援自動進退位的表達方式,進退位取決於 operator
E.g.
這邊的範例意思為,相對於前面的日期減少一年,再進位至下一個月份(因為 operator 是 gte,故是退位)
1 2 3 4 5 6 7 8 9 10
| GET /analyzer_test/_search { "query": { "range": { "created": { "gte": "2021/01/01||-1y\M", } } } }
|
Non null value
我們可以針對特定欄位,來搜尋該欄位非空的那些資料
E.g.
現在部分資料如下,有一些資料的 created 為 null
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
| ... { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "EwkQ_30BeiHXdjTuhISx", "_score" : 1.0, "_source" : { "created" : "2021/02/05" } }, { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "wxgQ_30BH9MdJOJuk0Kw", "_score" : 1.0, "_source" : { "created" : "2021/10/05" } }, { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "FAm5_30BeiHXdjTu64S8", "_score" : 1.0, "_source" : { "created" : null ...
|
假設現在要來搜尋欄位 created
非空值的資料,可以透過下面這個語法
1 2 3 4 5 6 7 8
| GET /analyzer_test/_search { "query": { "exists": { "field": "created" } } }
|
Prefix
假設目前有這兩筆資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ... "_index" : "analyzer_test", "_type" : "_doc", "_id" : "dxhU-30BH9MdJOJuJEKr", "_score" : 1.0, "_source" : { "tags" : [ "Red" ] } }, { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "0hjK_30BH9MdJOJu7ULf", "_score" : 1.0, "_source" : { "tags" : [ "Redd" ] } ...
|
因為這兩筆資料的開頭都是 Red,我們可以透過 prefix
來幫助我們找到這些資料
1 2 3 4 5 6 7 8
| GET /analyzer_test/_search { "query": { "prefix": { "tags.keyword": "Red" } } }
|
wildcard
ES 也支援透過 wildcard 的方式來搜尋特定的資料。
E.g.
1 2 3 4 5 6 7 8
| GET /analyzer_test/_search { "query": { "wildcard": { "tags.keyword": "Re*" } } }
|
因為 wildcard 在搜尋時會遍巡該欄位的資料,如果 * or ? 放在字首來搜尋的話,效能會變差。
regexp
ES 支援透過 regular expression (Regex) 的方式來搜尋特定的資料。
E.g.
1 2 3 4 5 6 7 8
| GET /analyzer_test/_search { "query": { "regexp": { "tags.keyword": "R.*" } } }
|
Elastic 使用的是 Lucene’s regular expression engine,所以部分 Regex 的功能並不支援。
Match
在 ES 中, query type match
用來做 full text query
E.g.
搜尋 title 裡頭有符合搜尋內容的資料
1 2 3 4 5 6 7 8
| GET /analyzer_test/_search { "query": { "match": { "title": "Cat" } } }
|
透過 match 尋找的結果會被分析,也會得到相對應的分數
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
| { "took" : 791, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 0.25069216, "hits" : [ { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "KBhPBH4BH9MdJOJu9UMH", "_score" : 0.25069216, "_source" : { "title" : "Dog and Cat and Cat" } }, { "_index" : "analyzer_test", "_type" : "_doc", "_id" : "FglQBH4BeiHXdjTu_YRA", "_score" : 0.18232156, "_source" : { "title" : "Dog and Horse and Cat" } } ] } }
|
match 預設使用的 opreator 是 or
,可以修改 operator 來改變搜尋模式。
1 2 3 4 5 6 7 8 9 10 11
| GET /analyzer_test/_search { "query": { "match": { "title": { "query": "dog is cat", "operator": "and" } } } }
|
因為使用 and,所以預期 query 的內容都要出現 dog & is & cat,但 title 裡面沒有出現 is,因此資料就算有 dog & cat,也不會顯示出來。
如果希望字句是按照順序的呈現,可以使用 match_phrase
E.g.
1 2 3 4 5 6 7 8 9
| GET /analyzer_test/_search { "query": { "match_phrase": { "title": "dog and cat" } } } }
|
若是希望在不同的欄位同時找到同一個字,可以使用 multi_match
。
E.g.
要在欄位 title 及 description 找到 cat & dog。
1 2 3 4 5 6 7 8 9
| GET /analyzer_test/_search { "query": { "multi_match": { "query": "cat dog", "fields": ["title", "description"] } } }
|
假如搜尋的字有兩個,兩個字出現在同一個欄位的分數與任一個字出現在其中一個欄位,前者的分數會比較高,而且 multi_match 也會選用分數較高的。
Reference
- Complete Guide to Elasticsearch