/**
* elastic文档注解,定义每个elasticsearch文档上的属性
*
* @author xiangwang
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface Document {
String index();
String type() default "_doc";
boolean useServerConfiguration() default false;
short shards() default 1;
short replicas() default 0;
String refreshInterval() default "1s";
String indexStoreType() default "fs";
}
然后再创建elasticsearch文档(相当于数据表):
/**
* elastic文档对象
*
* @author xiangwang
*/
@Document(index = "document", type = "_doc", shards = 1, replicas = 0)
public class ElasticDocument {
private static final long serialVersionUID = 2879048112350101009L;
// 文档编码
@DocField(name = "guid", type = FieldType.Keyword)
protected String guid = "";
// 标题
@DocField(name = "title", type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")
protected String title = "";
// 文档创建时间(资源实际创建时间)
@DocField(name = "createtime", type = FieldType.Long)
protected long createtime;
// 文档更新时间(资源实际更新时间)
@DocField(name = "updatetime", type = FieldType.Long)
protected long updatetime;
public ElasticDocument() {
}
public String getGuid() {
return guid;
}
public void setGuid(String guid) {
this.guid = guid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public long getCreatetime() {
return createtime;
}
public void setCreatetime(long createtime) {
this.createtime = createtime;
}
public long getUpdatetime() {
return updatetime;
}
public void setUpdatetime(long updatetime) {
this.updatetime = updatetime;
}
@Override
public String toString() {
return String.format("{/"guid/":/"%s/", /"title/":/"%s/", /"createtime/":%d, " +
"/"updatetime/":%d}", guid, title, createtime, updatetime);
}
这里面的@Document就是刚才创建的文档注解。
最后,创建一个真正的执行者,就由它来完成所有材料的拼装:
/**
* ElasticDao
*
* @author xiangwang
*/
@Component
public class ElasticDao {
// ElasticConfiguration中定义的Bean对象
@Autowired
private RestHighLevelClient client;
/**
* 索引是否存在
*
*/
public boolean indexExist(final String index) {
try {
return client.indices().exists(new GetIndexRequest(index), RequestOptions.DEFAULT);
} catch (IOException e) {
System.out.println("index exist exception");
}
return false;
}
/**
* 解析类注解,获取包括父类字段在内的所有字段
* 因为解析的时候,会把父类及自身的一些额外字段给解析进去
* 如logger、serialVersionUID等
* 所以需要把这些无用的字段排除掉
* 这里不存在继承,所以直接调用clazz.getDeclaredFields()
* 另外,如果存在继承关系,该怎么处理呢?(可以思考一下)
*
*/
public static List<Field> getAllDeclaredFields(Class<?> clazz) {
return new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
}
/**
* 创建索引,前面都是为了实现它作准备
* 这里会通过注解,一路解析文档的字段,拼接成可执行的脚本交给elasticsearch的api去执行
*
*/
public boolean createIndex(final String index, final Class<?> clazz) {
try {
Document document = (Document) clazz.getAnnotation(Document.class);
int shards = document.shards();
int replicas = document.replicas();
if (indexExist(index)) {
return false;
}
CreateIndexRequest request = new CreateIndexRequest(index);
request.settings(Settings.builder()
.put("index.number_of_shards", shards)
.put("index.number_of_replicas", replicas)
);
StringBuilder builder = new StringBuilder();
builder.append("{/n");
builder.append(" /"properties/": {/n");
List<Field> list = getAllDeclaredFields(clazz);
int length = list.size();
for (int i = 0; i < length; i++) {
DocField docField = list.get(i).getAnnotation(DocField.class);
if (null == docField) {
continue;
}
builder.append(" /"").append(docField.name()).append("/" : {/n");
builder.append(" /"type/" : /"").append(docField.type().value).append("/"");
if (docField.index()) {
builder.append(", /n");
builder.append(" /"index/" : ").append(docField.index());
}
if (docField.fielddata()) {
builder.append(", /n");
builder.append(" /"fielddata/" : ").append(docField.fielddata());
}
if (docField.store()) {
builder.append(", /n");
builder.append(" /"store/" : ").append(docField.store());
}
if (StringUtils.isNotBlank(docField.analyzer())) {
builder.append(", /n");
builder.append(" /"analyzer/" : /"").append(docField.analyzer()).append("/"");
}
if (StringUtils.isNotBlank(docField.format())) {
builder.append(", /n");
builder.append(" /"format/" : /"").append(docField.format()).append("/"");
}
if (StringUtils.isNotBlank(docField.searchAnalyzer())) {
builder.append(", /n");
builder.append(" /"search_analyzer/" : /"").append(docField.searchAnalyzer()).append("/"");
}
if (StringUtils.isNotBlank(docField.pattern())) {
builder.append(", /n");
builder.append(" /"pattern/" : /"").append(docField.pattern()).append("/"");
}
if (StringUtils.isNotBlank(docField.normalizer())) {
builder.append(", /n");
builder.append(" /"normalizer/" : /"").append(docField.normalizer()).append("/"");
}
if (i == length -1) {
builder.append("/n }/n");
} else {
builder.append("/n }, /n");
}
}
builder.append(" }/n");
builder.append("}/n");
request.mapping(JSON.parseObject(builder.toString()).toJSONString(), XContentType.JSON);
CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
boolean acknowledged = response.isAcknowledged();
return acknowledged;
} catch (IOException e) {
System.out.println("create index exception");
}
return false;
}
}
好了,现在该搭个台子让这个执行者上台表演了:
/**
* 索引Service实现
*
* @author xiangwang
*/
@Service
public class IndexService {
@Resource
private ElasticDao elasticDao;
/**
* 索引初始化
*
* 这个方法可以在启动应用时调用,可以在接口中调用,也可以在main方法中调用
*/
@PostConstruct
private void initIndex() {
boolean flag = false;
// 创建一个名为Test的索引
if (!elasticDao.indexExist("Test")) {
flag = elasticDao.createIndex("Test", ElasticDocument.class);
if (flag) {
System.out.println("create Test index success");
} else {
System.out.println("create Test index failure");
}
} else {
System.out.println("Test index exist");
}
}
}
这就是整个注解结合Elasticsearch的真实案例。
其实这玩意一开始只是作为代码里面的小工具,但到后来随着需求越来越多,越来越变态,在我们后来的系统中它发展成了一个内部的小系统,可以通过管理后台的功能按钮来动态创建、修改、删除Elasticsearch的索引和文档,以及导出、导入数据等等功能,既非常强大,也非常方便。
我想,那些目前主流开发的框架也都是这么从小做起,一点点发展起来的吧。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/291411.html