Elasticsearch字符串搜索方式

前言

剛開(kāi)始接觸Elasticsearch的時(shí)候被Elasticsearch的搜索功能搞得暈頭轉(zhuǎn)向,每次想在Kibana里面查詢某個(gè)字段的時(shí)候,查出來(lái)的結(jié)果經(jīng)常不是自己想要的,然而又不知道問(wèn)題出在了哪里。出現(xiàn)這個(gè)問(wèn)題歸根結(jié)底是因?yàn)閷?duì)于Elasticsearch的底層索引原理以及各個(gè)查詢搜索方式的不了解,在Elasticsearch中僅僅字符串相關(guān)的查詢就有19個(gè)之多,如果不弄清楚查詢語(yǔ)句的工作方式,應(yīng)用可能就不會(huì)按照我們預(yù)想的方式運(yùn)作。這篇文章就詳細(xì)介紹了Elasticsearch的19種搜索方式及其原理,老板再也不用擔(dān)心我用錯(cuò)搜索語(yǔ)句啦!

簡(jiǎn)介

Elasticsearch為所有類(lèi)型的數(shù)據(jù)提供實(shí)時(shí)搜索和分析,不管數(shù)據(jù)是結(jié)構(gòu)化文本還是非結(jié)構(gòu)化文本、數(shù)字?jǐn)?shù)據(jù)或地理空間數(shù)據(jù),都能保證在支持快速搜索的前提下對(duì)數(shù)據(jù)進(jìn)行高效的存儲(chǔ)和索引。用戶不僅可以進(jìn)行簡(jiǎn)單的數(shù)據(jù)檢索,還可以聚合信息來(lái)發(fā)現(xiàn)數(shù)據(jù)中的趨勢(shì)和模式。

搜索是Elasticsearch系統(tǒng)中最重要的一個(gè)功能,它支持結(jié)構(gòu)化查詢、全文查詢以及結(jié)合二者的復(fù)雜查詢。結(jié)構(gòu)化查詢有點(diǎn)像SQL查詢,可以對(duì)特定的字段進(jìn)行篩選,然后按照特定的字段進(jìn)行排序得到結(jié)果。全文查詢會(huì)根據(jù)查詢字符串尋找相關(guān)的文檔,并且按照相關(guān)性排序。

Elasticsearch內(nèi)包含很多種查詢類(lèi)型,下面介紹是其中最重要的19種。如果你的app想要添加一個(gè)搜索框,為用戶提供搜索操作,并且數(shù)據(jù)量很大用MySQL會(huì)造成慢查詢想改用Elasticsearch,那么我相信這篇文章會(huì)給你帶來(lái)很大的幫助。

query和filter區(qū)別

在正式進(jìn)入到搜索部分之前,我們需要區(qū)分query(查詢)和filter(過(guò)濾)的區(qū)別。

在進(jìn)行query的時(shí)候,除了完成匹配的過(guò)程,我們實(shí)際上在問(wèn)“這個(gè)結(jié)果到底有多匹配我們的搜索關(guān)鍵詞”。在所有的返回結(jié)果的后面都會(huì)有一個(gè)_score字段表示這個(gè)結(jié)果的匹配程度,也就是相關(guān)性。相關(guān)性越高的結(jié)果就越排在前面,相關(guān)性越低就越靠后。當(dāng)兩個(gè)文檔的相關(guān)性相同的時(shí)候,會(huì)根據(jù)lucene內(nèi)部的doc_id字段來(lái)排序,這個(gè)字段對(duì)于用戶是不可見(jiàn)的也不能控制。

而在進(jìn)行filter的時(shí)候,僅僅是在問(wèn)“這個(gè)文檔符不符合要求”,這僅僅是一個(gè)過(guò)濾的操作判斷文檔是否滿足我們的篩選要求,不會(huì)計(jì)算任何的相關(guān)性。比如timestamp的范圍是否在2019和2020之間,status狀態(tài)是否是1等等。

