赞
踩
本文主要讲解,springBoot 使用spring-boot-starter-data-elasticsearch方式整合Elasticsearch
文末附源码
什么是Spring Data ElasticSearch
Spring Data ElasticSearch 基于 spring data API 简化 elasticSearch操作,将原始操作elasticSearch的客户端API进行封装 。Spring Data为Elasticsearch项目提供集成搜索引擎。Spring Data Elasticsearch POJO的关键功能区域为中心的模型与Elastichsearch交互文档和轻松地编写一个存储库数据访问层。
这里需要注意一下版本
官方版本对照:传送门
优点:
缺点:
开发环境:
官网文档4.0版本client使用介绍:传送门
创建springBoot项目,添加整合elasticsearch依赖
<!--spring boot 整合 elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
server.port=8091
spring.elasticsearch.rest.uris=http://47.114.56.113:9200
spring.elasticsearch.rest.username=elastic
spring.elasticsearch.rest.password=1234567
创建一个员工的实体类
import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.Document; import org.springframework.data.elasticsearch.annotations.Field; /** * 员工实体类 * * @author 程序员小强 */ @Document(indexName = "employee_info", shards = 2, replicas = 2) public class EmployeeInfo { @Id private Long id; /** * 工号 */ @Field(name = "job_no") private String jobNo; /** * 姓名 */ @Field(name = "name") private String name; /** * 英文名 */ @Field(name = "english_name") private String englishName; /** * 工作岗位 */ private String job; /** * 性别 */ private Integer sex; /** * 年龄 */ private Integer age; /** * 薪资 */ private BigDecimal salary; /** * 入职时间 */ @Field(name = "job_day",format= DateFormat.date_time) private Date jobDay; /** * 备注 */ private String remark; //省略get set }
注:注解说明
Document注解
@Persistent @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface Document { String indexName();//索引库的名称,个人建议以项目的名称命名 @Deprecated String type() default "";//类型,7.x之后以废弃 short shards() default 1;//默认分区数 short replicas() default 1;//每个分区默认的备份数 String refreshInterval() default "1s";//刷新间隔 String indexStoreType() default "fs";//索引文件存储类型 boolean createIndex() default true; //是否创建索引 VersionType versionType() default VersionType.EXTERNAL; //版本 }
@Field
注解
可以通过@Field
注解来进行详细的指定,如果无特殊需求,那么只需要添加@Document即可。
常见@Field,用于定义别名,或者针对日期属性进行格式化设定。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @Documented @Inherited public @interface Field { @AliasFor("name") String value() default ""; //属性别名 @AliasFor("value") String name() default ""; //属性别名 FieldType type() default FieldType.Auto; //属性类型,默认自动根据参数类型自动属性的类型 boolean index() default true; //默认情况下分词 DateFormat format() default DateFormat.none; //时间格式化 String pattern() default ""; boolean store() default false; //默认情况下不存储原文 String searchAnalyzer() default ""; //指定字段搜索时使用的分词器 String indexAnalyzer() default "";//指定字段建立索引时指定的分词器 String[] ignoreFields() default {};//忽略某些字段 //以下是一些不常用的设置 boolean includeInParent() default false; String[] copyTo() default {}; int ignoreAbove() default -1; boolean coerce() default true; boolean docValues() default true; boolean ignoreMalformed() default false; IndexOptions indexOptions() default IndexOptions.none; boolean indexPhrases() default false; IndexPrefixes[] indexPrefixes() default {}; boolean norms() default true; String nullValue() default ""; int positionIncrementGap() default -1; Similarity similarity() default Similarity.Default; TermVector termVector() default TermVector.none; double scalingFactor() default 1; int maxShingleSize() default -1; }
import com.example.elasticsearch.bean.EmployeeInfo;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* @author 程序员小强
*/
//泛型的参数分别是实体类型和主键类型
public interface EmployeeInfoRepository extends ElasticsearchRepository<EmployeeInfo, Long> {
}
/**
* @author 程序员小强
*/
public interface EmployeeInfoService {
void deleteIndex(String index);
void save(EmployeeInfo docBean);
void saveAll(List<EmployeeInfo> list);
Iterator<EmployeeInfo> findAll();
}
/** * @author 程序员小强 */ @Service public class EmployeeInfoServiceImpl implements EmployeeInfoService { @Resource private EmployeeInfoRepository elasticRepository; @Resource private ElasticsearchRestTemplate elasticsearchTemplate; @Override public void deleteIndex(String index) { elasticsearchTemplate.deleteIndex(index); } @Override public void save(EmployeeInfo docBean) { elasticRepository.save(docBean); } @Override public void saveAll(List<EmployeeInfo> list) { elasticRepository.saveAll(list); } @Override public Iterator<EmployeeInfo> findAll() { return elasticRepository.findAll().iterator(); } }
@RestController @RequestMapping("/employeeInfo") public class EmployeeElasticController { @Autowired private EmployeeInfoService employeeInfoService; @RequestMapping("/batchSave") public String init() throws Exception { List<EmployeeInfo> list = new ArrayList<>(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); list.add(new EmployeeInfo(1001L, "2001", "张三", "zhangsan", "Java", 1, 19, new BigDecimal("12500.01"), simpleDateFormat.parse("2019-09-10"), "备注")); list.add(new EmployeeInfo(1002L, "2002", "李四", "lisi", "PHP", 1, 18, new BigDecimal("11600.01"), simpleDateFormat.parse("2019-09-10"), "备注")); list.add(new EmployeeInfo(1003L, "2003", "王五", "wangwu", "C++", 1, 20, new BigDecimal("9900.01"), simpleDateFormat.parse("2019-09-10"), "备注")); list.add(new EmployeeInfo(1004L, "2004", "赵六", "zhaoliu", "Java Leader", 1, 20, new BigDecimal("20000.01"), simpleDateFormat.parse("2019-09-10"), "备注")); list.add(new EmployeeInfo(1005L, "2005", "小五", "xiaowu", "H5", 1, 17, new BigDecimal("10600.01"), simpleDateFormat.parse("2019-09-10"), "备注")); list.add(new EmployeeInfo(1006L, "2006", "小六", "xaioliu", "web", 1, 20, new BigDecimal("12600.01"), simpleDateFormat.parse("2019-09-10"), "备注")); list.add(new EmployeeInfo(1007L, "2007", "小七", "xiaoqi", "app", 1, 22, new BigDecimal("20000.01"), simpleDateFormat.parse("2019-09-10"), "备注")); list.add(new EmployeeInfo(1008L, "2008", "小八", "xaioba", "Java", 1, 21, new BigDecimal("11000.01"), simpleDateFormat.parse("2019-09-10"), "备注")); list.add(new EmployeeInfo(1009L, "2009", "小九", "xiaojiu", "Java", 1, 20, new BigDecimal("14000.01"), simpleDateFormat.parse("2019-09-10"), "备注")); list.add(new EmployeeInfo(1010L, "2010", "大十", "dashi", "Java", 1, 20, new BigDecimal("13000.01"), simpleDateFormat.parse("2019-09-10"), "备注")); employeeInfoService.batchSaveOrUpdate(list); return "success -> " + list.size(); } @GetMapping("/listAll") public Iterator<EmployeeInfo> all() { return employeeInfoService.findAll(); } }
简介
spring data elsaticsearch提供了三种构建查询模块的方式:
接口只要继承 ElasticsearchRepository 类即可。默认会提供很多实现,比如 CRUD 和搜索相关的实现。类似于 JPA 读取数据。
支持的默认方法有:
count(), findAll(), findOne(ID), delete(ID), deleteAll(), exists(ID), save(DomainObject), save(Iterable)。
接口的命名是遵循规范的。常用命名规则如下:
表格内容摘自官网(官方文档:传送门)
关键字 | 方法命名 | Elasticsearch 查询DSL语法示例 |
---|---|---|
And | findByNameAndPrice | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } }, { “query_string” : { “query” : “?”, “fields” : [ “price” ] } } ] } }} |
Or | findByNameOrPrice | { “query” : { “bool” : { “should” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } }, { “query_string” : { “query” : “?”, “fields” : [ “price” ] } } ] } }} |
Is | findByName | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } } ] } }} |
Not | findByNameNot | { “query” : { “bool” : { “must_not” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } } ] } }} |
Between | findByPriceBetween | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }} |
LessThan | findByPriceLessThan | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : false } } } ] } }} |
LessThanEqual | findByPriceLessThanEqual | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }} |
GreaterThan | findByPriceGreaterThan | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : false, “include_upper” : true } } } ] } }} |
GreaterThanEqual | findByPriceGreaterThan | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : true, “include_upper” : true } } } ] } }} |
Before | findByPriceBefore | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }} |
After | findByPriceAfter | { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : true, “include_upper” : true } } } ] } }} |
Like | findByNameLike | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?*”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }} |
StartingWith | findByNameStartingWith | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?*”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }} |
EndingWith | findByNameEndingWith | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “*?”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }} |
Contains/Containing | findByNameContaining | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }} |
In (when annotated as FieldType.Keyword) | findByNameIn(Collectionnames) | { “query” : { “bool” : { “must” : [ {“bool” : {“must” : [ {“terms” : {“name” : ["?","?"]}} ] } } ] } }} |
In | findByNameIn(Collectionnames) | { “query”: {“bool”: {“must”: [{“query_string”:{“query”: “”?" “?”", “fields”: [“name”]}}]}}} |
NotIn (when annotated as FieldType.Keyword) | findByNameNotIn(Collectionnames) | { “query” : { “bool” : { “must” : [ {“bool” : {“must_not” : [ {“terms” : {“name” : ["?","?"]}} ] } } ] } }} |
NotIn | findByNameNotIn(Collectionnames) | {“query”: {“bool”: {“must”: [{“query_string”: {“query”: “NOT(”?" “?”)", “fields”: [“name”]}}]}}} |
Near | findByStoreNear | Not Supported Yet ! |
True | findByAvailableTrue | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “true”, “fields” : [ “available” ] } } ] } }} |
False | findByAvailableFalse | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “false”, “fields” : [ “available” ] } } ] } }} |
OrderBy | findByAvailableTrueOrderByNameDesc | { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “true”, “fields” : [ “available” ] } } ] } }, “sort”:[{“name”:{“order”:“desc”}}] } |
按照接口的命名方法示例
/** * @author 程序员小强 */ public interface EmployeeInfoRepository extends ElasticsearchRepository<EmployeeInfo, Long> { /** * 精确查找 * 方法名规则:finByxxx * * @param name * @return 员工数据集 */ List<EmployeeInfo> findByName(String name); /** * AND 语句查询 * * @param name * @param age * @return 员工数据集 */ List<EmployeeInfo> findByNameAndAge(String name, Integer age); /** * OR 语句查询 * * @param name * @param age * @return 员工数据集 */ List<EmployeeInfo> findByNameOrAge(String name, Integer age); /** * 分页查询员工信息 * * @param name * @param page * @return 员工数据集 * 注:等同于下面代码 @Query("{\"bool\" : {\"must\" : {\"term\" : {\"name\" : \"?0\"}}}}") */ Page<EmployeeInfo> findByName(String name, Pageable page); /** * NOT 语句查询 * * @param name * @param page * @return 员工数据集 */ Page<EmployeeInfo> findByNameNot(String name, Pageable page); /** * LIKE 语句查询 * * @param name * @param page * @return 员工数据集 */ Page<EmployeeInfo> findByNameLike(String name, Pageable page); }
Data ElasticSearch 支持了一些常见的查询
但是一些高级查询呢?可以使用类组装DSL语法支持
/** * 聚合查询-groupBy * 聚合所有的年龄 */ @Test public void groupByAge() { //1.构建查询对象 NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("groupByAge") .field("age").size(30)); SearchHits<EmployeeInfo> search = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), EmployeeInfo.class); Aggregations aggregations = search.getAggregations(); //解析聚合分组后结果数据 ParsedLongTerms parsedLongTerms = aggregations.get("groupByAge"); //groupBy后的年龄集 List<String> ageList = parsedLongTerms.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()); System.out.println(ageList); }
/** * 分页查询 * 带参数 */ @Test public void listPageMatch() { int pageNo = 1; int pageSize = 5; NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder(); nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", "小")); //注:Pageable类中 pageNum需要减1,如果是第一页 数值为0 Pageable pageable = PageRequest.of(pageNo - 1, pageSize); nativeSearchQueryBuilder.withPageable(pageable); SearchHits<EmployeeInfo> searchHitsResult = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), EmployeeInfo.class); //7.获取分页数据 SearchPage<EmployeeInfo> searchPageResult = SearchHitSupport.searchPageFor(searchHitsResult, pageable); System.out.println("分页查询"); System.out.println(String.format("totalPages:%d, pageNo:%d, size:%d", searchPageResult.getTotalPages(), pageNo, pageSize)); System.out.println(JSON.toJSONString(searchPageResult.getSearchHits(), SerializerFeature.PrettyFormat)); }
关注程序员小强公众号更多编程趣事,知识心得与您分享
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。