昨天把拼了一半的注解+Elasticsearch积木放下了,因为东西太多了拼不好,还容易乱。休息了一晚上接着来。

接着昨天,创建elasticsearch文档注解(相当于数据表的注解):

/** * 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 */@Componentpublic 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 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 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 */@Servicepublic 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的索引和文档,以及导出、导入数据等等功能,既非常强大,也非常方便。

我想,那些目前主流开发的框架也都是这么从小做起,一点点发展起来的吧。