提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
Spring Data ElasticSearch有下边这几种方法操作ElasticSearch:
QueryBuilders是ES中的查询条件构造器
QueryBuilders.boolQuery #子方法must可多条件联查
QueryBuilders.termQuery #精确查询指定字段
QueryBuilders.matchQuery #按分词器进行模糊查询
QueryBuilders.rangeQuery #按指定字段进行区间范围查询


/**
* 测试boot整合es
*/
@SpringBootTest(classes = {SpringbootEsApplication.class})
@RunWith(SpringRunner.class)
public class testES {
@Autowired
private ElasticsearchRestTemplate restTemplate;
/**
* 创建索引,并设置映射。
* 需要通过两次访问实现,1、创建索引;2、设置映射。
*/
@Test
public void testInitIndex(){
// 创建索引,根据类型上的Document注解创建
boolean isCreated = restTemplate.createIndex(Item.class);
// 设置映射,根据属性上的Field注解设置 0201
boolean isMapped = restTemplate.putMapping(Item.class);
System.out.println("创建索引是否成功:" + isCreated);
System.out.println("设置映射是否成功:" + isMapped);
}
/**
* 删除索引
*/
@Test
public void deleteIndex(){
// 扫描Item类型上的Document注解,删除对应的索引。
boolean isDeleted = restTemplate.deleteIndex(Item.class);
System.out.println("删除Item对应索引是否成功:" + isDeleted);
// 直接删除对应名称的索引。
isDeleted = restTemplate.deleteIndex("test_index3");
System.out.println("删除default_index索引是否成功:" + isDeleted);
}
}
index逻辑,相当于使用PUT请求,实现数据的新增
/**
* 新增数据到ES
*/
@Test
public void testInsert(){
Item item = new Item();
item.setId("19216811");
item.setTitle("沃什.伊戈.史莱姆");
item.setSellPoint("关于我成为史莱姆却因为铺垫太长遭人骂这档事");
item.setPrice(996666L);
item.setNum(233);
//方式 1:这个是构建器放入的方式
IndexQuery indexQuery = new IndexQueryBuilder() // 创建一个IndexQuery的构建器
.withObject(item) // 设置要新增的Java对象
.build(); // 构建IndexQuery类型的对象。
//方式 2: 这个是直接放入的方式
// IndexQuery query = new IndexQuery();
// query.setObject(item);
// index逻辑,相当于使用PUT请求,实现数据的新增。
String result = restTemplate.index(indexQuery);
System.out.println(result);
}
批量新增,使用的是bulkIndex() 操作
/**
* 批量新增
* bulk操作
*/
@Test
public void testBatchInsert(){
List<IndexQuery> queries = new ArrayList<IndexQuery>();
Item item = new Item();
item.setId("20210224");
item.setTitle("IPHONE 12 手机");
item.setSellPoint("很贵");
item.setPrice(499900L);
item.setNum(999);
queries.add(new IndexQueryBuilder().withObject(item).build());
item = new Item();
item.setId("20210225");
item.setTitle("华为P40照相手机");
item.setSellPoint("可以拍月亮");
item.setPrice(599900L);
item.setNum(999);
queries.add(new IndexQueryBuilder().withObject(item).build());
item = new Item();
item.setId("20210226");
item.setTitle("红米k30");
item.setSellPoint("大品牌值得信赖");
item.setPrice(699900L);
item.setNum(999);
queries.add(new IndexQueryBuilder().withObject(item).build());
// 批量新增,使用的是bulk操作。
restTemplate.bulkIndex(queries);
}
/**
* 新增单个文档
*/
@Test
void insert(){
Student student = new Student();
student.setAge(23);
student.setData("123");
student.setDesc("华为手机");
student.setId("1");
student.setName("张三");
Student save = elasticsearchRestTemplate.save(student);
System.out.println(save);
}
/**
* 批量新增
*/
@Test
void batchInsert(){
List<Student> list = new ArrayList<>();
list.add(new Student("2","李四","苹果手机","1",22));
list.add(new Student("3","王五","oppo手机","2",24));
list.add(new Student("4","赵六","voio手机","3",25));
list.add(new Student("5","田七","小米手机","4",26));
Iterable<Student> result = elasticsearchRestTemplate.save(list);
System.out.println(result);
}
/**
* 删除文档
*/
@Test
public void testDelete(){
// 根据主键删除, 常用
String result = restTemplate.delete(Item.class, "192168111");
System.out.println(result);
// 根据查询结果,删除查到的数据。 应用较少。
DeleteQuery query = new DeleteQuery();
query.setIndex("hrt-item");
query.setType("item");
query.setQuery(QueryBuilders.matchQuery("title", "沃什.伊戈.史莱姆1"));
restTemplate.delete(query, Item.class);
}
/**
* 根据查询条件删除
*/
@Test
void delete() {
QueryStringQueryBuilder queryBuilder = QueryBuilders.queryStringQuery("张三");
Query query = new NativeSearchQuery(queryBuilder);
ByQueryResponse QueryDelete = elasticsearchRestTemplate.delete(query, Student.class);
System.out.println(QueryDelete);
}
全量替换
/**
* 批量修改
*/
@Test
void update(){
Student student = new Student();
student.setId("1");
student.setAge(23);
student.setData("99");
student.setDesc("华为手机AND苹果手机");
student.setName("张三");
Student save = elasticsearchRestTemplate.save(student);
System.out.println(save);
}
部分修改
/**
* 部分修改
*/
@Test
void update2(){
// ctx._source 固定写法
String script = "ctx._source.age = 27;ctx._source.desc = 'oppo手机and苹果电脑'";
UpdateQuery build = UpdateQuery.builder("3").withScript(script).build();
IndexCoordinates indexCoordinates = IndexCoordinates.of("student_index");
UpdateResponse update = elasticsearchRestTemplate.update(build, indexCoordinates);
System.out.println(update.getResult());
}
/**
* 修改文档
* 如果是全量替换,可以使用index方法实现,只要主键在索引中存在,就是全量替换。<=>新增or批量新增操作
* 如果是部分修改,则可以使用update实现。
*/
@Test
public void testUpdate() throws Exception{
UpdateRequest request = new UpdateRequest();
request.doc(
XContentFactory.jsonBuilder()
.startObject()
.field("name", "测试update更新数据,商品名称")
.endObject()
);
UpdateQuery updateQuery =
new UpdateQueryBuilder()
.withUpdateRequest(request)
.withClass(Item.class)
.withId("20210224")
.build();
restTemplate.update(updateQuery);
}

