Elasticsearch查詢之term/match解析

es種有兩種查詢模式,一種是像傳遞URL參數同樣去傳遞查詢語句,被稱爲簡單搜索或查詢字符串(query string)搜索,好比sql

GET /megacorp/employee/_search //查詢所有員工json

GET /megacorp/employee/_search?q=last_name:Smith //查詢last_name爲Smith的員工restful

另一種是經過DSL語句來進行查詢,被稱爲DSL查詢(Query DSL),DSL是Elasticsearch提供的一種豐富且靈活的查詢語言,該語言以json請求體的形式出現,經過restful請求與Elasticsearch進行交互,本文主要講DSL查詢的一些經常使用規則,在介紹以前,咱們先簡單插入一個測試用的小例子。curl

$curl -XPOST http://localhost:9200/index/doc/1 -d'{"content":"美國留給伊拉克的是個爛攤子嗎","title":"標題","tags":["美國","伊拉克","爛攤子"]}'

 $curl -XPOST http://localhost:9200/index/doc/2 -d'{"content":"中國是世界上人口最多的國家","title":"中國","tags":["中國","人口"]}'

 $curl -XPOST http://localhost:9200/index/doc/3 -d'{"content":"同一個世界同一個夢想","title":"北京奧運","tags":["和平"]}'

 $curl -XPOST http://localhost:9200/index/doc/4 -d'{"content":"杭州是一個美麗的城市,歡迎來到杭州","title":"宣傳","tags":["旅遊","城市"]}'

檢查一下咱們的數據是否導入成功elasticsearch

$curl -XGET http://localhost:9200/index/doc/_search測試

{"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":4,"max_score":1.0,"hits":[{"_index":"index","_type":"doc","_id":"2","_score":1.0,"_source":{"content":"中國是世界上人口最多的國家","title":"中國","tags":["中國","人口"]}},{"_index":"index","_type":"doc","_id":"4","_score":1.0,"_source":{"content":"杭州是一個美麗的城市,歡迎來到杭州","title":"宣傳","tags":["旅遊","城市"]}},{"_index":"index","_type":"doc","_id":"1","_score":1.0,"_source":{"content":"美國留給伊拉克的是個爛攤子嗎","title":"標題","tags":["美國","伊拉克","爛攤子"]}},{"_index":"index","_type":"doc","_id":"3","_score":1.0,"_source":{"content":"同一個世界同一個夢想","title":"北京奧運","tags":["和平"]}}]}}

ok,導入成功,接下來利用這些數據逐步介紹各類經常使用查詢url

term查詢

term是表明徹底匹配,也就是精確查詢,搜索前不會再對搜索詞進行分詞,因此咱們的搜索詞必須是文檔分詞集合中的一個。好比說咱們要找標題爲北京奧運的全部文檔rest

$curl -XGET http://localhost:9200/index/doc/_search?pretty -d 
'{
  "query":{
    "term":{
        "title":"北京奧運"
    }
  }
}'

將會獲得以下結果code

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "hits": {
    "total": 1,
    "max_score": 0.92055845,
    "hits": [
     {
        "_index": "index",
        "_type": "doc",
        "_id": "3",
        "_score": 0.92055845,
        "_source": {
           "content": "同一個世界同一個夢想",
           "title": "北京奧運",
           "tags": [
               "和平"
            ]
        }
      }
    ]
  }
}

搜索title包含北京或者奧運的,結果也同樣,可是若是你搜索詞爲京奧,或者北京奧這樣的,那麼搜索結果將爲空索引

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
      "total" : 5,
      "successful" : 5,
      "failed" : 0
  },
  "hits" : {
      "total" : 0,
      "max_score" : null,
      "hits" : [ ]
  }
}

這是由於在對文檔創建索引時,會將北京奧運分詞爲北京,奧運,北京奧運,只要搜索詞爲這三個之一,均可以將這篇文章搜索出來,而京奧和北京奧並不在分詞集合裏,因此沒法搜索到該文檔。 若是對於某個字段,你想精確匹配,即搜索什麼詞匹配什麼詞,相似sql中的=操做,好比只能經過北京奧運搜索到文檔3而不想讓北京和奧運也搜索到,那麼,你能夠在創建索引階段指定該字段爲"index": "not_analyzed",此時,elasticsearch將不會對該字段的值進行分詞操做,只保留全文字索引。好比本例子中的tags字段,我在創建索引時設置了"index": "not_analyzed", 搜索時,不論是指定tags爲美,仍是國,都沒法將第一條結果搜索出來

$curl -XGET http://localhost:9200/index/doc/_search?pretty -d

'{
  "query":{
    "term":{
        "tags":"美"
    }
  }
}'

搜索結果:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
      "total" : 5,
      "successful" : 5,
      "failed" : 0
  },
  "hits" : {
      "total" : 0,
      "max_score" : null,
      "hits" : [ ]
  }
}

而全詞美國卻能夠

$curl -XGET http://localhost:9200/index/doc/_search?pretty -d 
'{
  "query":{
    "term":{
        "tags":"美國"
    }
  }
}'

搜索結果:

{
    "took" : 2,
    "timed_out" : false,
    "_shards" : {
        "total" : 5,
        "successful" : 5,
        "failed" : 0
    },
    "hits" : {
        "total" : 1,
        "max_score" : 0.30685282,
        "hits" : [ {
            "_index" : "index",
            "_type" : "doc",
            "_id" : "1",
            "_score" : 0.30685282,
            "_source" : {
                  "content" : "美國留給伊拉克的是個爛攤子嗎",
                  "title" : "標題",
                  "tags" : [ "美國", "伊拉克", "爛攤子" ]
            }
      } ]
  }
}

