Spring boot Java技术相关javaWeb学习

Spring Boot + Solr全文检索微服务简易集成

2019-04-03  本文已影响76人  JasonGofen

本文内容主要讲解Solr 7.7.1 环境搭建后使用Spring boot 2.1.3集成SolrJ实现简易全文检索微服务,对于Solr与Spring boot的介绍内容网上资料很多,本文不再赘述。
关于本文内容所涉及资源在最后会给大家统一都列出来。

一、环境说明

Spring boot 结合 SolrJ 实现对Solr Server的访问是非常简单的,它们相互之间的关系如下图所示。

主体实现是通过在Spring boot微服务中集成SolrJ,配置好Solr Server参数,调用SolrJ中的CRUD API实现请求Solr Server端进行添加、修改、删除索引和查询的操作。至于如何结合现有业务系统,我们后面介绍。

图1 简易环境说明

下面以最简单快速的方式实现全文检索基础CRUD功能。

二、搭建Solr 7.7.1 Server端

因自己的电脑硬盘空间满了没装Linux虚拟机,就以Windows为例安装Solr Server端了。其实总体而言两个系统对Solr Server端的操作类似,各位看官举一反三就好,就两种系统部署Solr的不同下面也会做相应的提及。


我们首先来了解一下Solr的常见命令:

./solr start
./solr stop -all
./solr restart
./solr create -c YourCoreName -d _default
./solr delete -c YourCoreName

关于Solr更细致的内容,大家可以参阅下方链接:


要想搭建Solr Server拢共分3步:


步骤1:部署Solr 7.7.1

步骤1-1:下载安装JDK和Solr Server端

步骤1-2:解压Solr-7.7.1压缩包

C:\myworking\solr-7.7.1
tar zxvf solr-7.7.1.tgz -C /myworking

步骤1-3:进入bin目录运行Solr
Windows与Linux操作solr的命令都是一样的,但都需要进入到Solr的bin目录下,我的目录是C:\myworking\solr-7.7.1\binlinux的目录使用cd命令进入即可。

./solr start
图2 控制台Solr启动完成图

个人比较推荐第一种,原因有二,一是方便快捷在命令行一句话搞定,Ctrl+c Ctrl+v齐活儿,不用在页面上点来点去还得敲文字,二是所在公司大牛在实践过后说是如果采用的第二种方式创建出的Core会有一些问题。本着听人话,吃饱饭的态度就采用第一种吧~ ~大家可以实践一下然后分享给我哦。当然两种方式还是要介绍下的。

./solr create -c TestCore -d _default

步骤2:配置IK-Analyzer中文分词

介绍性的内容还是不罗列了,大家自己百度吧。Ik-Analyzer分词据说是国内最好用的中文分词,大部分人都用这个。目前Solr-7.7.1也自带了一个中文分词,具体的对比我没做过,等装完IK后大家回来可以进行下对比。同时欢迎分享给我哦。

如果只是在我们原有的业务系统中简单集成Solr,那暂时没必要去了解动态词库自动加载。如果像商城等业务系统中,对于搜索模块是业务系统的核心之一,那么简单使用IK可能无法达到线上使用的要求。在IK分词器中默认是一次启动将主词库、停用词以及扩展词库全部加载完毕,后续如果再想要增加额外的扩展词,就必须得修改对应的扩展词表重新打包上传并重启服务方能生效,这种方式不适合应用与线上服务。那么到底如何实现这种无缝扩充词库呢?大家可以看看上面的博客。

步骤2-1:配置IK-analyzer jar包和词库
首先需要大家下载jar包和源码,我们下面需要放到Solr Server端对应的目录下。

  1. 将下载好的ik-analyzer-7.7.1.jar包放入Solr服务的 webapp\WEB-INF\lib目录下,我的目录全路径是:
C:\myworking\solr-7.7.1\server\solr-webapp\webapp\WEB-INF\lib
  1. 将下载好的ik-analyzer-solr7-master源码下的src\main\resources目录中的文件(如下),放入到webapp\WEB-INF\classes目录下:
