搜索機制
搜索的流程圖如下:
1,文檔寫入ES的時候,ES中會存儲兩份數據。一份是文檔的原始數據,即_source中存儲的信息。另一份是通過分詞、過濾等一系列動作之后生成的倒排索引文件,該文件記錄了詞項和文檔的對應關系。
2,用戶對文檔進行搜索,ES接受到用戶的關鍵詞之后到倒排索引中取查找,通過倒排記錄表查找到關鍵詞對應的文檔,然后在通過評分、高亮、排序等處理,返回給用戶。
3,搜索機制解決的是相關度的問題。用戶輸入一個查詢,ES通過計算關鍵詞和文檔之間的相關度,按照評分排序后返回相關度最高的文檔給用戶。
搜索過程
索引的創建、字段映射、文檔的導入等工作參考前文,此處略過。
ES中有了數據就可以進行下一步的搜索了。
ES中的restful的查詢語句要封裝成json對象的形式,稱為查詢DSL語句。
例如查看books索引的全部數據的DSL語句如下:
get books/_search
{
? ? "query":{
? ? ? ? "match_all":{}
}
}
match_all query 會返回所有的文檔,文檔的評分都是1??梢院唽憺?/p>
get books/_search
下面以term query(精確匹配) 為例子進行講解。
term 查詢用來查詢包含指定詞項的文檔。term 查詢不被解析,只有查詢項和文檔中的詞精確匹配到該文檔才會被索引到。應用場景為查詢需要精確匹配的需求。
例如查詢title中含有“思想”的文檔,DSL語句如下:
GET books/_search
{
"from":"0" //從哪條開始
"size":"100" //返回的條數”
"_source":["title","price"], //指定返回的字段,如果不指定,默認返回所有的字段
"min_score":"0.6" //最下評分過濾機制,只有評分大于0.6的文檔才會返回。
"query":{
????????"term":{"title":"思想"}
}
//高亮查詢關鍵字
"highlight":{
"fields":{
"title":{}
}
}
}
全文索引
match query
match query 會對查詢語句進行分詞,分詞后查詢語句中的任何一個詞被匹配到,那么文檔就會被索引到。如果想匹配所有關鍵詞的文檔,可以用and關鍵字連接。例如:
GET books/_search
{
"query":{
? ? "match":{
? ? ? ? "title":{
? ? ? ? ? ? "query":"java"
? ? ? ? ? ? "operator":"and"
}
}
}
}
match一phrase query
match-phrase query 查詢首先會把查詢語句分詞,分詞器可以自定義。同時文檔需要滿足一下兩個條件文檔才會被索引到
1,分詞后的字段要全部出現在目標字段中
2,字段中的詞項順序需要一致
match_phrase_prefix query
multi一match query
multi一match query? 是match的升級版,用于搜索多個字段。例如查詢語句為:“Java編程”,
查詢域為title和description。查詢DSL語句如下:
GET books/_search
"query":{
? ? multi-match:{
? ? ? ? query:"Java編程"
? ? ? ? //指定查詢的字段,*_name 為通配符的形式
? ? ? ? fields:["title","description","*_name"]
}
}
common_terms query
common terms query 是一種在不犧牲性能的情況下替代停用詞提高搜索準確率和召回率
的方案。
query_string query
query string query 是與Lucene 查詢語句的語法結合非常緊密的一種查詢,允許在一個查
詢語句中使用多個特殊條件關鍵字( 如:AND|OR|NOT ) 對多個字段進行查詢,建議熟悉
Lucene 查詢語法的用戶去使用。
詞項查詢
term query? ?
上文講過了,不在贅述
terms 查詢
如下,查詢北京和西安的數據
{
? "query": {
? ? "terms": {
? ? ? "city": [
? ? ? ? "北京",
? ? ? ? "西安"
? ? ? ]
? ? }
? }
}
range query
range 用于檢索某一個范圍內的文檔。比如說價格50到100的書籍信息等。range有四個參數
gt:大于(>),查詢范圍的最小值,不包含臨界值
gte: 大于等于(>=)。查詢范圍最小值,包含臨界值。
lt: 小于(<)。查詢范圍的上界,不包含臨界值。
lte:小于等于(<=)
查詢價格50到100的書籍
GET books/_search
{
query:{
? ? "range":{
? ? ? ? "price":{
? ? ? ? ? ? "gt":50,
? ? ? ? ? ? "lte":100
}
}
}
}
exists query
返回字段中至少有一個非空值的文檔。如:
{
? "query": {
? ? "exists": {
? ? ? "field": "ownerName"
? ? }
? }
}
返回結果ownerName不為空的文檔。
prefix query
prefix 查詢用于查詢某個字段中以給定前綴開始的文檔,比如查詢title 中含有以java 為前
綴的關鍵詞的文檔,那么含有java javascript javaee 等所有以java 開頭關鍵詞的文檔都會被
匹配。如
{
? "query": {
? ? "prefix": {
? ? ? "title": "Java"
? ? }
? }
}
wildcard query
通配符查詢。支持單通配符和多通配符的形式。?用來一個字符,*用來匹配一個或者是多個字符。如:
{
? "query": {
? ? "wildcard": {
? ? ? "title": "Java*"
? ? }
? }
}
regexp query
正則匹配。例如需要匹配以W 開頭緊跟著數字的郵政編碼:
{
? "query": {
? ? "regexp": {
? ? ? "postcode": ""W[0-9].*"
? ? }
? }
}
fuzzy query
編輯距離又稱Levenshtein 距離,是指兩個字串之間,由一個轉成另一個所需的最少編輯
操作次數。許可的編輯操作包括將一個字符替換成另一個字符,插入一個字符,刪除一個字符。
fuzzy 查詢就是通過計算詞項與文檔的編輯距離來得到結果的,但是使用fozzy 查詢需要消耗
的資源比較大,查詢效率不高,適用于需要模糊查詢的場景。舉例如下,用戶在輸入查詢關鍵
詞時不小心把“javascript ” 拼成“javascritp ” ,在存在拼寫錯誤的情況下使用模糊查詢仍然可
以搜索到含有“javascript ” 的文檔,查詢語句如下:
{
? "query": {
? ? "fuzzy?": {
? ? ? "title": "javascript?"
? ? }
? }
}
type query
type query 用于查詢具有指定類型的文檔。例如查詢Elasticsearch 中type 為IT 的文檔,查
詢語句如下:
{
? "query": {
? ? "type": {
? ? ? "value": "IT"
? ? }
? }
}
ids query
ids query 用于查詢具有指定id 的文檔。。例如,查詢類型為IT, id 為1 、3 、5
的文檔,查詢語句如下:
{
? "query": {
? ? "ids": {
? ? ? "type": "IT",
? ? ? ? "values":["1","3","5"]
? ? }
? }
}
復合查詢
復合查詢就是把一些簡單查詢組合在一起實現更復雜的查詢需求,除此之外復合查詢還
可以控制另外一個查詢的行為。
constan_score
constant score query 可以包裝一個其他類型的查詢,并返回匹配過濾器中的查詢條件且具
有相同評分的文檔。下面的查詢語句會返回title 字段中含有關鍵詞“java ” 的文檔,所有文檔
的評分都是1.2:
{
? "query": {
? ? "constantscore": {
? ? ? ? "filter":{
? ? ? ? ? ? "term":{"title":"java"}
? ? ? ? ?}
? ? ? ? "boost":"1.2"
? ? ? ?}
? ?}
}
bool
bool查詢可以把任意多個簡單查詢組合在一起。使用must、should、must_not、filter選項來表達各個簡單查詢之間的邏輯組合關系。每個選項都可以出現0次或者是多次。
must:文檔必須匹配must選項下的查詢條件。相當于邏輯運算的and
should:文檔可以匹配should選項下的查詢條件也可以不匹配。相當于邏輯運算的or。
must_not:于must相反。匹配到must_not查詢條件的文檔不會被返回。相當于邏輯運算的not
filter:功能和must類似。區別在于filter只起到過濾的作用,不對文檔進行評分。
假設要查詢title 中包含關鍵詞java, 并且price 不能高于70, description 可以包含也可以
不包含虛擬機的書籍, 構造bool 查詢語句如下:
GET books/ search
"query": {
? ? ? ?"bool": {
????????????"minimum—should一match": 1,}
????????"must": {
????????????"match": { "title": "java"}}
????????"should": [
????????????????{"match": { "description": " 虛擬機" }}]
? ? ? ? must_not": {
????????"range": {"price": {"gte": 70}}
????}
}
dis_max
dis max與bool query 有一定聯系也有一定區別,dis max query 支持多并發查詢。
可返回與任意查詢條件子句匹配的任何文檔類型。與bool 查詢可以將所有匹配查詢的分數相
結合使的方式不同,dis 查詢只使用最佳匹配查詢條件的分數
fonction_score
fonction_score query 可以修改查詢的文檔得分,這個查詢在有些情況下非常有用,比如通
過評分函數計算文檔得分代價較高,可以改用過濾器加自定義評分函數的方式來取代傳統的評
分方式。
使用fiinction score query , 用戶需要定義一個查詢和一至多個評分函數,評分函數會對查
詢到的每個文檔分別計算得分。
boosting
boosting 查詢用于需要對兩個查詢的評分進行調整的場景,boosting 查詢會把兩個查詢封
裝在一起并降低其中一個查詢的評分
indices
indices query 適用于需要在多個索引之間進行查詢的場景,它允許指定一個索引名字列表
和內部查詢。indices query 中有query 和no match query 兩部分,query 中用于搜索指定索引
列表中的文檔,no_match_query 中的查詢條件用于搜索指定索引列表之外的文檔。
嵌套查詢
在Elasticsearch 這樣的分布式系統中執行全SQL 風格的連接查詢代價昂貴,是不可行的。
相應地,為了實現水平規模地擴展,Elasticsearch 提供了以下兩種形式的join:
nested query (嵌套查詢)
文檔中可能包含嵌套類型的字段,這些字段用來索引一些數組對象,每個對象都可以作為
一條獨立的文檔被查詢出來。
has child query (有子查詢)和has_parent query (有父查詢)
父子關系可以存在單個的索引的兩個類型的文檔之間。has_child 查詢將返回其子文檔能滿
足特定查詢的父文檔,而has_parent 則返回其父文檔能滿足特定查詢的子文檔。
has_child
現在有兩個索引:員工employee和城市(branch)。類似于數據的兩張表。在ES中,員工是child type ,城市是parent type 。需要在映射中聲明如下:
PUT /company
"mappings": {
????????"branch": {},
????????"employee” {"_parent": { "type": "branch"}}
}
通過子文檔查詢父文檔需要使用has_child查詢。例如,搜索1980 年以后出生的員工所在的
城市。
GET company/branch/_search
"query": {
????"has_child": {
????"type": "employee",
????"query": {
????????"range": {"birthday": {"gte": "1980-01-01’ }}
has_parent
通過父文檔查詢子文檔使用has_parent 查詢。比如,搜索哪些employee 工作在UK, 查詢
命令如下:
GET /company/employee/_search
"query": {
????"has_parent": {
????????"paren_type": "branch",
????????"query": {"match": {"country": "UK"}}
}
位置查詢
Elasticsearch 可以對地理位置點geo_point 類型和地理位置形狀geo_shape 類型的數據進行
搜索。
geo_distance、geo_boundin_box、geo_polygon、geo_shape等。需要的時候可以具體的查詢API。
特殊查詢
more_like_this
more_like_this query 可以查詢和提供文本類似的文檔,通常用于近似文本的推薦等場景。
script
Elasticsearch 支持使用腳本進行查詢。
搜索高亮(略)
搜索排序
默認排序
Elasticsearch 是按照查詢和文檔的相關度進行排序的,默認按評分降序排序。
對于match_all query 而言,由于只返回所有文檔,不需要評分,文檔的順序為添加文檔的
順序。如果需要改變match_all query 的文檔返回順序,可以對_doc 進行排序。例如,返回最
后添加的那條文檔, 可以對_doc 降序排序,設置返回文檔條數為1 。
多字段排序
如按照價格降序排序,在根據年份升序排序。
GET books/ search
"sort": [
????{"price": { "order": "desc"} },
????{"year": {"order": "asc"}}]
分片影響評分
ES是在每一個分片上進行單獨評分的,分片的數量會影響到評分的結果。。同時,分詞器也會
影響評分,原因是使用不同的分詞器會使倒排索引中的詞項數發生改變,最終影響評分。
示例是一個SQL和SDL對應的關系
SELECT
? COUNT(1)
FROM
table
WHERE order_type = 'GUARANTEE'
? AND STATUS IN (
? ? 'FAILED',
? ? 'SUCCEEDED',
? ? 'REFUNDED',
? ? 'UNTESTED',
? ? 'UNTRANSFERED',
? ? 'TRANSFERED',
? ? 'SETTLED',
? ? 'APPLYREFUND'
? )
? AND? id = 306472658
? AND time >= "2019-01-01 00:00:00"
? AND time <= "2019-03-31 23:59:59"
SDL:
{
? "bool" : {
? ? "must" : [
? ? ? {
? ? ? ? "term" : {
? ? ? ? ? "id" : {
? ? ? ? ? ? "value" : "306472658",
? ? ? ? ? ? "boost" : 1.0
? ? ? ? ? }
? ? ? ? }
? ? ? },
? ? ? {
? ? ? ? "term" : {
? ? ? ? ? "order_type" : {
? ? ? ? ? ? "value" : "GUARANTEE",
? ? ? ? ? ? "boost" : 1.0
? ? ? ? ? }
? ? ? ? }
? ? ? },
? ? ? {
? ? ? ? "range" : {
? ? ? ? ? "time" : {
? ? ? ? ? ? "from" : "20190101000000",
? ? ? ? ? ? "to" : null,
? ? ? ? ? ? "include_lower" : true,
? ? ? ? ? ? "include_upper" : true,
? ? ? ? ? ? "boost" : 1.0
? ? ? ? ? }
? ? ? ? }
? ? ? },
? ? ? {
? ? ? ? "range" : {
? ? ? ? ? "time" : {
? ? ? ? ? ? "from" : null,
? ? ? ? ? ? "to" : "20190331235959",
? ? ? ? ? ? "include_lower" : true,
? ? ? ? ? ? "include_upper" : true,
? ? ? ? ? ? "boost" : 1.0
? ? ? ? ? }
? ? ? ? }
? ? ? },
? ? ? {
? ? ? ? "terms" : {
? ? ? ? ? "status" : [
? ? ? ? ? ? "FAILED",
? ? ? ? ? ? "SUCCEEDED",
? ? ? ? ? ? "REFUNDED",
? ? ? ? ? ? "UNTESTED",
? ? ? ? ? ? "UNTRANSFERED",
? ? ? ? ? ? "TRANSFERED",
? ? ? ? ? ? "SETTLED",
? ? ? ? ? ? "APPLYREFUND"
? ? ? ? ? ],
? ? ? ? ? "boost" : 1.0
? ? ? ? }
? ? ? }
? ? ],
? ? "disable_coord" : false,
? ? "adjust_pure_negative" : true,
? ? "boost" : 1.0
? }
}