1. 程式人生 > 其它 >Elasticsearch(es) 查詢語句語法詳解

Elasticsearch(es) 查詢語句語法詳解

Elasticsearch 查詢語句採用基於 RESTful 風格的介面封裝成 JSON 格式的物件,稱之為 Query DSL。Elasticsearch 查詢分類大致分為全文查詢詞項查詢複合查詢巢狀查詢位置查詢特殊查詢

 

Elasticsearch 查詢從機制分為兩種,一種是根據使用者輸入的查詢詞,通過排序模型計算文件與查詢詞之間的相關度,並根據評分高低排序返回;另一種是過濾機制,只根據過濾條件對文件進行過濾,不計算評分,速度相對較快。

 

全文查詢

 

es 全文查詢主要用於在全文欄位上,主要考慮查詢詞與文件的相關性(Relevance)。

 

match query

 

match query 用於搜尋單個欄位

,首先會針對查詢語句進行解析(經過 analyzer),主要是對查詢語句進行分詞,分詞後查詢語句的任何一個詞項被匹配,文件就會被搜到,預設情況下相當於對分詞後詞項進行 or 匹配操作。

 

GET article/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Elasticsearch 查詢優化"
      }
    }
  }
}

 

等同於 or 匹配操作,如下:

 

GET article/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Elasticsearch 查詢優化",
        "operator": "or"
      }
    }
  }
}

 

如果想查詢匹配所有關鍵詞的文件,可以用 and 操作符連線,如下:

 

GET article/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Elasticsearch 查詢優化",
        "operator": "and"
      }
    }
  }
}

 

match_phrase query

 

match_phrase query 首先會把 query 內容分詞,分詞器可以自定義,同時文件還要滿足以下兩個條件才會被搜尋到:

 

  1. 分詞後所有詞項都要出現在該欄位中(相當於 and 操作)
  2. 欄位中的詞項順序要一致

 

例如,有以下 3 個文件,使用 match_phrase 查詢 “what a wonderful life”,只有前兩個文件會被匹配:

 

PUT test_idx/test_tp/1
{ "desc": "what a wonderful life" }

PUT test_idx/test_tp/2
{ "desc": "what a life"}

PUT test_idx/test_tp/3
{ "desc": "life is what"}

GET test_idx/test_tp/_search
{
  "query": {
    "match_phrase": {
      "desc": "what life"
    }
  }
}

 

更多詳細內容在 match_phrase query 查詢

 

match_phrase_prefix query

 

match_phrase_prefix 和 match_phrase 類似,只不過 match_phrase_prefix 支援最後一個 term 的字首匹配。

 

GET test_idx/test_tp/_search
{
  "query": {
    "match_phrase_prefix": {
      "desc": "what li"
    }
  }
}

 

multi_match query

 

multi_match 是 match 的升級,用於搜尋多個欄位。查詢語句為 “java 程式設計”,查詢域為 title 和 description,查詢語句如下:

 

GET books/_search
{
  "query": {
    "multi_match": {
      "query": "java 程式設計",
      "fields": ["title", "description"]
    }
  }
}

 

multi_match 支援對要搜尋的欄位的名稱使用萬用字元,示例如下:

 

GET books/_search
{
  "query": {
    "multi_match": {
      "query": "java 程式設計",
      "fields": ["title", "*_name"]
    }
  }
}

 

同時,也可以用指數符指定搜尋欄位的權重。指定關鍵詞出現在 title 中的權重是出現在 description 欄位中的 3 倍,命令如下:

 

GET books/_search
{
  "query": {
    "multi_match": {
      "query": "java 程式設計",
      "fields": ["title^3", "description"]
    }
  }
}

 

更多詳細內容在 elasticsearch 的 multi_match 查詢

 

common_terms query

 

common_terms query 是一種在不犧牲效能的情況下替代停用詞提高搜尋準確率和召回率的方案。

 

查詢中的每個詞項都有一定的代價,以搜尋 “The brown fox” 為例,query 會被解析成三個詞項 “the”“brown” 和“fox”,每個詞項都會到索引中執行一次查詢。很顯然包含 “the” 的文件非常多,相比其他詞項,“the”的重要性會低很多。傳統的解決方案是把 “the” 當作停用詞處理,去除停用詞之後可以減少索引大小,同時在搜尋時減少對停用詞的收縮。

 