① IKAnalyzer.cfg.xml        扩展配置文件
② ext.dic                   扩展词库
③ stopword.dic              停止词库
④ ik.conf                   动态词库配置文件
⑤ dynamicdic.txt            动态词库

我的目录全路径是:

C:\myworking\solr-7.7.1\server\solr-webapp\webapp\WEB-INF\classses

步骤2-2. 配置TestCoreserver\solr\TestCore\conf\managed-schema,添加IK分词器,示例如下:

  <!-- ik分词器 -->
  <fieldType name="text_ik" class="solr.TextField">
   <analyzer type="index">
     <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" conf="ik.conf"/>
     <filter class="solr.LowerCaseFilterFactory"/>
   </analyzer>
   <analyzer type="query">
     <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="true" conf="ik.conf"/>
     <filter class="solr.LowerCaseFilterFactory"/>
   </analyzer>
  </fieldType>

PS:放在<schema name="default-config" version="1.6">标签下。同时在该标签下我们还需要配置field,请看步骤3。


步骤3:配置field

Solr filed域的配置极为重要,filed的配置会影响到索引的创建和查询出的结果展示。

<!-- Solr Test search -->
<field name="title" type="text_ik" indexed="true" stored="true" required="true" multiValued="true" />
<field name="content" type="text_ik" indexed="true" stored="true" required="true" multiValued="true" />
<field name="filetype" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<field name="uploadtime" type="string" indexed="false" stored="true" required="true" multiValued="false" />
<!-- 复制域,可以将多个Field复制到一个Field中,以便进行统一的检索,multiValued属性需要设置成true -->
<copyField source="content" dest="title" />

name:查询时的名称
type:这个是之前定义的FieldType的名称,在这使用的ik分词
indexed:是否索引(true/false)
stored:是否存储(是否将索引结果存储到索引库)
multivalued:是否多值(一般配合copyField使用)

在定义filed时,可能会随着业务主线作出变更,那么每次在managed-schema更改filed后,还需重启Solr也是个麻烦事儿。那么在生产环境如此操作可能显得不是那么理想了。那么可以在变更时使用类似通配符的方式建立动态filed,比如name="title"可以写成name="fl_*",这样只要以fl_开头的索引都可以被建立。


步骤4:重启Solr服务,测试ik分词

这里请注意Analyse Fieldname / FieldType:右边的下拉列表,需要去选中ik分词text_ik。我们可以看到输入中华人民共和国关键字后所出现的分词效果。

图6 分词效果图
至此我们的Solr Server端基础版就搭建完成了,下面开始使用Spring boot结合SolrJ进行全文检索微服务的搭建。

三、Spring boot 微服务实现

Spring boot的基础知识不讲解了,下面我们采用Spring boot 2.1.3结合SolrJ 7.7.1完成全文检索微服务的实现。


步骤1:在本地准备将要建立索引的文件

准备两个doc文档,写入一些自定内容,放到指定目录下,当然也不一定非得要doc。以下是我准备的两个文件:

C:\solrfile\data\鹅鹅鹅.pdf
C:\solrfile\data\静夜思.docx

步骤2:搭建Spring boot项目

<!-- SolrJ 7.7.1 API -->
<dependency>
    <groupId>org.apache.solr</groupId>
    <artifactId>solr-solrj</artifactId>
    <version>7.7.1</version>
</dependency>
<!-- 解析文档内容工具包 -->
<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-core</artifactId>
    <version>1.9</version>
</dependency>
├── src                         
│   └── main
│       ├── java
│       │   └── com.jasongofen
│       │       ├── client
│       │       │   └── SolrClient.java             // Solr客户端
│       │       ├── config
│       │       │   ├── CorsConfig.java             // 跨域配置文件
│       │       │   └── SolrConfigProperties.java   // yml属性实例化配置文件
│       │       ├── test
│       │       │   └── SolrCURDTest.java           // Solr API调用测试示例
│       │       ├── util
│       │       │   ├── ConvertUtil.java            // 自定义转换工具类
│       │       │   └── TikaUtil.java               // 提取文档内容工具类
│       │       └── SolrProjectApplication.java     // Spring boot启动类
│       └── resources
│           ├── application.yml                     // 项目配置
│           ├── banner.txt                          // banner配置
│           └── logback-spring.xml                  // 日志配置
├── pom.xml                                         // 依赖配置
└── README.md                                       // 项目帮助文档
# solr配置
solr:
  # Solr Server端地址
  server: localhost:8983    # 设置你的Solr Server访问地址
  # Solr Core名称
  core: TestCore            # 设置你的Solr Core名称
  # 上面准备的文档的所在本地路径
  dir: C:\solrfile\data\    # 设置你的需要建立索引的文件所在目录