在一個(gè)查詢語(yǔ)句里面可以同時(shí)存在queryfilter,只不過(guò)只有query的查詢字段會(huì)進(jìn)行相關(guān)性_score的計(jì)算,而filter僅僅用來(lái)篩選。比如在下面的查詢語(yǔ)句里面,只有title字段會(huì)進(jìn)行相關(guān)性的計(jì)算,而下面的status只是為了篩選并不會(huì)計(jì)算相關(guān)性。

GET /_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {"title": "Search"}}
      ],
      "filter": [
        {"term": {"state": 1}}
      ]
    }
  }
}

對(duì)于在實(shí)際應(yīng)用中應(yīng)該用query還是用filter需要根據(jù)實(shí)際的業(yè)務(wù)場(chǎng)景來(lái)看。如果你的產(chǎn)品的搜索只是需要篩選得到最后的搜索結(jié)果并不需要Elasticsearch的相關(guān)性排序(你可能自定義了其他的排序規(guī)則),那么使用filter就完全能夠滿足要求并且能夠有更好的性能(filter不需要計(jì)算相關(guān)性而且會(huì)緩存結(jié)果);如果需要考慮文檔和搜索詞的相關(guān)性,那么使用query就是最好的選擇。

相關(guān)性

上面講到了在使用query查詢的時(shí)候會(huì)計(jì)算相關(guān)性并且進(jìn)行排序,很多人都會(huì)好奇相關(guān)性是怎么計(jì)算的?

相關(guān)性的計(jì)算是比較復(fù)雜的,詳細(xì)的文檔可以看這兩篇博客——什么是相關(guān)性ElasticSearch 使用教程之_score(評(píng)分)介紹,我這里只是做一個(gè)簡(jiǎn)單的介紹。

Elasticsearch的相似度計(jì)算主要是利用了全文檢索領(lǐng)域的計(jì)算標(biāo)準(zhǔn)——TF/IDF(Term Frequency/Inverted Document Frequency)也就是檢索詞頻率反向文檔頻率

  1. TF(檢索詞頻率):檢索詞在這個(gè)字段里面出現(xiàn)的頻率越高,相關(guān)性越高。比如搜索詞出現(xiàn)5次肯定比出現(xiàn)1次的文檔相關(guān)性更高。
  2. IDF(反向文檔頻率):包含檢索詞的文檔的頻率越高,這個(gè)檢索詞的相關(guān)性比重越低。如果一個(gè)檢索詞在所有的文檔里面都出現(xiàn)了,比如中文的,那么這個(gè)檢索詞肯定就不重要,相對(duì)應(yīng)的根據(jù)這個(gè)檢索詞匹配的文檔的相關(guān)性權(quán)重應(yīng)該下降。
  3. 字段長(zhǎng)度:注意這個(gè)字段是文檔的里面被搜索的字段,不是檢索詞。如果這個(gè)字段的長(zhǎng)度越長(zhǎng),相關(guān)性就越低。這個(gè)主要是因?yàn)檫@個(gè)檢索詞在字段內(nèi)的重要性降低了,文檔就相對(duì)來(lái)說(shuō)不那么匹配了。

在復(fù)合查詢里面,比如bool查詢,每個(gè)子查詢計(jì)算出來(lái)的評(píng)分會(huì)根據(jù)特定的公式合并到綜合評(píng)分里面,最后根據(jù)這個(gè)綜合評(píng)分來(lái)排序。當(dāng)我們想要修改不同的查詢語(yǔ)句的在綜合評(píng)分里面的比重的時(shí)候,可以在查詢字段里面添加boost參數(shù),這個(gè)值是相對(duì)于1來(lái)說(shuō)的。如果大于1則這個(gè)查詢參數(shù)的權(quán)重會(huì)提高;如果小于1,權(quán)重就下降。

這個(gè)評(píng)分系統(tǒng)一般是系統(tǒng)默認(rèn)的,我們可以根據(jù)需要定制化我們自己的相關(guān)性計(jì)算方法,比如通過(guò)腳本自定義評(píng)分。

分析器

分析器是針對(duì)text字段進(jìn)行文本分析的工具。文本分析是把非結(jié)構(gòu)化的數(shù)據(jù)(比如產(chǎn)品描述或者郵件內(nèi)容)轉(zhuǎn)化成結(jié)構(gòu)化的格式從而提高搜索效率的過(guò)程,通常在搜索引擎里面應(yīng)用的比較多。

