基本数据查询
1.数据查询
1.1 普通查询
通过ElasticsearchRepository定义的接口查询
Article articleByIdTmp = articleRepository.findOne(article.getId());
通过自己定义的接口查询,通过title去查询数据,这里是全部匹配查询,而不是模糊查询
Article articleByTitle = articleRepository.findByTitle(article.getTitle());
基本的查询都能够通过jpa的CrudRepository完成
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
//保存
<S extends T> S save(S entity);
//通过id查找
T findOne(ID primaryKey);
//获取所有对象
Iterable<T> findAll();
//获取的count
Long count();
//删除
void delete(T entity);
//判断是否存在
boolean exists(ID primaryKey);
// … more functionality omitted.
}
1.2 复杂分页查询
这里主要是处理一些用方法名和一些基础的增删改差不能解决的,withFilter是原生里面的方法,跟query差不多,但是一般情况都会使用Filter,因为Filter的性能比query的高,这里主要是使用的andQuery,来配合多个条件的查询,同时andQuery里面也可以嵌套orQuery。同时添加PageRequest对象来进行分页.
Page<Article> page = articleRepository.search(new NativeSearchQueryBuilder()
.withFilter(
andQuery(multiMatchQuery("content", "我是中国"),termQuery("title","我是标题")))
.withPageable(new PageRequest(0,10))
.build());
通过上述拿到的Page对象包含了如下属性,这些属性都是加入查询条件进行筛选后的
public interface Page<T> extends Slice<T> {
/**
* Returns the number of total pages.
* 返回此次查询所有的页数
* @return the number of total pages
*/
int getTotalPages();
/**
* Returns the total amount of elements.
* 返回所有元素的数量
* @return the total amount of elements
*/
long getTotalElements();
/**
* Returns a new {@link Page} with the content of the current one mapped by the given {@link Converter}.
*
* @param converter must not be {@literal null}.
* @return a new {@link Page} with the content of the current one mapped by the given {@link Converter}.
* @since 1.10
*/
<S> Page<S> map(Converter<? super T, ? extends S> converter);
}
Page对象实现了Slice接口,当然也实现了相应的方法获取如下属性
public interface Slice<T> extends Iterable<T> {
/**
* Returns the number of the current {@link Slice}. Is always non-negative.
*
* @return the number of the current {@link Slice}.
*/
int getNumber();
/**
* Returns the size of the {@link Slice}.
*
* @return the size of the {@link Slice}.
*/
int getSize();
/**
* Returns the number of elements currently on this {@link Slice}.
*
* @return the number of elements currently on this {@link Slice}.
*/
int getNumberOfElements();
/**
* Returns the page content as {@link List}.
*
* @return
*/
List<T> getContent();
/**
* Returns whether the {@link Slice} has content at all.
*
* @return
*/
boolean hasContent();
/**
* Returns the sorting parameters for the {@link Slice}.
*
* @return
*/
Sort getSort();
/**
* Returns whether the current {@link Slice} is the first one.
*
* @return
*/
boolean isFirst();
/**
* Returns whether the current {@link Slice} is the last one.
*
* @return
*/
boolean isLast();
/**
* Returns if there is a next {@link Slice}.
*
* @return if there is a next {@link Slice}.
*/
boolean hasNext();
/**
* Returns if there is a previous {@link Slice}.
*
* @return if there is a previous {@link Slice}.
*/
boolean hasPrevious();
/**
* Returns the {@link Pageable} to request the next {@link Slice}. Can be {@literal null} in case the current
* {@link Slice} is already the last one. Clients should check {@link #hasNext()} before calling this method to make
* sure they receive a non-{@literal null} value.
*
* @return
*/
Pageable nextPageable();
/**
* Returns the {@link Pageable} to request the previous {@link Slice}. Can be {@literal null} in case the current
* {@link Slice} is already the first one. Clients should check {@link #hasPrevious()} before calling this method make
* sure receive a non-{@literal null} value.
*
* @return
*/
Pageable previousPageable();
/**
* Returns a new {@link Slice} with the content of the current one mapped by the given {@link Converter}.
*
* @param converter must not be {@literal null}.
* @return a new {@link Slice} with the content of the current one mapped by the given {@link Converter}.
* @since 1.10
*/
<S> Slice<S> map(Converter<? super T, ? extends S> converter);
}
2 数据保存
2.1 普通保存方式
使用了Spring Data Elasticsearch,封装了一系列的对象处理操作,把数据保存到Elasticsearch,只需要调用Repository的save方法即可,当然这个对象需要指定具体的索引和需要被存储的字段。如果使用原生的方式保存数据就比较麻烦,首先需要判断索引是否存在,然后建立的相应的mapping。才能够很好的去保存数据。
public class MainApp {
public static void main(String args[]) throws IOException, NoSuchMethodException {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-es.xml");
ElasticsearchTemplate elasticsearchTemplate = (ElasticsearchTemplate) classPathXmlApplicationContext.getBean("elasticsearchTemplate");
ArticleRepository articleRepository = classPathXmlApplicationContext.getBean(ArticleRepository.class);
Article article = new Article();
article.setContent("我是中国人,我不是日本人");
article.setTitle("我是标题");
article.setId(133);
articleRepository.save(article);
Page<Article> page = articleRepository.search(new NativeSearchQueryBuilder()
.withQuery(termQuery("content", "我是中国"))
.build());
}
}
@Document(indexName = "coffee_test")
public class Article {
@Id
private int id;
@Field(type = FieldType.String, index = FieldIndex.analyzed, store = true)
private String content;
@Field(type = FieldType.String, index = FieldIndex.not_analyzed, store = false)
private String title;
@Field(type = FieldType.Date, index = FieldIndex.not_analyzed, store = true)
private Date updateDate;
}
2.2 动态索引
首先为什么会有这个需求,因为在开发中会存在多个环境的问题,往往集群只有一个,同时还需要实现环境的数据隔离,只能通过建立不同的索引来实现。当然如果用原生的api就不用过多考虑这个问题,因为本身的索引就是可以动态指定的。但是Spring Data Elasticsearch
是通过注解的形式来指定索引的,这个时候脑海中浮现三个方案:
- 通过发布系统的模板(公司大神写的发布系统,感觉是基于travis ci二次开发的,因为界面真的是一样的,但是实现机制已经完全不一样了,采用的jenkins+mesos+Marathon进行的底层实现。估计大神只是觉得travis ci界面比较好看而已),编译的时候动态的改变java的注解。
- 通过配置,动态的去改变注解的字节码,这个比较麻烦,还需要添加第三方jar包。
- 通过原生api写代码,比较麻烦,不能为了这一个需求去妥协用原生恶心的代码。
- 使用ElasticsearchTemplate,来实现动态索引。
目前我能想到的也就4种方案,我选择的是第4中方案,因为比较简单实用。能用两句代码解决的问题,绝对不复杂化。这里也需要使用解析注解的方式,但是唯一好的是Spring Data Elasticsearch
已经实现了解析注解,但是没有公开出来,我这里干了一件事情,就是把解析部分copy出来。具体代码链接
核心代码如下:
public static void createIndexIfNotExists(ElasticsearchTemplate elasticsearchTemplate, Class clazz, String indexName) {
//判断索引是否存在
boolean ex = elasticsearchTemplate.indexExists(indexName);
if (!ex) {
//创建索引
elasticsearchTemplate.createIndex(indexName);
//建立mapping
elasticsearchTemplate.putMapping(indexName,
XContentBuilderUtils.getPersistentEntityFor(clazz, elasticsearchTemplate).getIndexType()
, XContentBuilderUtils.getXContentBuilder(clazz, elasticsearchTemplate)
);
}
}
如果使用elasticsearchTemplate,也是需要自己去处理索引的状态,这里封装了对象操作,并且可以动态的改变索引名字,XContentBuilderUtils主要是可以根据对象的field注解自动的去生成原生的XContentBuilder来实现mapping操作,具体可以尝试下原生的mapping操作就知道了,