Elasticsearch系列---實(shí)戰(zhàn)搜索語法

概要

本篇介紹Query DSL的語法案例,查詢語句的調(diào)試,以及排序的相關(guān)內(nèi)容砂豌。

基本語法

空查詢

最簡單的搜索命令厢岂,不指定索引和類型的空搜索,它將返回集群下所有索引的所有文檔(默認(rèn)顯示10條):

GET /_search
{}
搜索多個索引
GET /index1,index2/_doc/_search
{}
指定分頁搜索
GET /_search
{
  "from": 0,
  "size": 10
}
get帶request body

HTTP協(xié)議阳距,GET請求帶body是不規(guī)范的做法塔粒,但由于ES搜索的復(fù)雜性焕济,加上HTTP協(xié)議GET/POST方法表述的語義泻仙,GET更適合用來表述查詢的動作榔幸,雖然不規(guī)范燕侠,但還是這么用了。現(xiàn)在大多數(shù)瀏覽器也支持GET+request body嘲玫,如果遇到不支持的击纬,換成POST即可嘿歌。了解一下就行馍管,不用太慌張郭赐。

查詢表達(dá)式Query DSL

Query DSL是一種非常靈活、可讀性高的查詢語言确沸,body為JSON格式捌锭,絕大部分功能都可以用它來展現(xiàn),并且這種查詢語句更純粹张惹,讓學(xué)習(xí)者更專注于本身的功能舀锨,避免Client API的干擾岭洲。

上一節(jié)的空查詢宛逗,等價于這個:

GET /_search
{
    "query": {
        "match_all": {}
    }
}
基本語法
# 查詢語句結(jié)構(gòu)
{
    QUERY_NAME: {
        ARGUMENT: VALUE,
        ARGUMENT: VALUE,...
    }
}

# 針對某個字段的查詢
{
    QUERY_NAME: {
        FIELD_NAME: {
            ARGUMENT: VALUE,
            ARGUMENT: VALUE,...
        }
    }
}
合并查詢語句

再復(fù)雜的查詢語句,也是由一個一個的查詢條件疊加而成的盾剩,查詢語句有兩種形式:

  • 葉子語句:單個條件組成的語句雷激,如match語句,類似mysql的"id = 1"這種告私。
  • 復(fù)合語句:有多個條件屎暇,需要合并在一起才能組成一個完整的語句,需要使用bool進(jìn)行組合驻粟,里面的條件可以用must必須匹配根悼、must not必須不匹配、should可以匹配修飾蜀撑,也可以包含過濾器filter挤巡。類似mysql的"(status = 1 && language != 'french' && (author = 'John' || author = 'Tom'))"這種。

舉個例子:

{
    "bool": {
        "must":     { "match": { "status": 1 }},
        "must_not": { "match": { "language":  "french" }},
        "should":   { "match": { "author": "John Tom" }},
        "filter":   { "range": { "length" : { "gt" : 30 }} }
    }
}

復(fù)合語句可以嵌套酷麦,來實(shí)現(xiàn)更復(fù)雜的查詢需求矿卑,在上面的例子上簡單延伸一下:

"bool": {
        "must":     { "match": { "status": 1 }},
        "must_not": { "match": { "language":  "french" }},
        "should":   [
            {"match": { "author": "John Tom" }},
            {"bool": {
                "must":     { "match": { "name": "friend" }},
                "must_not": { "match": { "content":  "star" }}
            }}
        ],
        "filter":   { "range": { "length" : { "gt" : 30 }} }
    }
復(fù)合語句相關(guān)性分?jǐn)?shù)計算

每一個子查詢都獨(dú)自地計算文檔的相關(guān)性得分。一旦他們的得分被計算出來沃饶,bool 查詢就將這些得分進(jìn)行合并并且返回一個代表整個布爾操作的得分母廷,得分高的顯示在前面轻黑,filter內(nèi)的條件不參與分?jǐn)?shù)計算。

過濾器filter

我們還是以英文兒歌的索引為案例琴昆,看一個搜索需求:歌詞內(nèi)容包含friend氓鄙,同時歌長大于30秒的記錄

GET /music/children/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "content": "friend"
          }
        }
      ],
      "filter": {
        "range": {
          "length": {
            "gte": 30
          }
        }
      }
    }
  }
}

filter與query

  • 過濾情況filtering context

僅按照搜索條件把需要的數(shù)據(jù)篩選出來,不計算相關(guān)度分?jǐn)?shù)业舍。

  • 查詢情況query context

匹配條件的數(shù)據(jù)玖详,會根據(jù)搜索條件的相關(guān)度,計算每個document的分?jǐn)?shù)勤讽,然后按照分?jǐn)?shù)進(jìn)行排序蟋座,這個才是全文搜索的情況。

