match: 分词查询 match_phrase 整个词查询 aggs 聚合查询 avg平均值
ElasticsearchTemplate的详细使用,完成多条件查询、匹配度查询
ElasticsearchTemplate是Spring对ES的java api进行的封装,提供了大量的相关的类来完成各种各样的查询。在日常的使用中,应该说最常用的查询就是queryList方法。
public <T> List<T> queryForList(SearchQuery query, Class<T> clazz) {
return this.queryForPage(query, clazz).getContent();
}
而我们的主要工作就是构建SearchQuery查询条件,包括排序、分页等条件都包含在SearchQuery中。在之前的一篇文章中已经简单的讲过这几个类的继承关系了,这里我们主要看看使用方法( 先阅读该篇了解基本的继承关系)。
直接上例子,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
部分结果:
es使用过程中遇到的一些问题:
问题1
描述:资产编码“01FIRSTsec012017051510110”,加上FieldIndex.not_analyzed注解后,资产搜索(“FIRSTsec01”)搜不到字母与数字的模糊组合,使用analyzer = ""注解后可以搜索到
代码段:
//搜索不到
@Field(type = FieldType.String, index = FieldIndex.not_analyzed,store = true)
private String transAssetCode;
//搜索到
@Field(type = FieldType.String, analyzer = "",store = true)
private String transAssetCode;
原因解释:
采用ES默认分析器的过滤结果为:
curl -XGET 'localhost:9200/_analyze?analyzer=standard&pretty=true' -d 'FIRSTsec01'
{
"tokens" : [ {
"token" : "firstsec01",
"start_offset" : 0,
"end_offset" : 10,
"type" : "<ALPHANUM>",
"position" : 0
} ]
}
采用默认分析器时,‘FIRSTsec01’ 在存储进ES时被分析器抓换为小写的 ‘firstsec01’,代码中我们搜索时,一律把英文字符转换为小写搜索,因此搜索‘FIRSTsec01’转换为小写的‘firstsec01’,可以搜出结果
而采用FieldIndex.not_analyzed注解时,默认不做分析,此时ES中存储的是大写的字母,因此搜索时在代码中转换为小写后搜索不到结果。
问题2:
描述:更改字段的分析器类型后,启动报错
原因解释:
1.更改分析器后,ES启动在PutMapping时,会检查分析器类型是否一致,不一致会报错
所以当更改分析器类型时,只能reindex所有文档,要想做到无缝切换,可以使用别名
tips:增加字段不受影响,不需要reIndex!
参见:https://www.elastic.co/blog/changing-mapping-with-zero-downtime
ES配置相关
1.配置ES刷新间隔
curl -XPUT 'http://59.110.9.92:9200/cloud_asset/_settings' -d '{
"index" : {
"refresh_interval" : "-1"
}
}'
-1代表关闭自动刷新,这个刷新是指刷新doc进入segment,doc刷新后是可搜索状态,但并不是写入磁盘,见https://www.elastic.co/guide/en/elasticsearch/guide/current/near-real-time.html