Rest风格说明
一种软件架构风格,而不是标准。更易于实现缓存等机制
**
客户端使用规则
**
**
新增操作
**
保存一个数据,保存在哪个索引的哪个类型下(哪张数据库哪张表下),保存时用唯一标识指定
POST指定ID新增
结论:指定ID新增,如果ID一样,数据不存在,是新增;数据存在,是更新;而且_seq_no和_version 每次调用值都会发送改变
POST不指定ID新增
结论:不指定ID,每次点击都会自动生成ID值,而且都是新增
PUT指定id新增
结论:PUT可以新增也可以修改。PUT必须指定id;由于PUT需要指定id,我们一般用来做修改操作,不指定id会报错
返回数据:
带有下划线开头的,称为元数据,反映了当前的基本信息。
{
"_index" : "demo", 表明该数据在哪个数据库下;
"_type" : "person",表明该数据在哪个类型下;
"_id" : "3", 表明被保存数据的id;
"_version" : 1, 被保存数据的版本
"result" : "create",数据的状态创建还是更新
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1, 而序列号"_seq_no"则可以看做是索引的信息
在第一次为索引插入数据时为0,每对索引内数据操作成功一次
sqlNO加1, 并且文档会记录是第几次操作使它成为现在的情况的
"_primary_term" : 1
}
更新
POST带_update
只修改传入的值
POST不带_update
直接覆盖原先的数据
put操作
覆盖操作
删除
ES的批量操作——bulk
批量新增
index批量新增
create批量新增
结论
create 创建操作,如果需要创建的文档已经存在,那么创建失败
index 创建或替换操作,如果要创建的文档不存在则执行创建操作,如果已经存在则执行替换操作
批量更新
通过 _bulk 批量更新索引中的文档数据,其中一条更新成功,一条因为无法获取对应ID的文档,更新失败
批量删除
代码示例:
文档 id 更新:
public void updateBookById(String id) {
Document document = Document.create();
document.put("commentCount", 1214666);
document.put("price", 66.6);
UpdateQuery updateQuery = UpdateQuery.builder(id).withDocument(document).build();
UpdateResponse response = elasticsearchRestTemplate.update(updateQuery, IndexCoordinates.of("book"));
System.out.println(response.getResult().name());
}
根据多个文档 id 批量更新:
public void bulkUpdateBook(String... ids) {
List<UpdateQuery> updateQueryList = new ArrayList<>();
for (String id : ids) {
Document document = Document.create();
document.put("commentCount", 1214666);
document.put("price", 66.6);
UpdateQuery updateQuery = UpdateQuery.builder(id).withDocument(document).build();
updateQueryList.add(updateQuery);
}
elasticsearchRestTemplate.bulkUpdate(updateQueryList, IndexCoordinates.of("book"));
}
根据文档 id 删除:
public void deleteBookById(String id) {
String result = elasticsearchRestTemplate.delete(id, Book.class);
System.out.println(result);
}
自定义删除条件,例如根据 skuId 删除:
public void deleteBookBySkuId(String skuId) {
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.termQuery("skuId", skuId))
.build();
elasticsearchRestTemplate.delete(nativeSearchQuery, Book.class, IndexCoordinates.of("book"));
}
查询:
@Autowired
ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* @param keyword 关键字
* @param pageNum 页码从1开始
* @param pageSize 每页数据条数
*/
public List<Book> queryBook(String keyword, int pageNum, int pageSize) {
BoolQueryBuilder keywordQueryBuilder = QueryBuilders.boolQuery()
.should(QueryBuilders.matchPhraseQuery("author", keyword))
.should(QueryBuilders.matchPhraseQuery("name", keyword));
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
.must(keywordQueryBuilder)
.must(QueryBuilders.rangeQuery("commentCount").gte(10));
HighlightBuilder highlightBuilder = new HighlightBuilder()
.field("author").field("name")
.preTags("<span style='color:red'>")
.postTags("</span>")
// 如果要高亮显示的字段内容很多,需要如下配置,避免高亮显示不全、内容缺失
.fragmentSize(1000) // 最大高亮分片数
.numOfFragments(0);// 从第一个分片获取高亮片段
AvgAggregationBuilder priceAvgAggregation = AggregationBuilders.avg("avgPrice").field("price");
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.withPageable(PageRequest.of(pageNum - 1, pageSize))
.withFields("author", "name", "price", "commentCount")
.withSort(new FieldSortBuilder("price").order(SortOrder.ASC))
.withHighlightBuilder(highlightBuilder)
.addAggregation(priceAvgAggregation)
.build();
// 开始查询
SearchHits<Book> search = elasticsearchRestTemplate.search(nativeSearchQuery, Book.class);
long totalHits = search.getTotalHits();
long totalPage = (totalHits % pageSize == 0) ? totalHits / pageSize : totalHits / pageSize + 1;
System.out.println("总数据条数:" + totalHits);
System.out.println("总页数:" + totalPage);
System.out.println("当前页码:" + pageNum);
double avgPrice = ((Avg) search.getAggregations().get("avgPrice")).getValue();
System.out.println("搜索到的书籍均价:" + avgPrice);
List<Book> resultList = new ArrayList<>();
// 解析查询结果
for (SearchHit<Book> searchHit : search.getSearchHits()) {
Book book = searchHit.getContent();
if (searchHit.getHighlightFields().containsKey("author")) {
// 提取高亮字段
book.setAuthor(searchHit.getHighlightFields().get("author").get(0));
}
if (searchHit.getHighlightFields().containsKey("name")) {
// 提取高亮字段
book.setName(searchHit.getHighlightFields().get("name").get(0));
}
resultList.add(book);
System.out.println(JSONObject.toJSONString(book));
}
return resultList;
}
构造查询条件:
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.build()
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
// 分页
.withPageable(PageRequest.of(pageNum - 1, pageSize))
// 指定查询结果包含那些文档字段
.withFields("author", "name", "price", "commentCount")
// 查询结果如何排序
.withSort(new FieldSortBuilder("price").order(SortOrder.ASC))
// 设置查询结果高亮
.withHighlightBuilder(highlightBuilder)
// 添加聚合查询
.addAggregation(priceAvgAggregation)
.build();
ElasticsearchTemplate
ElasticsearchTemplate 封装ES客户端的一些原生api模板,方便实现一些查询
elasticsearchTemplate.queryForPage #是查询一个分页列表,用的就是一个对象实例
NativeSearchQuery #是springdata中的查询条件
NativeSearchQueryBuilder #用于建造一个NativeSearchQuery查询对象
QueryBuilders #设置查询条件,是ES中的类
SortBuilders #设置排序条件
HighlightBuilder #设置高亮显示
QueryBuilders
QueryBuilders是ES中的查询条件构造器
QueryBuilders.boolQuery #子方法must可多条件联查
QueryBuilders.termQuery #精确查询指定字段
QueryBuilders.matchQuery #按分词器进行模糊查询
QueryBuilders.rangeQuery #按指定字段进行区间范围查询
# 大于等于 .from .gte
# 小于等于 .to .lte
NativeSearchQuery
原生的查询条件类,用来和ES的一些原生查询方法进行搭配,实现一些比较复杂的查询,最终进行构建.build 可作为ElasticsearchTemplate. queryForPage的参数使用
//构建Search对象
NativeSearchQuery build = new NativeSearchQueryBuilder()
//条件
.withQuery(queryBuilder)
//排序
.withSort(SortBuilders.fieldSort("id").order(SortOrder.ASC))
//高亮
.withHighlightFields(name, ms)
//分页
.withPageable(PageRequest.of(pageNum - 1, pageSize))
//构建
.build();
AggregatedPage<Goods> aggregatedPage = elasticsearchTemplate.queryForPage(build, Goods.class,new Hig());
//queryForPage
参数一: NativeSearchQuery 封装的查询数据对象
参数二: es对应索引实体类
参数三: 调用高亮工具类
示例: 注意:
bulkIndex 批量索引文档更新,文档不存在就建立,存在就覆盖,如果文档原来有3个字段,批量更新时有2个字段,在bulkIndex之后,它最后会变成最新的2个字段
bulkUpdate 批量更新文档字段,如果文档原来有3个字段,批量更新时有2个字段,结果还是3个字段
.should() 相当于 or, .must() 相当于 and
@Autowired
ElasticsearchTemplate elasticsearchTemplate;
@RequestMapping(value = "list")
public String list(@ModelAttribute(value = "vo") QueryVo vo, @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "5") Integer pageSize, Model model){
//高亮显示
String pre = "<span style='color:red'>";
String post = "</span>";
//指定要高亮的字段将其加上头尾标签
HighlightBuilder.Field name = new HighlightBuilder.Field("name").preTags(pre).postTags(post);
HighlightBuilder.Field ms = new HighlightBuilder.Field("ms").preTags(pre).postTags(post);
//查询高亮结果不分片,不加此条会按分词器高亮显示(数据变少)
ms.numOfFragments(1);
//多查询条件 must 可不断添加条件
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
if(StringUtils.isNoneBlank(vo.getName())){
//精确查询
queryBuilder.must(QueryBuilders.termQuery("name.keyword",vo.getName()));
}
if(StringUtils.isNoneBlank(vo.getMs())){
//模糊查询
queryBuilder.must(QueryBuilders.matchQuery("ms",vo.getMs()));
}
//根据指定字段区间查询 from(gte) 大于等于 to(lte) 小于等于
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
if(vo.getStartpice() != null){
rangeQuery.from(vo.getStartpice());
}
if(vo.getEndpice() != null){
rangeQuery.to(vo.getEndpice());
}
queryBuilder.must(rangeQuery);
//构建Search对象
NativeSearchQuery build = new NativeSearchQueryBuilder()
//条件
.withQuery(queryBuilder)
//排序
.withSort(SortBuilders.fieldSort("id").order(SortOrder.ASC))
//高亮
.withHighlightFields(name, ms)
//分页
.withPageable(PageRequest.of(pageNum - 1, pageSize))
//构建
.build();
AggregatedPage<Goods> aggregatedPage = elasticsearchTemplate.queryForPage(build, Goods.class,new Hig());
//封装分页数据至list集合中
Page<Goods> page = new Page<>(pageNum, pageSize);
//填充分页总条数
page.setTotal(aggregatedPage.getTotalElements());
//封装至pageinfo内,实现列表
PageInfo<Goods> pg = new PageInfo<>(page);
//将es查询到当前页的数据 封装至pg中
pg.setList(aggregatedPage.getContent());
//传入前端,实现列表页面
model.addAttribute("pg",pg);
return "list";
}