es使用與原理4 -- phrase match ,slop近似匹配,搜索推薦等等

短語(yǔ)搜索和近似搜索
"match": { 
"content": "java spark" 
}

match query,只能搜索到包含java和spark的document,但是不知道java和spark是不是離的很近
包含java或包含spark,或包含java和spark的doc,都會(huì)被返回回來(lái)。我們其實(shí)并不知道哪個(gè)doc,java和spark距離的比較近。如果我們就是希望搜索java spark,中間不能插入任何其他的字符 或者指定中間只能隔num個(gè)字符,那這個(gè)時(shí)候match去做全文檢索,能搞定我們的需求嗎?答案是,搞不定。
兩個(gè)需求:
1、java spark,就靠在一起,中間不能插入任何其他字符,就要搜索出來(lái)這種doc
2、java spark,但是要求,java和spark兩個(gè)單詞靠的越近,doc的分?jǐn)?shù)越高,排名越靠前

phrase match 就是短語(yǔ)匹配,將短語(yǔ)作為一個(gè)整體去查找。

GET /forum/article/_search
{
    "query": {
        "match_phrase": {
            "content": "java spark"
        }
    }
}

3、分詞后的臨時(shí) position

GET _analyze
{
  "text": "hello world, java spark",
  "analyzer": "standard"
}

//返回結(jié)果
{
  "tokens": [
    {
      "token": "hello",
      "start_offset": 0,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "world",
      "start_offset": 6,
      "end_offset": 11,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "java",
      "start_offset": 13,
      "end_offset": 17,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "spark",
      "start_offset": 18,
      "end_offset": 23,
      "type": "<ALPHANUM>",
      "position": 3
    }
  ]
}

從結(jié)果中,我們可以看出 hello的臨時(shí)位置是0,world, java spark 的位置依次是 1,2,3

4、短語(yǔ)搜索的原理(近似搜索的原理也是這樣的)
hello world, java spark doc1
hi, spark java doc2

hello doc1(0)
wolrd doc1(1)
java doc1(2) doc2(2)
spark doc1(3) doc2(1)

java spark --> match phrase

java spark --> java和spark

java --> doc1(2) doc2(2)
spark --> doc1(3) doc2(1)

要找到每個(gè)term都在的一個(gè)共有的那些doc,就是要求一個(gè)doc,必須包含每個(gè)term,才能拿出來(lái)繼續(xù)計(jì)算

doc1 --> java和spark --> spark position恰巧比java大1 --> java的position是2,spark的position是3,恰好滿足條件 ,doc1符合條件

doc2 --> java和spark --> java position是2,spark position是1,spark position比java position小1,而不是大1 --> 光是position就不滿足,那么doc2不匹配
類似的 近似搜索原理也是這樣的

近似匹配:slop參數(shù)實(shí)現(xiàn)近似匹配
GET /forum/article/_search
{
    "query": {
        "match_phrase": {
            "content": {
                "query": "java spark",
                "slop":  3
            }
        }
    }
}
GET /forum/article/_search
{
  "query": {
    "match_phrase": {
      "content": {
        "query": "data spark",
        "slop": 5
      }
    }
  }
}

slop的含義是什么? 搜索文本,中的幾個(gè)term,要經(jīng)過(guò)幾次移動(dòng)才能與一個(gè)document匹配,這個(gè)移動(dòng)的次數(shù),就是slop。簡(jiǎn)單的說(shuō)就是 java 和 spark之間的距離 或者java和spark交換后的距離+2(這里的2是交換所需要的步數(shù)),slop搜索下,關(guān)鍵詞離的越近,relevance score就會(huì)越高。

混合使用match和近似匹配實(shí)現(xiàn)召回率與精準(zhǔn)度的平衡

比如你搜索一個(gè)java spark,含有java,或者含有是spark,或者同時(shí)含有,并且 盡可能讓包含java spark,或者是java和spark離的很近的doc,排在最前面,同時(shí)提供了召回率還兼顧了精準(zhǔn)率。

直接用match_phrase短語(yǔ)搜索,會(huì)導(dǎo)致必須有term都在doc field中出現(xiàn),而且距離在slop限定范圍內(nèi),才能匹配上