text格式的數(shù)據(jù)和keyword格式的數(shù)據(jù)在存儲(chǔ)和索引的時(shí)候差別比較大。keyword會(huì)直接被當(dāng)成整個(gè)字符串保存在文檔里面,而text格式數(shù)據(jù),需要經(jīng)過(guò)分析器解析之后,轉(zhuǎn)化成結(jié)構(gòu)化的文檔再保存起來(lái)。比如對(duì)于the quick fox字符串,如果使用keyword類(lèi)型,保存直接就是the quick fox,使用the quick fox作為關(guān)鍵詞可以直接匹配,但是使用the或者quick就不能匹配;但是如果使用text保存,那么分析器會(huì)把這句話解析成thequick、fox三個(gè)token進(jìn)行保存,使用the quick fox就無(wú)法匹配,但是單獨(dú)用the、quick、fox三個(gè)字符串就可以匹配。所以對(duì)于text類(lèi)型的數(shù)據(jù)的搜索需要格外注意,如果你的搜索詞得不到想要的結(jié)果,很有可能是你的搜索語(yǔ)句有問(wèn)題。

分析器的工作過(guò)程大概分成兩步:

  1. 分詞(Tokenization):根據(jù)停止詞把文本分割成很多的小的token,比如the quick fox會(huì)被分成thequick、fox,其中的停止詞就是空格,還有很多其他的停止詞比如&或者#,大多數(shù)的標(biāo)點(diǎn)符號(hào)都是停止詞
  2. 歸一化(Normalization):把分隔的token變成統(tǒng)一的形式方便匹配,比如下面幾種
  • 把單詞變成小寫(xiě),Quick會(huì)變成quick
  • 提取詞干,foxes變成fox
  • 合并同義詞,jumpleap是同義詞,會(huì)被統(tǒng)一索引成jump

Elasticsearch自帶了一個(gè)分析器,是系統(tǒng)默認(rèn)的標(biāo)準(zhǔn)分析器,使用標(biāo)準(zhǔn)分詞器,大多數(shù)情況下都能夠有不錯(cuò)的分析效果。用戶也可以定義自己的分析器,用于滿足不同的業(yè)務(wù)需求。

想要知道某個(gè)解析器的分析結(jié)果,可以直接在ES里面進(jìn)行分析,執(zhí)行下面的語(yǔ)句就行了:

POST /_analyze
{
  "analyzer": "standard",
  "text": "1 Fire's foxes"
}

返回的結(jié)果是:

{
  "tokens" : [
    {
      "token" : "1",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "<NUM>",
      "position" : 0
    },
    {
      "token" : "fire's",
      "start_offset" : 2,
      "end_offset" : 8,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "fox",
      "start_offset" : 9,
      "end_offset" : 12,
      "type" : "<ALPHANUM>",
      "position" : 2
    }
  ]
}

返回的tokens內(nèi)部就是所有的解析結(jié)果,token表示解析的詞語(yǔ)部分,start_offsetend_offset分別表示token在原text內(nèi)的起始和終止位置,type表示類(lèi)型,position表示這個(gè)token在整個(gè)tokens列表里面的位置。

OK!有了上面的基礎(chǔ)知識(shí),就可以進(jìn)行下面的搜索的介紹了。

term搜索

term搜索不僅僅可以對(duì)keyword類(lèi)型的字段使用,也可以對(duì)text類(lèi)型的數(shù)據(jù)使用,前提是使用的搜索詞必須要預(yù)先處理一下——不包含停止詞并且都是小寫(xiě)(標(biāo)準(zhǔn)解析器),因?yàn)槲臋n里面保存的text字段分詞后的結(jié)果,用term是可以匹配的。

exists

返回所有指定字段不為空的文檔,比如這個(gè)字段對(duì)應(yīng)的值是null或者[]或者沒(méi)有為這個(gè)字段建立索引。

GET /_search
{
  "query": {
    "exists": {
      "field": "user"
    }
  }
}

