ElasticsearchTemplate的詳細使用,完成多條件查詢、匹配度查詢等
阿新 • • 發佈:2019-01-03
ElasticsearchTemplate是Spring對ES的java api進行的封裝,提供了大量的相關的類來完成各種各樣的查詢。在日常的使用中,應該說最常用的查詢就是queryList方法。
public <T> List<T> queryForList(SearchQuery query, Class<T> clazz) {
return this.queryForPage(query, clazz).getContent();
}
而我們的主要工作就是構建SearchQuery查詢條件,包括排序、分頁等條件都包含在SearchQuery中。在之前的一篇文章中已經簡單的講過這幾個類的繼承關係了,這裡我們主要看看使用方法(http://blog.csdn.net/tianyaleixiaowu/article/details/76149547 先閱讀該篇瞭解基本的繼承關係)。直接上例子,Post物件
package com.example.demo.pojo; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; @Document(indexName="projectname",type="post",indexStoreType="fs",shards=5,replicas=1,refreshInterval="-1") public class Post { @Id private String id; private String title; private String content; private int userId; private int weight; @Override public String toString() { return "Post{" + "id='" + id + '\'' + ", title='" + title + '\'' + ", content='" + content + '\'' + ", userId=" + userId + ", weight=" + weight + '}'; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } }
import com.example.demo.pojo.Post;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface PostRepository extends ElasticsearchRepository<Post, String> {
}
初始化資料package com.example.demo.config; import com.example.demo.pojo.Post; import com.example.demo.repository.PostRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; @Component public class Init { @Autowired PostRepository postRepository; @PostConstruct public void init() { //只初始化一次 Iterable<Post> posts = postRepository.findAll(); if (posts.iterator().hasNext()) { return; } for (int i = 0; i < 40; i++) { Post post = new Post(); post.setTitle(getTitle().get(i)); post.setContent(getContent().get(i)); post.setWeight(i); post.setUserId(i % 10); postRepository.save(post); } } private List<String> getTitle() { List<String> list = new ArrayList<>(); list.add("《如夢令·常記溪亭日暮》"); list.add("《醉花陰·薄霧濃雲愁永晝》"); list.add("《聲聲慢·尋尋覓覓》"); list.add("《永遇樂·落日熔金》"); list.add("《如夢令·昨夜雨疏風驟》"); list.add("《漁家傲·雪裡已知春信至》"); list.add("《點絳脣·蹴[1]罷鞦韆》"); list.add("《點絳脣·寂寞深閨》"); list.add("《蝶戀花·淚溼羅衣脂粉滿》"); list.add("《蝶戀花 離情》"); list.add("《浣溪沙》"); list.add("《浣溪沙》"); list.add("《浣溪沙》"); list.add("《浣溪沙》"); list.add("《浣溪沙》"); list.add("《減字木蘭花·賣花擔上》"); list.add("《臨江仙·歐陽公作《蝶戀花》"); list.add("《臨江仙·庭院深深深幾許》"); list.add("《念奴嬌·蕭條庭院》"); list.add("《菩薩蠻·風柔日薄春猶早》"); list.add("《菩薩蠻·歸鴻聲斷殘雲碧》"); list.add("《武陵春·風住塵香花已盡》"); list.add("《一剪梅·紅藕香殘玉蕈秋》"); list.add("《漁家傲·天接雲濤連曉霧》"); list.add("《鷓鴣天·暗淡輕黃體性柔》"); list.add("《鷓鴣天·寒日蕭蕭上鎖窗》"); list.add("《一剪梅·紅藕香殘玉簟秋》"); list.add("《如夢令·常記溪亭日暮》"); list.add("《浣溪沙》"); list.add("《浣溪沙》"); list.add("《浣溪沙》"); list.add("《蝶戀花·淚溼羅衣脂粉滿》"); list.add("《蝶戀花·暖日晴風初破凍》"); list.add("《鷓鴣天·寒日蕭蕭上鎖窗》"); list.add("《醉花陰·薄霧濃雲愁永晝》"); list.add("《鷓鴣天·暗淡輕黃體性柔》"); list.add("《蝶戀花·永夜懨懨歡意少》"); list.add("《浣溪沙》"); list.add("《浣溪沙》"); list.add("《如夢令·誰伴明窗獨坐》"); return list; } private List<String> getContent() { List<String> list = new ArrayList<>(); list.add("初中 宋·李清照 常記溪亭日暮,沉醉不知歸路,興盡晚回舟,誤入藕花深處。爭渡,爭渡"); list.add("重陽節 宋·李清照 薄霧濃雲愁永晝,瑞腦消金獸。佳節又重陽,玉枕紗廚,半夜涼初透。東"); list.add("閨怨詩 宋·李清照 尋尋覓覓,冷冷清清,悽悽慘慘慼戚。乍暖還寒時候,最難將息。三杯兩"); list.add("元宵節 宋·李清照 落日熔金,暮雲合璧,人在何處。染柳煙濃,吹梅笛怨,春意知幾許。元"); list.add("婉約詩 宋·李清照 昨夜雨疏風驟,濃睡不消殘酒,試問卷簾人,卻道海棠依舊。知否,知否"); list.add("描寫梅花 宋·李清照 雪裡已知春信至,寒梅點綴瓊枝膩,香臉半開嬌旖旎,當庭際,玉人浴出"); list.add(" 宋·李清照 蹴罷鞦韆,起來慵整纖纖手。露濃花瘦,薄汗輕衣透。見客入來,襪剗金"); list.add("閨怨詩 宋·李清照 寂寞深閨,柔腸一寸愁千縷。惜春春去。幾點催花雨。倚遍闌干,只是無"); list.add("婉約詩 宋·李清照 淚溼羅衣脂粉滿。四疊陽關,唱到千千遍。人道山長水又斷。蕭蕭微雨聞"); list.add("描寫春天 宋·李清照 暖雨晴風初破凍,柳眼梅腮,已覺春心動。酒意詩情誰與共?淚融殘粉花"); list.add("寒食節 宋·李清照 淡蕩春光寒食天,玉爐瀋水嫋殘煙,夢迴山枕隱花鈿。海燕未來人鬥草,"); list.add(" 宋·李清照 髻子傷春慵更梳,晚風庭院落梅初,淡雲來往月疏疏,玉鴨薰爐閒瑞腦,"); list.add(" 宋·李清照 莫許杯深琥珀濃,未成沉醉意先融。疏鍾已應晚來風。瑞腦香消魂夢斷,"); list.add("閨怨詩 宋·李清照 小院閒窗春已深,重簾未卷影沉沉。倚樓無語理瑤琴,遠岫出山催薄暮。"); list.add("愛情詩 宋·李清照 繡幕芙蓉一笑開,斜偎寶鴨親香腮,眼波才動被人猜。一面風情深有韻,"); list.add("描寫春天 宋·李清照 賣花擔上,買得一枝春欲放。淚染輕勻,猶帶彤霞曉露痕。怕郎猜道,奴"); list.add("》 宋·李清照 歐陽公作《蝶戀花》,有“深深深幾許”之句,予酷愛之。用其語作“庭"); list.add("描寫梅花 宋·李清照 庭院深深深幾許,雲窗霧閣春遲,為誰憔悴損芳姿。夜來清夢好,應是發"); list.add("寒食節 宋·李清照 蕭條庭院,又斜風細雨,重門須閉。寵柳嬌花寒食近,種種惱人天氣。險"); list.add("思鄉詩 宋·李清照 風柔日薄春猶早,夾衫乍著心情好。睡起覺微寒,梅花鬢上殘。故鄉何處"); list.add("描寫春天 宋·李清照 歸鴻聲斷殘雲碧,背窗雪落爐煙直。燭底鳳釵明,釵頭人勝輕。角聲催曉"); list.add("閨怨詩 宋·李清照 風住塵香花已盡,日晚倦梳頭。物是人非事事休,欲語淚先流。聞說雙溪"); list.add(" 宋·李清照 紅藕香殘玉蕈秋,輕解羅裳,獨上蘭舟。雲中誰寄錦書來?雁字回時,月"); list.add("豪放詩 宋·李清照 天接雲濤連曉霧。星河欲轉千帆舞。彷彿夢魂歸帝所。聞天語。殷勤問我"); list.add("描寫花 宋·李清照 暗淡輕黃體性柔。情疏跡遠只香留。何須淺碧深紅色,自是花中第一流。"); list.add("描寫秋天 宋·李清照 寒日蕭蕭上瑣窗,梧桐應恨夜來霜。酒闌更喜團茶苦,夢斷偏宜瑞腦香。"); list.add("閨怨詩 宋·李清照 紅藕香殘玉簟秋。輕解羅裳,獨上蘭舟。雲中誰寄錦書來?雁字回時,月"); list.add(" 宋·李清照 常記溪亭日暮。沈醉不知歸路。興盡晚回舟,誤入藕花深處。爭渡。爭渡"); list.add(" 宋·李清照 莫許杯深琥珀濃。未成沈醉意先融。已應晚來風。瑞腦香消魂夢斷,"); list.add(" 宋·李清照 小院閒窗春色深。重簾未卷影沈沈。倚樓無語理瑤琴。遠岫出山催薄暮,"); list.add(" 宋·李清照 淡蕩春光寒食天。玉爐瀋水嫋殘煙。夢迴山枕隱花鈿。海燕未來人鬥草,"); list.add(" 宋·李清照 淚溼羅衣脂粉滿。四疊陽關,唱到千千遍。人道山長山又斷。蕭蕭微雨聞"); list.add(" 宋·李清照 暖日晴風初破凍。柳眼眉腮,已覺春心動。酒意詩情誰與共。淚融殘粉花"); list.add(" 宋·李清照 寒日蕭蕭上鎖窗。梧桐應恨夜來霜。酒闌更喜團茶苦,夢斷偏宜瑞腦香。"); list.add(" 宋·李清照 薄霧濃雲愁永晝。瑞腦消金獸。佳節又重陽,玉枕紗廚,半夜涼初透。東"); list.add(" 宋·李清照 暗淡輕黃體性柔。情疏跡遠只香留。何須淺碧深紅色,自是花中第一流。"); list.add(" 宋·李清照 永夜懨懨歡意少。空夢長安,認取長安道。為報今年春色好。花光月影宜"); list.add(" 宋·李清照 髻子傷春慵更梳。晚風庭院落梅初。淡雲來往月疏疏。玉鴨薰爐閒瑞腦,"); list.add(" 宋·李清照 繡面芙蓉一笑開。斜飛寶鴨襯香腮。眼波才動被人猜。一面風情深有韻,"); list.add(" 宋·李清照 誰伴明窗獨坐,我共影兒倆個。燈盡欲眠時,影也把人拋躲。無那,無那"); return list; } }
測試開始
定義出來
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
1 單字串全文查詢
/**
* 單字串模糊查詢,預設排序。將從所有欄位中查詢包含傳來的word分詞後字串的資料集
*/
@RequestMapping("/singleWord")
public Object singleTitle(String word, @PageableDefault Pageable pageable) {
//使用queryStringQuery完成單字串查詢
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryStringQuery(word)).withPageable(pageable).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
請求 http://localhost:8080/singleWord?word=浣溪沙&size=20結果:
[
{
"id": "AV56c5adcu4Vu652ZQrK",
"title": "《浣溪沙》",
"content": " 宋·李清照 莫許杯深琥珀濃,未成沉醉意先融。疏鍾已應晚來風。瑞腦香消魂夢斷,",
"userId": 2,
"weight": 12
},
{
"id": "AV56c5bfcu4Vu652ZQrM",
"title": "《浣溪沙》",
"content": "愛情詩 宋·李清照 繡幕芙蓉一笑開,斜偎寶鴨親香腮,眼波才動被人猜。一面風情深有韻,",
"userId": 4,
"weight": 14
},
{
"id": "AV56c5jdcu4Vu652ZQrb",
"title": "《浣溪沙》",
"content": " 宋·李清照 小院閒窗春色深。重簾未卷影沈沈。倚樓無語理瑤琴。遠岫出山催薄暮,",
"userId": 9,
"weight": 29
},
{
"id": "AV56c5nfcu4Vu652ZQrj",
"title": "《浣溪沙》",
"content": " 宋·李清照 髻子傷春慵更梳。晚風庭院落梅初。淡雲來往月疏疏。玉鴨薰爐閒瑞腦,",
"userId": 7,
"weight": 37
},
{
"id": "AV56c5aBcu4Vu652ZQrJ",
"title": "《浣溪沙》",
"content": " 宋·李清照 髻子傷春慵更梳,晚風庭院落梅初,淡雲來往月疏疏,玉鴨薰爐閒瑞腦,",
"userId": 1,
"weight": 11
},
{
"id": "AV56c5Zucu4Vu652ZQrI",
"title": "《浣溪沙》",
"content": "寒食節 宋·李清照 淡蕩春光寒食天,玉爐瀋水嫋殘煙,夢迴山枕隱花鈿。海燕未來人鬥草,",
"userId": 0,
"weight": 10
},
{
"id": "AV56c5i-cu4Vu652ZQra",
"title": "《浣溪沙》",
"content": " 宋·李清照 莫許杯深琥珀濃。未成沈醉意先融。已應晚來風。瑞腦香消魂夢斷,",
"userId": 8,
"weight": 28
},
{
"id": "AV56c5j-cu4Vu652ZQrc",
"title": "《浣溪沙》",
"content": " 宋·李清照 淡蕩春光寒食天。玉爐瀋水嫋殘煙。夢迴山枕隱花鈿。海燕未來人鬥草,",
"userId": 0,
"weight": 30
},
{
"id": "AV56c5a_cu4Vu652ZQrL",
"title": "《浣溪沙》",
"content": "閨怨詩 宋·李清照 小院閒窗春已深,重簾未卷影沉沉。倚樓無語理瑤琴,遠岫出山催薄暮。",
"userId": 3,
"weight": 13
},
{
"id": "AV56c5oAcu4Vu652ZQrk",
"title": "《浣溪沙》",
"content": " 宋·李清照 繡面芙蓉一笑開。斜飛寶鴨襯香腮。眼波才動被人猜。一面風情深有韻,",
"userId": 8,
"weight": 38
},
{
"id": "AV56c5Uwcu4Vu652ZQq-",
"title": "《如夢令·常記溪亭日暮》",
"content": "初中 宋·李清照 常記溪亭日暮,沉醉不知歸路,興盡晚回舟,誤入藕花深處。爭渡,爭渡",
"userId": 0,
"weight": 0
},
{
"id": "AV56c5ifcu4Vu652ZQrZ",
"title": "《如夢令·常記溪亭日暮》",
"content": " 宋·李清照 常記溪亭日暮。沈醉不知歸路。興盡晚回舟,誤入藕花深處。爭渡。爭渡",
"userId": 7,
"weight": 27
},
{
"id": "AV56c5e-cu4Vu652ZQrT",
"title": "《武陵春·風住塵香花已盡》",
"content": "閨怨詩 宋·李清照 風住塵香花已盡,日晚倦梳頭。物是人非事事休,欲語淚先流。聞說雙溪",
"userId": 1,
"weight": 21
}
]
共13個結果,按照預設的排序方式,即匹配相關度排序,前10個匹配度最高,都是完全帶“浣溪沙”三個字的。第10個、11個都是題目和正文都包含“溪”字而且出現了2次“溪”,最後一個是正文帶一個“溪”。
我們修改一下排序方式,按照weight從大到小排序
/**
* 單字串模糊查詢,單欄位排序。
*/
@RequestMapping("/singleWord1")
public Object singlePost(String word, @PageableDefault(sort = "weight", direction = Sort.Direction.DESC) Pageable pageable) {
//使用queryStringQuery完成單字串查詢
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryStringQuery(word)).withPageable(pageable).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
請求:http://localhost:8080/singleWord1?word=浣溪沙&size=20
結果:
[
{
"id": "AV56c5oAcu4Vu652ZQrk",
"title": "《浣溪沙》",
"content": " 宋·李清照 繡面芙蓉一笑開。斜飛寶鴨襯香腮。眼波才動被人猜。一面風情深有韻,",
"userId": 8,
"weight": 38
},
{
"id": "AV56c5nfcu4Vu652ZQrj",
"title": "《浣溪沙》",
"content": " 宋·李清照 髻子傷春慵更梳。晚風庭院落梅初。淡雲來往月疏疏。玉鴨薰爐閒瑞腦,",
"userId": 7,
"weight": 37
},
{
"id": "AV56c5j-cu4Vu652ZQrc",
"title": "《浣溪沙》",
"content": " 宋·李清照 淡蕩春光寒食天。玉爐瀋水嫋殘煙。夢迴山枕隱花鈿。海燕未來人鬥草,",
"userId": 0,
"weight": 30
},
{
"id": "AV56c5jdcu4Vu652ZQrb",
"title": "《浣溪沙》",
"content": " 宋·李清照 小院閒窗春色深。重簾未卷影沈沈。倚樓無語理瑤琴。遠岫出山催薄暮,",
"userId": 9,
"weight": 29
},
{
"id": "AV56c5i-cu4Vu652ZQra",
"title": "《浣溪沙》",
"content": " 宋·李清照 莫許杯深琥珀濃。未成沈醉意先融。已應晚來風。瑞腦香消魂夢斷,",
"userId": 8,
"weight": 28
},
{
"id": "AV56c5ifcu4Vu652ZQrZ",
"title": "《如夢令·常記溪亭日暮》",
"content": " 宋·李清照 常記溪亭日暮。沈醉不知歸路。興盡晚回舟,誤入藕花深處。爭渡。爭渡",
"userId": 7,
"weight": 27
},
{
"id": "AV56c5e-cu4Vu652ZQrT",
"title": "《武陵春·風住塵香花已盡》",
"content": "閨怨詩 宋·李清照 風住塵香花已盡,日晚倦梳頭。物是人非事事休,欲語淚先流。聞說雙溪",
"userId": 1,
"weight": 21
},
{
"id": "AV56c5bfcu4Vu652ZQrM",
"title": "《浣溪沙》",
"content": "愛情詩 宋·李清照 繡幕芙蓉一笑開,斜偎寶鴨親香腮,眼波才動被人猜。一面風情深有韻,",
"userId": 4,
"weight": 14
},
{
"id": "AV56c5a_cu4Vu652ZQrL",
"title": "《浣溪沙》",
"content": "閨怨詩 宋·李清照 小院閒窗春已深,重簾未卷影沉沉。倚樓無語理瑤琴,遠岫出山催薄暮。",
"userId": 3,
"weight": 13
},
{
"id": "AV56c5adcu4Vu652ZQrK",
"title": "《浣溪沙》",
"content": " 宋·李清照 莫許杯深琥珀濃,未成沉醉意先融。疏鍾已應晚來風。瑞腦香消魂夢斷,",
"userId": 2,
"weight": 12
},
{
"id": "AV56c5aBcu4Vu652ZQrJ",
"title": "《浣溪沙》",
"content": " 宋·李清照 髻子傷春慵更梳,晚風庭院落梅初,淡雲來往月疏疏,玉鴨薰爐閒瑞腦,",
"userId": 1,
"weight": 11
},
{
"id": "AV56c5Zucu4Vu652ZQrI",
"title": "《浣溪沙》",
"content": "寒食節 宋·李清照 淡蕩春光寒食天,玉爐瀋水嫋殘煙,夢迴山枕隱花鈿。海燕未來人鬥草,",
"userId": 0,
"weight": 10
},
{
"id": "AV56c5Uwcu4Vu652ZQq-",
"title": "《如夢令·常記溪亭日暮》",
"content": "初中 宋·李清照 常記溪亭日暮,沉醉不知歸路,興盡晚回舟,誤入藕花深處。爭渡,爭渡",
"userId": 0,
"weight": 0
}
]
可以看到排序已經按照weight從大到小來排了,比字串匹配度的權重更大。2 某欄位按字串模糊查詢
查詢某個欄位中模糊包含目標字串,使用matchQuery/**
* 單欄位對某字串模糊查詢
*/
@RequestMapping("/singleMatch")
public Object singleMatch(String content, Integer userId, @PageableDefault Pageable pageable) {
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("content", content)).withPageable(pageable).build();
// SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("userId", userId)).withPageable(pageable).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
請求:http://localhost:8080/singleMatch?content=落日熔金&size=20
部分結果:
結果集是content欄位包含“落日熔金”相關字樣。
把註釋放開,我們可以嘗試用userId查詢,http://localhost:8080/singleMatch?userId=1&size=20
matchQuery應該是比較常用的功能。
3 PhraseMatch查詢,短語匹配
/**
* 單欄位對某短語進行匹配查詢,短語分詞的順序會影響結果
*/
@RequestMapping("/singlePhraseMatch")
public Object singlePhraseMatch(String content, @PageableDefault Pageable pageable) {
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("content", content)).withPageable(pageable).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
結果只有一條:
[
{
"id": "AV56c5WDcu4Vu652ZQrB",
"title": "《永遇樂·落日熔金》",
"content": "元宵節 宋·李清照 落日熔金,暮雲合璧,人在何處。染柳煙濃,吹梅笛怨,春意知幾許。元",
"userId": 3,
"weight": 3
}
]
和match查詢類似,match_phrase查詢首先解析查詢字串來產生一個詞條列表。然後會搜尋所有的詞條,但只保留包含了所有搜尋詞條的文件,並且詞條的位置要鄰接。一個針對短語“中華共和國”的查詢不會匹配“中華人民共和國”,因為沒有含有鄰接在一起的“中華”和“共和國”詞條。這種完全匹配比較嚴格,類似於資料庫裡的“%落日熔金%”這種,使用場景比較狹窄。如果我們希望能不那麼嚴格,譬如搜尋“中華共和國”,希望帶“我愛中華人民共和國”的也能出來,就是分詞後,中間能間隔幾個位置的也能查出來,可以使用slop引數。我們先來新增一個類似的資料
@RequestMapping("/add")
public Object add() {
Post post = new Post();
post.setTitle("我是");
post.setContent("我愛中華人民共和國");
post.setWeight(1);
post.setUserId(1);
postRepository.save(post);
post = new Post();
post.setTitle("我是");
post.setContent("中華共和國");
post.setWeight(2);
post.setUserId(2);
return postRepository.save(post);
}
使用如下方法:
@RequestMapping("/singlePhraseMatch")
public Object singlePhraseMatch(String content, @PageableDefault Pageable pageable) {
// SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("content", content)).withPageable(pageable).build();
//少匹配一個分詞也OK、
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchPhraseQuery("content", content).slop(2)).withPageable(pageable).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
當請求“中華共和國時”結果如下
[
{
"id": "AV5-ekRHcu4Vu652ZQrw",
"title": "我是",
"content": "中華共和國",
"userId": 2,
"weight": 2
},
{
"id": "AV5-ekBvcu4Vu652ZQrv",
"title": "我是",
"content": "我愛中華人民共和國",
"userId": 1,
"weight": 1
}
]
關於slop的解釋,可以看看這一篇http://blog.csdn.net/xifeijian/article/details/51090707精確短語(Exact-phrase)匹配也許太過於嚴格了。也許我們希望含有"quick brown fox"的文件也能夠匹配"quick fox"查詢,即使位置並不是完全相等的。我們可以在短語匹配使用slop引數來引入一些靈活性:
GET /my_index/my_type/_search
{
"query": {
"match_phrase": {
"title": {
"query": "quick fox",
"slop": 1
}
}
}
}
slop引數告訴match_phrase查詢詞條能夠相隔多遠時仍然將文件視為匹配。相隔多遠的意思是,你需要移動一個詞條多少次來讓查詢和文件匹配?
我們以一個簡單的例子來闡述這個概念。為了讓查詢quick fox能夠匹配含有quick brown fox的文件,我們需要slop的值為1:
Pos 1 Pos 2 Pos 3
-----------------------------------------------
Doc: quick brown fox
-----------------------------------------------
Query: quick fox
Slop 1: quick ↳ fox
儘管在使用了slop的短語匹配中,所有的單詞都需要出現,但是單詞的出現順序可以不同。如果slop的值足夠大,那麼單詞的順序可以是任意的。
為了讓fox quick查詢能夠匹配我們的文件,需要slop的值為3:
Pos 1 Pos 2 Pos 3
-----------------------------------------------
Doc: quick brown fox
-----------------------------------------------
Query: fox quick
Slop 1: fox|quick ↵
Slop 2: quick ↳ fox
Slop 3: quick ↳ fox
4 Term查詢
這個是最嚴格的匹配,屬於低階查詢,不進行分詞的,參考這篇文章http://www.cnblogs.com/muniaofeiyu/p/5616316.html/**
* term匹配,即不分詞匹配,你傳來什麼值就會拿你傳的值去做完全匹配
*/
@RequestMapping("/singleTerm")
public Object singleTerm(Integer userId, @PageableDefault Pageable pageable) {
//不對傳來的值分詞,去找完全匹配的
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(termQuery("userId", userId)).withPageable(pageable).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
我們可以用這個來做那種需要==查詢的操作,當傳userId=1時,會查詢出來所有userId為1的集合。通常情況下,我們不會使用term查詢,絕大部分情況我們使用ES的目的就是為了使用它的分詞模糊查詢功能。而term一般適用於做過濾器filter的情況,譬如我們去查詢title中包含“浣溪沙”且userId=1時,那麼就可以用termQuery("userId", 1)作為查詢的filter。5 multi_match多個欄位匹配某字串
如果我們希望title,content兩個欄位去匹配某個字串,只要任何一個欄位包括該字串即可,就可以使用multimatch。/**
* 多欄位匹配
*/
@RequestMapping("/multiMatch")
public Object singleUserId(String title, @PageableDefault(sort = "weight", direction = Sort.Direction.DESC) Pageable pageable) {
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(multiMatchQuery(title, "title", "content")).withPageable(pageable).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
訪問http://localhost:8080/multiMatch?title=我是結果如下[
{
"id": "AV56c5oicu4Vu652ZQrl",
"title": "《如夢令·誰伴明窗獨坐》",
"content": " 宋·李清照 誰伴明窗獨坐,我共影兒倆個。燈盡欲眠時,影也把人拋躲。無那,無那",
"userId": 9,
"weight": 39
},
{
"id": "AV56c5mecu4Vu652ZQrh",
"title": "《鷓鴣天·暗淡輕黃體性柔》",
"content": " 宋·李清照 暗淡輕黃體性柔。情疏跡遠只香留。何須淺碧深紅色,自是花中第一流。",
"userId": 5,
"weight": 35
},
{
"id": "AV56c5g0cu4Vu652ZQrW",
"title": "《鷓鴣天·暗淡輕黃體性柔》",
"content": "描寫花 宋·李清照 暗淡輕黃體性柔。情疏跡遠只香留。何須淺碧深紅色,自是花中第一流。",
"userId": 4,
"weight": 24
},
{
"id": "AV56c5gIcu4Vu652ZQrV",
"title": "《漁家傲·天接雲濤連曉霧》",
"content": "豪放詩 宋·李清照 天接雲濤連曉霧。星河欲轉千帆舞。彷彿夢魂歸帝所。聞天語。殷勤問我",
"userId": 3,
"weight": 23
},
{
"id": "AV56c5e-cu4Vu652ZQrT",
"title": "《武陵春·風住塵香花已盡》",
"content": "閨怨詩 宋·李清照 風住塵香花已盡,日晚倦梳頭。物是人非事事休,欲語淚先流。聞說雙溪",
"userId": 1,
"weight": 21
},
{
"id": "AV56c5c7cu4Vu652ZQrP",
"title": "《臨江仙·庭院深深深幾許》",
"content": "描寫梅花 宋·李清照 庭院深深深幾許,雲窗霧閣春遲,為誰憔悴損芳姿。夜來清夢好,應是發",
"userId": 7,
"weight": 17
},
{
"id": "AV56c5YGcu4Vu652ZQrF",
"title": "《點絳脣·寂寞深閨》",
"content": "閨怨詩 宋·李清照 寂寞深閨,柔腸一寸愁千縷。惜春春去。幾點催花雨。倚遍闌干,只是無",
"userId": 7,
"weight": 7
},
{
"id": "AV5-ekRHcu4Vu652ZQrw",
"title": "我是",
"content": "中華共和國",
"userId": 2,
"weight": 2
},
{
"id": "AV5-ekBvcu4Vu652ZQrv",
"title": "我是",
"content": "我愛中華人民共和國",
"userId": 1,
"weight": 1
}
]
可以看到,無論是title還是content中,包含“我”“是”字樣的都被查詢了出來。MultiMatch還有一些其他的屬性,
包括Type、tieBreaker等的解釋看下圖
以上圖片來源於http://www.cnblogs.com/yjf512/p/4897294.html
完全包含查詢
之前的查詢中,當我們輸入“我天”時,ES會把分詞後所有包含“我”和“天”的都查詢出來,如果我們希望必須是包含了兩個字的才能被查詢出來,那麼我們就需要設定一下Operator。/**
* 單欄位包含所有輸入
*/
@RequestMapping("/contain")
public Object contain(String title) {
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", title).operator(MatchQueryBuilder.Operator.AND)).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
無論是matchQuery,multiMatchQuery,queryStringQuery等,都可以設定operator。預設為Or,設定為And後,就會把符合包含所有輸入的才查出來。如果是and的話,譬如使用者輸入了5個詞,但包含了4個,也是顯示不出來的。我們可以通過設定精度來控制。/**
* 單欄位包含所有輸入(按比例包含)
*/
@RequestMapping("/contain")
public Object contain(String title) {
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchQuery("title", title).operator(MatchQueryBuilder.Operator.AND).minimumShouldMatch("75%")).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
minimumShouldMatch可以用在match查詢中,設定最少匹配了多少百分比的能查詢出來。合併查詢
即boolQuery,可以設定多個條件的查詢方式。它的作用是用來組合多個Query,有四種方式來組合,must,mustnot,filter,should。must代表返回的文件必須滿足must子句的條件,會參與計算分值;filter代表返回的文件必須滿足filter子句的條件,但不會參與計算分值;should代表返回的文件可能滿足should子句的條件,也可能不滿足,有多個should時滿足任何一個就可以,通過minimum_should_match設定至少滿足幾個。mustnot代表必須不滿足子句的條件。譬如我想查詢title包含“XXX”,且userId=“1”,且weight最好小於5的結果。那麼就可以使用boolQuery來組合。/**
* 多欄位合併查詢
*/
@RequestMapping("/bool")
public Object bool(String title, Integer userId, Integer weight) {
SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQuery().must(termQuery("userId", userId))
.should(rangeQuery("weight").lt(weight)).must(matchQuery("title", title))).build();
return elasticsearchTemplate.queryForList(searchQuery, Post.class);
}
如果某個欄位需要匹配多個值,譬如userId為1,2,3任何一個的,類似於mysql中的in,那麼可以使用termsQuery("userId", ids).如果某欄位是字串,我建議空的就設定為null,不要為""空串,貌似某些版本的ES,使用matchQuery空串會不生效。詳細點的看這篇http://blog.csdn.net/dm_vincent/article/details/41743955boolQuery使用場景非常廣泛,應該是主要學習的知識之一。
Query和Filter的區別
從程式碼上就能看出來,query和Filter都是QueryBuilder,也就是說在使用時,你把Filter的條件放到withQuery裡也行,反過來也行。那麼它們兩個區別在哪?查詢在Query查詢上下文和Filter過濾器上下文中,執行的操作是不一樣的:
1、查詢:是在使用query進行查詢時的執行環境,比如使用search的時候。
在查詢上下文中,查詢會回答這個問題——“這個文件是否匹配這個查詢,它的相關度高麼?”
ES中索引的資料都會儲存一個_score分值,分值越高就代表越匹配。即使lucene使用倒排索引,對於某個搜尋的分值計算還是需要一定的時間消耗。
2、過濾器:在使用filter引數時候的執行環境,比如在bool查詢中使用Must_not或者filter
在過濾器上下文中,查詢會回答這個問題——“這個文件是否匹配?”
它不會去計算任何分值,也不會關心返回的排序問題,因此效率會高一點。
另外,經常使用過濾器,ES會自動的快取過濾器的內容,這對於查詢來說,會提高很多效能。
總而言之:
1 查詢上下文:查詢操作不僅僅會進行查詢,還會計算分值,用於確定相關度;
2 過濾器上下文:查詢操作僅判斷是否滿足查詢條件,不會計算得分,查詢的結果可以被快取。
所以,根據實際的需求是否需要獲取得分,考慮效能因素,選擇不同的查詢子句。
這篇大概就講這麼多,已經能滿足大部分場景了。