ElasticSearchRepository和ElasticSearchTemplate的使用

Spring-data-elasticsearch是Spring提供的操作ElasticSearch的数据层,封装了大量的基础操作,通过它可以很方便的操作ElasticSearch的数据。

版本说明

ElasticSearch目前最新的已到5.5.1

spring data elasticsearchelasticsearch
3.0.0.RC15.5.0
3.0.0.M45.4.0
2.0.4.RELEASE2.4.0
2.0.0.RELEASE2.2.0
1.4.0.M11.7.3
1.3.0.RELEASE1.5.2
1.2.0.RELEASE1.4.4
1.1.0.RELEASE1.3.2
1.0.0.RELEASE1.1.1

这有一个对应关系,不过不太完整,我目前使用的SpringBoot版本1.5.4对应的spring-data-ElasticSearch是2.1.4,在图上就没有体现。

但是可以预见对应的ElasticSearch应该在2.4.*往上,但应该是不支持5.4.0及以上。

注意:我这篇例子,所使用的ElasticSearch版本就是最新的5.5.1,SpringBoot版本是1.5.4,经初步试验,插入及查询都没问题。估计是5.5.*的新特性之类的会无法使用,基本操作应该都没问题。

ElasticSearchRepository的基本使用

[java]  view plain  copy
  1. @NoRepositoryBean  
  2. public interface ElasticsearchRepository<T, ID extends Serializable> extends ElasticsearchCrudRepository<T, ID> {  
  3.     <S extends T> S index(S var1);  
  4.   
  5.     Iterable<T> search(QueryBuilder var1);  
  6.   
  7.     Page<T> search(QueryBuilder var1, Pageable var2);  
  8.   
  9.     Page<T> search(SearchQuery var1);  
  10.   
  11.     Page<T> searchSimilar(T var1, String[] var2, Pageable var3);  
  12.   
  13.     void refresh();  
  14.   
  15.     Class<T> getEntityClass();  
  16. }  

我们是通过继承ElasticsearchRepository来完成基本的CRUD及分页操作的,和普通的JPA没有什么区别。

ElasticsearchRepository继承了ElasticsearchCrudRepository extends PagingAndSortingRepository.

先看看普通查询:
[java]  view plain  copy
  1. public interface BookRepository extends Repository<Book, String> {  
  2.   
  3.         List<Book> findByNameAndPrice(String name, Integer price);  
  4.   
  5.         List<Book> findByNameOrPrice(String name, Integer price);  
  6.           
  7.         Page<Book> findByName(String name,Pageable page);  
  8.   
  9.         Page<Book> findByNameNot(String name,Pageable page);  
  10.   
  11.         Page<Book> findByPriceBetween(int price,Pageable page);  
  12.   
  13.         Page<Book> findByNameLike(String name,Pageable page);  
  14.   
  15.         @Query("{\"bool\" : {\"must\" : {\"term\" : {\"message\" : \"?0\"}}}}")  
  16.         Page<Book> findByMessage(String message, Pageable pageable);  
  17.     }  
这个没什么特点,就是普通的JPA查询,这个很熟悉,通过上面的JPA查询就能完成很多的基本操作了。
插入数据也很简单:
[java]  view plain  copy
  1. @Autowired  
  2.         private SampleElasticsearchRepository repository;  
  3.   
  4.         String documentId = "123456";  
  5.         SampleEntity sampleEntity = new SampleEntity();  
  6.         sampleEntity.setId(documentId);  
  7.         sampleEntity.setMessage("some message");  
  8.   
  9.         repository.save(sampleEntity);  
还可以批量插入数据:
[java]  view plain  copy
  1. @Autowired  
  2.         private SampleElasticsearchRepository repository;  
  3.   
  4.         String documentId = "123456";  
  5.         SampleEntity sampleEntity1 = new SampleEntity();  
  6.         sampleEntity1.setId(documentId);  
  7.         sampleEntity1.setMessage("some message");  
  8.   
  9.         String documentId2 = "123457"  
  10.         SampleEntity sampleEntity2 = new SampleEntity();  
  11.         sampleEntity2.setId(documentId2);  
  12.         sampleEntity2.setMessage("test message");  
  13.   
  14.         List<SampleEntity> sampleEntities = Arrays.asList(sampleEntity1, sampleEntity2);  
  15.   
  16.         //bulk index  
  17.         repository.save(sampleEntities);  


特殊情况下,ElasticsearchRepository里面有几个特殊的search方法,这些是ES特有的,和普通的JPA区别的地方,用来构建一些ES查询的。
主要是看QueryBuilder和SearchQuery两个参数,要完成一些特殊查询就主要看构建这两个参数。
我们先来看看它们之间的类关系