此時(shí)可以用bool組合match query和match_phrase query一起,來(lái)實(shí)現(xiàn)上述效果

GET /forum/article/_search
{
  "query": {
    "bool": {
      "must": {
        "match": { 
          "title": {
            "query":   "java spark" --> java或spark或java spark,java和spark靠前,但是沒法區(qū)分java和spark的距離,也許java和spark靠的很近,但是沒法排在最前面
          }
        }
      },
      "should": {
        "match_phrase": { --> 在slop以內(nèi),如果java spark能匹配上一個(gè)doc,那么就會(huì)對(duì)doc貢獻(xiàn)自己的relevance score,如果java和spark靠的越近,那么就分?jǐn)?shù)越高
          "title": {
            "query": "java spark",
            "slop":  50
          }
        }
      }
    }
  }
}
使用rescoring機(jī)制優(yōu)化近似匹配搜索的性能

match --> 只要簡(jiǎn)單的匹配到了一個(gè)term,就可以理解將term對(duì)應(yīng)的doc作為結(jié)果返回,掃描倒排索引,掃描到了就ok

phrase match --> 首先掃描到所有term的doc list; 找到包含所有term的doc list; 然后對(duì)每個(gè)doc都計(jì)算每個(gè)term的position,是否符合指定的范圍; slop,需要進(jìn)行復(fù)雜的運(yùn)算,來(lái)判斷能否通過(guò)slop移動(dòng),匹配一個(gè)doc

match query的性能比phrase match和proximity match(有slop)要高很多。因?yàn)楹髢烧叨家?jì)算position的距離。
match query比phrase match的性能要高10倍,比proximity match的性能要高20倍。

默認(rèn)情況下,match也許匹配了1000個(gè)doc,proximity match全都需要對(duì)每個(gè)doc進(jìn)行一遍運(yùn)算,判斷能否slop移動(dòng)匹配上,然后去貢獻(xiàn)自己的分?jǐn)?shù)
但是很多情況下,match出來(lái)也許1000個(gè)doc,其實(shí)用戶大部分情況下是分頁(yè)查詢的,所以可能最多只會(huì)看前幾頁(yè),比如一頁(yè)是10條,最多也許就看5頁(yè),就是50條
proximity match只要對(duì)前50個(gè)doc進(jìn)行slop移動(dòng)去匹配,去貢獻(xiàn)自己的分?jǐn)?shù)即可,不需要對(duì)全部1000個(gè)doc都去進(jìn)行計(jì)算和貢獻(xiàn)分?jǐn)?shù)

match:1000個(gè)doc,其實(shí)這時(shí)候每個(gè)doc都有一個(gè)分?jǐn)?shù)了; proximity match,前50個(gè)doc,進(jìn)行rescore,重打分,即可; 讓前50個(gè)doc,term舉例越近的,排在越前面

GET /forum/article/_search 
{
  "query": {
    "match": {
      "content": "java spark"
    }
  },
  "rescore": {
    "window_size": 50,
    "query": {
      "rescore_query": {
        "match_phrase": {
          "content": {
            "query": "java spark",
            "slop": 50
          }
        }
      }
    }
  }
}
前綴搜索、通配符搜索、正則搜索

1、前綴搜索

C3D0-KD345
C3K5-DFG65
C4I8-UI365

C3 --> 上面這兩個(gè)都搜索出來(lái) --> 根據(jù)字符串的前綴去搜索

不用帖子的案例背景,因?yàn)楸容^簡(jiǎn)單,直接用自己手動(dòng)建的新索引,給大家演示一下就可以了

PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "title": {
          "type": "keyword"
        }
      }
    }
  }
}

GET my_index/my_type/_search
{
  "query": {
    "prefix": {
      "title": {
        "value": "C3"
      }
    }
  }
}

2、前綴搜索的原理

prefix query不計(jì)算relevance score,與prefix filter唯一的區(qū)別就是,filter會(huì)cache bitset

掃描整個(gè)倒排索引,舉例說(shuō)明

前綴越短,要處理的doc越多,性能越差,盡可能用長(zhǎng)前綴搜索

前綴搜索,它是怎么執(zhí)行的?性能為什么差呢?