雖然停用詞對文件評分影響不大,但是當停用詞仍然有重要意義的時候,去除停用詞就不是完美的解決方案了。如果去除停用詞,就無法區分 “happy” 和“not happy”, “The”“To be or not to be”就不會在索引中存在,搜尋的準確率和召回率就會降低。

 

common_terms query 提供了一種解決方案,它把 query 分詞後的詞項分成重要詞項(低頻詞項)和不重要的詞項(高頻詞,也就是之前的停用詞)。在搜尋的時候,首先搜尋和重要詞項匹配的文件,這些文件是詞項出現較少並且詞項對其評分影響較大的文件。然後執行第二次查詢,搜尋對評分影響較小的高頻詞項,但是不計算所有文件的評分,而是隻計算第一次查詢已經匹配的文件得分。如果一個查詢中只包含高頻詞,那麼會通過 and 連線符執行一個單獨的查詢,換言之,會搜尋所有的詞項。

 

詞項是高頻詞還是低頻詞是通過 cutoff frequency 來設定閥值的,取值可以是絕對頻率(頻率大於 1)或者相對頻率(0~1)。common_terms query 最有趣之處在於它能自適應特定領域的停用詞,例如,在視訊託管網站上,諸如 “clip” 或“video”之類的高頻詞項將自動錶現為停用詞,無須保留手動列表。

 

例如,文件頻率高於 0.1% 的詞項將會被當作高頻詞項,詞頻之間可以用 low_freq_operator、high_freq_operator 引數連線。設定低頻詞操作符為 “and” 使所有的低頻詞都是必須搜尋的,示例程式碼如下:

 

GET books/_search
{
	"query": {
		"common": {
			"body": {
				"query": "nelly the elephant as a cartoon",
				"cutoff_frequency": 0.001,
				"low_freq_operator": "and"
			}
		}
	}
}

 

上述操作等價於:

 

GET books/_search
{
	"query": {
		"bool": {
			"must": [
			  { "term": { "body": "nelly" } },
			  { "term": { "body": "elephant" } },
			  { "term": { "body": "cartoon" } }
			],
			"should": [
			  { "term": { "body": "the" } },
			  { "term": { "body": "as" } },
			  { "term": { "body": "a" } }
			]
		}
	}
}

 

query_string query

 

query_string query 是與 Lucene 查詢語句的語法結合非常緊密的一種查詢,允許在一個查詢語句中使用多個特殊條件關鍵字(如:AND | OR | NOT)對多個欄位進行查詢,建議熟悉 Lucene 查詢語法的使用者去使用。

 

simple_query_string

 

simple_query_string 是一種適合直接暴露給使用者,並且具有非常完善的查詢語法的查詢語句,接受 Lucene 查詢語法,解析過程中發生錯誤不會丟擲異常。例子如下:

 

GET books/_search
{
  "query": {
    "simple_query_string": {
      "query": "\"fried eggs\" +(eggplant | potato) -frittata",
      "analyzer": "snowball",
      "fields": ["body^5", "_all"],
      "default_operator": "and"
    }
  }
}

 

詞項查詢

 

全文查詢在執行查詢之前會分析查詢字串,詞項查詢時對倒排索引中儲存的詞項進行精確匹配操作。詞項級別的查詢通常用於結構化資料,如數字、日期和列舉型別。

 

term query

 

term 查詢用來查詢指定欄位中包含給定單詞的文件,term 查詢不被解析,只有查詢詞和文件中的詞精確匹配才會被搜尋到,應用場景為查詢人名、地名等需要精準匹配的需求。比如,查詢 title 欄位中含有關鍵詞 “思想” 的書籍,查詢命令如下:

 

GET books/_search
{
  "query": {
    "term": {
      "title": "思想"
    }
  }
}

 

避免 term 查詢對 text 欄位使用查詢。

預設情況下,Elasticsearch 針對 text 欄位的值進行解析分詞,這會使查詢 text 欄位值的精確匹配變得困難。

要搜尋 text 欄位值,需改用 match 查詢。

 

terms query

 