match類查詢

match查詢會先對搜索詞進行分詞,分詞完畢後再逐個對分詞結果進行匹配,所以相比於term的精確搜索,match是分詞匹配搜索,match搜索還有兩個類似功能的變種,一個是match_phrase,一個是multi_match,接下來詳細介紹一下 match

前面提到match搜索會先對搜索詞進行分詞,對於最基本的match搜索來講,只要搜索詞的分詞集合中的一個或多個存在於文檔中便可,例如,當咱們搜索中國杭州,搜索詞會先分詞爲中國和杭州,只要文檔中包含搜索和杭州任意一個詞,都會被搜索到

$curl -XGET http://localhost:9200/index/doc/_search?pretty -d

'{
    "query": {
        "match": {
            "content": "中國杭州"
        }
    }
}'

文檔3正文中有杭州,文檔2中有中國,所以搜索結果有兩個,文檔3中杭州出現兩次,因此排在前面,結果以下:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
      "total" : 2,
      "max_score" : 0.99999994,
      "hits" : [ {
            "_index" : "index",
            "_type" : "doc",
            "_id" : "4",
            "_score" : 0.99999994,
            "_source" : {
                 "content" : "杭州是一個美麗的城市,歡迎來到杭州",
                "title" : "宣傳",
                "tags" : [ "旅遊", "城市" ]
            }
       }, {
            "_index" : "index",
            "_type" : "doc",
            "_id" : "2",
            "_score" : 0.8838835,
            "_source" : {
                  "content" : "中國是世界上人口最多的國家",
                  "title" : "中國",
                  "tags" : [ "中國", "人口" ]
            }
       } ]
    }
}

一樣的,咱們用match的方式搜索中國世界,那麼,文檔2(含有中國和世界)和文檔3(含有世界都會被搜索出來)。若是咱們僅僅想搜索中國和世界都包含的文檔該怎麼辦呢? 其實,對於match搜索,能夠按照分詞後的分詞集合的or或者and進行匹配,默認爲or,這也是爲何咱們看到前面的搜索都是隻要有一個分詞出如今文檔中就會被搜索出來,一樣的,若是咱們但願是全部分詞都要出現,那隻要把匹配模式改爲and就好了

curl -XGET http://localhost:9200/index/doc/_search?pretty -d 
'{
    "query": {
        "match": {
            "content": {
                "query": "中國世界",
                "operator": "and"
            }
        }
    }
}'

如上所示,查詢時將operator設置爲and,此時,就只會搜索到既包含中國,也包含世界的文檔了(因返回的字段較多,後面搜索結果只展現_source中的內容)

"_source" : {
    "content" : "中國是世界上人口最多的國家",
    "title" : "中國",
    "tags" : [ "中國", "人口" ]
}

match_phrase

match_phrase爲按短語搜索,這個可能先用英文來解釋會直觀一點(中文分詞後其實已是一個一個有具體意思的詞語)。英文中以空格分詞,所以分詞後是一個個的單詞,當想搜索相似hope so這樣的短語時,你或許並不想將一些只含有hope的文檔搜索出來,也不想將一些相似I hope ×××. So ××這樣的搜索出來,此時,就能夠用match_phrase。 match_phrase的搜索方式和match相似,先對搜索詞創建索引,並要求全部分詞必須在文檔中出現(像不像operator爲and的match查詢),除此以外,還必須知足分詞在文檔中出現的順序和搜索詞中一致且各搜索詞之間必須緊鄰,所以match_phrase也能夠叫作緊鄰搜索。 因此,當咱們搜美國留給時

curl -XGET http://localhost:9200/index/doc/_search?pretty -d

'{
  "query": {
    "match_phrase": {
        "content": "美國留給"
    }
  }
}'

能搜出文檔美國留給伊拉克的是個爛攤子嗎

"_source" : {
        "content" : "美國留給伊拉克的是個爛攤子嗎",
        "title" : "標題",
        "tags" : [ "美國", "伊拉克", "爛攤子" ]
    }

可是咱們搜索留給美國或美國伊拉克時,卻沒有搜索結果,由於一個順序不對,一個不是緊鄰(隔着留給)。 緊鄰對於匹配度要求較高,爲了減少精度增長可操做性,引入了slop參數。該參數能夠指定相隔多少個詞仍被算做匹配成功。以下,

curl -XGET http://localhost:9200/index/doc/_search?pretty -d

'{
    "query": {
        "match_phrase": {
            "content": {
                "query": "美國伊拉克",
                "slop": "1"
            }
        }
    }
}'

當咱們將slop設置爲1時,文檔1已能被搜索到。

"_source" : {
    "content" : "美國留給伊拉克的是個爛攤子嗎",
    "title" : "標題",
    "tags" : [ "美國", "伊拉克", "爛攤子" ]
  }

須要注意的是,當slop的值過大時(超出文檔總分詞數),那麼分詞數據將能夠是隨意的,即跟operator爲and的match查詢效果同樣。好比咱們查詢

curl -XGET http://localhost:9200/index/doc/_search?pretty -d

'{
    "query": {
        "match_phrase": {
            "content": {
                "query": "伊拉克美國",
                "slop": "12"
            }
        }
    }
}'

將會獲得與上面同樣的結果

multi_match

文/木鳥飛魚(簡書做者) 原文連接:http://www.jianshu.com/p/eb30eee13923# 著做權歸做者全部,轉載請聯繫做者得到受權,並標註「簡書做者」。