0

Java注解(4):一个真实的Elasticsearch案例 - 湘王

 1 year ago
source link: https://www.cnblogs.com/xiangwang1111/p/16796980.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Java注解(4):一个真实的Elasticsearch案例

昨天把拼了一半的注解+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
 */
@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的索引和文档,以及导出、导入数据等等功能,既非常强大,也非常方便。

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK