【lucene-plus】初始化索引

       lucene-plus依赖spring-boot 2.xx实现,使用spring或其他spring-boot工程的同学可根据自己的需求调整源码,源码坐标:lucene-plus: 基于lucene进行通用CRUD的封装,享受lucene丝滑般的操作。

        有需求的同学可以fork工程的master分支进行调整开发,原则上不建议直接下载源码。lucene本身没有“初始化索引”的概念,所到之处皆是new,这给我一种很不爽的体验,所以创建lucene-plus的时候第一个实现的功能便是“初始化索引”,为什么是“lucene-plus”?因为我只是基于lucene进行了顶层的封装,而没有调整lucene任何源码。下面开始介绍lucene-plus如何进行索引的初始化。

1、引入maven坐标:

   <dependency>
      <groupId>cn.juque</groupId>
      <artifactId>lucene-plus</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>

2、application-${profile}.yml文件添加配置:

# 指定索引所在目录
lucene:
  index:
    directory: D:\DOC\multiFile\index\

3、添加lucene-plus的扫描目录(lucene-plus依赖hutool工具的bean操作,所以"cn.hutool.extra.spring"必须能被工程扫描到):

@ComponentScans(value = {@ComponentScan("cn.juque.luceneplus"), @ComponentScan("cn.hutool.extra.spring")})

4、至此,便已完成lucene-plus的依赖引入。比如我们需要初始化一个关于文件信息的索引,我们首先需要实现接口:IndexTemplate,定义整个索引的json形式:

@Component("indexFileHandler")
public class IndexFileHandler implements IndexTemplate {

    @Override
    public String getTemplate() {
        return "{\n"
            + "    \"indexName\":\"file_info\",\n"
            + "    \"fieldDTOList\":[\n"
            + "        {\n"
            + "            \"fieldName\":\"module_id\",\n"
            + "            \"fieldType\":\"STRING\",\n"
            + "            \"value\":\"${module_id}\",\n"
            + "            \"store\":\"YES\"\n"
            + "        },\n"
            + "        {\n"
            + "            \"fieldName\":\"server_path\",\n"
            + "            \"fieldType\":\"STRING\",\n"
            + "            \"value\":\"${server_path}\",\n"
            + "            \"store\":\"YES\"\n"
            + "        },\n"
            + "        {\n"
            + "            \"fieldName\":\"server_file_name\",\n"
            + "            \"fieldType\":\"STRING\",\n"
            + "            \"value\":\"${server_file_name}\",\n"
            + "            \"store\":\"YES\"\n"
            + "        },\n"
            + "        {\n"
            + "            \"fieldName\":\"client_file_name\",\n"
            + "            \"fieldType\":\"STRING\",\n"
            + "            \"value\":\"${client_file_name}\",\n"
            + "            \"store\":\"YES\"\n"
            + "        },\n"
            + "        {\n"
            + "            \"fieldName\":\"file_size\",\n"
            + "            \"fieldType\":\"DOUBLE\",\n"
            + "            \"value\":\"${file_size}\"\n"
            + "        }\n"
            + "    ]\n"
            + "}";
    }
}

索引参数:

参数: 描述
indexName 索引名称
fieldDTOList 字段信息的列表
fieldDTOList.fieldName 字段名称
fieldDTOList.fieldType 字段类型
fieldDTOList.value 当前保留参数,没有实际意义
fieldDTOList.store 是否存储。YES:存储;NO:不存储
fieldDTOList.point 是否支持范围查询,只对fieldType:{INTEGER、LONG、DOUBLE}有效
fieldDTOList.analyzer 分词器,支持的分词器参考:AnalyzerEnum

完成索引的json定义,启动服务后索引信息会自动完成初始化,后续新增、编辑都会基于初始化的索引信息进行Document实例化。(注:索引字段只允许增量操作,变更、删除操作均无效,索引名称变更无效)

5、查询操作(支持查询单个Document,普通分页、滚动式分页):

BooleanQuery.Builder builder = new BooleanQuery.Builder();
        Term term = new Term(IndexFileEnum.SERVER_FILE_NAME.getName(), serverName);
        TermQuery termQuery = new TermQuery(term);
        builder.add(termQuery, Occur.MUST);
        Document document = this.documentPlusService.searchDocument(IndexFileEnum.FILE_INFO.getName(), builder);

普通分页查询实现的是逻辑分页,目前支持最大20万数据量的分页。如果有同学能实现物理分页,可以分享下。

6、新增文档操作:

// 保存
        Map<String, Object> params = CollUtil.newHashMap(10);
        params.put(IndexFileEnum.MODULE_ID.getName(), multiFileBo.getModuleId());
        params.put(IndexFileEnum.CLIENT_FILE_NAME.getName(), multiFileBo.getFileName());
        params.put(IndexFileEnum.SERVER_FILE_NAME.getName(), serverName);
        params.put(IndexFileEnum.SERVER_PATH.getName(), serverPath);
        params.put(IndexFileEnum.DOWNLOAD_TIMES.getName(), 0);
        params.put(IndexFileEnum.UPLOAD_USER_ID.getName(), multiFileBo.getUserId());
        try {
            this.documentPlusService.addDocument(IndexFileEnum.FILE_INFO.getName(), params);
        } catch (IOException e) {
            log.error("save file error", e);
            throw new AppException(InternetStorageMsgEnum.SYSTEM_ERROR);
        }

lucene-plus会根据fieldName映射实例化Field,所以大家如果新增了字段,要先调整索引模板,否则单独添加到Map里面将无法生效。

7、更新操作:

BooleanQuery.Builder builder = new BooleanQuery.Builder();
        serverNames.forEach(t->{
            Term term = new Term(IndexFileEnum.SERVER_FILE_NAME.getName(), t);
            builder.add(new TermQuery(term), Occur.SHOULD);
        });
        Map<String, Object> params = CollUtil.newHashMap(1);
        params.put(IndexFileEnum.IS_VALID.getName(), Boolean.TRUE.toString());
        this.documentPlusService.updateDocument(IndexFileEnum.FILE_INFO.getName(), builder, params);

api会根据传递过来的条件查出相关Document,然后执行delete-insert。lucene的updateDocument的实现逻辑实际也是delete-insert,区别在于:lucene-plus实现了Document的局部更新,而原生update方法是全量字段更新;

8、删除操作,与原生的删除操作并没有多大区别。至此,基于lucene-plus的增删改查的介绍也告一段落了。

        文章结尾记录下开发过程遇到的问题,如有答案的同学,烦请告知一下

1、lucene-plus的更新操作是先查出来,然后把Document和Maps进行合并,最后根据条件执行delete操作,把合并后的Document重新保存。在实际操作中,最后一步调用原生的addDocument保存一直没成功,或是用原生的updateDocument也只是删除成功,而没有保存新的Document。无奈下,我改成调用lucene-plus的addDocument居然保存成功了,这个骚操作一直没整明白。。。

上一篇:ES是什么?看完这篇就不要再问这种低级问题了!


下一篇:vc++基础班[24]---系统各种路径信息的获取