SearchQuery query = new NativeSearchQuery(QueryBuilders.matchQuery("title", "华为mate40"));
List<Item> itemList = restTemplate.queryForList(query, Item.class);
SearchQuery - 是Spring Data Elasticsearch中定义的一个搜索接口
NativeSearchQuery - 是SearchQuery接口的实现类 ,构造的时候,需要提供一个QueryBuilder类型的对象
QueryBuilder是Elasticsearch的java客户端中定义的搜索条件类型。
QueryBuilders - 是QueryBuilder类型的工具类,可以快速实现QueryBuilder类型对象的创建工具类中,提供了大量的静态方法,方法命名和DSL搜索中的条件关键字相关。

new NativeSearchQuery(, , ,)


//构建Search对象
NativeSearchQuery query = new NativeSearchQueryBuilder()
//条件
.withQuery(QueryBuilders.matchQuery("title", "华为mate40"))
//排序
.withSort(SortBuilders.fieldSort("id").order(SortOrder.ASC))
//高亮
.withHighlightFields(name, ms)
//分页
.withPageable(PageRequest.of(pageNum - 1, pageSize))
//构建
.build();
//queryForPage 参数一: NativeSearchQuery 封装的查询数据对象
// 参数二: es对应索引实体类
// 参数三: 调用高亮工具类
AggregatedPage<Goods> aggregatedPage = elasticsearchTemplate.queryForPage(query , Goods.class,new Hig());
只查询一个id的
@Autowired
private ElasticsearchRestTemplate restTemplate;
/*
* 只查询一个id的
* QueryBuilders.idsQuery(String...type).ids(Collection ids)
*/
@Test
public void testIdsQuery() {
QueryBuilder query = QueryBuilders.idsQuery().ids("1");
List<Item> itemList = restTemplate.queryForList(query, Item.class);
}
去所有 field 中搜索指定条件。