如果字段是空字符串""或者包含null的數(shù)組[null,"foo"],都會(huì)被當(dāng)作字段存在。

這個(gè)方法可以用來(lái)搜索沒(méi)有被索引的值或者不存在的值。

fuzzy

fuzzy查詢是一種模糊查詢,會(huì)根據(jù)檢索詞和檢索字段的編輯距離(Levenshtein Distance)來(lái)判斷是否匹配。一個(gè)編輯距離就是對(duì)單詞進(jìn)行一個(gè)字符的修改,這種修改可能是

  • 修改一個(gè)字符,比如boxfox
  • 刪除一個(gè)字符,比如blacklack
  • 插入一個(gè)字符,比如sicsick
  • 交換兩個(gè)相鄰的字符的位置,比如actcat

在進(jìn)行fuzzy搜索的時(shí)候,ES會(huì)生成一系列的在特定編輯距離內(nèi)的變形,然后返回這些變形的準(zhǔn)確匹配。默認(rèn)情況下,當(dāng)檢索詞的長(zhǎng)度在0..2中間時(shí),必須準(zhǔn)確匹配;長(zhǎng)度在3..5之間的時(shí)候,編輯距離最大為1;長(zhǎng)度大于5的時(shí)候,最多允許編輯距離為2。

可以通過(guò)配置fuzziness修改最大編輯距離,max_expansions修改最多的變形的token的數(shù)量

比如搜索是以下條件的時(shí)候:

GET /_search
{
  "query": {
    "fuzzy": {
      "name": "Accha"
    }
  }
}

返回結(jié)果有Iccha、AccHa、accha還有ccha

ids

根據(jù)文檔的_id數(shù)組返回對(duì)應(yīng)的文檔信息

GET /_search
{
  "query": {
    "ids": {
      "values": ["1","4","100"]
    }
  }
}

prefix

返回所有包含以檢索詞為前綴的字段的文檔。

GET /_search
{
  "query": {
    "prefix": {
      "name": "ac"
    }
  }
}

返回所有以ac開(kāi)頭的字段,比如acchu、achu、achar等等

在某些場(chǎng)景下面比如搜索框里面,需要用戶在輸入內(nèi)容的同時(shí)也要實(shí)時(shí)展示與輸入內(nèi)容前綴匹配的搜索結(jié)果,就可以使用prefix查詢。為了加速prefix查詢,還可以在設(shè)置字段映射的時(shí)候,使用index_prefixes映射。ES會(huì)額外建立一個(gè)長(zhǎng)度在2和5之間索引,在進(jìn)行前綴匹配的時(shí)候效率會(huì)有很大的提高。

range

對(duì)字段進(jìn)行范圍的匹配。

GET /_search
{
  "query": {
    "range": {
      "age": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}

搜索年齡在10(包含)和20(包含)之間的結(jié)果

regexp

正則表達(dá)式匹配。通過(guò)正則表達(dá)式來(lái)尋找匹配的字段,lucene會(huì)在搜索的時(shí)候生成有限狀態(tài)機(jī),其中包含很多的狀態(tài),默認(rèn)的最多狀態(tài)數(shù)量是10000

GET /_search
{
  "query": {
    "regexp": {
      "name": "ac.*ha"
    }
  }
}

這個(gè)搜索會(huì)匹配achha、achintha還有achutha

term

根據(jù)檢索詞來(lái)準(zhǔn)確匹配字段。官方文檔建議不要用term去搜索text類(lèi)型的字段,因?yàn)榉治銎鞯脑蚝苡锌赡懿粫?huì)出現(xiàn)你想要的結(jié)果。但是直接使用term去搜索text字段還是可以工作的,前提是明白為什么會(huì)返回這些數(shù)據(jù)。比如通過(guò)下面的搜索:

GET /_search
{
  "query": {
    "term": {
      "name": {
        "value": "accha"
      }
    }
  }
}

如果name字段是keyword類(lèi)型的,沒(méi)有進(jìn)行解析,那么只會(huì)匹配所有nameaccha的文檔。

