和jpa类似,很简单,很强大。

pom

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

application.yml

spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.56.101:9300

实体类及注解

@Document(indexName = "item",type = "docs", shards = 1, replicas = 0)
public class Item {
@Id
private Long id; @Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title; //标题 @Field(type = FieldType.Keyword)
private String category;// 分类 @Field(type = FieldType.Keyword)
private String brand; // 品牌 @Field(type = FieldType.Double)
private Double price; // 价格 @Field(index = false, type = FieldType.Keyword)
private String images; // 图片地址
}

Spring Data通过注解来声明字段的映射属性,有下面的三个注解:

  • @Document 作用在类,标记实体类为文档对象,一般有两个属性

    • indexName:对应索引库名称
    • type:对应在索引库中的类型
    • shards:分片数量,默认5
    • replicas:副本数量,默认1
  • @Id 作用在成员变量,标记一个字段作为id主键
  • @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
    • type:字段类型,取值是枚举:FieldType
    • index:是否索引,布尔类型,默认是true
    • store:是否存储,布尔类型,默认是false
    • analyzer:分词器名称

CRUD基本操作

创建索引和映射

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ItcastElasticsearchApplication.class)
public class IndexTest { @Autowired
private ElasticsearchTemplate elasticsearchTemplate; @Test
public void testCreate(){
// 创建索引,会根据Item类的@Document注解信息来创建
elasticsearchTemplate.createIndex(Item.class);
// 配置映射,会根据Item类中的id、Field等字段来自动完成映射
elasticsearchTemplate.putMapping(Item.class);
}
}

删除索引

@Test
public void deleteIndex() {
esTemplate.deleteIndex("heima");
} 可以根据类名或索引名删除。

Repository文档操作

Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。

我们只需要定义接口,然后继承它就OK了。

public interface ItemRepository extends ElasticsearchRepository<Item,Long> {
}

新增文档

@Autowired
private ItemRepository itemRepository; @Test
public void index() {
Item item = new Item(1L, "小米手机7", " 手机",
"小米", 3499.00, "http://image.leyou.com/13123.jpg");
itemRepository.save(item);
}

批量新增

@Test
public void indexList() {
List<Item> list = new ArrayList<>();
list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.leyou.com/123.jpg"));
list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.leyou.com/3.jpg"));
// 接收对象集合,实现批量新增
itemRepository.saveAll(list);
}

修改文档

修改和新增是同一个接口,区分的依据就是id,这一点跟我们在页面发起PUT请求是类似的。

基本查询

@Test
public void testFind(){
// 查询全部,并安装价格降序排序
Iterable<Item> items = this.itemRepository.findAll(Sort.by(Sort.Direction.DESC, "price"));
items.forEach(item-> System.out.println(item));
}

自定义方法

Spring Data 的另一个强大功能,是根据方法名称自动实现功能。

比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。

当然,方法名称要符合一定的约定:

Keyword Sample Elasticsearch Query String
And findByNameAndPrice {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
Or findByNameOrPrice {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
Is findByName {"bool" : {"must" : {"field" : {"name" : "?"}}}}
Not findByNameNot {"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
Between findByPriceBetween {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
LessThanEqual findByPriceLessThan {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
GreaterThanEqual findByPriceGreaterThan {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
Before findByPriceBefore {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
After findByPriceAfter {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
Like findByNameLike {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
StartingWith findByNameStartingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
EndingWith findByNameEndingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
Contains/Containing findByNameContaining {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}
In findByNameIn(Collection<String>names) {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
NotIn findByNameNotIn(Collection<String>names) {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
Near findByStoreNear Not Supported Yet !
True findByAvailableTrue {"bool" : {"must" : {"field" : {"available" : true}}}}
False findByAvailableFalse {"bool" : {"must" : {"field" : {"available" : false}}}}
OrderBy findByAvailableTrueOrderByNameDesc {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

例如,我们来按照价格区间查询,定义这样的一个方法:

public interface ItemRepository extends ElasticsearchRepository<Item,Long> {

    /**
* 根据价格区间查询
* @param price1
* @param price2
* @return
*/
List<Item> findByPriceBetween(double price1, double price2);
}

然后添加一些测试数据:

@Test
public void indexList() {
List<Item> list = new ArrayList<>();
list.add(new Item(1L, "小米手机7", "手机", "小米", 3299.00, "http://image.leyou.com/13123.jpg"));
list.add(new Item(2L, "坚果手机R1", "手机", "锤子", 3699.00, "http://image.leyou.com/13123.jpg"));
list.add(new Item(3L, "华为META10", "手机", "华为", 4499.00, "http://image.leyou.com/13123.jpg"));
list.add(new Item(4L, "小米Mix2S", "手机", "小米", 4299.00, "http://image.leyou.com/13123.jpg"));
list.add(new Item(5L, "荣耀V10", "手机", "华为", 2799.00, "http://image.leyou.com/13123.jpg"));
// 接收对象集合,实现批量新增
itemRepository.saveAll(list);
}

不需要写实现类,然后我们直接去运行:

@Test
public void queryByPriceBetween(){
List<Item> list = this.itemRepository.findByPriceBetween(2000.00, 3500.00);
for (Item item : list) {
System.out.println("item = " + item);
}
}

虽然基本查询和自定义方法已经很强大了,但是如果是复杂查询(模糊、通配符、词条查询等)就显得力不从心了。此时,我们只能使用原生查询。

高级查询

基本查询

先看看基本玩法

@Test
public void testQuery(){
// 词条查询
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "小米");
// 执行查询
Iterable<Item> items = this.itemRepository.search(queryBuilder);
items.forEach(System.out::println);
}

Repository的search方法需要QueryBuilder参数,elasticSearch为我们提供了一个对象QueryBuilders:

QueryBuilders提供了大量的静态方法,用于生成各种不同类型的查询对象,例如:词条、模糊、通配符等QueryBuilder对象。

elasticsearch提供很多可用的查询方式,但是不够灵活。如果想玩过滤或者聚合查询等就很难了。

自定义查询

先来看最基本的match query:

@Test
public void testNativeQuery(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米"));
// 执行搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
// 打印总条数
System.out.println(items.getTotalElements());
// 打印总页数
System.out.println(items.getTotalPages());
items.forEach(System.out::println);
}

NativeSearchQueryBuilder:Spring提供的一个查询条件构建器,帮助构建json格式的请求体

Page<item>:默认是分页查询,因此返回的是一个分页的结果对象,包含属性:

  • totalElements:总条数
  • totalPages:总页数
  • Iterator:迭代器,本身实现了Iterator接口,因此可直接迭代得到当前页的数据
  • 其它属性:

分页查询

利用NativeSearchQueryBuilder可以方便的实现分页:

@Test
public void testNativeQuery(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机")); // 初始化分页参数
int page = 0;
int size = 3;
// 设置分页参数
queryBuilder.withPageable(PageRequest.of(page, size)); // 执行搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
// 打印总条数
System.out.println(items.getTotalElements());
// 打印总页数
System.out.println(items.getTotalPages());
// 每页大小
System.out.println(items.getSize());
// 当前页
System.out.println(items.getNumber());
items.forEach(System.out::println);
}

可以发现,**Elasticsearch中的分页是从第0页开始**。

排序

排序也通用通过NativeSearchQueryBuilder完成:

@Test
public void testSort(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机")); // 排序
queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.DESC)); // 执行搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
// 打印总条数
System.out.println(items.getTotalElements());
items.forEach(System.out::println);
}

聚合

聚合为桶

桶就是分组,比如这里我们按照品牌brand进行分组:

@Test
public void testAgg(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 不查询任何结果
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
// 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(
AggregationBuilders.terms("brands").field("brand"));
// 2、查询,需要把结果强转为AggregatedPage类型
AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
// 3、解析
// 3.1、从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
// 3.2、获取桶
List<StringTerms.Bucket> buckets = agg.getBuckets();
// 3.3、遍历
for (StringTerms.Bucket bucket : buckets) {
// 3.4、获取桶中的key,即品牌名称
System.out.println(bucket.getKeyAsString());
// 3.5、获取桶中的文档数量
System.out.println(bucket.getDocCount());
} }

关键API:

AggregationBuilders:聚合的构建工厂类。所有聚合都由这个类来构建,看看他的静态方法:

AggregatedPage:聚合查询的结果类。它是Page<T>的子接口:

AggregatedPagePage功能的基础上,拓展了与聚合相关的功能,它其实就是对聚合结果的一种封装,大家可以对照聚合结果的JSON结构来看。

而返回的结果都是Aggregation类型对象,不过根据字段类型不同,又有不同的子类表示

我们看下页面的查询的JSON结果与Java类的对照关系:

嵌套聚合,求平均值

@Test
public void testSubAgg(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 不查询任何结果
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
// 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(
AggregationBuilders.terms("brands").field("brand")
.subAggregation(AggregationBuilders.avg("priceAvg").field("price")) // 在品牌聚合桶内进行嵌套聚合,求平均值
);
// 2、查询,需要把结果强转为AggregatedPage类型
AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
// 3、解析
// 3.1、从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
// 3.2、获取桶
List<StringTerms.Bucket> buckets = agg.getBuckets();
// 3.3、遍历
for (StringTerms.Bucket bucket : buckets) {
// 3.4、获取桶中的key,即品牌名称 3.5、获取桶中的文档数量
System.out.println(bucket.getKeyAsString() + ",共" + bucket.getDocCount() + "台"); // 3.6.获取子聚合结果:
InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("priceAvg");
System.out.println("平均售价:" + avg.getValue());
} }

elasticsearch 集成springboot的更多相关文章

  1. ElasticSearch集成SpringBoot与常见使用方法

    目录 一.导包 二.核对导入的ES版本 修改导入版本 三.写配置类 四.开始测试 索引操作 1.创建索引 2.查看索引是否存在 3.删除索引 文档操作 1.添加文档 2.查看文档是否存在 3.修改文档 ...

  2. Spring Maven项目集成Springboot

    Maven管理的Spring项目,准备集成Springboot做接口 1.Springboot对Spring有版本要求 我用的Springboot版本:1.4.5.RELEASE,对应Spring的版 ...

  3. linux下elasticsearch集成mongodb详细教程

    由于公司业务需要,要用elasticsearch做索引库实现搜索功能,历尽千辛万苦,最后总算把mongodb和elasticsearch集成成功 1.搭建mongodb集群 参考https://www ...

  4. 【读书笔记】Elasticsearch集成Hadoop最佳实践

    前言 本文记录[Elasticsearch集成Hadoop最佳实战]读书笔记 本书总计209页,共7章节,计划时间:20180712-20180717 (每天至少40页) 本文代码地址: https: ...

  5. 实战:docker搭建FastDFS文件系统并集成SpringBoot

    实战:docker搭建FastDFS文件系统并集成SpringBoot 前言 15年的时候,那时候云存储还远远没有现在使用的这么广泛,归根结底就是成本和安全问题,记得那时候我待的公司是做建站开发的,前 ...

  6. springboot elasticsearch 集成注意事项

    文章来源: http://www.cnblogs.com/guozp/p/8686904.html 一 elasticsearch基础 这里假设各位已经简单了解过elasticsearch,并不对es ...

  7. 「Elasticsearch」SpringBoot快速集成ES

    Elastic Search 的底层是开源库 Lucene.但是Lucene的使用门槛比较高,必须自己写代码去调用它的接口.而Elastic Search的出现正是为了解决了这个问题,它是 Lucen ...

  8. ElasticSearch集成SpringData史上最全查询教程

    1.简单介绍 springboot 使用springdata操作es,ElasticsearchRepository使用QueryBuilder构造查询条件 2.集成es //maven集成 < ...

  9. elasticSearch 7.6.1 入门及elasticSearch整合springboot

    一.ElasticSearch概述 官网:https://www.elastic.co/cn/downloads/elasticsearch Elaticsearch,简称为es,es是一个开源的高扩 ...

随机推荐

  1. BZOJ2565最长双回文串——manacher

    题目描述 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同).输入长度为n的串S,求S的最长双回文子串T,即可将T分为两 ...

  2. P1567 气温统计

    P1567 题目描述 炎热的夏日,KC 非常的不爽.他宁可忍受北极的寒冷,也不愿忍受厦门的夏天.最近,他开始研究天气的变化.他希望用研究的结果预测未来的天气. 经历千辛万苦,他收集了连续 N(1≤N≤ ...

  3. Win10下创建Python3.7创建虚拟环境以及安装Flask框架

    鉴于现在看到的很多虚拟环境创建以及flask框架安装方式需要通过dos命令来做,虽然比较常用,但是每次运行都要激活虚拟环境,相对比较麻烦,而现在利用pycharm大可不必如此. 1.安装破解版pych ...

  4. 【转】SEGGER Embedded Studio 新建stm32f103工程

    @2018-12-22 SEGGER Embedded Studio 新建stm32f103工程

  5. js-基本语法2

    类型转换 1.直接转换 parseInt() 与 parseFloat() alert('12'+7); //弹出127 alert( parseInt('12') + 7 ); //弹出19 ale ...

  6. 739. Daily Temperatures && 单调栈 && Python collections deque

    题目大意 给你接下来每一天的气温,求出对于每一天的气温,下一次出现比它高气温的日期距现在要等多少天 解题思路 利用单调栈,维护一个单调递减的栈 将每一天的下标i入栈,维护一个温度递减的下标 若下一个温 ...

  7. 20165223《Java程序设计》第八周Java学习总结

    教材学习内容总结 第12章-JAVA多线程机制 要点 Java中的线程 Thread类与线程的创建 线程的常用方法 线程同步 协调同步的线程 线程联合 GUI线程 计时器线程 教材学习中的问题和解决过 ...

  8. JSON字符串 与 JSON对象 互转

    一,JSON字符串与JSON对象的区别 JSON对象是符合JSON格式的对象,可以用"对象.属性"进行存取值; JSON字符串是符合JSON格式的字符串; 二,JSON字符串-&g ...

  9. bzoj2839 集合计数

    F.A.Qs Home Discuss ProblemSet Status Ranklist Contest 入门OJ ModifyUser  Logout 捐赠本站 2839: 集合计数 Time ...

  10. 快速傅里叶变换(FFT)

    扯 去北京学习的时候才系统的学习了一下卷积,当时整理了这个笔记的大部分.后来就一直放着忘了写完.直到今天都腊月二十八了,才想起来还有个FFT的笔记没整完呢.整理完这个我就假装今年的任务全都over了吧 ...