/**
* 条件搜索
*/
@Test
public void testMatch(){
SearchQuery query = new NativeSearchQuery(QueryBuilders.matchQuery("title", "华为mate40"));
List<Item> itemList = restTemplate.queryForList(query, Item.class);
}
如果想使用一个字段匹配多个值,并且这多个值是and关系,如下 要求查询的数据中必须包含北京‘和’天津
- QueryBuilders.matchQuery(“address”,“北京 天津”).operator(Operator.AND)
如果想使用一个字段匹配多个值,并且这多个值是or关系,如下 要求查询的数据中必须包含北京‘或’天津
- QueryBuilders.matchQuery(“address”,“北京 天津”).operator(Operator.OR)
QueryBuilders.multiMatchQuery() 会分词 一个值对应多个字段 where username = ‘zs’ or password = ‘zs’
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* multimatch query
* 创建一个匹配查询的布尔型提供字段名称和文本。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
protected static QueryBuilder multiMatchQuery() {
//现住址和家乡在【山西省太原市7429街道】的人
return QueryBuilders.multiMatchQuery(
"zs", // Text you are looking for
"username ", "password " // Fields you query on
);
}
QueryBuilders.matchPhraseQuery() 不会分词,当成一个整体去匹配,相当于 %like%
短语搜索是对条件不分词,但是文档中属性根据配置实体类时指定的分词类型进行分词。
/**
* 短语搜索
* 只要被分词后锁短语含有, 则都会被搜索到
*/
@Test
public void testMatchPhrase(){
SearchQuery query = new NativeSearchQuery(QueryBuilders.matchPhraseQuery("title", "华为mate40"));
List<Item> itemList = restTemplate.queryForList(query, Item.class);
}
/**
* 搜索所有数据
*/
@Test
public void testMatchAll(){
SearchQuery query = new NativeSearchQuery(QueryBuilders.matchAllQuery());
List<Item> itemList = restTemplate.queryForList(query, Item.class);
}
/**
* 词组搜索
* 只有没有被切分的词组才能搜到结果, 例如: 华为荣耀已被切分, 则搜索不到; 而荣耀 不会被切分因此能够搜索到结果
* 区别于短语搜索
*/
@Test
public void testTerm(){
SearchQuery query = new NativeSearchQuery(QueryBuilders.termQuery("title", "mate40"));
List<Item> itemList = restTemplate.queryForList(query, Item.class);
}
一个查询相匹配的多个value
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* terms query
* 一个查询相匹配的多个value
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
protected static QueryBuilder termsQuery() {
return QueryBuilders.termsQuery("name", // field
"葫芦580娃", "葫芦3812娃") // values
.minimumMatch(1); // 设置最小数量的匹配提供了条件。默认为1。
}
protected static QueryBuilder termsQuery1() {
List<Long> channelIdList =new ArrayList<>();
return QueryBuilders.termsQuery("channel.channelId", channelIdList);
}
QueryBuilders.termQuery ------精确查询指定字段
QueryBuilders.matchQuery ------ 按分词器进行模糊查询
/**
* 范围搜索 range
* gte <==> 小于等于 ; gt <==> 小于
* lte <==> 大于等于 ; lt <==> 大于
*/
@Test
public void testRange(){
SearchQuery query = new NativeSearchQuery(
QueryBuilders.rangeQuery("price").gte(500000L).lte(400000L)
);
List<Item> itemList = restTemplate.queryForList(query, Item.class);
}
/**
* 复合条件搜索
*/
@Test
public void testBool(){
// 创建一个Bool搜索条件。 相当于定义 bool:{ must:[], should:[], must_not:[] }
BoolQueryBuilder builder = QueryBuilders.boolQuery();
List<QueryBuilder> mustList = builder.must();
//词组所搜+范围搜索
mustList.add(QueryBuilders.matchQuery("title", "华为"));
mustList.add(QueryBuilders.rangeQuery("price").gte(300000L));
// builder.mustNot();
// builder.should();
SearchQuery query = new NativeSearchQuery(builder);
List<Item> itemList = restTemplate.queryForList(query, Item.class);
}
/**
* 多条件查询
*/
@Test
void querysSearch(){
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
List<QueryBuilder> listQuery = new ArrayList<>();
listQuery.add(QueryBuilders.matchQuery("name","张三"));
listQuery.add(QueryBuilders.matchQuery("age","23"));
boolQueryBuilder.must().addAll(listQuery);// 逻辑 与
// boolQueryBuilder.should().addAll(listQuery);// 逻辑 或
NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(boolQueryBuilder );
SearchHits<Student> search = elasticsearchRestTemplate.search(nativeSearchQuery, Student.class);
List<SearchHit<Student>> searchHits = search.getSearchHits();
}
查询address 必须包含’汽车’ 或者’水果’的,查询id必须不能包含’2’和’0’的。此时需要构造两个条件,
@Test
public void testBoolQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest(indexName);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
boolQuery.must(QueryBuilders.matchQuery("address","水果 汽车").operator(Operator.OR));
boolQuery.mustNot(QueryBuilders.termsQuery("id","0","2"));
searchSourceBuilder.query(boolQuery);
searchRequest.source(searchSourceBuilder);
SearchResponse search = client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
for (SearchHit documentFields : search.getHits().getHits()) {
System.out.println(documentFields.getScore() + ":::" +documentFields.getSourceAsString());
}
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* boolean query and 条件组合查询
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
protected static QueryBuilder booleanQuery() {
return QueryBuilders
.boolQuery()
.must(QueryBuilders.termQuery("name", "葫芦3033娃"))
.must(QueryBuilders.termQuery("home", "山西省太原市7967街道"))
.mustNot(QueryBuilders.termQuery("isRealMen", false))
.should(QueryBuilders.termQuery("now_home", "山西省太原市"));
}
从上面的描述来看,你应该已经知道,如果只看查询的结果,must和filter是一样的。区别是场景不一样。如果结果需要算分就使用must,否则可以考虑使用filter。
filter查询就是用于精确过滤文档,它只关注文档是否符合条件,将匹配的文档包含在结果中。他们都不进行打分、排序或相关性计算,只担心是否匹配