步骤3:运行项目

下面就可以运行项目了,SolrJ API调用请看以下内容。
未完待续


SolrJ API调用说明

基础的API调用代码在项目目录src\main\java\com.jasongofen.test\SolrCURDTest.java文件中,该java是一个Controller可以以http请求的方式模拟其他业务系统调用过程。

@GetMapping("/Add")
public void solrAdd() throws Exception {

    // 设置文件路径
    List<String> files = new ArrayList<>();
    files.add("鹅鹅鹅.pdf");
    files.add("静夜思.docx");
    // 获取Solr客户端
    HttpSolrClient solr = SolrClient.getClient(solrConfigProperties.getServer());

    String prefix = "";
    for (String fi : files) {
        System.out.println(fi);
        // 取后缀名
        prefix = ConvertUtil.getFileSufix(fi);
        if (prefix.equalsIgnoreCase("txt") ||
                prefix.equalsIgnoreCase("docx") ||
                prefix.equalsIgnoreCase("doc") ||
                prefix.equalsIgnoreCase("pdf")) {

            String[] fileInfo = fi.split("\\.");
            String content = "";
            // 获取文件流,取出文件内容
            InputStream inputStream = new FileInputStream(solrConfigProperties.getDir() + fi);
            if (prefix.equals("txt")) {
                content = TikaUtil.txt2String(inputStream);
            } else if (prefix.equals("docx") || prefix.equals("doc") || prefix.equals("pdf")) {
                content = TikaUtil.doc2String(inputStream);
            } else {
                inputStream.close();
            }
            // 添加索引
            SolrInputDocument solrDoc = new SolrInputDocument();
            String formatDate = ConvertUtil.formatDate();
            // 执行添加 ps:如果id相同,则执行更新操作
            solrDoc.addField("id", UUID.randomUUID().toString().toUpperCase().replace("-", ""));
            solrDoc.addField("title", fileInfo[0]);
            solrDoc.addField("content", content);
            solrDoc.addField("filetype", prefix);
            solrDoc.addField("uploadtime", formatDate);
            solr.add(solrConfigProperties.getCore(), solrDoc);

        } else {
            continue;
        }
    }
    // 提交
    solr.commit(solrConfigProperties.getCore());
    solr.close();
}
@GetMapping("/Query")
public SolrDocumentList solrQuery() throws Exception {

    HttpSolrClient solrClient = SolrClient.getClient(solrConfigProperties.getServer());
    // 定义查询条件
    Map<String, String> params = new HashMap<String, String>();
    params.put("q", "*:*");
    SolrParams mapSolrParams = new MapSolrParams(params);
    //执行查询 第一个参数是collection,就是我们在solr中创建的core
    QueryResponse response = solrClient.query(solrConfigProperties.getCore(), mapSolrParams);
    // 获取结果集
    SolrDocumentList results = response.getResults();
    for (SolrDocument result : results) {
        // SolrDocument 数据结构为Map
        System.out.println(result);
    }
    solrClient.close();
    return results;

}
@GetMapping("/Delete")
public void solrDelete(@RequestParam("id") String id) throws Exception {

    HttpSolrClient solrClient = SolrClient.getClient(solrConfigProperties.getServer());
    // 通过id删除 执行要删除的collection(core)
    solrClient.deleteById(solrConfigProperties.getCore(), id);
    // 还可以通过查询条件删除
    // solrClient.deleteByQuery(solrConfigProperties.getCore(), "查询条件");
    // 提交删除
    solrClient.commit(solrConfigProperties.getCore());
    solrClient.close();

}

本文所用资源汇总:

上一篇下一篇

猜你喜欢

热点阅读