作者
劉曉國,Elastic 公司社區佈道師。新加坡國立大學碩士,西北工業大學碩士,曾就職於新加坡科技,康柏電腦,通用汽車,愛立信,諾基亞,Linaro,Ubuntu,Vantiq 等企業。
編輯
叢聿,架構師(搜索方向)
前言
Elasticsearch 是一款功能強大且功能豐富的搜索工具。本文將介紹一種小眾的數據類型 Percolator ,同時介紹Percolate query的使用。 您需要基本瞭解 Elasticsearch,尤其是mapping和search。
概念
Elasticsearch 的正常工作流程是將文檔(JSON數據)存儲在索引中,然後在執行搜索時通過索引查詢這些文檔的信息。設想如果反轉這種使用流程將如何(即先有查詢條件,再有文檔),Percolate即可實現這種逆轉的流程。其使用流程是先存儲search條件,之後使用文檔詢問是否可命中這些搜索條件。本文接下來將介紹如何構造和使用percolator。
Percolation功能圍繞percolator字段類型展開。 與其他字段類型一樣(先在mappings中定義,再進行寫入),不同的是它將搜索條件作為文檔進行存儲。當存儲數據時,索引會將此搜索條件的文檔處理為可執行形式,並將其保存以備後用。
Percolate query接受一個或多個文檔,並返回預先存儲的搜索條件文檔(該條件至少匹配一個傳入的文檔)。在執行搜索時,Percolate query的工作原理與其他任何查詢模式一樣,不同的一些細節將在下文介紹。
深入理解
在底層,具有percolate字段的索引將保留於一個隱藏的索引(內存中)。查詢時,首先將在 percolate query 中列出的文檔放入該索引,然後對該索引執行常規查詢,看與原始的含 percolate 字段的搜索條件是否匹配。
該隱藏索引是從原始 percolator 索引獲取其映射的。因此,用於 percolate query 的索引字段需要具有適合原始數據和查詢文檔數據的mappings配置。
這引入了一些索引管理的問題,因為你的索引數據和 percolate query 文檔可能以不同的方式使用同一字段。一個簡單的方式是使用對象類型(object type) 將 percolate 相關的映射與普通文檔映射分開,具體可參考後文給出的例子。
假設你使用的查詢最初是為另一個索引A中的數據編寫的,那麼最直接的方法是將數據隔離以避免數據直接寫到 percolate 索引中去,並將索引A中根級別的mappings在 percolate索引中進行定義。
此外,由於percolate field被解析為搜索條件並在索引時保存,因此在升級ES主版本後可能需要reindex Percolate文檔。
示例
在此示例中,我們將建立一個索引,該索引含有保存的玩具名字和玩具價格搜索條件。其背後思路是,用戶輸入搜索詞和最高價格,然後在與該玩具名匹配的商品價格低於用戶指定價格時立即得到通知。此外用戶還可以打開和關閉這些通知。下面的映射通過percolate索引來支持此功能。與保存的搜索條件本身相關的字段位於search對象中,而與原始玩具相關的字段位於映射的根級別。
首先,我們使用如下命令來創建一個索引:
PUT toys {
"mappings": {
"properties": {
"search": {
"properties": {
"query": {
"type": "percolator"
},
"user_id": {
"type": "integer"
},
"enabled": {
"type": "boolean"
}
}
},
"price": {
"type": "float"
},
"description": {
"type": "text"
}
}
}
}
我們接著使用命令寫入一個文檔,即用戶的查詢條件。此處我們將數據存儲在 search 對象字段中。price 和 description 的映射僅用於支持 percolate query。
PUT toys/_doc/1
{
"search": {
"user_id": 5,
"enabled": true,
"query": {
"bool": {
"filter": [
{
"match": {
"description": {
"query": "nintendo switch"
}
}
},
{
"range": {
"price": {
"lte": 300
}
}
}
]
}
}
}
}
查詢時,我們要同時使用普通對象字段和“特殊的” percolator 字段。 此查詢將在用戶搜索時檢查是否有當前啟用的搜索條件與文檔匹配。
GET toys/_search
{
"query": {
"bool": {
"filter": [
{
"percolate": {
"field": "search.query",
"document": {
"description": "Nintendo Switch",
"price": 250
}
}
},
{
"term": {
"search.enabled": true
}
},
{
"term": {
"search.user_id": 5
}
}
]
}
}
}
請注意,此處結合使用了基礎字段的查詢(search.user_id和search.enabled字段),以及percolator條件字段的查詢(search.query),用以對指定的用戶ID在啟用狀態下生效。
運行上面的指令後,我們可以看到如下結果:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.0,
"hits" : [
{
"_index" : "toys",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.0,
"_source" : {
"search" : {
"user_id" : 5,
"enabled" : true,
"query" : {
"bool" : {
"filter" : [
{
"match" : {
"description" : {
"query" : "nintendo switch"
}
}
},
{
"range" : {
"price" : {
"lte" : 300
}
}
}
]
}
}
}
},
"fields" : {
"_percolator_document_slot" : [
0
]
}
}
]
}
}
如果我們改用如下搜索條件
GET toys/_search
{
"query": {
"bool": {
"filter": [
{
"percolate": {
"field": "search.query",
"document": {
"description": "Nintendo Switch",
"price": 500
}
}
},
{
"term": {
"search.enabled": true
}
},
{
"term": {
"search.user_id": 5
}
}
]
}
}
}
其中price不滿足percolate預先存儲的條件,因此將找不到任何結果:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
}
結束語
在實際使用中,我們可以在 Logstash的Elasticsearch過濾器中 針對每個事件來使用 Elasticsearch 做 query。即我們也可以得到這個事件是否滿足預設的 search條件,如果滿足條件則可以執行其後續流程。
參考
【1】https://www.elastic.co/blog/elasticsearch-data-enrichment-with-logstash-a-few-security-examples
聲明
本文由作者劉曉國授權轉載,版權歸作者所有,未經許可不得擅自轉載或引用。
出處鏈接:https://elasticstack.blog.csdn.net/.
【阿里雲Elastic Stack】100%兼容開源ES,獨有9大能力,提供免費 X-pack服務(單節點價值$6000)
相關活動
更多折扣活動,請訪問阿里雲 Elasticsearch 官網
阿里雲 Elasticsearch 商業通用版,1核2G ,SSD 20G首月免費
阿里雲 Logstash 2核4G首月免費
下載白皮書:Elasticsearch 八大經典場景應用