//2、根据类目ID进行过滤
if (null != param.getCategoryId()) {
boolQueryBuilder.filter(QueryBuilders.termQuery("categoryId", param.getCategoryId()));
}
//3、根据品牌ID进行过滤
if (null != param.getBrandId() && param.getBrandId().size() > 0) {
boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", param.getBrandId()));
}
if (null != param.getHasStock()) {
boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", param.getHasStock() == 1));
}
如果实体类中主键只有@Id 注解,String id 对应 ES 中是 text 类型,text 类型是不允许被排序,
/**
* 分页和排序
* 所有的Spring Data子工程中的分页和排序逻辑使用的都是相似的方式。
* 根据PageRequest和Sort实现分页或排序。
*/
@Test
public void testPageable(){
SearchQuery query = new NativeSearchQuery(QueryBuilders.matchAllQuery());
// 设置分页
query.setPageable(PageRequest.of(0, 2));
// 设置排序
query.addSort(Sort.by(Sort.Direction.DESC, "price"));
// 设置分页的同时设置排序
// query.setPageable(PageRequest.of(0, 2, Sort.by(Sort.Direction.DESC, "price")))
List<Item> itemList = restTemplate.queryForList(query, Item.class);
}
//构建Search对象
NativeSearchQuery query = new NativeSearchQueryBuilder()
//条件
.withQuery(QueryBuilders.matchQuery("title", "华为mate40"))
//排序
.withSort(SortBuilders.fieldSort("id").order(SortOrder.ASC))
//高亮
.withHighlightFields(name, ms)
//分页
.withPageable(PageRequest.of(pageNum - 1, pageSize))
//构建
.build();
//queryForPage 参数一: NativeSearchQuery 封装的查询数据对象
// 参数二: es对应索引实体类
// 参数三: 调用高亮工具类
AggregatedPage<Goods> aggregatedPage = elasticsearchTemplate.queryForPage(query , Goods.class,new Hig());
/**
* 高亮
*/
@Test
public void testHighlight(){
HighlightBuilder.Field field = new HighlightBuilder.Field("title");
field.preTags("");
field.postTags("");
NativeSearchQuery query =
new NativeSearchQueryBuilder()
// 排序
.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC))
// 分页
.withPageable(PageRequest.of(0, 2))
// 搜索条件
.withQuery(QueryBuilders.matchQuery("title", "华为"))
// 设置高亮字段
.withHighlightFields(field)
.build();
AggregatedPage<? extends Item> pageResult =
restTemplate.queryForPage(query, Item.class, new SearchResultMapper() {
// 处理搜索结果,搜索的完整结果,也就是那个集合。
// response - 就是搜索的结果,相当于在Kibana中执行搜索的结果内容。
// clazz - 就是返回结果的具体类型
// pageable - 分页处理,就是queryForPage方法参数query中的pageable对象。
public <T> AggregatedPage<T> mapResults(SearchResponse response,
Class<T> clazz,
Pageable pageable) {
// 获取搜索的结果数据
SearchHit[] hits = response.getHits().getHits();
List<T> resultList = new ArrayList<T>();
for(SearchHit hit : hits){
// 搜索的source源
Map<String, Object> map = hit.getSourceAsMap();
Item item = new Item();
item.setId(map.get("id").toString());
item.setSellPoint(map.get("sellPoint").toString());
item.setPrice(Long.parseLong(map.get("price").toString()));
item.setNum(Integer.parseInt(map.get("num").toString()));
// 高亮数据处理。key - 字段名, value - 是高亮数据结果
Map<String, HighlightField> highlightFieldMap = hit.getHighlightFields();
HighlightField highlightField = highlightFieldMap.get("title");
if (highlightField == null){ // 没有高亮的title
item.setTitle(map.get("title").toString());
}else{ // 有高亮的title
item.setTitle(highlightField.getFragments()[0].toString());
}
resultList.add((T)item);
}
// 返回处理后的结果
return new AggregatedPageImpl<T>(
resultList, pageable, response.getHits().getTotalHits()
);
}
// 不提供实现,这个是处理每个搜索结果的方法
public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
return null;
}
});
for(Item item : pageResult.getContent()){
System.out.println(item);
}
}


/**
* TODO NotSolved
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* nested query
* 嵌套查询
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
protected static QueryBuilder nestedQuery() {
return QueryBuilders.nestedQuery("location", // Path
QueryBuilders.boolQuery() // Your query
.must(QueryBuilders.matchQuery("location.lat", 0.962590433140581))
.must(QueryBuilders.rangeQuery("location.lon").lt(0.00000000000000000003))
)
.scoreMode("total"); // max, total, avg or none