terms 查詢是 term 查詢的升級,可以用來查詢文件中包含多個詞的文件。比如,想查詢 title 欄位中包含關鍵詞 “java” 或 “python” 的文件,構造查詢語句如下:

 

{
  "query": {
    "terms": {
      "title": ["java", "python"]
    }
  }
}

 

range query

 

range query 即範圍查詢,用於匹配在某一範圍內的數值型、日期型別或者字串型欄位的文件,比如搜尋哪些書籍的價格在 50 到 100 之間、哪些書籍的出版時間在 2015 年到 2019 年之間。使用 range 查詢只能查詢一個欄位,不能作用在多個欄位上

 

range 查詢支援的引數有以下幾種:

 

  • gt 大於,查詢範圍的最小值,也就是下界,但是不包含臨界值。
  • gte 大於等於,和 gt 的區別在於包含臨界值。
  • lt 小於,查詢範圍的最大值,也就是上界,但是不包含臨界值。
  • lte 小於等於,和 lt 的區別在於包含臨界值。

 

例如,想要查詢價格大於 50,小於等於 70 的書籍,即 50 < price <= 70,構造查詢語句如下:

 

GET bookes/_search
{
  "query": {
    "range": {
      "price": {
        "gt": 50,
        "lte": 70
      }
    }
  }
}

 

查詢出版日期在 2015 年 1 月 1 日和 2019 年 12 月 31 之間的書籍,對 publish_time 欄位進行範圍查詢,命令如下:

 

{
  "query": {
    "range": {
      "publish_ time": {
        "gte": "2015-01-01",
        "lte": "2019-12-31",
        "format": "yyyy-MM-dd"
      }
    }
  }
}

 

有關 es 更詳細的 range query 可以檢視 Elasticsearch(es)範圍查詢及 Lucene 底層原理實現

 

exists query

 

exists 查詢會返回欄位中至少有一個非空值的文件。

 

舉例說明如下:

 

{
  "query": {
    "exists": {
      "field": "user"
    }
  }
}

 

以下文件會匹配上面的查詢:

 

  • { "user" : "jane" } 有 user 欄位,且不為空。
  • { "user" : "" } 有 user 欄位,值為空字串。
  • { "user" : "-" } 有 user 欄位,值不為空。
  • { "user" : [ "jane" ] } 有 user 欄位,值不為空。
  • { "user" : [ "jane", null ] } 有 user 欄位,至少一個值不為空即可。

 

下面的文件都不會被匹配:

 

  • { "user" : null } 雖然有 user 欄位,但是值為空。
  • { "user" : [] } 雖然有 user 欄位,但是值為空。
  • { "user" : [null] } 雖然有 user 欄位,但是值為空。
  • { "foo" : "bar" } 沒有 user 欄位。

 

prefix query

 

prefix 查詢用於查詢某個欄位中以給定字首開始的文件,比如查詢 title 中含有以 java 為字首的關鍵詞的文件,那麼含有 java、javascript、javaee 等所有以 java 開頭關鍵詞的文件都會被匹配。查詢 description 欄位中包含有以 win 為字首的關鍵詞的文件,查詢語句如下:

 

GET books/_search
{
	"query": {
		"prefix": {
			"description": "win"
		}
	}
}

 

wildcard query

 

wildcard query 中文譯為萬用字元查詢,支援單字元萬用字元和多字元萬用字元,? 用來匹配一個任意字元,* 用來匹配零個或者多個字元。

 

以 H?tland 為例,Hatland、Hbtland 等都可以匹配,但是不能匹配 Htland,? 只能代表一位。H*tland 可以匹配 Htland、Habctland 等,* 可以代表 0 至多個字元。和 prefix 查詢一樣,wildcard 查詢的查詢效能也不是很高,需要消耗較多的 CPU 資源。

 

下面舉一個 wildcard 查詢的例子,假設需要找某一作者寫的書,但是忘記了作者名字的全稱,只記住了前兩個字,那麼就可以使用萬用字元查詢,查詢語句如下:

 

GET books/_search
{
  "query": {
    "wildcard": {
      "author": "李永*"
    }
  }
}

 

regexp query

 