性能差異

filter只做過濾脚牍,不作排序向臀,并且會緩存結(jié)果到內(nèi)存中,性能非常高诸狭。
query匹配條件券膀,要做評分,沒有緩存驯遇,性能要低一些芹彬。

應(yīng)用場景

filter一個非常重要的作用就是減少不相關(guān)數(shù)據(jù)對query的影響,提升query的性能叉庐,二者常常搭配在一起使用舒帮。
組合使用的時候,把期望符合條件的document的搜索條件放在query里陡叠,把要濾掉的條件放在filter里玩郊。

constant_score查詢

如果一個查詢只有filter過濾條件,可以用constant_score來替代bool查詢枉阵,這樣的查詢語句更簡潔译红、更清晰,只是沒有評分兴溜,示例如下:

GET /music/children/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": { "content": "gymbo"}
      }
    }
  }
}

filter內(nèi)不支持terms語法侦厚,注意一下。

最常用的查詢

再復(fù)雜的查詢語句拙徽,也是由最基礎(chǔ)的查詢變化而來的刨沦,而最常用的查詢其實(shí)也就那么幾個。

  1. match_all查詢

查詢簡單的匹配所有文檔

GET /_search
{
    "query": {
        "match_all": {}
    }
}
  1. match查詢

無論是全文搜索還是精確查詢斋攀,match查詢是最基本的標(biāo)準(zhǔn)

# 全文搜索例子
{ "match": { "content": "loves smile" }}

# 精確搜索
{ "match": { "likes":    15           }}
{ "match": { "date":   "2019-12-05" }}
{ "match": { "isOwner": true         }}
{ "match": { "keyword":    "love you"  }}

對于精確值的查詢已卷,我們可以使用filter來替代,filter有緩存的效果淳蔼。

  1. multi_match查詢

可以在多個字段上執(zhí)行相同的match查詢

{
    "multi_match": {
        "query":    "my sunshine",
        "fields":   [ "name", "content" ]
    }
}
  1. range查詢

查詢指定區(qū)間內(nèi)的數(shù)字或時間侧蘸,query和filter都支持裁眯,一般是filter用得多,允許的操作符如下:

  • gt 大于
  • gte 大于或等于
  • lt 小于
  • lte 小于或等于
{
    "range": {
        "length": {
            "gte":  45,
            "lt":   60
        }
    }
}
  1. term查詢

用于精確值匹配讳癌,精確值可以是數(shù)字穿稳,日期,boolean或keyword類型的字符串

{ "term": { "likes":    15           }}
{ "term": { "date":   "2019-12-05" }}
{ "term": { "isOwner": true         }}
{ "term": { "keyword":    "love you"  }}

建立索引時mapping設(shè)置為not_analyzed時晌坤,match等同于term逢艘,用得多的是match和range。

  1. terms查詢

跟term類似骤菠,只是允許一次指定多個值進(jìn)行匹配它改,只要有任何一個匹配上,都滿足條件

{ "terms": { "content": [ "love", "gymbo", "sunshine" ] }}

查詢語句調(diào)試

復(fù)雜的查詢語句商乎,可能會有幾百行央拖,可以先使用調(diào)試工具檢測一下查詢語句,定位不合法的搜索及原因鹉戚,完整語法如下:

GET /index/type/_validate/query?explain
{
  "query": {
    ...
  }
}

explain參數(shù)可以提供更詳細(xì)的查詢不合法的信息鲜戒,便于問題定位。寫一個錯誤的例子抹凳,比如使用中文標(biāo)點(diǎn)符號:

GET /music/children/_validate/query?explain
{
  "query": {
    "terms": { "content“: [ "love", "gymbo", "sunshine" ] }
  }
}

錯誤提示如下:

{
  "valid": false,
  "error": """
ParsingException[Failed to parse]; nested: JsonParseException[Unexpected character ('l' (code 108)): was expecting a colon to separate field name and value
 at [Source: org.elasticsearch.transport.netty4.ByteBufStreamInput@5e57280e; line: 3, column: 33]];; com.fasterxml.jackson.core.JsonParseException: Unexpected character ('l' (code 108)): was expecting a colon to separate field name and value
 at [Source: org.elasticsearch.transport.netty4.ByteBufStreamInput@5e57280e; line: 3, column: 33]
"""
}

valid關(guān)鍵字遏餐,true為驗(yàn)證通過,false為不通過赢底,如上提示信息失都,會指明3行33列錯誤,原因是使用了中文的引號颖系。將語法修正后嗅剖,得到的正確響應(yīng)如下:

{
  "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
  },
  "valid": true,
  "explanations": [
    {
      "index": "music",
      "valid": true,
      "explanation": "+content:(gymbo love sunshine) #*:*"
    }
  ]
}

