大家好,我是Edison。
上一篇:基於Term和全文的ES查詢
結構化數據
結構化搜索(Structured Search)是指對結構化數據的搜索,那麼,什麼數據是結構化的呢?
ES中日期、布爾類型和數字都是結構化的。
另外,文本也可以是結構化的:
-
比如彩色筆可以有離散的顏色集合:紅、藍、綠等;
-
一個博客也可能被標記了一些標籤:分佈式、搜索、架構等;
-
電商網站上的商品都有UPCs(通用產品碼)或其他的唯一標識,它們都需要遵從嚴格規定的、結構化的格式。
結構化搜索
結構化搜索(Structured Search)是指對結構化數據的搜索,那麼我們接下來就看看如何做結構化搜索。在ES中對結構化數據進行匹配,主要使用term查詢。
NOTE:對於文本的全文查詢,主要使用match喲。
首先,我們先添加一些測試數據,以便後續場景中的示例。請注意裏面這些測試數據的結構,後面兩條並沒有date字段喲。
DELETE products POST /products/_bulk { "index":{"_id":1}} { "productID": "XHDK-A-1293-#fJ3", "price":10, "avaliable":true, "date":"2024-04-01"} { "index":{"_id":2}} { "productID": "KDKE-B-9947-#kL5", "price":20, "avaliable":true, "date":"2023-04-01"} { "index":{"_id":3}} { "productID": "J0DL-X-1937-#pV7", "price":30, "avaliable":true } { "index":{"_id":4}} { "productID": "QQPX-R-3956-#aD8", "price":30, "avaliable":false }
場景1:針對布爾、時間、日期和數字類型的結構化數據
這類數據有精確的格式,可以直接對這些格式進行邏輯操作,比如 比較數字或時間的範圍,或判定兩個值的大小。
(1)布爾值
// 對布爾值進行查詢(有算分) // ES會返回3條記錄 POST /products/_search { "profile": "true", "query": { "term": { "avaliable": true } } }
ES返回結果:3條記錄,有計算分
從之前的學習中,我們知道,可以使用constant_score轉成filtering,進而避免算分,提高性能。
// 對布爾值進行查詢(使用constant_score,無算分) // ES會返回3條記錄 POST /products/_search { "profile": "true", "query": { "constant_score": { "filter": { "term": { "avaliable": true } } } } }
ES返回結果:3條記錄,沒有計算分
(2)數字和日期Range
// 數字Range查詢價格>=30 並 <=40的記錄 POST /products/_search { "profile": "true", "query": { "constant_score": { "filter": { "range": { "price": { "gte": 30, "lte": 40 } } } } } } // 日期Range查詢:查詢近一年的記錄 // y 年 M 月 w 周 d 天 // H / h 小時 m 分鐘 s 秒 POST /products/_search { "profile": "true", "query": { "constant_score": { "filter": { "range": { "date": { "gte": "now-1y" } } } } } }
(3)處理空值
// exists 可以過濾不包含某字段的記錄 POST /products/_search { "profile": "true", "query": { "constant_score": { "filter": { "exists": { "field": "date" } } } } }
場景2:針對結構化文本做部分匹配 或 精確匹配
爲了演示,我們插入兩個測試數據來看看處理多值的情況:
POST /movies/_bulk { "index":{"_id":1}} { "title": "Father of the Bridge Part II", "year": 1995, "genre": "Comedy" } { "index":{"_id":2}} { "title": "Dave", "year": 1993, "genre": ["Comedy", "Romance"] }
可以看到,第二條數據的genre字段包含了多個值,是一個集合字段。
然後,我們可以通過term+ keyword來處理多值字段的查詢:查找genre屬於Comedy(喜劇)的記錄。這裏需要注意的是,term查詢的邏輯關係是包含而不是完全相等,所以它做的是部分匹配。
// 查詢包含單個值的記錄 POST /movies/_search { "query": { "constant_score": { "filter": { "term": { "genre.keyword": "Comedy" } } } } } // 查詢包含多個值的記錄 POST /movies/_search { "query": { "constant_score": { "filter": { "terms": { "genre.keyword": [ "Comedy", "Romance" ] } } } } }
這裏如果我們想做精確匹配,我們可以增加一個count字段並使用bool查詢來解決。
Step1.從業務角度,按需改進ES的數據模型
POST /movies_new/_bulk { "index":{"_id":1}} { "title": "Father of the Bridge Part II", "year": 1995, "genre": "Comedy", "genre_count":1 } { "index":{"_id":2}} { "title": "Dave", "year": 1993, "genre": ["Comedy", "Romance"], "genre_count":2 }
這裏可以看到,我們給genre字段新增一個匹配的genre_count字段,代表genre字段有幾個值。
Step2.使用bool查詢進行精確匹配
// 方式一:使用must,有算分 POST /movies_new/_search { "query": { "bool": { "must": [ {"term":{"genre.keyword": {"value":"Comedy"}}}, {"term":{"genre_count": {"value":1}}} ] } } } // 方式二:使用filter,無算分 POST /movies_new/_search { "query": { "bool": { "filter": [ {"term":{"genre.keyword": {"value":"Comedy"}}}, {"term":{"genre_count": {"value":1}}} ] } } }
小結
本篇,我們瞭解了ElasticSearch中的結構化數據和結構化搜索的概念,並通過幾個實例瞭解瞭如何對結構化數據進行搜索。在ES中,我們主要使用term對結構化數據進行搜索,而主要使用match對文本進行全文搜索。
參考資料
極客時間,阮一鳴,《ElasticSearch核心技術與實戰》