Elasticsearch 也支援正則表示式查詢,通過 regexp query 可以查詢指定欄位包含與指定正則表示式匹配的文件。可以代表任意字元, “a.c.e” 和 “ab...” 都可以匹配 “abcde”,a{3}b{3}、a{2,3}b{2,4}、a{2,}{2,} 都可以匹配字串 “aaabbb”。

 

例如需要匹配以 W 開頭緊跟著數字的郵政編碼,使用正則表示式查詢構造查詢語句如下:

 

GET books/_search
{
	"query": {
		"regexp": {
			"postcode": "W[0-9].+"
		}
	}
}

 

fuzzy query

 

編輯距離又稱 Levenshtein 距離,是指兩個字串之間,由一個轉成另一個所需的最少編輯操作次數。許可的編輯操作包括將一個字元替換成另一個字元,插入一個字元,刪除一個字元。fuzzy 查詢就是通過計算詞項與文件的編輯距離來得到結果的,但是使用 fuzzy 查詢需要消耗的資源比較大,查詢效率不高,適用於需要模糊查詢的場景。舉例如下,使用者在輸入查詢關鍵詞時不小心把 “javascript” 拼成 “javascritp”,在存在拼寫錯誤的情況下使用模糊查詢仍然可以搜尋到含有 “javascript” 的文件,查詢語句如下:

 

GET books/_search
{
	"query": {
		"fuzzy": {
			"title": "javascritp"
		}
	}
}

 

type query

 

type query 用於查詢具有指定型別的文件。例如查詢 Elasticsearch 中 type 為 computer 的文件,查詢語句如下:

 

GET books/_search
{
	"query": {
		"type": {
			"value": "computer"
		}
	}
}

 

ids query

 

ids query 用於查詢具有指定 id 的文件。型別是可選的,也可以省略,也可以接受一個數組。如果未指定任何型別,則會查詢索引中的所有型別。例如,查詢型別為 computer,id 為 1、3、5 的文件,本質上是對文件 _id 的查詢,所以對應的 value 是字串型別,查詢語句如下:

 

GET books/_search
{
  "query": {
    "ids": {
      "type": "computer",
      "values": ["1", "3", "5"]
    }
  }
}

 

es 查詢中如果要排除一些指定的 id 列表可以結合 ids query 和 bool 查詢的 must_not,具體參照 Elasticsearch(es)不匹配或排除指定的 id 列表

 

複合查詢

 

複合查詢就是把一些簡單查詢組合在一起實現更復雜的查詢需求,除此之外,複合查詢還可以控制另外一個查詢的行為。

 

bool query

 

bool 查詢可以把任意多個簡單查詢組合在一起,使用 must、should、must_not、filter 選項來表示簡單查詢之間的邏輯,每個選項都可以出現 0 次到多次,它們的含義如下:

 

  • must 文件必須匹配 must 選項下的查詢條件,相當於邏輯運算的 AND,且參與文件相關度的評分。
  • should 文件可以匹配 should 選項下的查詢條件也可以不匹配,相當於邏輯運算的 OR,且參與文件相關度的評分。
  • must_not 與 must 相反,匹配該選項下的查詢條件的文件不會被返回;需要注意的是,must_not 語句不會影響評分,它的作用只是將不相關的文件排除
  • filter 和 must 一樣,匹配 filter 選項下的查詢條件的文件才會被返回,但是 filter 不評分,只起到過濾功能,與 must_not 相反

 

假設要查詢 title 中包含關鍵詞 java,並且 price 不能高於 70,description 可以包含也可以不包含虛擬機器的書籍,構造 bool 查詢語句如下:

 