排序

查詢請求得到的結(jié)果,默認(rèn)排序是相關(guān)性得分降序嘁扼。如果我們只使用filter過濾,符合filter條件的文檔黔攒,評分都是一樣的(bool的filter得分是null趁啸,constant_score得分是1),結(jié)果文檔還是隨機(jī)返回督惰,顯然這樣的排序不符合我們的預(yù)期不傅。

sort排序規(guī)則

為此,我們可以使用sort屬性赏胚,對文檔進(jìn)行排序访娶,sort的用法與mysql如出一轍,示例如下:

GET /music/children/_search
{
  "query": {
    "bool": {
        "filter":   { "range": { "length" : { "gt" : 30 }} }
    }
  },
  "sort": [
    {
      "length": {
        "order": "desc"
      }
    }
  ]
}

sort內(nèi)可以同時指定多個排序字段觉阅,一旦使用sort排序后崖疤,_score得分將變成null秘车,因?yàn)槲覀冎付伺判蛞?guī)則,_score沒有實(shí)際意義了劫哼,就不用耗費(fèi)精力再去計算它叮趴。

字符串排序問題

我們知道text類型的字段,會有關(guān)鍵詞分詞處理权烧,對這樣的字段進(jìn)行排序眯亦,結(jié)果往往都不準(zhǔn)確,6.x版本以后的text類型般码,會再自動建立一個keyword類型的字段妻率,這個字段是不分詞的,所以這樣就有了分工板祝,text類型的負(fù)責(zé)搜索舌涨,keyword類型則負(fù)責(zé)排序。
我們回顧一下music索引的mapping信息(節(jié)選):

{
  "music": {
    "mappings": {
      "children": {
        "properties": {
          "content": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          }
        }
      }
    }
  }
}

例如name字段扔字,有一個text類型的囊嘉,里面fields還有一個類型為keyword,名稱也為keyword的字段革为,所以在排序的場景中扭粱,我們應(yīng)該使用name.keyword,示例如下:

GET /music/children/_search
{
  "sort": [
    {
      "name.keyword": {
        "order": "asc"
      }
    }
  ]
}

小結(jié)

本篇介紹Query DSL的語法及基礎(chǔ)實(shí)戰(zhàn)內(nèi)容震檩,順帶點(diǎn)了一下filter與query的區(qū)別琢蛤,面對復(fù)雜查詢語句時,建議先用驗(yàn)證工具進(jìn)行排查抛虏,最后介紹了一下排序方面的知識博其,基礎(chǔ)語法、上機(jī)案例多實(shí)踐即可迂猴。

專注Java高并發(fā)慕淡、分布式架構(gòu),更多技術(shù)干貨分享與心得沸毁,請關(guān)注公眾號:Java架構(gòu)社區(qū)


Java架構(gòu)社區(qū)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末峰髓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子息尺,更是在濱河造成了極大的恐慌携兵,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搂誉,死亡現(xiàn)場離奇詭異徐紧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門并级,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拂檩,“玉大人,你說我怎么就攤上這事死遭」慊郑” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵呀潭,是天一觀的道長钉迷。 經(jīng)常有香客問我,道長钠署,這世上最難降的妖魔是什么糠聪? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮谐鼎,結(jié)果婚禮上舰蟆,老公的妹妹穿的比我還像新娘。我一直安慰自己狸棍,他們只是感情好身害,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著草戈,像睡著了一般塌鸯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唐片,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天丙猬,我揣著相機(jī)與錄音,去河邊找鬼费韭。 笑死茧球,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的星持。 我是一名探鬼主播抢埋,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼钉汗!你這毒婦竟也來了羹令?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤损痰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后酒来,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卢未,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辽社。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伟墙。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖滴铅,靈堂內(nèi)的尸體忽然破棺而出戳葵,到底是詐尸還是另有隱情,我是刑警寧澤汉匙,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布拱烁,位于F島的核電站,受9級特大地震影響噩翠,放射性物質(zhì)發(fā)生泄漏戏自。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一伤锚、第九天 我趴在偏房一處隱蔽的房頂上張望擅笔。 院中可真熱鬧,春花似錦屯援、人聲如沸猛们。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽弯淘。三九已至,卻和暖如春徘铝,著一層夾襖步出監(jiān)牢的瞬間耳胎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工惕它, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留怕午,地道東北人。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓淹魄,卻偏偏與公主長得像郁惜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子甲锡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容