如果name字段是text類(lèi)型的,原字段經(jīng)過(guò)分詞、小寫(xiě)化處理之后,只能匹配到解析之后的單獨(dú)token,比如使用標(biāo)準(zhǔn)解析器,這個(gè)搜索會(huì)匹配Accha Baccha、so cute accha baccha或者Accha Baccha Shivam等字段。

terms

根據(jù)檢索詞列表來(lái)批量搜索文檔,每個(gè)檢索詞在搜索的時(shí)候相當(dāng)于or的關(guān)系,只要一個(gè)匹配就行了。Elasticsearch最多允許65,536個(gè)term同時(shí)查詢。

GET /_search
{
  "query": {
    "terms": {
      "name": [
        "accha",
        "ghazali"
      ]
    }
  }
}

上面的查詢會(huì)匹配name字段為acchaghazali的文檔。

除了直接指定查詢的term列表,還可以使用Terms lookUp功能,也就是指定某一個(gè)存在的文檔的某一個(gè)字段(可能是數(shù)字、字符串或者列表)來(lái)作為搜索條件,進(jìn)行terms搜索。

比如有一個(gè)文件indexmy_doc,id10,name字段是term并且值為accha,搜索可以這樣寫(xiě):

{
  "query": {
    "terms": {
      "name": {
        "index": "my_doc",
        "id": "10",
        "path": "name"
      }
    }
  }
}

這樣就可以返回所有name字段值是accha的文檔里,這個(gè)通??梢杂脕?lái)查詢所有和某個(gè)文檔某個(gè)字段重復(fù)的文檔并且不需要提前知道這個(gè)字段的值是什么。

terms_set

terms_set和terms十分類(lèi)似,只不過(guò)是多了一個(gè)最少需要匹配數(shù)量minimum_should_match_field參數(shù)。當(dāng)進(jìn)行匹配的時(shí)候,只有至少包含了這么多的terms中的term的時(shí)候,才會(huì)返回對(duì)應(yīng)的結(jié)果。

GET /_search
{
  "query": {
    "terms_set": {
      "programming_languages": {
        "terms": ["c++","java","php"],
        "minimum_should_match_field": "required_match"
      }
    }
  }
}

{
    "name":"Jane Smith",
    "programming_languages":[
        "c++",
        "java"
    ],
    "required_matches":2
}

那么只有programming_languages列表里面至少包含["c++", "java", "php"]其中的2項(xiàng)才能滿足條件

還可以使用minimum_should_match_script腳本來(lái)配置動(dòng)態(tài)查詢

{
  "query": {
    "terms_set": {
      "programming_languages": {
        "terms": ["c++","java","php"],
        "minimum_should_match_script": {
          "source": "Math.min(params.num_terms, doc['required_matches'].value)"
        }
      }
    }
  }
}

其中params.num_terms是在terms字段中的元素的個(gè)數(shù)

wildcard

通配符匹配,返回匹配包含通配符的檢索詞的結(jié)果。

目前只支持兩種通配符:

  • ?:匹配任何單一的字符
  • *:匹配0個(gè)或者多個(gè)字符

在進(jìn)行wildcard搜索的時(shí)候最好避免在檢索詞的開(kāi)頭使用*或者?,這會(huì)降低搜索性能。

GET /_search
{
  "query": {
    "wildcard": {
      "name": {
        "value": "acc*"
      }
    }
  }
}

這個(gè)搜索會(huì)匹配acchuacche或者accio父

text搜索

text搜索實(shí)際上是針對(duì)被定義為text類(lèi)型的字段的搜索,通常搜索的時(shí)候不能根據(jù)輸入的字符串的整體來(lái)理解,而是要預(yù)先處理一下,把搜索詞變成小的token,再來(lái)查看每個(gè)token的匹配。

interval

返回按照檢索詞的特定排列順序排列的文檔。這個(gè)查詢比較復(fù)雜,這里只是簡(jiǎn)單的介紹,詳細(xì)的介紹可以看官方文檔

比如我們想查詢同時(shí)包含rajnayaka的字段并且ray正好在nayaka前面,查詢語(yǔ)句如下:

