1、DSL查詢文檔
1.1 DSL查詢分類
1.1.1 DSLQuery的分類
Elasticsearch提供了基于JSON的DSL(Domain Specific? Language)來定義查詢。常見的查詢類型包括:
查詢所有:查詢出所有數(shù)據(jù),一般測(cè)試用。例如:match_all
全文檢索(full text)查詢:利用分詞器對(duì)用戶輸入內(nèi)容分詞,然后去倒排索引庫中匹配。例如:
- match_query
- multi_match_query
精確查詢:根據(jù)精確詞條值查找數(shù)據(jù),一般是查找keyword、數(shù)值、日期、boolean等類型字段。例如:
- ids
- range
- term
地理(geo)查詢:根據(jù)經(jīng)緯度查詢。例如:
- geo_distance
- geo_bounding_box
復(fù)合(compound)查詢:復(fù)合查詢可以將上述各種查詢條件組合起來,合并查詢條件。例如:
- bool
- function_score
1.1.2 Query基本語法
條件查詢:
GET /indexName/_search { "query": { "查詢類型": { "查詢條件": "條件值" } } }
查詢所有
GET /indexName/_search { "query": { "match_all": { } } }
總結(jié):查詢DSL的基本語法是什么?
- GET /索引庫名/_search
- { "query": { "查詢類型": { "FIELD": "TEXT"}}}
1.2 全文檢索查詢
全文檢索查詢,會(huì)對(duì)用戶輸入內(nèi)容分詞,常用于搜索框搜索:
match查詢:全文檢索查詢的一種,會(huì)對(duì)用戶輸入內(nèi)容分詞,然后去倒排索引庫檢索,語法:
GET /indexName/_search { "query": { "match": { "FIELD": "TEXT" } } }
multi_match:與match查詢類似,只不過允許同時(shí)查詢多個(gè)字段,語法:
GET /indexName/_search { "query": { "multi_match": { "query": "TEXT", "fields": ["FIELD1", " FIELD12"] } } }
match和multi_match的區(qū)別是什么?
match:根據(jù)一個(gè)字段查詢
multi_match:根據(jù)多個(gè)字段查詢,參與查詢字段越多,查詢性能越差
1.3 精準(zhǔn)查詢
精確查詢一般是查找keyword、數(shù)值、日期、boolean等類型字段。所以不會(huì)對(duì)搜索條件分詞。常見的有:
term:根據(jù)詞條精確值查詢
range:根據(jù)值的范圍查詢
精確查詢常見的有term查詢和range查詢。語法如下:
term查詢
GET /indexName/_search { "query": { "term": { "FIELD": { "value": "VALUE" } } } }
range查詢
GET /indexName/_search { "query": { "range": { "FIELD": { "gte": 10, "lte": 20 } } } }
總結(jié):精確查詢常見的有哪些?
term查詢:根據(jù)詞條精確匹配,一般搜索keyword類型、數(shù)值類型、布爾類型、日期類型字段
range查詢:根據(jù)數(shù)值范圍查詢,可以是數(shù)值、日期的范圍
1.4 地理坐標(biāo)查詢
根據(jù)經(jīng)緯度查詢。常見的使用場(chǎng)景包括:
攜程:搜索我附近的酒店
滴滴:搜索我附近的出租車
微信:搜索我附近的人
geo_bounding_box:查詢geo_point值落在某個(gè)矩形范圍的所有文檔
GET /indexName/_search { "query": { "geo_bounding_box": { "FIELD": { "top_left": { "lat": 31.1, "lon": 121.5 }, "bottom_right": { "lat": 30.9, "lon": 121.7 } } } } }
geo_distance:查詢到指定中心點(diǎn)小于某個(gè)距離值的所有文檔
GET /indexName/_search { "query": { "geo_distance": { "distance": "15km", "FIELD": "31.21,121.5" } } }
1.5 組合查詢
復(fù)合(compound)查詢:復(fù)合查詢可以將其它簡單查詢組合起來,實(shí)現(xiàn)更復(fù)雜的搜索邏輯,例如:
fuction score:算分函數(shù)查詢,可以控制文檔相關(guān)性算分,控制文檔排名。例如百度競價(jià)
相關(guān)性算分
當(dāng)我們利用match查詢時(shí),文檔結(jié)果會(huì)根據(jù)與搜索詞條的關(guān)聯(lián)度打分(_score),返回結(jié)果時(shí)按照分值降序排列。
例如,我們搜索 "虹橋如家",結(jié)果如下:
[
??{
????"_score"?:?17.850193,
????"_source"?:?{
??????"name"?:?"虹橋如家酒店真不錯(cuò)",
????}
??},
??{
????"_score"?:?12.259849,
????"_source"?:?{
??????"name"?:?"外灘如家酒店真不錯(cuò)",
????}
??},
??{
????"_score"?:?11.91091,
????"_source"?:?{
??????"name"?:?"迪士尼如家酒店真不錯(cuò)",
????}
??}
]
elasticsearch中的相關(guān)性打分算法是什么?
TF-IDF:在elasticsearch5.0之前,會(huì)隨著詞頻增加而越來越大
BM25:在elasticsearch5.0之后,會(huì)隨著詞頻增加而增大,但增長曲線會(huì)趨于水平
Function Score Query
使用 function score query,可以修改文檔的相關(guān)性算分(query score),根據(jù)新得到的算分排序。
案例:給“如家”這個(gè)品牌的酒店排名靠前一些
把這個(gè)問題翻譯一下,function score需要的三要素:
1、哪些文檔需要算分加權(quán)?
品牌為如家的酒店
2、算分函數(shù)是什么?
weight就可以
3、加權(quán)模式是什么?
求和
GET /hotel/_search
{
"query": {
"function_score": {
"query": {// ... },
"functions": [ // 算分函數(shù)
{
"filter": { // 滿足的條件,品牌必須是如家
"term": {
"brand": "如家"
}
},
"weight": 2 // 算分權(quán)重為2
}
],
"boost_mode": "sum"
}
}
}
function score query定義的三要素是什么?
- 過濾條件:哪些文檔要加分
- 算分函數(shù):如何計(jì)算function score
- 加權(quán)方式:function score 與 query score如何運(yùn)算
復(fù)合查詢 Boolean Query
布爾查詢是一個(gè)或多個(gè)查詢子句的組合。子查詢的組合方式有:
must:必須匹配每個(gè)子查詢,類似“與”
should:選擇性匹配子查詢,類似“或”
must_not:必須不匹配,不參與算分,類似“非”
filter:必須匹配,不參與算分
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{"term": {"city": "上海" }}
],
"should": [
{"term": {"brand": "皇冠假日" }},
{"term": {"brand": "華美達(dá)" }}
],
"must_not": [
{ "range": { "price": { "lte": 500 } }}
],
"filter": [
{ "range": {"score": { "gte": 45 } }}
]
}
}
}
案例:利用bool查詢實(shí)現(xiàn)功能
需求:搜索名字包含“如家”,價(jià)格不高于400,在坐標(biāo)31.21,121.5周圍10km范圍內(nèi)的酒店。
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{
"match": {"name": "如家"}
}
],
"must_not": [
{
"range": { "price": {"gt": 400}}
}
],
"filter": [
{
"geo_distance": {
"distance": "10km", "location": {"lat": 31.21, "lon": 121.5}
}
}
]
}
}
}
總結(jié):bool查詢有幾種邏輯關(guān)系?
must:必須匹配的條件,可以理解為“與”
should:選擇性匹配的條件,可以理解為“或”
must_not:必須不匹配的條件,不參與打分
filter:必須匹配的條件,不參與打分
2、搜索結(jié)果處理
2.1 排序
elasticsearch支持對(duì)搜索結(jié)果排序,默認(rèn)是根據(jù)相關(guān)度算分(_score)來排序。可以排序字段類型有:keyword類型、數(shù)值類型、地理坐標(biāo)類型、日期類型等。
GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"FIELD": "desc" // 排序字段和排序方式ASC、DESC
}
]
}
GET /indexName/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"_geo_distance" : {
"FIELD" : "緯度,經(jīng)度",
"order" : "asc",
"unit" : "km"
}
}
]
}
案例:對(duì)酒店數(shù)據(jù)按照用戶評(píng)價(jià)降序排序,評(píng)價(jià)相同的按照價(jià)格升序排序
評(píng)價(jià)是score字段,價(jià)格是price字段,按照順序添加兩個(gè)排序規(guī)則即可。
案例:實(shí)現(xiàn)對(duì)酒店數(shù)據(jù)按照到你的位置坐標(biāo)的距離升序排序
獲取經(jīng)緯度的方式:獲取鼠標(biāo)點(diǎn)擊經(jīng)緯度-地圖屬性-示例中心-JS API 2.0 示例 | 高德地圖API
2.2 分頁
elasticsearch 默認(rèn)情況下只返回top10的數(shù)據(jù)。而如果要查詢更多數(shù)據(jù)就需要修改分頁參數(shù)了。
elasticsearch中通過修改from、size參數(shù)來控制要返回的分頁結(jié)果:
GET /hotel/_search
{
"query": {
"match_all": {}
},
"from": 990, // 分頁開始的位置,默認(rèn)為0
"size": 10, // 期望獲取的文檔總數(shù)
"sort": [
{"price": "asc"}
]
}
深度分頁問題
ES是分布式的,所以會(huì)面臨深度分頁問題。例如按price排序后,獲取from = 990,size =10的數(shù)據(jù):
- 首先在每個(gè)數(shù)據(jù)分片上都排序并查詢前1000條文檔。
- 然后將所有節(jié)點(diǎn)的結(jié)果聚合,在內(nèi)存中重新排序選出前1000條文檔
- 最后從這1000條中,選取從990開始的10條文檔
如果搜索頁數(shù)過深,或者結(jié)果集(from + size)越大,對(duì)內(nèi)存和CPU的消耗也越高。因此ES設(shè)定結(jié)果集查詢的上限是10000
深度分頁解決方案
針對(duì)深度分頁,ES提供了兩種解決方案:
- search after:分頁時(shí)需要排序,原理是從上一次的排序值開始,查詢下一頁數(shù)據(jù)。官方推薦使用的方式。
- scroll:原理將排序數(shù)據(jù)形成快照,保存在內(nèi)存。官方已經(jīng)不推薦使用。
總結(jié):
from + size:
優(yōu)點(diǎn):支持隨機(jī)翻頁
缺點(diǎn):深度分頁問題,默認(rèn)查詢上限(from + size)是10000
場(chǎng)景:百度、京東、谷歌、淘寶這樣的隨機(jī)翻頁搜索
after search:
優(yōu)點(diǎn):沒有查詢上限(單次查詢的size不超過10000)
缺點(diǎn):只能向后逐頁查詢,不支持隨機(jī)翻頁
場(chǎng)景:沒有隨機(jī)翻頁需求的搜索,例如手機(jī)向下滾動(dòng)翻頁
scroll:
優(yōu)點(diǎn):沒有查詢上限(單次查詢的size不超過10000)
缺點(diǎn):會(huì)有額外內(nèi)存消耗,并且搜索結(jié)果是非實(shí)時(shí)的
場(chǎng)景:海量數(shù)據(jù)的獲取和遷移。從ES7.1開始不推薦,建議用 after search方案。
2.3 高亮
高亮:就是在搜索結(jié)果中把搜索關(guān)鍵字突出顯示。
原理是這樣的:
將搜索結(jié)果中的關(guān)鍵字用標(biāo)簽標(biāo)記出來
在頁面中給標(biāo)簽添加css樣式
語法:
GET /hotel/_search
{
"query": {
"match": {
"FIELD": "TEXT"
}
},
"highlight": {
"fields": { // 指定要高亮的字段
"FIELD": {
"pre_tags": "<em>", // 用來標(biāo)記高亮字段的前置標(biāo)簽
"post_tags": "</em>" // 用來標(biāo)記高亮字段的后置標(biāo)簽
}
}
}
}
搜索結(jié)果處理整體語法:
GET /hotel/_search
{
"query": {
"match": {
"name": "如家"
}
},
"from": 0, // 分頁開始的位置
"size": 20, // 期望獲取的文檔總數(shù)
"sort": [
{ "price": "asc" }, // 普通排序
{
"_geo_distance" : { // 距離排序
"location" : "31.040699,121.618075",
"order" : "asc",
"unit" : "km"
}
}
],
"highlight": {
"fields": { // 高亮字段
"name": {
"pre_tags": "<em>", // 用來標(biāo)記高亮字段的前置標(biāo)簽
"post_tags": "</em>" // 用來標(biāo)記高亮字段的后置標(biāo)簽
}
}
}
}
3、RestClient查詢文檔
3.1 快速入門
我們通過match_all來演示下基本的API,先看請(qǐng)求DSL的組織:
我們通過match_all來演示下基本的API,再看結(jié)果的解析:
RestAPI中其中構(gòu)建DSL是通過HighLevelRestClient中的resource()來實(shí)現(xiàn)的,其中包含了查詢、排序、分頁、高亮等所有功能:
RestAPI中其中構(gòu)建查詢條件的核心部分是由一個(gè)名為QueryBuilders的工具類提供的,其中包含了各種查詢方法:
總結(jié):查詢的基本步驟是:
- 創(chuàng)建SearchRequest對(duì)象
- 準(zhǔn)備Request.source(),也就是DSL。
QueryBuilders來構(gòu)建查詢條件
傳入Request.source() 的 query() 方法- 發(fā)送請(qǐng)求,得到結(jié)果
- 解析結(jié)果(參考JSON結(jié)果,從外到內(nèi),逐層解析)
3.2 match查詢
全文檢索的match和multi_match查詢與match_all的API基本一致。差別是查詢條件,也就是query的部分。
同樣是利用QueryBuilders提供的方法:
// 單字段查詢QueryBuilders.matchQuery("all", "如家");
// 多字段查詢QueryBuilders.multiMatchQuery("如家", "name", "business");
GET /hotel/_search
{
"query": {
"match_all": {}
}
}
GET /hotel/_search
{
"query": {
"match": {
"all": "如家"
}
}
}
GET /hotel/_search
{
"query": {
"multi_match": {
"query": "如家",
"fields": ["brand", "name"]
}
}
}
3.3 精確查詢
精確查詢常見的有term查詢和range查詢,同樣利用QueryBuilders實(shí)現(xiàn):
// 詞條查詢QueryBuilders.termQuery("city", "杭州");
// 范圍查詢QueryBuilders.rangeQuery("price").gte(100).lte(150);
GET /hotel/_search
{
"query": {
"term": {
"city": "杭州"
}
}
}
GET /hotel/_search
{
"query": {
"range": {
"price": { "gte": 100, "lte": 150 }
}
}
}
3.4 復(fù)合查詢
精確查詢常見的有term查詢和range查詢,同樣利用QueryBuilders實(shí)現(xiàn):
// 創(chuàng)建布爾查詢
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 添加must條件
boolQuery.must(QueryBuilders.termQuery("city", "杭州"));
// 添加filter條件
boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250));
GET /hotel/_search
{
"query": {
"bool": {
"must": [
{
"term": { "city": "杭州" }
}
],
"filter": [
{
"range": {
"price": { "lte": 250 }
}
}
]
}
}
}
總結(jié):要構(gòu)建查詢條件,只要記住一個(gè)類:QueryBuilders
3.5 排序、分頁、高亮
搜索結(jié)果的排序和分頁是與query同級(jí)的參數(shù),對(duì)應(yīng)的API如下:
// 查詢
request.source().query(QueryBuilders.matchAllQuery());
// 分頁
request.source().from(0).size(5);
GET /indexName/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 5,
"sort": [
{
"FIELD": "desc"
},
]
}
高亮API包括請(qǐng)求DSL構(gòu)建和結(jié)果解析兩部分。我們先看請(qǐng)求的DSL構(gòu)建:
GET /hotel/_search
{
"query": {
"match": {
"all": "如家"
}
},
"highlight": {
"fields": {
"name": {
"require_field_match": "false"
}
}
}
}
總結(jié):
所有搜索DSL的構(gòu)建,記住一個(gè)API:
SearchRequest的source()方法。
高亮結(jié)果解析是參考JSON結(jié)果,逐層解析
4、搜索旅游案例
4.1 基本搜索和分頁-酒單搜索功能,完成關(guān)鍵字搜索和分頁
先實(shí)現(xiàn)其中的關(guān)鍵字搜索功能,實(shí)現(xiàn)步驟如下:
- 定義實(shí)體類,接收前端請(qǐng)求
- 定義controller接口,接收頁面請(qǐng)求,調(diào)用IHotelService的search方法
- 定義IHotelService中的search方法,利用match查詢實(shí)現(xiàn)根據(jù)關(guān)鍵字搜索酒店信息
步驟1:定義類,接收前端請(qǐng)求參數(shù):
@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
}
步驟2:定義controller接口,接收前端請(qǐng)求:
定義一個(gè)HotelController,聲明查詢接口,滿足下列要求:
- 請(qǐng)求方式:Post
- 請(qǐng)求路徑:/hotel/list
- 請(qǐng)求參數(shù):對(duì)象,類型為RequestParam
- 返回值:PageResult,包含兩個(gè)屬性
Long total:總條數(shù)
List hotels:酒店數(shù)據(jù)
步驟3:在IHotelService中定義一個(gè)方法,實(shí)現(xiàn)搜索功能:
- 在IHotelService中定義一個(gè)方法,聲明如下:
/**
* 根據(jù)關(guān)鍵字搜索酒店信息
* @param params 請(qǐng)求參數(shù)對(duì)象,包含用戶輸入的關(guān)鍵字
* @return 酒店文檔列表
*/
PageResult search(RequestParam params);
- 在HotelService中實(shí)現(xiàn)該方法,滿足下列要求:
- 利用match查詢,根據(jù)參數(shù)中的key搜索all字段,查詢酒店信息并返回
- 利用參數(shù)中的page、size實(shí)現(xiàn)分頁
4.2 條件過濾-添加品牌、城市、星級(jí)、價(jià)格等過濾功能
步驟:
修改RequestParams類,添加brand、city、starName、minPrice、maxPrice等參數(shù)
修改search方法的實(shí)現(xiàn),在關(guān)鍵字搜索時(shí),如果brand等參數(shù)存在,對(duì)其做過濾
步驟一:拓展IUserService的search方法的參數(shù)列表
修改RequestParams類,接收所有參數(shù):
@Data
public class RequestParams {
private String key;
private Integer page;
private Integer size;
private String sortBy;
private String brand;
private String starName;
private String city;
private Integer minPrice;
private Integer maxPrice;
}
步驟二:修改search方法,在match查詢基礎(chǔ)上添加過濾條件
過濾條件包括:
- city精確匹配
- brand精確匹配
- starName精確匹配
- price范圍過濾
注意事項(xiàng):
- 多個(gè)條件之間是AND關(guān)系,組合多條件用BooleanQuery
- 參數(shù)存在才需要過濾,做好非空判斷
4.3 廣告置頂-讓指定的酒店在搜索結(jié)果中排名置頂
我們給需要置頂?shù)木频晡臋n添加一個(gè)標(biāo)記。然后利用function score給帶有標(biāo)記的文檔增加權(quán)重。
實(shí)現(xiàn)步驟分析:文章來源:http://www.zghlxwxcb.cn/news/detail-830979.html
- 給HotelDoc類添加isAD字段,Boolean類型
- 挑選幾個(gè)你喜歡的酒店,給它的文檔數(shù)據(jù)添加isAD字段,值為true
- 修改search方法,添加function score功能,給isAD值為true的酒店增加權(quán)重
文章來源地址http://www.zghlxwxcb.cn/news/detail-830979.html
到了這里,關(guān)于分布式搜索引擎elasticsearch搜索功能介紹及實(shí)際案例剖析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!