1. 程式人生 > >ElasticSearch常用結構化搜索

ElasticSearch常用結構化搜索

rop tar 情況 test art ice 都是 其中 ngs

最近,需要用到ES的一些常用的結構化搜索命令,因此,看了一些官方的文檔,學習了一下。結構化查詢指的是查詢那些具有內在結構的數據,比如日期、時間、數字都是結構化的。

它們都有精確的格式,我們可以對這些數據進行邏輯操作,比較常見的操作包括比較時間區間,或者獲取兩個數字間的較大值。

精確查詢

當進行精確查詢時,過濾器filter是十分重要的,因為它們效率非常高,過濾器不計算相關性(直接跳過了整個記分階段)而且很容易進行緩存。

過濾數字

我們首先看 term filter,它最常用,可以用來處理數字,布爾值,日期和文本。

例如我們有一些產品:

POST /my_store/products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }

我們想要做的是要查詢具有某個價格的所有產品,如果對於SQL熟悉,那麽它的表達式是:

SELECT * FROM   products WHERE  price = 20

在ES查詢中,我們使用 term 達到相同的目的:

{
    "term" : {
        "price" : 20
    }
}

但是在ES裏,term 不能單獨使用,search API期望的是一個 query 而不是 filter,所以,我們需要把 term 放在一個filter query裏進行使用:

GET /my_store/products/_search
{
    "query" : {
        "filtered" : { #filtered 查詢同時接受一個 query 和 filter
            "query" : {
                "match_all" : {} #match_all 會返回所有匹配的文件,這是個默認行為
            },
            "filter" : {
                "term" : { #term 過濾我們之前說到的,需要註意的是這裏 term塊 是處於 filter 之內的
                    "price" : 20
                }
            }
        }
    }
}

執行結果正如我們期望一樣,它只會返回文檔2,這裏我們稱為命中hit。

"hits" : [
    {
        "_index" : "my_store",
        "_type" :  "products",
        "_id" :    "2",
        "_score" : 1.0, #1
        "_source" : {
          "price" :     20,
          "productID" : "KDKE-B-9947-#kL5"
        }
    }
]

之前我們說到filter不會進行記分或相關性計算,這裏的分數來自於我們查詢時使用的關鍵字 match_all

,它會同等對待所有的文件,並對所有的結果都給以1的記分。

過濾文本

term 同樣可以用來過濾文本,如果我們想要查詢某個具體UPC id的產品,SQL語句會是下面這樣:

SELECT product FROM   products WHERE  productID = "XHDK-A-1293-#fJ3"

轉換成ES查詢,同樣使用 term 來查詢:

GET /my_store/products/_search
{
    "query" : {
        "filtered" : {
            "filter" : {
                "term" : {
                    "productID" : "XHDK-A-1293-#fJ3"
                }
            }
        }
    }
}

但這裏有個小問題,我們沒有如預期得到想要的結果!為什麽呢?問題並不出在 term 查詢上,問題出在數據索引的方式。如果使用 analyze API(Test Analyzers),我們可以看到這裏的UPC碼以及被拆分成多個小的token:

GET /my_store/_analyze?field=productID
XHDK-A-1293-#fJ3

結果

{
  "tokens" : [ {
    "token" :        "xhdk",
    "start_offset" : 0,
    "end_offset" :   4,
    "type" :         "<ALPHANUM>",
    "position" :     1
  }, {
    "token" :        "a",
    "start_offset" : 5,
    "end_offset" :   6,
    "type" :         "<ALPHANUM>",
    "position" :     2
  }, {
    "token" :        "1293",
    "start_offset" : 7,
    "end_offset" :   11,
    "type" :         "<NUM>",
    "position" :     3
  }, {
    "token" :        "fj3",
    "start_offset" : 13,
    "end_offset" :   16,
    "type" :         "<ALPHANUM>",
    "position" :     4
  } ]
}

所以,當我們用 term 去過濾值 XHDK-A-1293-#fJ3 的時候,找不到任何文件,因為這個token不在我們的反向索引(inverted index)之中,正如上面呈現的,索引裏面有4個token。