POST /_search
{
  "query": {
    "intervals": {
      "name": {
        "match": {
          "query": "raj nayaka",
          "max_gaps": 0,
          "ordered": true
        }
      }
    }
  }
}

上面的查詢會(huì)匹配Raj Nayaka Acchu ValmikiYateesh Raj Nayaka。

如果把ordered:true去掉,就會(huì)匹配nayaka raj。

如果把max_gaps:0去掉,系統(tǒng)會(huì)用默認(rèn)值-1也就是沒(méi)有距離要求,就會(huì)匹配Raj Raja nayaka或者Raj Kumar Nayaka

其中有兩個(gè)關(guān)鍵詞orderedmax_gaps分別用來(lái)控制這個(gè)篩選條件是否需要排序以及兩個(gè)token之間的最大間隔

match

查找和檢索詞短語(yǔ)匹配的文檔,這些檢索詞在進(jìn)行搜索之前會(huì)先被分析器解析,檢索詞可以是文本、數(shù)字、日期或者布爾值。match檢索也可以進(jìn)行模糊匹配。

GET /_search
{
  "query": {
    "match": {
      "name": "nagesh acchu"
    }
  }
}

以上的查詢會(huì)匹配NaGesh Acchu、Acchu Acchuacchu。系統(tǒng)默認(rèn)是在分詞后匹配任何一個(gè)token都可以完成匹配,如果修改operatorAND,則會(huì)匹配同時(shí)包含nageshacchu的字段。

GET /_search
{
  "query": {
    "match": {
      "name": {
        "query": "nagesh acchu",
        "operator": "and"
      }
    }
  }
}

上面這個(gè)查詢就只會(huì)返回NaGesh Acchu

查詢的時(shí)候也可以使用模糊查詢,修改fuzziness參數(shù)

GET /_search
{
  "query": {
    "match": {
      "name": {
        "query": "nagesh acchu",
        "operator": "and",
        "fuzziness": 1
      }
    }
  }
}

上面的語(yǔ)句會(huì)匹配NaGesh Acchu還有Nagesh Bacchu

match_bool_prefix

match_bool_prefix會(huì)解析檢索詞,然后生成一個(gè)bool復(fù)合檢索語(yǔ)句。如果檢索詞由很多個(gè)token構(gòu)成,除了最后一個(gè)會(huì)進(jìn)行prefix匹配,其他的會(huì)進(jìn)行term匹配。

比如使用nagesh ac進(jìn)行match_bool_prefix搜索

GET /_search
{
  "query": {
    "match_bool_prefix": {
      "name": "nagesh ac"
    }
  }
}

上面的查詢會(huì)匹配Nagesh NageshRakshith Achar或者ACoco

實(shí)際查詢等價(jià)于

GET /_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "name": {
              "value": "nagesh"
            }
          }
        },
        {
          "prefix": {
            "name": {
              "value": "ac"
            }
          }
        }
      ]
    }
  }
}

match_phrase

詞組匹配會(huì)先解析檢索詞,并且標(biāo)注出每個(gè)的token相對(duì)位置,搜索匹配的字段的必須包含所有的檢索詞的token,并且他們的相對(duì)位置也要和檢索詞里面相同。

GET /_search
{
  "query": {
    "match_phrase": {
      "name": "Bade Acche"
    }
  }
}

這個(gè)搜索會(huì)匹配Bade Acche Lagte,但是不會(huì)匹配Acche Bade Lagte或者Bade Lagte Acche

如果我們不要求這兩個(gè)單詞相鄰,希望放松一點(diǎn)條件,可以添加slop參數(shù),比如設(shè)置成1,代表兩個(gè)token之間相隔的最多的距離(最多需要移動(dòng)多少次才能相鄰)。下面的查詢語(yǔ)句會(huì)匹配Bade Lagte Acche

GET /_search
{
  "query": {
    "match_phrase": {
      "name": {
        "query": "Bade Acche",
        "slop": 1
      }
    }
  }
}

match_phrase_prefix