从这个关系中可以看到ES的search方法需要的参数SearchQuery是一个接口,有一个实现类叫NativeSearchQuery,实际使用中,我们的主要任务就是构建NativeSearchQuery来完成一些复杂的查询的。


我们可以看到要构建NativeSearchQuery,主要是需要几个构造参数
[java]  view plain  copy
  1. public NativeSearchQuery(QueryBuilder query, QueryBuilder filter, List<SortBuilder> sorts, Field[] highlightFields) {  
  2.         this.query = query;  
  3.         this.filter = filter;  
  4.         this.sorts = sorts;  
  5.         this.highlightFields = highlightFields;  
  6.     }  
当然了,我们没必要实现所有的参数。
可以看出来,大概是需要QueryBuilder,filter,和排序的SortBuilder,和高亮的字段。
一般情况下,我们不是直接是new NativeSearchQuery,而是使用NativeSearchQueryBuilder。
通过NativeSearchQueryBuilder.withQuery(QueryBuilder1).withFilter(QueryBuilder2).withSort(SortBuilder1).withXXXX().build();这样的方式来完成NativeSearchQuery的构建。



从名字就能看出来,QueryBuilder主要用来构建查询条件、过滤条件,SortBuilder主要是构建排序。
譬如,我们要查询距离某个位置100米范围内的所有人、并且按照距离远近进行排序:
[java]  view plain  copy
  1. double lat = 39.929986;  
  2.         double lon = 116.395645;  
  3.   
  4.         Long nowTime = System.currentTimeMillis();  
  5.         //查询某经纬度100米范围内  
  6.         GeoDistanceQueryBuilder builder = QueryBuilders.geoDistanceQuery("address").point(lat, lon)  
  7.                 .distance(100, DistanceUnit.METERS);  
  8.   
  9.         GeoDistanceSortBuilder sortBuilder = SortBuilders.geoDistanceSort("address")  
  10.                 .point(lat, lon)  
  11.                 .unit(DistanceUnit.METERS)  
  12.                 .order(SortOrder.ASC);  
  13.   
  14.         Pageable pageable = new PageRequest(050);  
  15.   
  16.         NativeSearchQueryBuilder builder1 = new NativeSearchQueryBuilder().withFilter(builder).withSort(sortBuilder).withPageable(pageable);  
  17.         SearchQuery searchQuery = builder1.build();  
要完成字符串的查询:
[java]  view plain  copy
  1. SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.queryStringQuery("spring boot OR 书籍")).build();  
要构建QueryBuilder,我们可以使用工具类QueryBuilders,里面有大量的方法用来完成各种各样的QueryBuilder的构建,字符串的、Boolean型的、match的、地理范围的等等。
要构建SortBuilder,可以使用SortBuilders来完成各种排序。
然后就可以通过NativeSearchQueryBuilder来组合这些QueryBuilder和SortBuilder,再组合分页的参数等等,最终就能得到一个SearchQuery了。
至此,我们明白了ElasticSearchRepository里那几个search查询方法需要的参数的含义和构建方式了。

ElasticSearchTemplate的使用

ElasticSearchTemplate更多是对ESRepository的补充,里面提供了一些更底层的方法。



这里主要是一些查询相关的,同样是构建各种SearchQuery条件。
也可以完成add操作
[java]  view plain  copy
  1. String documentId = "123456";  
  2.         SampleEntity sampleEntity = new SampleEntity();  
  3.         sampleEntity.setId(documentId);  
  4.         sampleEntity.setMessage("some message");  
  5.         IndexQuery indexQuery = new IndexQueryBuilder().withId(sampleEntity.getId()).withObject(sampleEntity).build();  
  6.         elasticsearchTemplate.index(indexQuery);  
add主要是通过index方法来完成,需要构建一个IndexQuery对象


构建这个对象,主要是设置一下id,就是你的对象的id,Object就是对象本身,indexName和type就是在你的对象javaBean上声明的