GET books/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "status": 1
        }
      },
      "must_not": {
        "range": {
          "price": {
            "gte": 70
          }
        }
      },
      "must": {
        "match": {
          "title": "java"
        }
      },
      "should": [
        {
          "match": {
            "description": "虛擬機器"
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

 

有關布林查詢更詳細的資訊參考 bool query(組合查詢)詳解

 

boosting query

 

boosting 查詢用於需要對兩個查詢的評分進行調整的場景,boosting 查詢會把兩個查詢封裝在一起並降低其中一個查詢的評分。

 

boosting 查詢包括 positive、negative 和 negative_boost 三個部分,positive 中的查詢評分保持不變,negative 中的查詢會降低文件評分,negative_boost 指明 negative 中降低的權值。如果我們想對 2015 年之前出版的書降低評分,可以構造一個 boosting 查詢,查詢語句如下:

 

GET books/_search
{
	"query": {
		"boosting": {
			"positive": {
				"match": {
					"title": "python"
				}
			},
			"negative": {
				"range": {
					"publish_time": {
						"lte": "2015-01-01"
					}
				}
			},
			"negative_boost": 0.2
		}
	}
}

 

boosting 查詢中指定了抑制因子為 0.2,publish_time 的值在 2015-01-01 之後的文件得分不變,publish_time 的值在 2015-01-01 之前的文件得分為原得分的 0.2 倍。

 

constant_score query

 

constant_score query 包裝一個 filter query,並返回匹配過濾器查詢條件的文件,且它們的相關性評分都等於 boost 引數值(可以理解為原有的基於 tf-idf 或 bm25 的相關分固定為 1.0,所以最終評分為 1.0 * boost,即等於 boost 引數值)。下面的查詢語句會返回 title 欄位中含有關鍵詞 elasticsearch 的文件,所有文件的評分都是 1.8:

 

GET books/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "title": "elasticsearch"
        }
      },
      "boost": 1.8
    }
  }
}

 

dis_max query

 

dis_max query 與 bool query 有一定聯絡也有一定區別,dis_max query 支援多併發查詢,可返回與任意查詢條件子句匹配的任何文件型別。與 bool 查詢可以將所有匹配查詢的分數相結合使用的方式不同,dis_max 查詢只使用最佳匹配查詢條件的分數。請看下面的例子:

 

GET books/_search
{
	"query": {
		"dis_max": {
			"tie_breaker": 0.7,
			"boost": 1.2,
			"queries": [{
					"term": {
						"age": 34
					}
				},
				{
					"term": {
						"age": 35
					}
				}
			]
		}
	}
}

 

function_score query

 

function_score query 可以修改查詢的文件得分,這個查詢在有些情況下非常有用,比如通過評分函式計算文件得分代價較高,可以改用過濾器加自定義評分函式的方式來取代傳統的評分方式。

 

使用 function_score query,使用者需要定義一個查詢和一至多個評分函式,評分函式會對查詢到的每個文件分別計算得分。

 

下面這條查詢語句會返回 books 索引中的所有文件,文件的最大得分為 5,每個文件的得分隨機生成,權重的計算模式為相乘模式。

 

GET books/_search
{
  "query": {
    "function_score": {
      "query": {
        "match all": {}
      },
      "boost": "5",
      "random_score": {},
      "boost_mode": "multiply"
    }
  }
}

 

使用指令碼自定義評分公式,這裡把 price 值的十分之一開方作為每個文件的得分,查詢語句如下:

 

GET books/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "title": "java"
        }
      },
      "script_score": {
        "inline": "Math.sqrt(doc['price'].value/10)"
      }
    }
  }
}

 

關於 function_score 的更多詳細內容請檢視 Elasticsearch function_score 查詢最強詳解

 

indices query

 

indices query 適用於需要在多個索引之間進行查詢的場景,它允許指定一個索引名字列表和內部查詢。indices query 中有 query 和 no_match_query 兩部分,query 中用於搜尋指定索引列表中的文件,no_match_query 中的查詢條件用於搜尋指定索引列表之外的文件。下面的查詢語句實現了搜尋索引 books、books2 中 title 欄位包含關鍵字 javascript,其他索引中 title 欄位包含 basketball 的文件,查詢語句如下:

 

GET books/_search
{
	"query": {
		"indices": {
			"indices": ["books", "books2"],
			"query": {
				"match": {
					"title": "javascript"
				}
			},
			"no_match_query": {
				"term": {
					"title": "basketball"
				}
			}
		}
	}
}

 

巢狀查詢

 

