短語(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
-----------------------------------搜索推薦 未完。。。。。。。。。