导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
添加配置
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.88.130:9300
创建一个实体类
@Data
public class TestModel {
private int id;
private String name;
private String brand;
private String imageurl;
}
映射
Spring Data通过注解来声明字段的映射属性,有下面的三个注解:
-
@Document
作用在类,标记实体类为文档对象,一般有两个属性- indexName:对应索引库名称
- type:对应在索引库中的类型
- shards:分片数量,默认5
- replicas:副本数量,默认1
-
@Id
作用在成员变量,标记一个字段作为id主键 -
@Field
作用在成员变量,标记为文档的字段,并指定字段映射属性:- type:字段类型,取值是枚举:FieldType
- index:是否索引,布尔类型,默认是true
- store:是否存储,布尔类型,默认是false
- analyzer:分词器名称
示例:
@Data
@Document(indexName = "testindex", type = "equipment", shards = 1, replicas = 1)
public class TestModel {
@Id
private int id;
@Field(type = FieldType.text, analyzer = "ik_max_word")
private String name;
@Field(type = FieldType.keyword)
private String brand;
@Field(type = FieldType.keyword, index = false)
private String imageurl;
}
1.Template索引操作
1.1创建索引和映射
@RestController
@RequestMapping("/")
public class TestController {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@PutMapping("test1")
public void createIndex(){
elasticsearchTemplate.createIndex(TestModel.class); //根据TestModel类,生成index
elasticsearchTemplate.putMapping(TestModel.class); //根据TestModel类,生成映射
}
}
1.2测试api 测试
1.3结果: 结果
删除索引用 deleteIndex(自行测试)
2.Repository文档操作
Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。
我们只需要定义接口,然后继承它就OK了。
2.1创建Repository接口
public interface TestModelRepository extends ElasticsearchRepository<TestModel,Integer> {
}
2.2新增文档
@Autowired
private TestModelRepository testModelRepository;
@PostMapping("test2")
public void saveDocment(){
TestModel testModel = new TestModel(1, "testname", "testbrand", "1.jpg");
testModelRepository.save(testModel);
}
2.2.1测试 测试
2.2,2结果 结果
2.3使用saveAll()方法可以实现批量新增
2.4修改文档
修改和新增是同一个接口,区分的依据就是id,这一点跟我们在页面发起PUT请求是类似的。
2.5基本查询
@GetMapping("test3")
public Object getAll(){
return testModelRepository.findAll();
}
2.5.1测试&结果
结果2.6 自定义方法查询
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 TestModelRepository extends ElasticsearchRepository<TestModel,Integer> {
/**
* 根据id区间查询
* @param start
* @param end
* @return
*/
List<TestModel> findByIdBetween(Integer start, Integer end);
}
不需要写实现类,然后我们直接去运行:
@GetMapping("test4")
public Object queryById()
{
return testModelRepository.findByIdBetween(1,2);
}
2.6.1结果:
image.png虽然基本查询和自定义方法已经很强大了,但是如果是复杂查询(模糊、通配符、词条查询等)就显得力不从心了。此时,我们只能使用原生查询。
2.7高级查询
2.7.1基本玩法
@GetMapping("test5")
public Object testQuery()
{
// 词条查询
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("name", "testname");
//执行查询
Iterable<TestModel> testModels = testModelRepository.search(queryBuilder);
return testModels;
}
结果
结果 Repository的search方法需要QueryBuilder参数,elasticSearch为我们提供了一个对象QueryBuilders:QueryBuilders提供了大量的静态方法,用于生成各种不同类型的查询对象,例如:词条、模糊、通配符等QueryBuilder对象。 image.png虽然elasticsearch提供很多可用的查询方式,但是不够灵活。如果想玩过滤或者聚合查询等就很难了
2.8自定义查询
2.8.1基本match query
@GetMapping("test6")
public Object testNativeQuery(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
// 执行搜索,获取结果
Page<TestModel> testModels = testModelRepository.search(queryBuilder.build());
// 打印总条数
System.out.println(testModels.getTotalElements());
// 打印总页数
System.out.println(testModels.getTotalPages());
return testModels;
}
这个效果和上面的match query一样
2.8.2 分页查询
@GetMapping("test7")
public Object testNativeQuery(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
// 初始化分页参数
int page = 0;
int size = 1;
// 设置分页参数
queryBuilder.withPageable(PageRequest.of(page, size));
// 执行搜索,获取结果
Page<TestModel> testModels = testModelRepository.search(queryBuilder.build());
// 打印总条数
System.out.println(testModels.getTotalElements());
// 打印总页数
System.out.println(testModels.getTotalPages());
return testModels;
}
注意:page是从0开始的,不是从1开始的
2.8.3 排序
@GetMapping("test8")
public Object testNativeQuery(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
//排序
queryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));
// 执行搜索,获取结果
Page<TestModel> testModels = testModelRepository.search(queryBuilder.build());
// 打印总条数
System.out.println(testModels.getTotalElements());
// 打印总页数
System.out.println(testModels.getTotalPages());
return testModels;
}
这里是根据id,降序排序
结果
2.9 聚合
@GetMapping("test9")
public void testNativeQuery(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
//添加聚合,聚合的名称为brands,聚合的字段为brand
queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brand"));
// 执行搜索,获取结果
AggregatedPage<TestModel> aggPage = (AggregatedPage<TestModel>)testModelRepository.search(queryBuilder.build());
//解析
// 从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms brands = (StringTerms)aggPage.getAggregation("brands");
List<StringTerms.Bucket> buckets = brands.getBuckets();
System.out.println(buckets);
for (StringTerms.Bucket bucket : buckets) {
// 获取桶中的key,即品牌名称
System.out.println(bucket.getKeyAsString());
// 获取桶中的文档数量
System.out.println(bucket.getDocCount());
}
image.png
2.9.1嵌套聚合
@GetMapping("test9")
public void testNativeQuery(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本的分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("name", "testname"));
//添加聚合,聚合的名称为brands,聚合的字段为brand
queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brand")
.subAggregation(AggregationBuilders.avg("avgId").field("id")));
// 执行搜索,获取结果
AggregatedPage<TestModel> aggPage = (AggregatedPage<TestModel>)testModelRepository.search(queryBuilder.build());
//解析
// 从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms brands = (StringTerms)aggPage.getAggregation("brands");
List<StringTerms.Bucket> buckets = brands.getBuckets();
System.out.println(buckets);
for (StringTerms.Bucket bucket : buckets) {
// 获取桶中的key,即品牌名称
System.out.println(bucket.getKeyAsString());
// 获取桶中的文档数量
System.out.println(bucket.getDocCount());
InternalAvg avgId = (InternalAvg)bucket.getAggregations().asMap().get("avgId");
System.out.println(avgId.getValue());
}
}
网友评论