文章目录
8.1 环境准备
如果你还未安装es的相关信息,请先移步至:《ElasticSearch安装》进行安装
如果您的SpringBoot项目还未整合es,请移步至:《SpringBoot整合ElasticSearch实现模糊查询,批量CRUD,排序,分页,高亮》
同时本文的操作中涉及到ElasticSearchRepository和ElasticsearchRestTemplate的使用,如果您还不是很理解他们是怎么使用的,请移步:《ElasticSearchRepository和ElasticsearchRestTemplate的使用》
如果以上内容您都有掌握或了解,那就继续向下学习吧
8.2 数据准备
因为本文都是数据的搜索,所以我们需要在我们的es服务器里先插入一些数据以供我们后面使用
esUserService
public interface EsUserService extends ElasticsearchRepository<User, Integer> {
}
@RestController
public class EsController {
@Autowired
private ElasticsearchRestTemplate elasticsearchTemplate;
@Autowired
private EsUserService esUserService;
@Autowired
private RestHighLevelClient client;
private String[] names = {"诸葛亮", "曹操", "李白", "韩信", "赵云", "小乔", "狄仁杰", "李四", "诸小明", "王五"};
private String[] infos = {"我来自中国的一个小乡村,地处湖南省", "我来自中国的一个大城市,名叫上海,人们称作魔都"
, "我来自杭州,这是一个浪漫的城市"};
/**
* 准备数据
*
* @return
*/
@GetMapping("prepareDate")
public Object saveUser() {
//添加索引mapping索引会自动创建但mapping自只用默认的这会导致分词器不生效 所以这里我们手动导入mapping
Random random = new Random();
List<User> users = new ArrayList<>();
for (int i = 0; i < 20; i++) {
User user = new User();
user.setId(i);
user.setName(names[random.nextInt(9)]);
user.setAge(random.nextInt(40) + i);
user.setInfo(infos[random.nextInt(2)]);
users.add(user);
}
Iterable<User> users1 = esUserService.saveAll(users);
return users1;
}
}
下面开始我们本文的重点
由于我们需要频繁的使用map转对象,所以给大家一个比较好用的map转对象方法
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Objects;
/**
* 利用反射讲数据转化为Object
*
* @param <T>
*/
public class BeanHandler<T> {
private Class<T> clazz;
public BeanHandler(Class<T> clazz) {
this.clazz = clazz;
}
/**
* 讲sql 查询结果 ResultSet转化为对象
*
* @param rs
* @return
* @throws Exception
*/
public T handle(ResultSet rs) throws Exception {
//结果集默认指向为第一个数据的前一个
if (rs.next()) {
//根据传入的字节码创建传入的指定对象
T obj = clazz.newInstance();
//获取指定字节码信息
BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);
//获取所有属性描述器
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
//获取结果集中对应字段名的值
Object o = rs.getObject(pd.getName());
//执行当前方法并传入参数
pd.getWriteMethod().invoke(obj, o);
}
return obj;
}
return null;
}
/**
* 将map 利用反射转化为对象
*
* @param map
* @return
* @throws Exception
*/
public T handle(Map<String, Object> map) throws Exception {
//结果集默认指向为第一个数据的前一个
//根据传入的字节码创建传入的指定对象
T obj = clazz.newInstance();
BeanInfo beanInfo = Introspector.getBeanInfo(clazz, Object.class);
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
Object o = map.get(pd.getName());
if (Objects.nonNull(o)) {
// !!!这里需要对属性类型做强制类型转化,
o = getPropertyTypeObject(pd, o);
// 下面的方法相当于属性的 set方法
pd.getWriteMethod().invoke(obj, o);
}
}
return obj;
}
/**
* 将对应的mapValue 强转为实体类对应的类型
*
* @param pd
* @param o
* @return
*/
public Object getPropertyTypeObject(PropertyDescriptor pd, Object o) {
//当前属性的类型
String name = pd.getPropertyType().getName();
name = name.substring(name.lastIndexOf(".") + 1);
switch (name) {
case "String":
o = String.valueOf(o);
break;
case "Long":
o = Long.valueOf(String.valueOf(o));
break;
case "Double":
o = Double.valueOf(String.valueOf(o));
break;
case "Integer":
o = Integer.valueOf(String.valueOf(o));
break;
case "BigDecimal":
o = new BigDecimal(String.valueOf(o));
break;
}
return o;
}
}
8.3 单条件精确查询
@PostMapping("singleConditionPreciseQuery")
public Object singleConditionPreciseQuery(@RequestParam(value = "query") String query) throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.matchQuery("name", query));
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
List<User> users = new ArrayList<>();
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.4 范围查询
/**
* 范围查询
* 包括from、to
*
* @return
*/
@PostMapping("rangeSearch1")
public Object rangeSearch1(@RequestParam(value = "from") int from, @RequestParam(value = "to") int to) throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.rangeQuery("age").from(from).to(to));
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 解析查询结果
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
/**
* 范围查询
* 不包括from、to
*
* @return
*/
@PostMapping("rangeSearch2")
public Object rangeSearch2(@RequestParam(value = "from") int from, @RequestParam(value = "to") int to) throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.rangeQuery("age").from(from, false).to(to, false));
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 解析查询结果
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
/**
* 范围查询
* lt:小于,gt:大于
*
* @return
*/
@PostMapping("rangeSearch3")
public Object rangeSearch3(@RequestParam(value = "from") int from, @RequestParam(value = "to") int to) throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.rangeQuery("age").lt(to).gt(from));
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 解析查询结果
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.5 模糊查询,支持通配符
/**
* 模糊查询,支持通配符
*
* @return
*/
@PostMapping("vagueSearch")
public Object vagueSearch(@RequestParam(value = "query") String query) throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.wildcardQuery("name", query));
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
/**
* 不使用通配符的模糊查询,左右匹配
*
* @return
*/
@PostMapping("vagueSearch1")
public Object vagueSearch1(@RequestParam(value = "query") String query) throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.queryStringQuery(query).field("name"));
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
/**
* 多字段模糊查询
*
* @return
*/
@PostMapping("vagueSearch2")
public Object vagueSearch2(@RequestParam(value = "query") String query) throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.multiMatchQuery(query, "name", "info"));
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.6 排序
/**
* 排序
*
* @return
*/
@PostMapping("sortSearch")
public Object sortSearch(@RequestParam(value = "query") String query) throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.multiMatchQuery(query, "name", "info"))
.sort("age", SortOrder.ASC);//按照年龄正序
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.7 精确统计筛选文档数
/**
* 精确统计筛选文档数,查询性能有所降低
*
* @return
*/
@PostMapping("countSearch")
public Object countSearch() throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.trackTotalHits(true);
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 解析查询结果
return response;
}
8.8 设置源字段过滤返回
/**
* 设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段
*
* @return
*/
@PostMapping("filterSearch")
public Object filterSearch() throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.fetchSource(new String[]{"name", "age"}, new String[]{"id", "info"});
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
通过上图的结果我们也可以看出:
- 第一个参数结果集包括哪些字段
- 第二个参数表示结果集不包括哪些字段
8.9 根据 id 精确匹配
/**
* 根据Id精准查询
*
* @return
*/
@PostMapping("searchByIds")
public Object searchByIds(@RequestBody Map<String, Object> params) throws Exception {
List<Integer> ids = (List<Integer>) params.get("ids");
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.termsQuery("_id", ids));
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.10 matchAllQuery 搜索全部
/**
* matchAllQuery 搜索全部
*
* @return
*/
@PostMapping("martchAllQuery")
public Object martchAllQuery() throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.matchAllQuery());
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.11 match 搜索匹配
/**
* match 搜索匹配
*
* @return
*/
@PostMapping("martch")
public Object martch(@RequestBody Map<String, Object> params) throws Exception {
List<String> querys = (List<String>) params.get("querys");
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.matchQuery("name", querys));
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.12 bool组合查询
/**
* bool组合查询
*
* @return
*/
@PostMapping("boolSearch")
public Object boolSearch(@RequestBody Map<String, Object> params) throws Exception {
List<String> queryNames = (List<String>) params.get("names");
int maxAge = (int) params.get("maxAge");
int minAge = (int) params.get("minAge");
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(QueryBuilders.termsQuery("name", queryNames));
boolQueryBuilder.must(QueryBuilders.rangeQuery("age").lte(maxAge).gte(minAge));
builder.query(boolQueryBuilder);
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.13 nested类型嵌套查询
有时候,我们需要查询一个对象内部类的值,发现通过平时的查询查询不到数据,这时DSL(Domain Specific language,即特定领域专用语言)出场了!
elasticsearch中的内部对象无法按预期工作,这里的问题是elasticsearch(lucene)使用的库没有内部对象的概念,因此内部对象被扁平化为一个简单的字段名称和值列表。
/**
* nested类型嵌套查询
*
* @return
*/
@PostMapping("nestedSearch")
public Object nestedSearch() throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder();
//条件查询
BoolQueryBuilder mainBool=new BoolQueryBuilder();
mainBool.must(QueryBuilders.matchQuery("name", "赵六"));
//nested类型嵌套查询
BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();
boolQueryBuilder.must(QueryBuilders.matchQuery("user.name", "A"));
boolQueryBuilder.must(QueryBuilders.matchQuery("user.info", "浦东"));
NestedQueryBuilder nested = QueryBuilders.nestedQuery("user",boolQueryBuilder, ScoreMode.None);
mainBool.must(nested);
builder.query(mainBool);
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.14 多条件查询 + 排序 + 分页
/**
* 多条件查询 + 排序 + 分页
*
* @return
*/
@PostMapping("multiConditionSearch")
public Object multiConditionSearch() throws Exception {
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder();
//条件搜索
BoolQueryBuilder boolQueryBuilder=new BoolQueryBuilder();
boolQueryBuilder.must(QueryBuilders.matchQuery
("name", "张").operator(Operator.AND);//需要 满足所有字段);
boolQueryBuilder.must(QueryBuilders.rangeQuery("age").lte(30).gte(20));
builder.query(boolQueryBuilder);
//结果集合分页
builder.from(0).size(2);
//排序
builder.sort("age",SortOrder.ASC);
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 执行请求
List<User> users = new ArrayList<>();
for (SearchHit searchHit : response.getHits().getHits()) {
Map<String, Object> map = searchHit.getSourceAsMap();
BeanHandler<User> beanHandler = new BeanHandler<>(User.class);
User user = beanHandler.handle(map);
users.add(user);
}
// 解析查询结果
return users;
}
8.15 聚合查询
/**
* 求和
*
* @return
*/
@PostMapping("sumSearch")
public Object sumSearch() throws Exception {
Map<String, Object> result = new HashMap<>();
// 创建请求
SearchSourceBuilder builder = new SearchSourceBuilder()
.query(QueryBuilders.termsQuery("_id", new int[]{1, 2, 3}));
//条件搜索
//结果集合分页
builder.from(0).size(2);
builder.query(QueryBuilders.matchAllQuery());
//聚合查询
AggregationBuilder aggregation = AggregationBuilders.sum("sum_age").field("age");
builder.aggregation(aggregation);
//搜索
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("user");
searchRequest.types("_doc");
searchRequest.source(builder);
// 执行请求
SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
// 解析查询结果
return response;
}
值得我们注意的是在进行聚合操作的fild上,如果我们该字段设置成key或者text,则会出现以下错误
原因是:
文本字段未针对需要每个文档字段数据(如聚合和排序)的操作进行优化,因此默认情况下禁用这些操作。
我们需要改用关键字字段。或者,在设置了text或者key的字段上设置fielddata=true,以便通过取消反转索引来加载字段数据。
请注意,这可能会占用大量内存
git地址:https://gitee.com/ninesuntec/es-better.git
PS:本章git上的代码如果有被注释掉的,只是为了防止和后面的章节不冲突,并无错误,大家自行解注查看即可