在 Elasticsearch 這樣的分散式系統中執行全 SQL 風格的連線查詢代價昂貴,是不可行的。相應地,為了實現水平規模地擴充套件,Elasticsearch 提供了以下兩種形式的 join:

 

  • nested query(巢狀查詢)

    文件中可能包含巢狀型別的欄位,這些欄位用來索引一些陣列物件,每個物件都可以作為一條獨立的文件被查詢出來。

  • has_child query(有子查詢)和 has_parent query(有父查詢)

    父子關係可以存在單個的索引的兩個型別的文件之間。has_child 查詢將返回其子文件能滿足特定查詢的父文件,而 has_parent 則返回其父文件能滿足特定查詢的子文件。

 

nested query

 

文件中可能包含巢狀型別的欄位,這些欄位用來索引一些陣列物件,每個物件都可以作為一條獨立的文件被查詢出來(用巢狀查詢)。

 

PUT /my_index
{
	"mappings": {
		"type1": {
			"properties": {
				"obj1": {
					"type": "nested"
				}
			}
		}
	}
}

 

has_child query

 

文件的父子關係建立索引時在對映中宣告,這裡以員工(employee)和工作城市(branch)為例,它們屬於不同的型別,相當於資料庫中的兩張表,如果想把員工和他們工作的城市關聯起來,需要告訴 Elasticsearch 文件之間的父子關係,這裡 employee 是 child type,branch 是 parent type,在對映中宣告,執行命令:

 

PUT /company
{
	"mappings": {
		"branch": {},
		"employee": {
			"parent": { "type": "branch" }
		}
	}
}

 

使用 bulk api 索引 branch 型別下的文件,命令如下:

 

POST company/branch/_bulk
{ "index": { "_id": "london" }}
{ "name": "London Westminster","city": "London","country": "UK" }
{ "index": { "_id": "liverpool" }}
{ "name": "Liverpool Central","city": "Liverpool","country": "UK" }
{ "index": { "_id": "paris" }}
{ "name": "Champs Elysees","city": "Paris","country": "France" }

 

新增員工資料:

 

POST company/employee/_bulk
{ "index": { "_id": 1,"parent":"london" }}
{ "name": "Alice Smith","dob": "1970-10-24","hobby": "hiking" }
{ "index": { "_id": 2,"parent":"london" }}
{ "name": "Mark Tomas","dob": "1982-05-16","hobby": "diving" }
{ "index": { "_id": 3,"parent":"liverpool" }}
{ "name": "Barry Smith","dob": "1979-04-01","hobby": "hiking" }
{ "index": { "_id": 4,"parent":"paris" }}
{ "name": "Adrien Grand","dob": "1987-05-11","hobby": "horses" }

 

通過子文件查詢父文件要使用 has_child 查詢。例如,搜尋 1980 年以後出生的員工所在的分支機構,employee 中 1980 年以後出生的有 Mark Thomas 和 Adrien Grand,他們分別在 london 和 paris,執行以下查詢命令進行驗證:

 

GET company/branch/_search
{
	"query": {
		"has_child": {
			"type": "employee",
			"query": {
				"range": { "dob": { "gte": "1980-01-01" } }
			}
		}
	}
}

 

搜尋哪些機構中有名為 “Alice Smith” 的員工,因為使用 match 查詢,會解析為 “Alice” 和 “Smith”,所以 Alice Smith 和 Barry Smith 所在的機構會被匹配,執行以下查詢命令進行驗證:

 

GET company/branch/_search
{
	"query": {
		"has_child": {
			"type": "employee",
			"score_mode": "max",
			"query": {
				"match": { "name": "Alice Smith" }
			}
		}
	}
}

 

可以使用 min_children 指定子文件的最小個數。例如,搜尋最少含有兩個 employee 的機構,查詢命令如下:

 

GET company/branch/_search?pretty
{
	"query": {
		"has_child": {
			"type": "employee",
			"min_children": 2,
			"query": {
				"match_all": {}
			}
		}
	}
}

 

has_parent query

 

通過父文件查詢子文件使用 has_parent 查詢。比如,搜尋哪些 employee 工作在 UK,查詢命令如下:

 