match_phrase_prefix相當(dāng)于是結(jié)合了match_bool_prefix和match_phrase。ES會(huì)先解析檢索詞,分成很多個(gè)token,然后除去最后一個(gè)token,對(duì)其他的token進(jìn)行match_phrase的匹配,即全部都要匹配并且相對(duì)位置相同;對(duì)于最后一個(gè)token,需要進(jìn)行前綴匹配并且匹配的這個(gè)單詞在前面的match_phrase匹配的結(jié)果的后面。

GET /_search
{
  "query": {
    "match_phrase_prefix": {
      "name": "acchu ac"
    }
  }
}

上面的查詢能夠匹配Acchu Acchu1Acchu Acchu Papu,但是不能匹配acc acchu或者acchu pa

multi_match

multi_match可以同時(shí)對(duì)多個(gè)字段進(jìn)行查詢匹配,ES支持很多種不同的查詢類(lèi)型比如best_fields(任何字段match檢索詞都表示匹配成功)、phrase(用match_phrase代替match)還有cross_field(交叉匹配,通常用在所有的token必須在至少一個(gè)字段中出現(xiàn))等等

下面是普通的best_fields的匹配

GET /_search
{
  "query": {
    "multi_match": {
      "query": "acchu",
      "fields": [
        "name",
        "intro"
      ]
    }
  }
}

只要name或者intro字段任何一個(gè)包含acchu都會(huì)完成匹配。

如果使用cross_fields匹配如下

GET /_search
{
  "query": {
    "multi_match": {
      "query": "call acchu",
      "type": "cross_fields",
      "fields": [
        "name",
        "intro"
      ],
      "operator": "and"
    }
  }
}

上面的匹配需要同時(shí)滿足下面兩個(gè)條件:

  • name中出現(xiàn)callintro中出現(xiàn)call
  • name中出現(xiàn)acchuintro中出現(xiàn)acchu

所以這個(gè)查詢能夠匹配name包含acchuintro包含call的文檔,或者匹配name同時(shí)包含callacchu的文檔。

common

common查詢會(huì)把查詢語(yǔ)句分成兩個(gè)部分,較為重要的分為一個(gè)部分(這個(gè)部分的token通常在文章中出現(xiàn)頻率比較低),不那么重要的為一個(gè)部分(出現(xiàn)頻率比較高,以前可能被當(dāng)作停止詞),然后分別用low_freq_operator、high_freq_operator以及minimum_should_match來(lái)控制這些語(yǔ)句的表現(xiàn)。

在進(jìn)行查詢之前需要指定一個(gè)區(qū)分高頻和低頻詞的分界點(diǎn),也就是cutoff_frequency,它既可以是小數(shù)比如0.001代表該字段所有的token的集合里面出現(xiàn)的頻率也可以是大于1的整數(shù)代表這個(gè)詞出現(xiàn)的次數(shù)。當(dāng)token的頻率高于這一個(gè)閾值的時(shí)候,他就會(huì)被當(dāng)作高頻詞。

GET /_search
{
  "query": {
    "common": {
      "body": {
        "query": "nelly the elephant as a cartoon",
        "cutoff_frequency": 0.001,
        "low_freq_operator": "and"
      }
    }
  }
}

其中高頻詞是theaas,低頻詞是nellyelephantcartoon,上面的搜索大致等價(jià)于下面的查詢

GET /_search
{
  "query": {
    "bool": {
      "must": [
        {"term": {"body": "nelly"}},
        {"term": {"body": "elephant"}},
        {"term": {"body": "cartoon"}}
      ],
      "should": [
        {"term": {"body": "the"}},
        {"term": {"body": "as"}},
        {"term": {"body": "a"}}
      ]
    }
  }
}

但是第一個(gè)查詢的效率要優(yōu)于第二個(gè),因?yàn)閏ommon語(yǔ)句有性能上的優(yōu)化,只有重要的token匹配之后的文檔,才會(huì)在不重要的文檔的查詢時(shí)候計(jì)算_score;不重要的token在查詢的時(shí)候不會(huì)計(jì)算_score

query_string

輸入一個(gè)查詢語(yǔ)句,返回和這個(gè)查詢語(yǔ)句匹配的所有的文檔。