match

C3-D0-KD345
C3-K5-DFG65
C4-I8-UI365

全文檢索

每個(gè)字符串都需要被分詞

c3 doc1,doc2
d0
kd345
k5
dfg65
c4
i8
ui365

c3 --> 掃描倒排索引 --> 一旦掃描到c3,就可以停了,因?yàn)閹3的就2個(gè)doc,已經(jīng)找到了 --> 沒有必要繼續(xù)去搜索其他的term了

match性能往往是很高的

如果前綴搜索那么 (前綴搜索是不分詞的)

C3-D0-KD345
C3-K5-DFG65
C4-I8-UI365

c3 --> 先掃描到了C3-D0-KD345,很棒,找到了一個(gè)前綴帶c3的字符串 --> 還是要繼續(xù)搜索的,因?yàn)楹竺孢€有一個(gè)C3-K5-DFG65,也許還有其他很多的前綴帶c3的字符串 --> 你掃描到了一個(gè)前綴匹配的term,
不能停,必須繼續(xù)搜索 --> 直到掃描完整個(gè)的倒排索引,才能結(jié)束,所以prefix性能很差

3、通配符搜索

跟前綴搜索類似,功能更加強(qiáng)大

C3D0-KD345
C3K5-DFG65
C4I8-UI365

5字符-D任意個(gè)字符5

5?-*5:通配符去表達(dá)更加復(fù)雜的模糊搜索的語(yǔ)義

GET my_index/my_type/_search
{
  "query": {
    "wildcard": {
      "title": {
        "value": "C?K*5"
      }
    }
  }
}

?:任意字符
*:0個(gè)或任意多個(gè)字符

性能一樣差,必須掃描整個(gè)倒排索引,才ok

4、正則搜索

GET /my_index/my_type/_search 
{
  "query": {
    "regexp": {
      "title": "C[0-9].+"
    }
  }
}

C[0-9].+

[0-9]:指定范圍內(nèi)的數(shù)字
[a-z]:指定范圍內(nèi)的字母
.:一個(gè)字符
+:前面的正則表達(dá)式可以出現(xiàn)一次或多次

wildcard和regexp,與prefix原理一致,都會(huì)掃描整個(gè)索引,性能很差

match_phrase_prefix實(shí)現(xiàn)search-time搜索推薦

輸入 hello w ,會(huì)聯(lián)想到hello world,hello we,hello win,hello wind 等等
原理跟match_phrase類似,唯一的區(qū)別,就是把最后一個(gè)term作為前綴去搜索。
hello就是去進(jìn)行match,搜索對(duì)應(yīng)的doc
w,會(huì)作為前綴,去掃描整個(gè)倒排索引,找到所有w開頭的doc
然后找到所有doc中,即包含hello,又包含w開頭的字符的doc
根據(jù)你的slop去計(jì)算,看在slop范圍內(nèi),能不能讓hello w,正好跟doc中的hello和w開頭的單詞的position相匹配

也可以指定slop,但是只有最后一個(gè)term會(huì)作為前綴

max_expansions:指定prefix最多匹配多少個(gè)term,超過(guò)這個(gè)數(shù)量就不繼續(xù)匹配了,限定性能

默認(rèn)情況下,前綴要掃描所有的倒排索引中的term,去查找w打頭的單詞,但是這樣性能太差。可以用max_expansions限定,w前綴最多匹配多少個(gè)term,就不再繼續(xù)搜索倒排索引了。

盡量不要用,因?yàn)椋詈笠粋€(gè)前綴始終要去掃描大量的索引,性能可能會(huì)很差,可以使用ngram來(lái)實(shí)現(xiàn)

通過(guò)ngram分詞機(jī)制實(shí)現(xiàn)index-time搜索推薦

什么是ngram
quick,5種長(zhǎng)度下的ngram
ngram length=1,q u i c k
ngram length=2,qu ui ic ck
ngram length=3,qui uic ick
ngram length=4,quic uick
ngram length=5,quick

什么是edge ngram
quick,首字母后進(jìn)行ngram
q
qu
qui
quic
quick
-----------------------------------搜索推薦 未完。。。。。。。。。

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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