GET company/employee/_search
{
	"query": {
		"has_parent": {
			"parent_type": "branch",
			"query": {
				"match": { "country": "UK }
			}
		}
	}
}

 

位置查詢

 

Elasticsearch 可以對地理位置點 geo_point 型別和地理位置形狀 geo_shape 型別的資料進行搜尋。為了學習方便,這裡準備一些城市的地理座標作為測試資料,每一條文件都包含城市名稱和地理座標這兩個欄位,這裡的座標點取的是各個城市中心的一個位置。首先把下面的內容儲存到 geo.json 檔案中:

 

{"index":{ "_index":"geo","_type":"city","_id":"1" }}
{"name":"北京","location":"39.9088145109,116.3973999023"}
{"index":{ "_index":"geo","_type":"city","_id": "2" }}
{"name":"烏魯木齊","location":"43.8266300000,87.6168800000"}
{"index":{ "_index":"geo","_type":"city","_id":"3" }}
{"name":"西安","location":"34.3412700000,108.9398400000"}
{"index":{ "_index":"geo","_type":"city","_id":"4" }}
{"name":"鄭州","location":"34.7447157466,113.6587142944"}
{"index":{ "_index":"geo","_type":"city","_id":"5" }}
{"name":"杭州","location":"30.2294080260,120.1492309570"}
{"index":{ "_index":"geo","_type":"city","_id":"6" }}
{"name":"濟南","location":"36.6518400000,117.1200900000"}

 

建立一個索引並設定對映:

 

PUT geo
{
	"mappings": {
		"city": {
			"properties": {
				"name": {
					"type": "keyword"
				},
				"location": {
					"type": "geo_point"
				}
			}
		}
	}
}

 

然後執行批量匯入命令:

 

curl -XPOST "http://localhost:9200/_bulk?pretty" --data-binary @geo.json

 

geo_distance query

 

geo_distance query 可以查詢在一箇中心點指定範圍內的地理點文件。例如,查詢距離天津 200km 以內的城市,搜尋結果中會返回北京,命令如下:

 

GET geo/_search
{
	"query": {
		"bool": {
			"must": {
				"match_all": {}
			},
			"filter": {
				"geo_distance": {
					"distance": "200km",
					"location": {
						"lat": 39.0851000000,
						"lon": 117.1993700000
					}
				}
			}
		}
	}
}

 

按各城市離北京的距離排序:

 

GET geo/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [{
    "_geo_distance": {
      "location": "39.9088145109,116.3973999023",
      "unit": "km",
      "order": "asc",
      "distance_type": "plane"
    }
  }]
}

 

其中 location 對應的經緯度欄位;unit 為 km 表示將距離以 km 為單位寫入到每個返回結果的 sort 鍵中;distance_type 為 plane 表示使用快速但精度略差的 plane 計算方式。

 

geo_bounding_box query

 

geo_bounding_box query 用於查詢落入指定的矩形內的地理座標。查詢中由兩個點確定一個矩形,然後在矩形區域內查詢匹配的文件。

 

GET geo/_search
{
	"query": {
		"bool": {
			"must": {
				"match_all": {}
			},
			"filter": {
				"geo_bounding_box": {
					"location": {
						"top_left": {
							"lat": 38.4864400000,
							"lon": 106.2324800000
						},
						"bottom_right": {
							"lat": 28.6820200000,
							"lon": 115.8579400000
						}
					}
				}
			}
		}
	}
}

 

geo_polygon query

 

geo_polygon query 用於查詢在指定多邊形內的地理點。例如,呼和浩特、重慶、上海三地組成一個三角形,查詢位置在該三角形區域內的城市,命令如下:

 

GET geo/_search
{
	"query": {
		"bool": {
			"must": {
				"match_all": {}
			}
		},
		"filter": {
			"geo_polygon": {
				"location": {
					"points": [{
						"lat": 40.8414900000,
						"lon": 111.7519900000
					}, {
						"lat": 29.5647100000,
						"lon": 106.5507300000
					}, {
						"lat": 31.2303700000,
						"lon": 121.4737000000
					}]
				}
			}
		}
	}
}

 

geo_shape query

 

geo_shape query 用於查詢 geo_shape 型別的地理資料,地理形狀之間的關係有相交、包含、不相交三種。建立一個新的索引用於測試,其中 location 欄位的型別設為 geo_shape 型別。

 

PUT geoshape
{
	"mappings": {
		"city": {
			"properties": {
				"name": {
					"type": "keyword"
				},
				"location": {
					"type": "geo_shape"
				}
			}
		}
	}
}

 