其他的字段自行发掘含义,构建完IndexQuery后就可以通过Template的index方法插入了。
template里还有各种deleteIndex,delete,update等方法,用到的时候就查查看吧。
下面讲一个批量插入的方法,我们经常需要往ElasticSearch中插入大量的测试数据来完成测试搜索,一条一条插肯定是不行的,ES提供了批量插入数据的功能——bulk。
前面讲过JPA的save方法也可以save(List)批量插值,但适用于小数据量,要完成超大数据的插入就要用ES自带的bulk了,可以迅速插入百万级的数据。
在ElasticSearchTemplate里也提供了对应的方法
[java]  view plain  copy
  1. public void bulkIndex(List<IndexQuery> queries) {  
  2.         BulkRequestBuilder bulkRequest = this.client.prepareBulk();  
  3.         Iterator var3 = queries.iterator();  
  4.   
  5.         while(var3.hasNext()) {  
  6.             IndexQuery query = (IndexQuery)var3.next();  
  7.             bulkRequest.add(this.prepareIndex(query));  
  8.         }  
  9.   
  10.         BulkResponse bulkResponse = (BulkResponse)bulkRequest.execute().actionGet();  
  11.         if (bulkResponse.hasFailures()) {  
  12.             Map<String, String> failedDocuments = new HashMap();  
  13.             BulkItemResponse[] var5 = bulkResponse.getItems();  
  14.             int var6 = var5.length;  
  15.   
  16.             for(int var7 = 0; var7 < var6; ++var7) {  
  17.                 BulkItemResponse item = var5[var7];  
  18.                 if (item.isFailed()) {  
  19.                     failedDocuments.put(item.getId(), item.getFailureMessage());  
  20.                 }  
  21.             }  
  22.   
  23.             throw new ElasticsearchException("Bulk indexing has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages [" + failedDocuments + "]", failedDocuments);  
  24.         }  
  25.     }  
  26.   
  27.     public void bulkUpdate(List<UpdateQuery> queries) {  
  28.         BulkRequestBuilder bulkRequest = this.client.prepareBulk();  
  29.         Iterator var3 = queries.iterator();  
  30.   
  31.         while(var3.hasNext()) {  
  32.             UpdateQuery query = (UpdateQuery)var3.next();  
  33.             bulkRequest.add(this.prepareUpdate(query));  
  34.         }  
  35.   
  36.         BulkResponse bulkResponse = (BulkResponse)bulkRequest.execute().actionGet();  
  37.         if (bulkResponse.hasFailures()) {  
  38.             Map<String, String> failedDocuments = new HashMap();  
  39.             BulkItemResponse[] var5 = bulkResponse.getItems();  
  40.             int var6 = var5.length;  
  41.   
  42.             for(int var7 = 0; var7 < var6; ++var7) {  
  43.                 BulkItemResponse item = var5[var7];  
  44.                 if (item.isFailed()) {  
  45.                     failedDocuments.put(item.getId(), item.getFailureMessage());  
  46.                 }  
  47.             }  
  48.   
  49.             throw new ElasticsearchException("Bulk indexing has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages [" + failedDocuments + "]", failedDocuments);  
  50.         }  
  51.     }  
和index插入单条数据一样,这里需要的是List<IndexQuery>仅此而已,是不是很简单。

[java]  view plain  copy
  1. public void bulkIndex(List<Person> personList) {  
  2.         int counter = 0;  
  3.         try {  
  4.             if (!elasticsearchTemplate.indexExists(PERSON_INDEX_NAME)) {  
  5.                 elasticsearchTemplate.createIndex(PERSON_INDEX_TYPE);  
  6.             }  
  7.             List<IndexQuery> queries = new ArrayList<>();  
  8.             for (Person person : personList) {  
  9.                 IndexQuery indexQuery = new IndexQuery();  
  10.                 indexQuery.setId(person.getId() + "");  
  11.                 indexQuery.setObject(person);  
  12.                 indexQuery.setIndexName(PERSON_INDEX_NAME);  
  13.                 indexQuery.setType(PERSON_INDEX_TYPE);  
  14.   
  15.                 //上面的那几步也可以使用IndexQueryBuilder来构建  
  16.                 //IndexQuery index = new IndexQueryBuilder().withId(person.getId() + "").withObject(person).build();  
  17.   
  18.                 queries.add(indexQuery);  
  19.                 if (counter % 500 == 0) {  
  20.                     elasticsearchTemplate.bulkIndex(queries);  
  21.                     queries.clear();  
  22.                     System.out.println("bulkIndex counter : " + counter);  
  23.                 }  
  24.                 counter++;  
  25.             }  
  26.             if (queries.size() > 0) {  
  27.                 elasticsearchTemplate.bulkIndex(queries);  
  28.             }  
  29.             System.out.println("bulkIndex completed.");  
  30.         } catch (Exception e) {  
  31.             System.out.println("IndexerService.bulkIndex e;" + e.getMessage());  
  32.             throw e;  
  33.         }  
  34.     }  
这里是创建了100万个Person对象,每到500就用bulkIndex插入一次,速度飞快,以秒的速度插入了百万数据。

OK,这篇主要是讲一些ElasticSearchRepository和ElasticSearchTemplate的用法,构造QueryBuilder的方式。下一篇用实例来看一下,在百万或者更大量级的数据中查询距离某个坐标100米范围内的所有数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值