這個(gè)查詢語(yǔ)句不是簡(jiǎn)單的檢索詞,而是包含特定語(yǔ)法的的搜索語(yǔ)句,里面包含操作符比如ANDOR,在進(jìn)行查詢之前會(huì)被一個(gè)語(yǔ)法解析器解析,轉(zhuǎn)化成可以執(zhí)行的搜索語(yǔ)句進(jìn)行搜索。用戶可以生成一個(gè)特別復(fù)雜的查詢語(yǔ)句,里面可能包含通配符、多字段匹配等等。在搜索之前ES會(huì)檢查查詢語(yǔ)句的語(yǔ)法,如果有語(yǔ)法錯(cuò)誤會(huì)直接報(bào)錯(cuò)。

GET /_search
{
  "query": {
    "query_string": {
      "default_field": "name",
      "query": "acchu AND nagesh"
    }
  }
}

上面的查詢會(huì)匹配所有的同時(shí)包含acchunagesh的結(jié)果。簡(jiǎn)化一下可以這樣寫(xiě):

GET /_search
{
  "query": {
    "query_string": {
      "query": "name: acchu AND nagesh"
    }
  }
}

query_string里面還支持更加復(fù)雜的寫(xiě)法:

  • name: acchu nagesh:查詢name包含acchunagesh其中的任意一個(gè)
  • book.\*:(quick OR brown)book的任何子字段比如book.titlebook.content,包含quick或者brown
  • _exists_: titletitle字段包含非null
  • name: acch*:通配符,匹配任何acch開(kāi)頭的字段
  • name:/joh?n(ath[oa]n)/:正則表達(dá)式,需要把內(nèi)容放到兩個(gè)斜杠/中間
  • name: acch~:模糊匹配,默認(rèn)編輯距離為2,不過(guò)80%的情況編輯距離為1就能解決問(wèn)題name: acch~1
  • count:[1 TO 5]:范圍查詢,或者count: >10

下面的查詢?cè)试S匹配多個(gè)字段,字段之間時(shí)OR的關(guān)系

GET /_search
{
  "query": {
    "query_string": {
      "fields": [
        "name",
        "intro"
      ],
      "query": "nagesh"
    }
  }
}

simple_query_string

和上面的query_string類(lèi)似,但是使用了更加簡(jiǎn)單的語(yǔ)法。使用了下面的操作符:

  • +表示AND操作
  • |表示OR操作
  • -表示否定
  • "用于圈定一個(gè)短語(yǔ)
  • *放在token的后面表示前綴匹配
  • ()表示優(yōu)先級(jí)
  • ~N放在token后面表示模糊查詢的最大編輯距離fuzziness
  • ~N放在phrase后面表示模糊匹配短語(yǔ)的slop
GET /_search
{
  "query": {
    "simple_query_string": {
      "query": "acch* + foll~2 + -Karen",
      "fields": [
        "intro"
      ]
    }
  }
}

上面的搜索相當(dāng)于搜索包含前綴為acch的、和foll編輯距離最大是2的并且不包含Karen的字段,這樣的語(yǔ)句會(huì)匹配call me acchu或者acchu follow me

總結(jié)

Elasticsearch提供了強(qiáng)大的搜索功能,使用query匹配可以進(jìn)行相關(guān)性的計(jì)算排序但是filter可能更加適用于大多數(shù)的過(guò)濾查詢的情況,如果用戶對(duì)于標(biāo)準(zhǔn)解析器不太滿意可以自定義解析器或者第三方解析器比如支持中文的IK解析器。

在進(jìn)行搜索的時(shí)候一定要注意搜索keywordtext字段時(shí)候的區(qū)別,使用term相關(guān)的查詢只能匹配單個(gè)的token但是使用text相關(guān)的搜索可以利用前面的term搜索進(jìn)行組合查詢,text搜索更加靈活強(qiáng)大,但是性能相對(duì)差一點(diǎn)。

參考

什么是相關(guān)性? ElasticSearch 使用教程之_score(評(píng)分)介紹 Full text queries Term-level queries Elasticsearch query performance using filter query Unicode Text Segmentation 短詞匹配 Top hits query with same score?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容