1. 簡介
SpringBoot提供了與ElasticSearch的集成的starter包,並封裝了ElasticsearchRestTemplate類,還實現了與Java對象與ElasticSearch索引的映射關系,可以采用與JPA相似的Repository接口,來操作ES數據。
需要使用maven引用以下依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> <exclusions> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> </exclusion> <exclusion> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> <version>6.5.0</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>6.5.0</version> </dependency>
注意:以上的依賴版本可以根據你使用的ES的版本來定。比如我當前的ElasticSearch的版本是6.5.0,就需要手動替換成6.5.0版本的jar包。
2. 配置文件
2.1 application.yml
在SpringBoot項目中application.yml文件中增加以下配置:
spring: elasticsearch: rest: uris: http://192.168.220.11:9200 ---ES的連接地址,多個地址用逗號分隔 username: ---用戶名 password: ---密碼 connection-timeout: 1000 ---連接超時時間 read-timeout: 1000 ---讀取超時時間
2.2 創建映射對象
我這里定義了一個員工類,並使用@Document定義員工類關聯的index索引、typeso因類型,shards主分區,replicas副分區。
在@Document中有個屬性是createIndex,表示當索引不存在時,操作這個對象會默認創建索引,默認為true。如果你的索引設置比較多,就把createIndex設置為false,再通過其他接口手動觸發創建索引操作。
@Id 表示索引的主鍵
@Field 用來描述字段的ES數據類型,是否分詞等配置,等於Mapping描述
/** * 員工對象 * <p> * 注解:@Document用來聲明Java對象與ElasticSearch索引的關系 * indexName 索引名稱 * type 索引類型 * shards 主分區數量,默認5 * replicas 副本分區數量,默認1 * createIndex 索引不存在時,是否自動創建索引,默認true*/ @Getter @Setter @NoArgsConstructor @Accessors(chain = true) @Document(indexName = "employee_index", type = "employee_type", shards = 1, replicas = 0,createIndex = true) public class EmployeeBean { @Id private String id; /** * 員工編碼 */ @Field(type = FieldType.Keyword) private String studentCode; /** * 員工姓名 */ @Field(type = FieldType.Keyword) private String name; /** * 員工簡歷 */ @Field(type = FieldType.Text, analyzer = "ik_max_word") private String desc; /** * 員工住址 */ @Field(type = FieldType.Text, analyzer = "ik_max_word") private Integer type; /** * 手機號碼 */ @Field(type = FieldType.Keyword) private String mobile; }
2.3 創建Repository接口
Repository需要繼承ElasticsearchRepository接口,參數<映射對象,主鍵ID的數據類型>。之后Repository類就可以使用類似JPA的方法操作ElasticSearch數據。
@Component public interface EmployeeRepository extends ElasticsearchRepository<EmployeeBean, String> { }
我們在操作索引和數據時,需要引用這2個類
@Autowired private ElasticsearchRestTemplate restTemplate; @Autowired private EmployeeRepository repository;
3. 索引操作
3.1 判斷索引是否存在
/** * 判斷索引是否存在 * @return boolean */ public boolean indexExists() { return restTemplate.indexExists(EmployeeBean.class); } /** * 判斷索引是否存在 * @param indexName 索引名稱 * @return boolean */ public boolean indexExists(String indexName) { return restTemplate.indexExists(indexName); }
3.2 創建索引
/** * 創建索引(推薦使用:因為Java對象已經通過注解描述了Setting和Mapping) * @return boolean */ public boolean indexCreate() { return restTemplate.createIndex(EmployeeBean.class); } /** * 創建索引 * @param indexName 索引名稱 * @return boolean */ public boolean indexCreate(String indexName) { return restTemplate.createIndex(indexName); }
3.3 刪除索引
/** * 索引刪除 * @param indexName 索引名稱 * @return boolean */ public boolean indexDelete(String indexName) { return restTemplate.deleteIndex(indexName); }
4. 數據操作
4.1 新增數據
/** * 新增數據 * @param bean 數據對象 */ public void save(EmployeeBean bean) { repository.save(bean); } /** * 批量新增數據 * @param list 數據集合 */ public void saveAll(List<EmployeeBean> list) { repository.saveAll(list); }
4.2 修改數據
/** * 修改數據 * @param indexName 索引名稱 * @param type 索引類型 * @param bean 修改數據對象,ID不能為空 */ public void update(String indexName, String type, EmployeeBean bean) { UpdateRequest updateRequest = new UpdateRequest(); updateRequest.retryOnConflict(1);//沖突重試 updateRequest.doc(JSONUtil.toJsonStr(bean), XContentType.JSON); updateRequest.routing(bean.getId());//默認是_id來路由的,用來路由到不同的shard,會對這個值做hash,然后映射到shard。所以分片 UpdateQuery query = new UpdateQueryBuilder().withIndexName(indexName).withType(type).withId(bean.getId()) .withDoUpsert(true)//不加默認false。true表示更新時不存在就插入 .withClass(EmployeeBean.class).withUpdateRequest(updateRequest).build(); UpdateResponse updateResponse = restTemplate.update(query); }
4.3 刪除數據
/** * 根據ID,刪除數據 * @param id 數據ID */public void deleteById(String id) { repository.deleteById(id); } /** * 根據對象刪除數據,主鍵ID不能為空 * @param bean 對象 */public void deleteByBean(EmployeeBean bean) { repository.delete(bean); } /** * 根據對象集合,批量刪除 * @param beanList 對象集合 */public void deleteAll(List<EmployeeBean> beanList) { repository.deleteAll(beanList); } /** * 刪除所有 */public void deleteAll() { repository.deleteAll(); } /** * 根據條件,自定義刪除(在setQuery中的條件,可以根據需求自由拼接各種參數,與查詢方法一樣) * @param indexName 索引 * @param type 索引類型 */public void delete(String indexName, String type) { DeleteQuery deleteQuery = new DeleteQuery(); deleteQuery.setIndex(indexName); deleteQuery.setType(type);//建index沒配置就是類名全小寫 deleteQuery.setQuery(new BoolQueryBuilder().must(QueryBuilders.termQuery("mobile","13526568454"))); restTemplate.delete(deleteQuery); }
4.4 批量操作
/** * 批量新增 * @param indexName 索引名稱 * @param type 索引類型 * @param beanList 新增對象集合 */public void batchSave(String indexName, String type, List<EmployeeBean> beanList) { List<IndexQuery> queries = new ArrayList<>(); IndexQuery indexQuery; int counter = 0; for (EmployeeBean item : beanList) { indexQuery = new IndexQuery(); indexQuery.setId(item.getId()); indexQuery.setSource(JSONUtil.toJsonStr(item)); indexQuery.setIndexName(indexName); indexQuery.setType(type); queries.add(indexQuery); //分批提交索引 if (counter != 0 && counter % 1000 == 0) { restTemplate.bulkIndex(queries); queries.clear(); System.out.println("bulkIndex counter : " + counter); } counter++; } //不足批的索引最后不要忘記提交 if (queries.size() > 0) { restTemplate.bulkIndex(queries); } restTemplate.refresh(indexName); } /** * 批量修改 * @param indexName 索引名稱 * @param type 索引類型 * @param beanList 修改對象集合 */public void batchUpdate(String indexName, String type, List<EmployeeBean> beanList) { List<UpdateQuery> queries = new ArrayList<>(); UpdateQuery updateQuery; UpdateRequest updateRequest; int counter = 0; for (EmployeeBean item : beanList) { updateRequest = new UpdateRequest(); updateRequest.retryOnConflict(1);//沖突重試 updateRequest.doc(item); updateRequest.routing(item.getId()); updateQuery = new UpdateQuery(); updateQuery.setId(item.getId()); updateQuery.setDoUpsert(true); updateQuery.setUpdateRequest(updateRequest); updateQuery.setIndexName(indexName); updateQuery.setType(type); queries.add(updateQuery); //分批提交索引 if (counter != 0 && counter % 1000 == 0) { restTemplate.bulkUpdate(queries); queries.clear(); System.out.println("bulkIndex counter : " + counter); } counter++; } //不足批的索引最后不要忘記提交 if (queries.size() > 0) { restTemplate.bulkUpdate(queries); } restTemplate.refresh(indexName); }
4.5 數據查詢
/** * 數據查詢,返回List * @param field 查詢字段 * @param value 查詢值 * @return List<EmployeeBean> */ @Override public List<EmployeeBean> queryMatchList(String field, String value) { MatchQueryBuilder builder = QueryBuilders.matchQuery(field, value); SearchQuery searchQuery = new NativeSearchQuery(builder); return restTemplate.queryForList(searchQuery, EmployeeBean.class); } /** * 數據查詢,返回Page * @param field 查詢字段 * @param value 查詢值 * @return AggregatedPage<EmployeeBean> */ @Override public AggregatedPage<EmployeeBean> queryMatchPage(String field, String value) { MatchQueryBuilder builder = QueryBuilders.matchQuery(field, value); SearchQuery searchQuery = new NativeSearchQuery(builder).setPageable(PageRequest.of(0, 100)); AggregatedPage<EmployeeBean> page = restTemplate.queryForPage(searchQuery, EmployeeBean.class); long totalElements = page.getTotalElements(); // 總記錄數 int totalPages = page.getTotalPages(); // 總頁數 int pageNumber = page.getPageable().getPageNumber(); // 當前頁號 List<EmployeeBean> beanList = page.toList(); // 當前頁數據集 Set<EmployeeBean> beanSet = page.toSet(); // 當前頁數據集 return page; }
QueryBuilders對象是用於創建查詢方法的,支持多種查詢類型,常用的查詢API包括以下方法:
/** * 關鍵字匹配查詢 * * @param name 字段的名稱 * @param value 查詢值 */ public static TermQueryBuilder termQuery(String name, String value) { return new TermQueryBuilder(name, value); } public static TermQueryBuilder termQuery(String name, int value) { return new TermQueryBuilder(name, value); } public static TermQueryBuilder termQuery(String name, long value) { return new TermQueryBuilder(name, value); } public static TermQueryBuilder termQuery(String name, float value) { return new TermQueryBuilder(name, value); } public static TermQueryBuilder termQuery(String name, double value) { return new TermQueryBuilder(name, value); } public static TermQueryBuilder termQuery(String name, boolean value) { return new TermQueryBuilder(name, value); } public static TermQueryBuilder termQuery(String name, Object value) { return new TermQueryBuilder(name, value); } /** * 關鍵字查詢,同時匹配多個關鍵字 * * @param name 字段名稱 * @param values 查詢值 */ public static TermsQueryBuilder termsQuery(String name, String... values) { return new TermsQueryBuilder(name, values); } /** * 創建一個匹配多個關鍵字的查詢,返回boolean * * @param fieldNames 字段名稱 * @param text 查詢值 */ public static MultiMatchQueryBuilder multiMatchQuery(Object text, String... fieldNames) { return new MultiMatchQueryBuilder(text, fieldNames); // BOOLEAN is the default } /** * 關鍵字,精確匹配 * * @param name 字段名稱 * @param text 查詢值 */ public static MatchQueryBuilder matchQuery(String name, Object text) { return new MatchQueryBuilder(name, text); } /** * 關鍵字范圍查詢(后面跟范圍條件) * * @param name 字段名稱 */ public static RangeQueryBuilder rangeQuery(String name) { return new RangeQueryBuilder(name); } /** * 判斷字段是否有值 * * @param name 字段名稱 */ public static ExistsQueryBuilder existsQuery(String name) { return new ExistsQueryBuilder(name); } /** * 模糊查詢 * * @param name 字段名稱 * @param value 查詢值 */ public static FuzzyQueryBuilder fuzzyQuery(String name, String value) { return new FuzzyQueryBuilder(name, value); } /** * 組合查詢對象,可以同時引用上面的所有查詢對象 */ public static BoolQueryBuilder boolQuery() { return new BoolQueryBuilder(); }
4.6 聚合查詢
AggregationBuilders對象是用於創建聚合方法的,支持多種查詢類型,常用的查詢API包括以下方法:
/** * 根據字段聚合,統計該字段的每個值的數量 */ public static TermsAggregationBuilder terms(String name) { return new TermsAggregationBuilder(name, null); } /** * 統計操作的,過濾條件 */ public static FilterAggregationBuilder filter(String name, QueryBuilder filter) { return new FilterAggregationBuilder(name, filter); } /** * 設置多個過濾條件 */ public static FiltersAggregationBuilder filters(String name, KeyedFilter... filters) { return new FiltersAggregationBuilder(name, filters); } /** * 統計該字段的數據總數 */ public static ValueCountAggregationBuilder count(String name) { return new ValueCountAggregationBuilder(name, null); } /** * 計算平均值 */ public static AvgAggregationBuilder avg(String name) { return new AvgAggregationBuilder(name); } /** * 計算最大值 */ public static MaxAggregationBuilder max(String name) { return new MaxAggregationBuilder(name); } /** * 計算最小值 */ public static MinAggregationBuilder min(String name) { return new MinAggregationBuilder(name); } /** * 計算總數 */ public static SumAggregationBuilder sum(String name) { return new SumAggregationBuilder(name); }
