Elasticsearch從入門到精通-05ES匹配查詢
??作者簡介:大家好,我是程序員行走的魚
?? 本篇主要介紹和大家一塊學習一下ES各種場景下的匹配查詢,有助于我們在項目中進行綜合使用
前提
創(chuàng)建索引并指定ik分詞器:
PUT /es_db
{
"settings": {
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
添加數據:
PUT /es_db/_doc/1
{
"name": "張三",
"sex": 1,
"age": 25,
"address": "廣州天河公園",
"remark": "java developer"
}
PUT /es_db/_doc/2
{
"name": "李四",
"sex": 1,
"age": 28,
"address": "廣州荔灣大廈",
"remark": "java assistant"
}
PUT /es_db/_doc/3
{
"name": "rod",
"sex": 0,
"age": 26,
"address": "廣州白云山公園",
"remark": "php developer"
}
PUT /es_db/_doc/4
{
"name": "admin",
"sex": 0,
"age": 22,
"address": "長沙橘子洲頭",
"remark": "python assistant"
}
PUT /es_db/_doc/5
{
"name": "小明",
"sex": 0,
"age": 19,
"address": "長沙岳麓山",
"remark": "java architect assistant"
}
案例1:字段包含關鍵詞中幾個
需要搜索的document中的remark字段包含java和developer詞組
operator實現
GET /es_db/_search
{
"query": {
"match": {
"remark": {
"query": "java developer",
"operator": "and"
}
}
}
}
上述語法中,如果將operator的值改為or。則與remark": "java developer"等價 。默認的ES執(zhí)行搜索的時候,operator就是or。如果在搜索的結果document中,需要remark字段中包含多個搜索詞條中的一定比例,可以使用下述語法實現搜索。其中minimum_should_match可以使用百分比或固定數字。百分比代表query搜索條件中詞條百分比,如果無法整除,向下匹配(如,query條件有3個單詞,如果使用百分比提供精準度計算,那么是無法除盡的,如果需要至少匹配兩個單詞,則需要用67%來進行描述。如果使用66%描述,ES則認為匹配一個單詞即可。)。固定數字代表query搜索條件中的詞條,至少需要匹配多少個。
minimum_should_match實現
完全匹配上:
GET /es_db/_search
{
"query": {
"match": {
"remark": {
"query": "java architect assistant",
"minimum_should_match": "100%"
}
}
}
}
至少匹配兩個:
GET /es_db/_search
{
"query": {
"match": {
"remark": {
"query": "java architect assistant",
"minimum_should_match": "68%"
}
}
}
}
bool+should實現
如果使用should+bool搜索的話,也可以控制搜索條件的匹配度。具體如下:下述案例代表搜索的document中的remark字段中,必須匹配java、developer、assistant三個詞條中的至少2個。
GET /es_db/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"remark": "java"
}
},
{
"match": {
"remark": "developer"
}
},
{
"match": {
"remark": "assistant"
}
}
],
"minimum_should_match": 2
}
}
}
match的底層轉換
其實在ES中,執(zhí)行match搜索的時候,ES底層通常都會對搜索條件進行底層轉換,來實現最終的搜索結果。如:
1.
GET /es_db/_search
{
"query": {
"match": {
"remark": "java developer"
}
}
}
轉化后:
//轉換后是:
GET /es_db/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"remark": "java"
}
},
{
"term": {
"remark": {
"value": "developer"
}
}
}
]
}
}
}
GET /es_db/_search
{
"query": {
"match": {
"remark": {
"query": "java developer",
"operator": "and"
}
}
}
}
轉換后:
GET /es_db/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"remark": "java"
}
},
{
"term": {
"remark": {
"value": "developer"
}
}
}
]
}
}
}
GET /es_db/_search
{
"query": {
"match": {
"remark": {
"query": "java architect assistant",
"minimum_should_match": "68%"
}
}
}
}
轉換后:
GET /es_db/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"remark": "java"
}
},
{
"term": {
"remark": "architect"
}
},
{
"term": {
"remark": "assistant"
}
}
],
"minimum_should_match": 2
}
}
}
建議,如果不怕麻煩,盡量使用轉換后的語法執(zhí)行搜索,效率更高。如果開發(fā)周期短,工作量大,使用簡化的寫法。
案例2:boost權重控制
一般用于搜索時相關度排序使用。如:電商中的綜合排序。將一個商品的銷量,廣告投放,評價值,庫存,單價比較綜合排序。在上述的排序元素中,廣告投放權重最高,庫存權重最低,這樣權重越大的排序越考前,我們以一下例子為例來詳細學習權重的作用。
搜索document中remark字段中包含java的數據,如果remark中包含developer或architect,則包含architect的document優(yōu)先顯示。(就是將architect數據匹配時的相關度分數增加)。
GET /es_db/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"remark": "java"
}
}
],
"should": [
{
"match": {
"remark": {
"query": "developer",
"boost": 1
}
}
},
{
"match": {
"remark": {
"query": "architect",
"boost": 3
}
}
}
]
}
}
}
在搜索所有remark包含java文檔中,我們設置包含architect的權重比包含developer權重大,所以排序后會出現以上的結果,如果我們把developer權重調為比architect權重大,那么developer的排序會比architect高
案例3:基于dis_max實現best fields策略進行多字段搜索
best fields策略: 搜索到的結果,應該是某一個field中匹配到了盡可能多的關鍵詞,被排在前面;而不是盡可能多的field匹配到了少數的關鍵詞,排在了前面.
dis_max語法:直接取多個query中,分數最高的那一個query的分數即可,下邊以案例解釋這句話的意思
數據準備
PUT /forum
{ "settings" : { "number_of_shards" : 1 }}
POST /forum/article/_bulk
{"index":{"_id":1}}
{"title":"this is java and elasticsearch blog","content":"i like to write best elasticsearch article"}
{"index":{"_id":2}}
{"title":"this is java blog","content":"i think java is the best programming language"}
{"index":{"_id":3}}
{"title":"this is elasticsearch blog","content":"i am only an elasticsearch beginner"}
{"index":{"_id":4}}
{"title":"this is java, elasticsearch, hadoop blog","content":"elasticsearch and hadoop are all very good solution, i am a beginner"}
{"index":{"_id":5}}
{"title":"this is spark blog","content":"spark is best big data solution based on scala ,an programming language similar to java"}
普通DSL:
GET /forum/article/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"title": "java solution"
}
},
{
"match": {
"content": "java solution"
}
}
],
"minimum_should_match": 1
}
}
}
來分析一下結果
計算每個document的relevance score:每個query的分數,乘以matched query數量,除以總query數量
算一下doc2的分數:
{ “match”: { “title”: “java solution” }}:針對文檔2,java匹配上了,可以得到一個分數 1.1
{ “match”: { “content”: “java solution” }}:針對文檔2,java匹配上了,可以得到一個分數 1.2
假設分數如下 , 所以是兩個分數加起來,比如說,1.1 + 1.2 = 2.3
matched query數量 = 2
總query數量 = 2
2.3 * 2 / 2 = 2.3 當前文檔獲取分數就是2.1
算一下doc5的分數
{ “match”: { “title”: “java solution” }}:針對文檔5,沒有關鍵字匹配,所以沒有分數
{ “match”: { “content”: “java solution” }}:針對文檔5,java匹配上了,可以得到一個分數2.3
所以說,只有一個query是有分數的,比如2.3 matched query數量 = 1 總query數量 = 2
2.3 * 1 / 2 = 1.15
doc5的分數 = 1.15 < doc2的分數 = 2.3
id=2的數據排在了前面,其實我們希望id=5的排在前面,畢竟id=5的數據 content字段既有java又有solution. 那看下dis_max吧
我們再來計算下兩個文檔的分數:
{ “match”: { “title”: “java solution” }}:針對文檔2,java匹配上了,可以得到一個分數1.1
{ “match”: { “content”: “java solution” }}:針對文檔2,java匹配上了,可以得到一個分數1.2
取最大分數,1.2
{ “match”: { “title”: “java solution” }}:針對文檔2,java匹配上了,可以得到一個分數0
{ “match”: { “content”: “java solution” }}:針對文檔2,java匹配上了,可以得到一個分數2.3
取最大分數,2.3
然后doc2的分數 = 1.2 < doc5的分數 = 2.3,所以doc5就可以排在更前面的地方.
dis_max優(yōu)點:精確匹配的數據可以盡可能的排列在最前端,且可以通過minimum_should_match來去除長尾數據,避免長尾數據字段對排序結果的影響
長尾數據比如說我們搜索4個關鍵詞,但很多文檔只匹配1個,也顯示出來了,這些文檔其實不是我們想要的,這時候我們就需要對dis_max改進一下:
比如我們查詢content中必須包含java solution的文檔
GET /forum/article/_search
{
"query": {
"dis_max": {
"queries": [
{
"match": {
"title": "java solution"
}
},
{
"match": {
"content": {
"query": "java solution",
"minimum_should_match": "100%"
}
}
}
]
}
}
}
案例4:基于tie_breaker參數優(yōu)化dis_max搜索效果
dis_max是將多個搜索query條件中相關度分數最高的用于結果排序,忽略其他query分數,在某些情況下,可能還需要其他query條件中的相關度介入最終的結果排序,這個時候可以使用tie_breaker參數來優(yōu)化dis_max搜索。tie_breaker參數代表的含義是:將其他query搜索條件的相關度分數乘以參數值,再參與到結果排序中。如果不定義此參數,相當于參數值為0。所以其他query條件的相關度分數被忽略。
GET /forum/article/_search
{
"query": {
"dis_max": {
"queries": [
{
"match": {
"title": "java beginner"
}
},
{
"match": {
"content":"java beginner"}
}
],
"tie_breaker": 0.5
}
}
}
可以明顯看出來,文檔5直接排序來到了最后一位,因為其他query也參與了計算,綜合算起來是score是低于其他文檔的。
案例5:使用multi_match簡化dis_max+tie_breaker
GET /forum/article/_search
{
"query": {
"multi_match": {
"query": "best java solution",
"fields": [
"title",
"content^2"#^n代表權重,說明如果content匹配到多個關鍵字的話權重高一點。
],
"type": "best_fields",
"tie_breaker": 0.5,
"minimum_should_match": "50%"
}
}
}
如果需要的結果是有特殊要求,如:hello world必須是一個完整的短語,不可分割;或document中的field內,包含的hello和world單詞,且兩個單詞之間離的越近,相關度分數越高。那么這種特殊要求的搜索就是近似搜索。包括hell搜索條件在hello world數據中搜索,包括h搜索提示等都數據近似搜索的一部分。下邊的案例都是近似匹配的情況
案例6:短語搜索
短語搜索。就是搜索條件不分詞,代表搜索條件不可分割。如果java assistant是一個不可分割的短語,我們可以使用前文學過的短語搜索match phrase來實現。語法如下:
GET _search
{
"query": {
"match_phrase": {
"remark": "java assistant"
}
}
}
ES是如何實現match phrase短語搜索的?其實在ES中,使用match phrase做搜索的時候,也是和match類似,首先對搜索條件進行分詞-analyze。將搜索條件拆分成java和assistant。既然是分詞后再搜索,ES是如何實現短語搜索的?
這里涉及到了倒排索引的建立過程。在倒排索引建立的時候,ES會先對document數據進行分詞,如:
從上述結果中,可以看到。ES在做分詞的時候,除了將數據切分外,還會保留一個position。position代表的是這個詞在整個數據中的下標。當ES執(zhí)行match phrase搜索的時候,首先將搜索條件hello world分詞為hello和world。然后在倒排索引中檢索數據,如果hello和world都在某個document的某個field出現時,那么檢查這兩個匹配到的單詞的position是否是連續(xù)的,如果是連續(xù)的,代表匹配成功,如果是不連續(xù)的,則匹配失敗。
案例7:match phrase優(yōu)化
在做搜索的時候,如果搜索參數是hello spark。而ES中存儲的數據是hello world, java spark。那么使用match phrase則無法搜索到。在這個時候,可以使用match來解決這個問題。但是,當我們需要在搜索的結果中,做一個特殊的要求:hello和spark兩個單詞距離越近,document在結果集合中排序越靠前,這個時候再使用match則未必能得到想要的結果.針對這種情況,在ES的搜索中,對match phrase提供了參數slop。slop代表match phrase短語搜索的時候,單詞最多移動多少次,可以實現數據匹配。在所有匹配結果中,多個單詞距離越近,相關度評分越高,排序越靠前。這種使用slop參數的match phrase搜索,就稱為近似匹配(proximity search)
舉例:
數據為:hello world, java spark
搜索為:match phrase : hello spark。
slop為: 3 (代表單詞最多移動3次。)
執(zhí)行短語搜索的時候,將條件hello spark分詞為hello和spark兩個單詞。并且連續(xù)。
接下來,可以根據slop參數執(zhí)行單詞的移動。
下標 : 0 1 2 3
doc : hello world java spark
搜索 : hello spark
移動1: hello spark
移動2: hello spark
匹配成功,不需要移動第三次即可匹配。
再如:
數據為: hello world, java spark
搜索為: match phrase : spark hello。
slop為: 5 (代表單詞最多移動5次。)
執(zhí)行短語搜索的時候,將條件hello spark分詞為hello和spark兩個單詞。并且連續(xù)。
接下來,可以根據slop參數執(zhí)行單詞的移動。
下標 : 0 1 2 3
doc : hello world java spark
搜索 : spark hello
移動1: spark/hello
移動2: hello spark
移動3: hello spark
移動4: hello spark
匹配成功,不需要移動第五次即可匹配。
如果當slop移動次數使用完畢,還沒有匹配成功,則無搜索結果。如果使用中文分詞,則移動次數更加復雜,因為中文詞語有重疊情況,很難計算具體次數,需要多次嘗試才行。
英語測試:
GET _analyze
{
"text": "hello world, java spark",
"analyzer": "standard"
}
POST /test_a/_doc/3
{
"f": "hello world, java spark"
}
GET /test_a/_search
{
"query": {
"match_phrase": {
"f": {
"query": "hello spark",
"slop": 2
}
}
}
}
中文測試:
DELETE /test_a
GET _analyze
{
"text": "中國,一個世界上最強的國",
"analyzer": "ik_max_word"
}
PUT /test_a
{
"settings": {
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
POST /test_a/_doc/3
{
"f": "中國,一個世界上最強的國家"
}
只移動五次:
移動100次:
案例8:前綴搜索
使用前綴匹配實現搜索能力。通常針對keyword類型字段,也就是不分詞的字段。
當然,我們也可以對非keywork類型的字段進行前綴搜索,只需要在字段后邊加上.keyword即可
GET /es_db/_search
{
"query": {
"prefix": {
"address.keyword": {
"value": "廣州"
}
}
}
}
前綴搜索效率比較低,前綴搜索不會計算相關度分數。前綴越短,效率越低。如果使用前綴搜索,建議使用長前綴。因為前綴搜索需要掃描完整的索引內容,所
以前綴越長,相對效率越高。
使用match和proximity search實現召回率和精準度平衡。
召回率:召回率就是搜索結果比率,如:索引A中有100個document,搜索時返回多少個document,就是召回率(recall)。
精準度:就是搜索結果的準確率,如:搜索條件為hello java,在搜索結果中盡可能讓短語匹配和hello java離的近的結果排序靠前,就是精準度
(precision)。
如果在搜索的時候,只使用match phrase語法,會導致召回率底下,因為搜索結果中必須包含短語(包括proximity search)。
如果在搜索的時候,只使用match語法,會導致精準度底下,因為搜索結果排序是根據相關度分數算法計算得到。
那么如果需要在結果中兼顧召回率和精準度的時候,就需要將match和proximity search混合使用,來得到搜索結果。
案例9:通配符搜索
ES中也有通配符,但是和java還有數據庫不太一樣,通配符可以在倒排索引中使用,也可以在keyword類型字段中使用。
常用通配符:
- ? : 一個任意字符
- * :0~n個任意字符
GET /es_db/_search
{
"query": {
"wildcard": {
"remark": {
"value": "d*"
}
}
}
}
性能很低,需要掃描完整的索引。
案例10:正則搜索
ES支持正則表達式,可以在倒排索引或keyword類型字段中使用。
常用符號:
-
[]
: 范圍,如: [0-9]是0~9的范圍數字 -
.
: 一個字符
+ - 前面的表達式可以出現多次。
GET /es_db/_search
{
"query": {
"regexp": {
"remark": {
"value": "[a-a].+"
}
}
}
}
性能很低,需要掃描完整索引。
案例11:即時搜索
搜索推薦: search as your type, 搜索提示。如:索引中有若干數據,以“hello”開頭,那么在輸入hello的時候,推薦相關信息。(類似百度輸入框)
其原理和match phrase類似,是先使用match匹配term數據(java),然后在指定的slop移動次數范圍內,前綴匹配(d),max_expansions是用于指定prefix
最多匹配多少個term(單詞),超過這個數量就不再匹配了。這種語法的限制是,只有最后一個term會執(zhí)行前綴搜索,執(zhí)行性能很差,畢竟最后一個term是需要掃描所有符合slop要求的倒排索引的term,因為效率較低,如果必須使用,則一定要使用參數max_expansions。
案例12:模糊搜索技術
搜索的時候,可能搜索條件文本輸入錯誤,如:hello world -> hello word。這種拼寫錯誤還是很常見的。fuzzy技術就是用于解決錯誤拼寫的(在英文中很有效,在中文中幾乎無效。),其中fuzziness代表value的值word可以修改多少個字母來進行拼寫錯誤的糾正(修改字母的數量包含字母變更,增加或減少字)。
GET /es_db/_search
{
"query": {
"fuzzy": {
"remark": {
"value": "jva",
"fuzziness": 1
}
}
}
}
案例13:高亮查詢
在搜索中,經常需要對搜索關鍵字做高亮顯示,高亮顯示也有其常用的參數,在這個案例中做一些常用參數的介紹。
highlight簡單使用
數據準備:
PUT /news_website
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"content": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
PUT /news_website/_doc/1
{
"title": "我的第一篇文章",
"content": "大家好,這是我寫的第一篇文章,特別喜歡這個文章門戶網站!?。?
}
高亮顯示:
GET /news_website/_doc/_search
{
"query": {
"match": {
"content": "文章"
}
},
"highlight": {
"fields": {
"content": {}
}
}
}
會變成紅色,所以說你的指定的field中,如果包含了那個搜索詞的話,就會在那個field的文本中,對搜索詞進行紅色的高亮顯示。
highlight中要高亮的字段和要查詢的字段是要進行一一匹配的,比如如果我們也想title中關鍵字做高亮顯示可以這樣做:
GET /news_website/_doc/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"title": "文章"
}
},
{
"match": {
"content": "文章"
}
}
]
}
},
"highlight": {
"fields": {
"title": {},
"content": {}
}
}
}
highlight類型
在es中支持好幾種highlight
- plain highlight(lucene highlight):默認
- posting highlight:性能比plain highlight要高,因為不需要重新對高亮文本進行分詞,對磁盤消耗低。
- fast vector highlight:對大field而言(大于1mb),性能更高
我們看看其他兩種的使用方式:
posting highlight
:在mappering中設置 "index_options": "offsets"
DELETE news_website
PUT /news_website
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"content": {
"type": "text",
"analyzer": "ik_max_word",
"index_options": "offsets"
}
}
}
}
PUT /news_website/_doc/1
{
"title": "我的第一篇文章",
"content": "大家好,這是我寫的第一篇文章,特別喜歡這個文章門戶網站?。?!"
}
GET /news_website/_doc/_search
{
"query": {
"match": {
"content": "文章"
}
},
"highlight": {
"fields": {
"content": {}
}
}
}
fast vector highlight
:在mappings中設置"term_vector": "with_positions_offsets"
DELETE /news_website
PUT /news_website
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"content": {
"type": "text",
"analyzer": "ik_max_word",
"term_vector": "with_positions_offsets"
}
}
}
}
指定highlight
當然,如果我們在映射中指定了highlight,但是我們想要查詢的時候也指定highlight,那么可以這樣查詢
GET /news_website/_doc/_search
{
"query": {
"match": {
"content": "文章"
}
},
"highlight": {
"fields": {
"content": {
"type": "plain"
}
}
}
}
設置highlight標簽
我們高亮的標簽默認是是標簽,我們可以通過屬性修改高亮標簽
-
pre_tags:
前置標簽 -
post_tags:
后置標簽
GET /news_website/_doc/_search
{
"query": {
"match": {
"content": "文章"
}
},
"highlight": {
"pre_tags": [
"<span color='red'>"
],
"post_tags": [
"</span>"
],
"fields": {
"content": {
"type": "plain"
}
}
}
}
##高亮片段fragment顯示
有時候如果我們的內容特別多,不可能所有關鍵字都高亮顯示,那么我們就可以指定片段數量和文本長度。
-
fragment_size:
一個Field的值,比如有長度是1萬,但是你不可能在頁面上顯示這么長,我們可以設置要顯示出來的fragment文本判斷的長度,默認是100 -
number_of_fragments:
可能你的高亮的fragment文本片段有多個片段,你可以指定就顯示幾個片段
GET /_search
{
"query": {
"match": {
"content": "文章"
}
},
"highlight": {
"fields": {
"content": {
"fragment_size": 10,
"number_of_fragments": 1
}
}
}
}
案例14:CrossFields 多字段搜索策略
數據準備:
POST /testcross/_bulk
{"index":{"_id": 1}}
{"empId" : "111","name" : "員工1","age" : 20,"sex" : "男","mobile" : "19000001111","salary":1333,"deptName" : "技術部","provice" : "湖北省","city":"武漢","area":"光谷大道","address":"湖北省武漢市洪山區(qū)光谷大廈","content" : "i like to write best elasticsearch article"}
{"index":{"_id": 2}}
{"empId" : "222","name" : "員工2","age" : 25,"sex" : "男","mobile" : "19000002222","salary":15963,"deptName" : "銷售部","provice" : "湖北省","city":"武漢","area":"江漢區(qū)","address" : "湖北省武漢市江漢路","content" : "i think java is the best programming language"}
{"index":{"_id": 3}}
{ "empId" : "333","name" : "員工3","age" : 30,"sex" : "男","mobile" : "19000003333","salary":20000,"deptName" : "技術部","provice" : "湖北省","city":"武漢","area":"經濟技術開發(fā)區(qū)","address" : "湖北省武漢市經濟開發(fā)區(qū)","content" : "i am only an elasticsearch beginner"}
{"index":{"_id": 4}}
{"empId" : "444","name" : "員工4","age" : 20,"sex" : "女","mobile" : "19000004444","salary":5600,"deptName" : "銷售部","provice" : "湖北省","city":"武漢","area":"沌口開發(fā)區(qū)","address" : "湖北省武漢市沌口開發(fā)區(qū)","content" : "elasticsearch and hadoop are all very good solution, i am a beginner"}
{"index":{"_id": 5}}
{ "empId" : "555","name" : "員工5","age" : 20,"sex" : "男","mobile" : "19000005555","salary":9665,"deptName" : "測試部","provice" : "湖北省","city":"高新開發(fā)區(qū)","area":"武漢","address" : "湖北省武漢市東湖隧道","content" : "spark is best big data solution based on scala ,an programming language similar to java"}
{"index":{"_id": 6}}
{"empId" : "666","name" : "員工6","age" : 30,"sex" : "女","mobile" : "19000006666","salary":30000,"deptName" : "技術部","provice" : "武漢市","city":"湖北省","area":"江漢區(qū)","address" : "湖北省武漢市江漢路","content" : "i like java developer"}
{"index":{"_id": 7}}
{"empId" : "777","name" : "員工7","age" : 60,"sex" : "女","mobile" : "19000007777","salary":52130,"deptName" : "測試部","provice" : "湖北省","city":"黃岡市","area":"邊城區(qū)","address" : "湖北省黃岡市邊城區(qū)","content" : "i like elasticsearch developer"}
{"index":{"_id": 8}}
{"empId" : "888","name" : "員工8","age" : 19,"sex" : "女","mobile" : "19000008888","salary":60000,"deptName" : "技術部","provice" : "湖北省","city":"武漢","area":"漢陽區(qū)","address" : "湖北省武漢市江漢大學","content" : "i like spark language"}
{"index":{"_id": 9}}
{"empId" : "999","name" : "員工9","age" : 40,"sex" : "男","mobile" : "19000009999","salary":23000,"deptName" : "銷售部","provice" : "河南省","city":"鄭州市","area":"二七區(qū)","address" : "河南省鄭州市鄭州大學","content" : "i like java developer"}
{"index":{"_id": 10}}
{"empId" : "101010","name" : "張湖北","age" : 35,"sex" : "男","mobile" : "19000001010","salary":18000,"deptName" : "測試部","provice" : "湖北省","city":"武漢","area":"高新開發(fā)區(qū)","address" : "湖北省武漢市東湖高新","content" : "i like java developer i also like elasticsearch"}
{"index":{"_id": 11}}
{"empId" : "111111","name" : "王河南","age" : 61,"sex" : "男","mobile" : "19000001011","salary":10000,"deptName" : "銷售部",,"provice" : "河南省","city":"開封市","area":"金明區(qū)","address" : "河南省開封市河南大學","content" : "i am not like java "}
{"index":{"_id": 12}}
{"empId" : "121212","name" : "張大學","age" : 26,"sex" : "女","mobile" : "19000001012","salary":1321,"deptName" : "測試部",,"provice" : "河南省","city":"開封市","area":"金明區(qū)","address" : "河南省開封市河南大學","content" : "i am java developer thing java is good"}
{"index":{"_id": 13}}
{"empId" : "131313","name" : "李江漢","age" : 36,"sex" : "男","mobile" : "19000001013","salary":1125,"deptName" : "銷售部","provice" : "河南省","city":"鄭州市","area":"二七區(qū)","address" : "河南省鄭州市二七區(qū)","content" : "i like java and java is very best i like it do you like java "}
{"index":{"_id": 14}}
{"empId" : "141414","name" : "王技術","age" : 45,"sex" : "女","mobile" : "19000001014","salary":6222,"deptName" : "測試部",,"provice" : "河南省","city":"鄭州市","area":"金水區(qū)","address" : "河南省鄭州市金水區(qū)","content" : "i like c++"}
{"index":{"_id": 15}}
{"empId" : "151515","name" : "張測試","age" : 18,"sex" : "男","mobile" : "19000001015","salary":20000,"deptName" : "技術部",,"provice" : "河南省","city":"鄭州市","area":"高新開發(fā)區(qū)","address" : "河南省鄭州高新開發(fā)區(qū)","content" : "i think spark is good"}
地址存儲的時候 不能直接存儲 ”湖北省武漢市東湖高新區(qū)“ 這樣的字符串,一個完整的地址需要用多個字段來唯一標識,一般存儲的時候 省/市/區(qū) 分別是"provice", “city”, "area"三個字段,那我搜尋湖北省武漢市江漢區(qū)這個完整地址 的時候,會如何查詢 provice=”湖北省“ , city=“武漢市” , area=“東湖高新” ?
我們可以使用most fields試下:
從結果來看:
- 不支持 operator=AND,沒有一個doc可以match到,因為你的關鍵字是分布在多個字段中的用了and就是 provice包含湖北省武漢市江漢區(qū)或者city包含這三個詞,后者 area包含這三個詞, 沒有一個doc能匹配,因為字段是打散的.
- 如果用 operator = OR 出來幾十條, 從語義上也是錯誤的, 會把所有包含湖北省,武漢市都搜出來,因為OR操作就是任一字段匹配,就會大量重復無用數據 比如出現 湖北省 XX市 XX區(qū) 的數據,甚至是 河南省 鄭州市 江漢區(qū)的類似數據.
- 搜索不準確,因為MostFields 會把多個詞計算權重后參與最終分計算,累加求和,這就導致如果 有個 鄭州市的江漢區(qū),他的權重較高,然后會影響到 湖北省武漢市東湖高 的排序,比他會優(yōu)先排序
也有人會想,我們直接使用組合查詢,provice、city、area三個進行must匹配不就可以查詢出來了嗎,就如下邊的效果:
get /testcross/_search
{
"query":{
"bool": {
"must": [
{
"match_phrase": {
"provice": "湖北省"
}
},
{
"match_phrase": {
"city": "武漢"
}
},
{
"match_phrase": {
"area": "高新開發(fā)區(qū)"
}
}
]
}
}
}
似乎沒什么問題, 但是正常我們的搜索,很多時候我是不知道他是具體什么字段的,比如我只知道 省/市/區(qū) 這三個ABC字段中包含了 湖北省,武漢,高新開發(fā)區(qū)的查詢字段, 就給我命中返回,我不關心哪個字段匹配上,只要三個字段都存在就行
到底是 A:湖北省 B:武漢 C:高新開發(fā)區(qū)
還是 A:武漢 B:湖北省,C:高新開發(fā)區(qū)
還是 A:湖北省, B:高新開發(fā)區(qū),C:武漢
所以And也是不滿足的
基于這種情況,我們可以使用詞條為中心的CrossFields 搜索,我們可以實現當我們不關系具體哪些字段,我們只需要將多個field的信息整合成一個作為唯一標識返回即可。
get /testcross/_search
{
"query":{
"multi_match": {
"query": "湖北省 武漢 高新開發(fā)區(qū)",
"fields": ["provice","city","area"],
"type": "cross_fields",
"operator": "and"
}
}
}
CrossFields 提高權重控制排名
可以查看剛才CrossFields的查詢結果
員工 | 分數 |
---|---|
員工10 | 8.200133 |
員工5 | 6.558913 |
如果 provice, city 及area 每個詞的權重不同, 比如想要把city權重放的更高點,讓權重優(yōu)先的更考前的返回,我們可以直接在fields中計入權重計算, 可以看到 city 被我改成了 city ^ 2 就是權重擴大 2倍,默認都是1倍
city:高新開發(fā)區(qū)的被提前了 ,因為 city:武漢 有很多個文檔,但是city:高新開發(fā)區(qū)的就只有幾個,所以 TFIDF模型認為 高新開發(fā)區(qū) 的權重在city字段上更有代表性,所以權重更大,這就影響了結果的排序.
案例15:CopyTo字段組合實現邏輯多字段搜索
場景:
淘寶中搜手機,點擊搜索,那么一個商品有很多屬性,比如 商品名稱,商品賣點,商品描述,商品評價等等等,那么如果搜索手機關鍵字,并且一個搜索條件都沒限定,那底層到底是在那幾個字段內進行匹配
如果僅用單一的字段比如名稱字段來匹配,很有可能不是用戶要的結果,如果使用全字段匹配?這樣明顯也不合適,比如商品價格,商品庫存,商品店鋪等,不包含手機信息,那么如何實現?
解決辦法:
- copy_to:就是將多個字段,復制到一個字段中,來實現多字段組合查詢,用于解決搜索條件默認字段信息
- copy_to需要在創(chuàng)建mapping結構的時候就指定 需要將當前字段 copy_to 到哪個字段上,默認搜索條件生效的字段就是你要復制到的字段
準備數據:
注意:
put /testcopy
PUT /testcopy/_mapping
{
"properties" : {
"address" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
},
"copy_to":"info"
},
"age" : {
"type" : "long"
},
"area" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"city" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"content" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"deptName" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
},
"copy_to":"info"
},
"empId" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"mobile" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
},
"copy_to":"info"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
},
"copy_to":"info"
},
"provice" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"salary" : {
"type" : "long"
},
"sex" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
POST /testcopy/_bulk
{"index":{"_id": 1}}
{"empId" : "111","name" : "員工1","age" : 20,"sex" : "男","mobile" : "19000001111","salary":1333,"deptName" : "技術部","provice" : "湖北省","city":"武漢","area":"光谷大道","address":"湖北省武漢市洪山區(qū)光谷大廈","content" : "i like to write best elasticsearch article"}
{"index":{"_id": 2}}
{"empId" : "222","name" : "員工2","age" : 25,"sex" : "男","mobile" : "19000002222","salary":15963,"deptName" : "銷售部","provice" : "湖北省","city":"武漢","area":"江漢區(qū)","address" : "湖北省武漢市江漢路","content" : "i think java is the best programming language"}
{"index":{"_id": 3}}
{ "empId" : "333","name" : "員工3","age" : 30,"sex" : "男","mobile" : "19000003333","salary":20000,"deptName" : "技術部","provice" : "湖北省","city":"武漢","area":"經濟技術開發(fā)區(qū)","address" : "湖北省武漢市經濟開發(fā)區(qū)","content" : "i am only an elasticsearch beginner"}
{"index":{"_id": 4}}
{"empId" : "444","name" : "員工4","age" : 20,"sex" : "女","mobile" : "19000004444","salary":5600,"deptName" : "銷售部","provice" : "湖北省","city":"武漢","area":"沌口開發(fā)區(qū)","address" : "湖北省武漢市沌口開發(fā)區(qū)","content" : "elasticsearch and hadoop are all very good solution, i am a beginner"}
{"index":{"_id": 5}}
{ "empId" : "555","name" : "員工5","age" : 20,"sex" : "男","mobile" : "19000005555","salary":9665,"deptName" : "測試部","provice" : "湖北省","city":"高新開發(fā)區(qū)","area":"武漢","address" : "湖北省武漢市東湖隧道","content" : "spark is best big data solution based on scala ,an programming language similar to java"}
{"index":{"_id": 6}}
{"empId" : "666","name" : "員工6","age" : 30,"sex" : "女","mobile" : "19000006666","salary":30000,"deptName" : "技術部","provice" : "武漢市","city":"湖北省","area":"江漢區(qū)","address" : "湖北省武漢市江漢路","content" : "i like java developer"}
{"index":{"_id": 7}}
{"empId" : "777","name" : "員工7","age" : 60,"sex" : "女","mobile" : "19000007777","salary":52130,"deptName" : "測試部","provice" : "湖北省","city":"黃岡市","area":"邊城區(qū)","address" : "湖北省黃岡市邊城區(qū)","content" : "i like elasticsearch developer"}
{"index":{"_id": 8}}
{"empId" : "888","name" : "員工8","age" : 19,"sex" : "女","mobile" : "19000008888","salary":60000,"deptName" : "技術部","provice" : "湖北省","city":"武漢","area":"漢陽區(qū)","address" : "湖北省武漢市江漢大學","content" : "i like spark language"}
{"index":{"_id": 9}}
{"empId" : "999","name" : "員工9","age" : 40,"sex" : "男","mobile" : "19000009999","salary":23000,"deptName" : "銷售部","provice" : "河南省","city":"鄭州市","area":"二七區(qū)","address" : "河南省鄭州市鄭州大學","content" : "i like java developer"}
{"index":{"_id": 10}}
{"empId" : "101010","name" : "張湖北","age" : 35,"sex" : "男","mobile" : "19000001010","salary":18000,"deptName" : "測試部","provice" : "湖北省","city":"武漢","area":"高新開發(fā)區(qū)","address" : "湖北省武漢市東湖高新","content" : "i like java developer i also like elasticsearch"}
{"index":{"_id": 11}}
{"empId" : "111111","name" : "王河南","age" : 61,"sex" : "男","mobile" : "19000001011","salary":10000,"deptName" : "銷售部",,"provice" : "河南省","city":"開封市","area":"金明區(qū)","address" : "河南省開封市河南大學","content" : "i am not like java "}
{"index":{"_id": 12}}
{"empId" : "121212","name" : "張大學","age" : 26,"sex" : "女","mobile" : "19000001012","salary":1321,"deptName" : "測試部",,"provice" : "河南省","city":"開封市","area":"金明區(qū)","address" : "河南省開封市河南大學","content" : "i am java developer thing java is good"}
{"index":{"_id": 13}}
{"empId" : "131313","name" : "李江漢","age" : 36,"sex" : "男","mobile" : "19000001013","salary":1125,"deptName" : "銷售部","provice" : "河南省","city":"鄭州市","area":"二七區(qū)","address" : "河南省鄭州市二七區(qū)","content" : "i like java and java is very best i like it do you like java "}
{"index":{"_id": 14}}
{"empId" : "141414","name" : "王技術","age" : 45,"sex" : "女","mobile" : "19000001014","salary":6222,"deptName" : "測試部",,"provice" : "河南省","city":"鄭州市","area":"金水區(qū)","address" : "河南省鄭州市金水區(qū)","content" : "i like c++"}
{"index":{"_id": 15}}
{"empId" : "151515","name" : "張測試","age" : 18,"sex" : "男","mobile" : "19000001015","salary":20000,"deptName" : "技術部",,"provice" : "河南省","city":"鄭州市","area":"高新開發(fā)區(qū)","address" : "河南省鄭州高新開發(fā)區(qū)","content" : "i think spark is good"}
我們搜索插入的數據是否正確及 info字段是否存在?
很顯然是不存在這個字段的,但是我們搜索的時候卻可以使用info字段取匹配
get /testcopy/_search
{
"query":{
"match_phrase": {
"info": "員工"
}
}
}
get /testcopy/_search
{
"query":{
"match_phrase": {
"info": "湖北"
}
}
}
至此 我們已經能夠利用 CopyTo字段組合來實現邏輯多字段搜索,可以看到存儲結構上并沒有 info字段,但是我們可以用copy_to把多個字段關聯(lián)起來實現多字段搜索.文章來源:http://www.zghlxwxcb.cn/news/detail-843787.html
loper thing java is good"}
{“index”:{“_id”: 13}}
{“empId” : “131313”,“name” : “李江漢”,“age” : 36,“sex” : “男”,“mobile” : “19000001013”,“salary”:1125,“deptName” : “銷售部”,“provice” : “河南省”,“city”:“鄭州市”,“area”:“二七區(qū)”,“address” : “河南省鄭州市二七區(qū)”,“content” : “i like java and java is very best i like it do you like java “}
{“index”:{”_id”: 14}}
{“empId” : “141414”,“name” : “王技術”,“age” : 45,“sex” : “女”,“mobile” : “19000001014”,“salary”:6222,“deptName” : “測試部”,“provice” : “河南省”,“city”:“鄭州市”,“area”:“金水區(qū)”,“address” : “河南省鄭州市金水區(qū)”,“content” : “i like c++”}
{“index”:{“_id”: 15}}
{“empId” : “151515”,“name” : “張測試”,“age” : 18,“sex” : “男”,“mobile” : “19000001015”,“salary”:20000,“deptName” : “技術部”,“provice” : “河南省”,“city”:“鄭州市”,“area”:“高新開發(fā)區(qū)”,“address” : “河南省鄭州高新開發(fā)區(qū)”,“content” : “i think spark is good”}
我們搜索插入的數據是否正確及 info字段是否存在?
[外鏈圖片轉存中...(img-byM2NlK2-1710668677317)]
很顯然是不存在這個字段的,但是我們搜索的時候卻可以使用info字段取匹配
```json
get /testcopy/_search
{
"query":{
"match_phrase": {
"info": "員工"
}
}
}
[外鏈圖片轉存中…(img-be8FlMn8-1710668677317)]
get /testcopy/_search
{
"query":{
"match_phrase": {
"info": "湖北"
}
}
}
[外鏈圖片轉存中…(img-BMqw1ekq-1710668677318)]
至此 我們已經能夠利用 CopyTo字段組合來實現邏輯多字段搜索,可以看到存儲結構上并沒有 info字段,但是我們可以用copy_to把多個字段關聯(lián)起來實現多字段搜索.
??至此本篇就結束了,下一篇將介紹ES統(tǒng)計查詢語法,希望和小伙伴們可以繼續(xù)堅持學習!文章來源地址http://www.zghlxwxcb.cn/news/detail-843787.html
到了這里,關于Elasticsearch從入門到精通-05ES匹配查詢的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!