顯然,這種對於id碼或其他任何精確值的處理方式不是我們想要的。

為了避免這種問題,我們需要告訴ElasticSearch這個字段具有精確值,需要被設置成 not_analyzed 。 我們可以在定制化字段mapping中找到相關內容。為了修正這個問題,我們需要首先刪除老的index,然後再創建一個新的

DELETE /my_store #1

PUT /my_store #2
{
    "mappings" : {
        "products" : {
            "properties" : {
                "productID" : {
                    "type" : "string",
                    "index" : "not_analyzed" #3
                }
            }
        }
    }

}

然後我們就可以對文件重索引了:

POST /my_store/products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }

組合過濾器

上面的兩個例子都是單個filter的使用方式,在實際中,我們很多情況下會同時會對多個值或字段使用filter。例如,在ElasticSearch中,如何標識下面這個SQL?

SELECT product FROM   products WHERE  (price = 20 OR productID = "XHDK-A-1293-#fJ3") AND  (price != 30)

在這種情況下,我們需要 bool filter。這是一個復合過濾器可以接收多個參數,然後將他們組合成布爾組合。

布爾過濾器(Bool Filter)

bool filter包括三部分:

{
   "bool" : {
      "must" :     [],
      "should" :   [],
      "must_not" : [],
   }
}
  • must:所有的語句必須匹配,與 AND 等價。

  • must_not:所有的語句都不能匹配,與 NOT 等價。

  • should:至少有一個語句匹配,與 OR 等價。

用ES查詢實現我們上面SQL裏的查詢:

GET /my_store/products/_search
{
   "query" : {
      "filtered" : { 
         "filter" : {
            "bool" : {
              "should" : [
                 { "term" : {"price" : 20}},
                 { "term" : {"productID" : "XHDK-A-1293-#fJ3"}} 
              ],
              "must_not" : {
                 "term" : {"price" : 30}
              }
           }
         }
      }
   }
}

我們搜索的結果返回了2個hits,兩個文件各滿足其中一個條件:

"hits" : [
    {
        "_id" :     "1",
        "_score" :  1.0,
        "_source" : {
          "price" :     10,
          "productID" : "XHDK-A-1293-#fJ3" 
        }
    },
    {
        "_id" :     "2",
        "_score" :  1.0,
        "_source" : {
          "price" :     20, 
          "productID" : "KDKE-B-9947-#kL5"
        }
    }
]

嵌套布爾過濾器(Nesting Boolean Filters)

盡管 bool 是一個復合的過濾器,可以接受多個子過濾器,需要註意的是 bool 過濾器本身仍然是一個過濾器(filter)。這意味著我們可以將一個bool過濾器置於另外一個bool過濾器內部,這為我們提供了復雜布爾邏輯的處理能力:

對於一個SQL語句:

SELECT document FROM   products WHERE  productID  = "KDKE-B-9947-#kL5" OR ( productID = "JODL-X-1937-#pV7" AND price = 30 )

我們將其轉換成一個嵌套的 bool 過濾器:

GET /my_store/products/_search
{
   "query" : {
      "filtered" : {
         "filter" : {
            "bool" : {
              "should" : [
                { "term" : {"productID" : "KDKE-B-9947-#kL5"}}, #1
                { "bool" : { #2
                  "must" : [
                    { "term" : {"productID" : "JODL-X-1937-#pV7"}}, #3
                    { "term" : {"price" : 30}} #4
                  ]
                }}
              ]
           }
         }
      }
   }
}

得到的結果有兩個文件,他們各滿足 should 中的一個條件:

"hits" : [
    {
        "_id" :     "2",
        "_score" :  1.0,
        "_source" : {
          "price" :     20,
          "productID" : "KDKE-B-9947-#kL5" #1
        }
    },
    {
        "_id" :     "3",
        "_score" :  1.0,
        "_source" : {
          "price" :      30, #2
          "productID" : "JODL-X-1937-#pV7" #3
        }
    }
]

ElasticSearch常用結構化搜索