關於經緯度的順序這裡做一個說明,geo_point 型別的欄位緯度在前經度在後,但是對於 geo_shape 型別中的點,是經度在前緯度在後,這一點需要特別注意。

 

把西安和鄭州連成的線寫入索引:

 

POST geoshape/city/1
{
	"name": "西安-鄭州",
	"location": {
		"type": "linestring",
		"coordinates": [
			[108.9398400000, 34.3412700000],
			[113.6587142944, 34.7447157466]
		]
	}
}

 

查詢包含在由銀川和南昌作為對角線上的點組成的矩形的地理形狀,由於西安和鄭州組成的直線落在該矩形區域內,因此可以被查詢到。命令如下:

 

GET geoshape/_search
{
	"query": {
		"bool": {
			"must": {
				"match_all": {}
			},
			"filter": {
				"geo_shape": {
					"location": {
						"shape": {
							"type": "envelope",
							"coordinates": [
								[106.23248, 38.48644],
								[115.85794, 28.68202]
							]
						},
						"relation": "within"
					}
				}
			}
		}
	}
}

 

特殊查詢

 

more_like_this query

 

more_like_this query 可以查詢和提供文字類似的文件,通常用於近似文字的推薦等場景。查詢命令如下:

 

GET books/_search
{
	"query": {
		"more_like_ this": {
			"fields": ["title", "description"],
			"like": "java virtual machine",
			"min_term_freq": 1,
			"max_query_terms": 12
		}
	}
}

 

可選的引數及取值說明如下:

 

  • fields 要匹配的欄位,預設是 _all 欄位。
  • like 要匹配的文字。
  • min_term_freq 文件中詞項的最低頻率,預設是 2,低於此頻率的文件會被忽略。
  • max_query_terms query 中能包含的最大詞項數目,預設為 25。
  • min_doc_freq 最小的文件頻率,預設為 5。
  • max_doc_freq 最大文件頻率。
  • min_word length 單詞的最小長度。
  • max_word length 單詞的最大長度。
  • stop_words 停用詞列表。
  • analyzer 分詞器。
  • minimum_should_match 文件應匹配的最小詞項數,預設為 query 分詞後詞項數的 30%。
  • boost terms 詞項的權重。
  • include 是否把輸入文件作為結果返回。
  • boost 整個 query 的權重,預設為 1.0。

 

script query

 

Elasticsearch 支援使用指令碼進行查詢。例如,查詢價格大於 180 的文件,命令如下:

 

GET books/_search
{
  "query": {
    "script": {
      "script": {
        "inline": "doc['price'].value > 180",
        "lang": "painless"
      }
    }
  }
}

 

percolate query

 

一般情況下,我們是先把文件寫入到 Elasticsearch 中,通過查詢語句對文件進行搜尋。percolate query 則是反其道而行之的做法,它會先註冊查詢條件,根據文件來查詢 query。例如,在 my-index 索引中有一個 laptop 型別,文件有 price 和 name 兩個欄位,在對映中宣告一個 percolator 型別的 query,命令如下:

 

PUT my-index
{
	"mappings": {
		"laptop": {
			"properties": {
				"price": { "type": "long" },
				"name": { "type": "text" }
			},
			"queries": {
				"properties": {
					"query": { "type": "percolator" }
				}
			}
		}
	}
}

 

註冊一個 bool query,bool query 中包含一個 range query,要求 price 欄位的取值小於等於 10000,並且 name 欄位中含有關鍵詞 macbook:

 

PUT /my-index/queries/1?refresh
{
	"query": {
		"bool": {
			"must": [{
				"range": { "price": { "lte": 10000 } }
			}, {
				"match": { "name": "macbook" }
			}]
		}
	}
}

 

通過文件查詢 query:

 

GET /my-index/_search
{
	"query": {
		"percolate": {
			"field": "query",
			"document_type": "laptop",
			"document": {
				"price": 9999,
				"name": "macbook pro on sale"
			}
		}
	}
}

 

文件符合 query 中的條件,返回結果中可以查到上文中註冊的 bool query。percolate query 的這種特性適用於資料分類、資料路由、事件監控和預警等場景。