作者
劉曉國,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 八大經典場景應用