[Elasticsearch] 全文搜索 (一) - 基础概念和match查询
全文搜索(Full Text Search)
現在我們已經討論了搜索結構化數據的一些簡單用例,是時候開始探索全文搜索了 - 如何在全文字段中搜索來找到最相關的文檔。
對于全文搜索而言,最重要的兩個方面是:
相關度(Relevance)
查詢的結果按照它們對查詢本身的相關度進行排序的能力,相關度可以通過TF/IDF,參見什么是相關度,地理位置的鄰近程度(Proximity to a Geo-location),模糊相似性(Fuzzy Similarity)或者其它算法進行計算。
解析(Analysis)
解析用來將一塊文本轉換成單獨的,規范化的詞條(Tokens),參見解析和解析器(Analysis and Analyzers),用來完成:(a)倒排索引(Inverted Index)的創建;(b)倒排索引的查詢。
一旦我們開始討論相關度或者解析,也就意味著我們踏入了查詢(Query)的領域,而不再是過濾器(Filter)。
?
?
基于詞條(Term-based)和全文(Full-text)
?
盡管所有的查詢都會執行某種程度的相關度計算,并不是所有的查詢都存在解析階段。除了諸如bool或者function_score這類完全不對文本進行操作的特殊查詢外,對于文本的查詢可以被劃分兩個種類:
基于詞條的查詢(Term-based Queries)
類似term和fuzzy的查詢是不含有解析階段的低級查詢(Low-level Queries)。它們在單一詞條上進行操作。一個針對詞條Foo的term查詢會在倒排索引中尋找該詞條的精確匹配(Exact term),然后對每一份含有該詞條的文檔通過TF/IDF進行相關度_score的計算。
尤其需要記住的是term查詢只會在倒排索引中尋找該詞條的精確匹配 - 它不會匹配諸如foo或者FOO這樣的變體。它不在意詞條是如何被保存到索引中。如果你索引了["Foo", "Bar"]到一個not_analyzed字段中,或者將Foo Bar索引到一個使用whitespace解析器的解析字段(Analyzed Field)中,它們都會在倒排索引中得到兩個詞條:"Foo"以及"Bar"。
全文查詢(Full-text Queries)
類似match或者query_string這樣的查詢是高級查詢(High-level Queries),它們能夠理解一個字段的映射:
- 如果你使用它們去查詢一個date或者integer字段,它們會將查詢字符串分別當做日期或者整型數。
- 如果你查詢一個精確值(not_analyzed)字符串字段,它們會將整個查詢字符串當做一個單獨的詞條。
- 但是如果你查詢了一個全文字段(analyzed),它們會首先將查詢字符串傳入到合適的解析器,用來得到需要查詢的詞條列表。
一旦查詢得到了一個詞條列表,它就會使用列表中的每個詞條來執行合適的低級查詢,然后將得到的結果進行合并,最終產生每份文檔的相關度分值。
我們會在后續章節中詳細討論這個過程。
在很少的情況下,你才需要直接使用基于詞條的查詢(Term-based Queries)。通常你需要查詢的是全文,而不是獨立的詞條,而這個工作通過高級的全文查詢來完成會更加容易(在內部它們最終還是使用的基于詞條的低級查詢)。
如果你發現你確實需要在一個not_analyzed字段上查詢一個精確值,那么考慮一下你是否真的需要使用查詢,而不是使用過濾器。
單詞條查詢通常都代表了一個二元的yes|no問題,這類問題通常使用過濾器進行表達更合適,因此它們也能夠得益于過濾器緩存(Filter Caching):
GET /_search {"query": {"filtered": { "filter": { "term": { "gender": "female" } } } } }?
?
match查詢
?
在你需要對任何字段進行查詢時,match查詢應該是你的首選。它是一個高級全文查詢,意味著它知道如何處理全文字段(Full-text,?analyzed)和精確值字段(Exact-value,not_analyzed)。
即便如此,match查詢的主要使用場景仍然是全文搜索。讓我們通過一個簡單的例子來看看全文搜索時如何工作的。
索引一些數據
首先,我們會創建一個新的索引并通過bulk?API索引一些文檔:
DELETE /my_index PUT /my_index { "settings": { "number_of_shards": 1 }} POST /my_index/my_type/_bulk { "index": { "_id": 1 }} { "title": "The quick brown fox" } { "index": { "_id": 2 }} { "title": "The quick brown fox jumps over the lazy dog" } { "index": { "_id": 3 }} { "title": "The quick brown fox jumps over the quick dog" } { "index": { "_id": 4 }} { "title": "Brown fox brown dog" }注意到以上在創建索引時,我們設置了number_of_shards為1:在稍后的相關度壞掉了(Relevance is broken)一節中,我們會解釋為何這里創建了一個只有一個主分片(Primary shard)的索引。
單詞查詢(Single word query)
第一個例子我們會解釋在使用match查詢在一個全文字段中搜索一個單詞時,會發生什么:
GET /my_index/my_type/_search {"query": {"match": { "title": "QUICK!" } } }ES會按照如下的方式執行上面的match查詢:
檢查字段類型
title字段是一個全文字符串字段(analyzed),意味著查詢字符串也需要被分析。
解析查詢字符串
查詢字符串"QUICK!"會被傳入到標準解析器中,得到的結果是單一詞條"quick"。因為我們得到的只有一個詞條,match查詢會使用一個term低級查詢來執行查詢。
找到匹配的文檔
term查詢會在倒排索引中查詢"quick",然后獲取到含有該詞條的文檔列表,在這個例子中,文檔1,2,3會被返回。
對每份文檔打分
term查詢會為每份匹配的文檔計算其相關度分值_score,該分值通過綜合考慮詞條頻度(Term Frequency)("quick"在匹配的每份文檔的title字段中出現的頻繁程度),倒排頻度(Inverted Document Frequency)("quick"在整個索引中的所有文檔的title字段中的出現程度),以及每個字段的長度(較短的字段會被認為相關度更高)來得到。參考什么是相關度(What is Relevance?)
這個過程會給我們下面的結果(有省略):
"hits": [{"_id": "1", "_score": 0.5, "_source": { "title": "The quick brown fox" } }, { "_id": "3", "_score": 0.44194174, "_source": { "title": "The quick brown fox jumps over the quick dog" } }, { "_id": "2", "_score": 0.3125, "_source": { "title": "The quick brown fox jumps over the lazy dog" } } ]文檔1最相關,因為它的title字段短,意味著quick在它所表達的內容中占比較大。 文檔3比文檔2的相關度更高,因為quick出現了兩次。
總結
以上是生活随笔為你收集整理的[Elasticsearch] 全文搜索 (一) - 基础概念和match查询的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Webpack 4x 之路 ( 五 )
- 下一篇: Android adb命令,linux中