solr-8.5.1 安装与部署

361 阅读9分钟

Solr的运行需要Java环境,需要部署到Servlet容器中,可以选择JettyTomcat。Solr默认自带了Jetty容器,可以很轻松的完成部署。
以下将分别讲解如何在Jetty和Tomcat下完成Solr的部署。

部署前的准备工作

  1. 去JDK官网下载最新版本的JDK并安装
  2. solr官网下载最新版的solr,本文用的是solr-8.5.1.zip,解压后的内容如下图所示:

solr_install_dir.png

一. 在Jetty下部署Solr

进入bin目录,打开cmd,启动命令 solr start,默认端口 8983,启动时可以通过 -p 来指定端口号
如:solr start -p 6789,运行结果如下图所示:

solr_start_by_cmd.png

然后打开浏览器,输入http://localhost:6789/solr 即可看到下图所示表示部署成功。

solr_home_page.png

以下是几个常用的命令

// 启动solr  
solr start -p 端口号  
  
// 停止solr  
solr stop -all  
  
// 重启solr  
solr restart -p 端口号  
  
// 创建/删除一个core  
solr create -c name  
solr delete -c name  
  
// 查看状态  
solr status  

二. 在Tomcat下部署Solr

1. 安装Tomcat

2. 部署Solr

  • solr-8.5.1\server\solr-webapp\webapp文件夹拷贝到在Tomcat 9.0\webapps\下并将文件夹重命名为solr

  • 配置solr_home

    • 新建一个文件夹,做为solr_home,如D:/solr_home

    • 拷贝solr-8.5.1\server\solr文件夹下的所有文件到D:\solr_home

    • 修改Tomcat 9.0\webapps\solr\WEB-INF\web.xml文件内容,在现有的<web-app>标签体内添加以下内容

      <env-entry>  
             <env-entry-name>solr/home</env-entry-name>  
             <env-entry-value>D:/solr_home</env-entry-value>  
             <env-entry-type>java.lang.String</env-entry-type>  
      </env-entry>  
      
  • 拷贝拷贝solr-8.5.1\server\lib\solr-8.5.1\server\lib\ext\下的所有jar包到Tomcat 9.0\webapps\solr\WEB-INF\lib\

  • 配置classes

    • 新建文件夹classes,位置:Tomcat 9.0\webapps\solr\WEB-INF\classes
    • 拷贝solr-8.5.1\server\resources\jetty-logging.propertiesclasses文件夹下
  • 最后一步,就是将Tomcat 9.0\webapps\solr\WEB-INF\web.xml中的权限验证注释掉就行,即注释掉以下内容

      <!--  
      <security-constraint>  
        <web-resource-collection>  
          <web-resource-name>Disable TRACE</web-resource-name>  
          <url-pattern>/</url-pattern>  
          <http-method>TRACE</http-method>  
        </web-resource-collection>  
        <auth-constraint/>  
      </security-constraint>  
      <security-constraint>  
        <web-resource-collection>  
          <web-resource-name>Enable everything but TRACE</web-resource-name>  
          <url-pattern>/</url-pattern>  
          <http-method-omission>TRACE</http-method-omission>  
        </web-resource-collection>  
      </security-constraint>  
      -->  
    

    /update/extract

  • 至此便可以正常访问solr了,记得在tomcat里重新加载下solr,URL地址:localhost:8080/solr/index.html

三. 创建我的第一个索引

创建索引之前,需要先在solr home文件夹下先创建对应的文件夹,这里以C:\solr_home\为例

  • 在solr_home文件夹下新建文件夹my_first_core

  • 拷贝C:\solr_home\configsets\_default\conf文件夹到my_first_core下

  • 在solr页面中Add Core

    solr_add_core.png

  • 创建成功后可以看到以下内容

    solr_my_first_core.png

四. 安装中文分词器

常用的中文分词器有2个:SmartcnIKanalyzer

1. Smartcn

  • 第一步:关联所需jar包lucene-analyzers-smartcn-8.5.1.jar

    • 方式1:添加jar包的依赖路径(推荐)         修改solrconfig.xml文件,添加以下内容(其中相对路径是从solr_home文件夹开始拼接)

        <lib path="../../../contrib/analysis-extras/lucene-libs/lucene-analyzers-smartcn-8.5.1.jar" />  
      
    • 方式2:将所依赖的jar包拷贝到WEB-INF\lib路径下
      拷贝文件夹solr-8.5.1\contrib\analysis-extras\lucene-libs下的jar包到Tomcat 9.0\webapps\solr\WEB-INF\lib\

  • 第二步:编辑managed-schema文件,添加以下内容

    <!-- 配置中文分词器 -->  
    <fieldType name="text_smartcn" class="solr.TextField" positionIncrementGap="100">  
      <analyzer type="index">  
        <tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory"/>  
      </analyzer>  
      <analyzer type="query">  
        <tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory"/>  
      </analyzer>  
    </fieldType>  
    
  • 第三步:重新加载solr服务器后进入Analyse页面,试下中文分词器的效果,如下图所示

    solr_smartcn_example.png

2. IKanalyzer

查看Github主页的帮助文档进行安装配置即可

五. solrj的使用

以下用一个简单的示例来演示solrj的使用。配置3个字段,分别是:id、title和content

1. solr服务端字段配置(managed-schema)

在conf/managed-schema文件中添加以下内容,用以配置title和content字段(因为id字段默认已经含有,无需另外添加)

<field name="title" type="text_en" indexed="true" stored="true" multiValued="true"/>  
<field name="content" type="text_en" indexed="true" stored="true" omitNorms="true" multiValued="false"/>  

2. solrj客户端代码

import org.apache.solr.client.solrj.SolrQuery;  
import org.apache.solr.client.solrj.SolrClient;  
import org.apache.solr.client.solrj.response.QueryResponse;  
import org.apache.solr.common.SolrDocument;  
import org.apache.solr.common.SolrDocumentList;  
import org.apache.solr.common.SolrInputDocument;  
import org.apache.solr.client.solrj.impl.HttpSolrClient;  
  
public class CMySolrjTest {  
      
    final String SOLR_URL = "http://127.0.0.1:8080/solr/my_first_core";  
  
    public static void main(String[] args) {  
        CMySolrjTest test = new CMySolrjTest();  
          
        try {  
            //test.addDoc();  
            test.queryDoc();  
            //test.deleteDoc();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
          
        System.out.println("运行结束");  
    }  
      
    public void addDoc() throws Exception {  
        SolrClient solrClient = new HttpSolrClient.Builder(SOLR_URL).withConnectionTimeout(10000).withSocketTimeout(6000).build();  
        SolrInputDocument document = new SolrInputDocument();  
          
        document.addField("id", "ssx.txt");  
        document.addField("title", "This is my title-yaoyuliang");  
        document.addField("content", "This is my file content-yaoyuliang");  
   
        solrClient.add(document);  
        solrClient.commit();  
    }  
      
    public void queryDoc() throws Exception {  
         HttpSolrClient solrServer = new HttpSolrClient.Builder(SOLR_URL)  
             .withConnectionTimeout(10000)  
             .withSocketTimeout(60000)  
             .build();  
           
         SolrQuery query = new SolrQuery();  
         query.set("q", "content:yaoyuliang");  
         // 调用server的查询方法,查询索引库  
         QueryResponse response = solrServer.query(query);  
         // 查询结果  
         SolrDocumentList results = response.getResults();  
         // 查询结果总数  
         long cnt = results.getNumFound();  
         System.out.println("查询结果总数:" + cnt);  
         for (SolrDocument solrDocument : results) {  
             System.out.println(solrDocument.get("id"));  
             System.out.println(solrDocument.get("title"));  
             System.out.println(solrDocument.get("content"));  
         }  
    }  
      
    public void deleteDoc() throws Exception {  
        SolrClient solrClient=new HttpSolrClient.Builder(SOLR_URL).withConnectionTimeout(10000).withSocketTimeout(6000).build();  
        //根据id删除  
        //solrClient.deleteById("005");  
        //根据查询条件删除  
        solrClient.deleteByQuery("*:*");  
        //提交  
        solrClient.commit();  
    }  
}  

六. 导入pdf/office等富文本文件

导入富文本的方式有2种,一种是客户端直接上传文件,交由服务器端进行解析。另一种是客户端解析好,上传至服务器。下面将分别介绍2种方式的使用。

1. 在服务器端进行富文本解析(不推荐)

1.1. 编辑solrconfig.xml文件,添加相关jar包的依赖。

<lib dir="../../lib" />  
<lib dir="../../../dist" />  
<lib dir="../../../contrib/extraction/lib" />  

1.2. 编辑solrconfig.xml文件,添加requestHandler

  <requestHandler name="/update/extract" class="solr.extraction.ExtractingRequestHandler" startup="lazy">  
    <lst name="defaults">  
      <str name="uprefix">ignored_</str>  
    </lst>  
  </requestHandler>  
  • requestHandler的各字段含义表
关键字描述
fmap.XXX映射,示例:fmap.content=text(将Tick生成的content字段映射到text)
fmap.content 文件内容
fmap.stream_size 文件大小
fmap.stream_name 内容流的名称
fmap.stream_content_type 流的内容类型
literal.XXX使用指定的值创建一个字段
lowernames所有字段名称都将使用下划线映射为小写,如输入“ Content-Type”,则文档中的结果将是一个字段content_type
uprefix为没有在schema中定义的字段加一个统一的前缀。
示例:uprefix=ignored_
为所有未知字段添加ignored_前缀
可以在schema中同时添加以下语句实现不索引未定义的这些字段
<dynamicField name="ignored_*" type="ignored" />
captureAttr对属性进行索引,如从HTML提取时,Tika可以将标签中的href属性作为名为“ a”的字段返回
commitWithin在指定的毫秒数内添加文档。示例:commitWithin=10000(10秒)
ignoreTikaException如果为true,则将跳过在处理期间发现的异常。但是,所有可用的元数据都会被索引
multipartUploadLimitInKB定义要允许的文档大小(以千字节为单位)。默认值为2048(2Mb)。如果文档非常大,则应增加此文档,否则将被拒绝。
例: multipartUploadLimitInKB=2048000

1.3. 编辑managed-schema,添加文件内容字段(通过postman、或者web界面、或者直接编辑都行)

<field name="content" type="text_ik" uninvertible="false" indexed="true" stored="true"/>  

1.4. java示例代码

    // 添加指定的pdf文件入索引库  
    public void addPdf() throws Exception {  
        SolrClient client = new HttpSolrClient.Builder(SOLR_URL)  
                .withConnectionTimeout(10000)  
                .withSocketTimeout(6000)  
                .build();  
        ContentStreamUpdateRequest request = new ContentStreamUpdateRequest("/update/extract");  
        request.addFile(new File("c:/tmp/xxx.pdf"), "application/pdf");  
        request.setParam("literal.id", "c:/tmp/xxx.pdf");  
        request.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);  
        client.request(request);  
    }  
  
    // 遍历C:/tmp文件夹下的所有文件,全部导入到solr中  
    public void createIndexFromDir() throws Exception {  
        File dir = new File("c:/tmp");  
        File[] fileList = dir.listFiles();  
          
        SolrClient client = new HttpSolrClient.Builder(SOLR_URL)  
                .withConnectionTimeout(10000).withSocketTimeout(6000).build();  
        ContentStreamUpdateRequest request;  
          
        // 遍历文件夹下所有文件  
        for(File file : fileList) {  
            if(!file.isDirectory()) {  
                request = new ContentStreamUpdateRequest("/update/extract");  
                request.addFile(file, getFileContentType(file.getAbsolutePath()));  
                request.setParam("literal.id", file.getAbsolutePath());  
                client.request(request);  
                System.out.println("push file" + file.getAbsolutePath());  
            }  
        }  
        client.commit();  
    }  
      
    private static String getFileContentType(String filename) {  
        String contentType = "";  
        String prefix = filename.substring(filename.lastIndexOf(".") + 1);  
        if (prefix.equals("pdf")) {  
            contentType = "application/pdf";  
        } else if (prefix.equals("txt") || prefix.equals("xml")) {  
            contentType = "text/plain";  
        } else if (prefix.equals("xlsx")) {  
            contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";  
        } else if (prefix.equals("doc")) {  
            contentType = "application/msword";  
        } else if (prefix.equals("xls")) {  
            contentType = "application/vnd.ms-excel";  
        } else if (prefix.equals("docx")) {  
            contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";  
        } else if (prefix.equals("ppt")) {  
            contentType = "application/vnd.ms-powerpoint";  
        } else if (prefix.equals("pptx")) {  
            contentType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";  
        } else {  
            contentType = "othertype";  
        }  
  
        return contentType;  
    }  
      

2. 在客户端进行富文本解析(推荐)

2.1. 编辑managed-schema,添加文件内容字段(通过postman、或者web界面、或者直接编辑都行)

<field name="content" type="text_ik" uninvertible="false" indexed="true" stored="true"/>  

2.2. 将所依赖的jar包导入到客户端工程中去

具体哪些jar包还没来的急整理,大概是这些文件夹下的jar先全依赖上
dist文件夹下的
extraction文件夹下的
solr-8.5.1\server\lib\文件夹
commons-lang3-3.9.jar

需要额外下载的jar包
jai-imageio-core-1.4.0.jar 也可以去bintray.com下载其它版本,或去项目的Github主页下载。
jai-imageio-jpeg2000-1.3.0.jar 也可以去mvnrepository.com下载其它版本,或去项目的Github主页下载。
sqlite-jdbc-3.30.1.jar 也可以去mvnrepository.com下载其它版本。

2.3. Java示例代码

  
import org.apache.solr.client.solrj.SolrQuery;  
import org.apache.solr.client.solrj.SolrServerException;  
import org.apache.solr.client.solrj.SolrClient;  
import org.apache.solr.client.solrj.response.QueryResponse;  
import org.apache.solr.client.solrj.response.UpdateResponse;  
import org.apache.solr.common.SolrDocument;  
import org.apache.solr.common.SolrDocumentList;  
import org.apache.solr.common.SolrInputDocument;  
import org.apache.solr.client.solrj.impl.HttpSolrClient;  
import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;  
import org.apache.solr.client.solrj.request.AbstractUpdateRequest;  
  
import org.apache.tika.metadata.Metadata;  
import org.apache.tika.parser.AutoDetectParser;  
import org.apache.tika.parser.ParseContext;  
import org.apache.tika.sax.BodyContentHandler;  
import org.xml.sax.ContentHandler;  
  
  
public class CMySolrjTest {  
      
    final String SOLR_URL = "http://127.0.0.1:6789/solr/my_first_core/";  
    private AutoDetectParser autoParser;  
    HttpSolrClient client;  
    private Collection<SolrInputDocument> docList = new ArrayList<SolrInputDocument>();  
    private int totalTika = 0;  
  
    public void addByTika() throws Exception {  
        autoParser = new AutoDetectParser();  
        client = new HttpSolrClient.Builder(SOLR_URL)  
                .withConnectionTimeout(10000)  
                .withSocketTimeout(6000)  
                .build();  
          
        doTikaDocuments(new File("C:/tmp/"));  
    }  
  
    private void doTikaDocuments(File root) throws IOException, SolrServerException{  
        for (File file : root.listFiles()) {  
            if (file.isDirectory()) {  
                doTikaDocuments(file);  
                continue;  
            }  
      
            ContentHandler textHandler = new BodyContentHandler();  
            Metadata metadata = new Metadata();  
            ParseContext context = new ParseContext();  
      
            InputStream input = new FileInputStream(file);  
  
            try {  
                autoParser.parse(input, textHandler, metadata, context);  
            }  
            catch (Exception e) {  
                System.out.println(String.format("File %s failed", file.getCanonicalPath()));  
                e.printStackTrace();  
                continue;  
            }  
  
            //dumpMetadata(file.getCanonicalPath(), metadata);  
      
            SolrInputDocument doc = new SolrInputDocument();  
            doc.addField("id", file.getCanonicalPath());  
            doc.addField("content", textHandler.toString());  
      
            docList.add(doc);  
            ++totalTika;  
      
            //if (docList.size() >= 1000) {  
                UpdateResponse resp = client.add(docList, 300000);  
                if (resp.getStatus() != 0) {  
                    System.out.println("Some horrible error has occurred, status is: " + resp.getStatus());  
                }  
                docList.clear();  
            //}  
             client.commit();  
        }  
    }  
      
    private void dumpMetadata(String fileName, Metadata metadata) {  
        System.out.println("Dumping metadata for file: " + fileName);  
        for (String name : metadata.names()) {  
            System.out.println("  " + name + ":" + metadata.get(name));  
        }  
    }  
}  

七. DataImport问题

- 拷贝solr-8.5.1\dist下的jar包到Tomcat 9.0\webapps\solr\WEB-INF\lib\下
主要是拷这2个jar包文件
solr-dataimporthandler-8.5.1.jar
solr-dataimporthandler-extras-8.5.1.jar
- 修改solrconfig.xml指向数据描述文件

<requestHandler name="/dataimport" class="solr.DataImportHandler">   
    <lst name="defaults">   
        <str name="config">data-config.xml</str>   
    </lst>   
</requestHandler>  

- 添加文件data-config.xml的内容如下:

<dataConfig>  
  <dataSource name="fileDataSource" type="FileDataSource" />  
      
    <!--<document>    
        <entity name="tika-test" processor="TikaEntityProcessor"    
                url="C:/docs/solr-word.pdf" format="text">    
                <field column="Author" name="author" meta="true"/>    
                <field column="title" name="title" meta="true"/>    
                <field column="text" name="text"/>    
        </entity>    
    </document>-->    
      
  <dataSource name="urlDataSource" type="BinURLDataSource" />  
  <!--baseDir="D:/work/Solr/solr-6.6.0/ImportDoc" fileName=".*\.(doc)|(pdf)|(docx)|(txt)"-->  
  <document>  
    <entity name="files" dataSource="null" rootEntity="false"  
    processor="FileListEntityProcessor"  
    baseDir="D:/work/Solr/solr-6.6.0/ImportDoc" fileName=".*\.(json)|(txt)|(csv)|(xml)"  
    onError="skip"  
    recursive="true">  
      <field column="file" name="id"/>  
        
      <field column="fileAbsolutePath" name="filePath" />  
      <field column="fileSize" name="size" />  
      <field column="fileLastModified" name="lastModified" />  
  
      <entity processor="PlainTextEntityProcessor" name="txtfile" url="${files.fileAbsolutePath}" dataSource="fileDataSource">  
        <field column="plainText" name="text"/>  
      </entity>  
    </entity>  
  </document>  
</dataConfig>  

八. 各配置文件的作用

- managed-schema:定义了索引数据类型,索引字段等信息。老版本的schema配置文件是schema.xml,它的编辑方式是手动编辑,而managed-schema的编辑方式是通过Schema API来配置
- uniqueKey:文档的唯一标示,相当于主键,每次更新,删除的时候都根据这个字段来进行操作
- fieldtype
- 定义数据类型
- 定义当前类型建立索引和查询数据的时候使用的查询分词器
managed_schema_demo.png     - field:指定建立索引和查询数据的字段
- dynamicField:动态定义一个字段,只要符合规则的字段都可以
        <dynamicField name="*_i" stored="true" indexed="true" type="int"/>         
*_i 只要以_i结尾的字段都满足这个定义。
- copyField:把一个字段的值复制到另一个字段中,这样搜索的时候都可以根据一个字段来进行搜索

- solrconfig.xml:定义了Solr的一些处理规则,包括索引数据的存放位置,更新,删除,查询的一些规则配置
- luceneMatchVersion:表示solr底层使用的是lucene版本
- lib:表示solr引用包的位置,当dir对应的目录不存在时候,会忽略此属性
- datadDir:定义了索引数据和日志文件的存放位置
- directoryFactory:索引存储方案
- codecFactory:指定编码、解码器
- indexConfig:设置索引的低级别的属性
- updateHandler
- updateLog:设置索引库更新日志
- autoCommit:设置自动硬提交方式
- query:设置查询相关参数
- requestHandler:solr请求转发器
- requestHandler:solr请求映射处理器

Schema API操作managed-schema

准备工具Postman

  • add-field
    postman_add_field.png     
    效果就是在文件中添加了以下内容:

    <field name="sell_by" type="text_en" indexed="true" stored="true"/>  
    
  • delete-field postman_delete_field.png

- 更多API操作参见官方schema-api手册

Solr查询URL

  • 查询所有
        http://localhost:8080/solr/primary/select?q=*:*     
  • 限定返回字段(如查询所有,只返回productId字段)
        http://localhost:8080/solr/primary/select?q=*:*&fl=productId     
  • 查询前六条记录,只返回productId字段
        http://localhost:8080/solr/primary/select?q=*:*&fl=productId&rows=6&start=0     
  • q - 查询字符串,必须的。
  • fl - 指定返回那些字段内容,用逗号或空格分隔多个。
  • start - 返回第一条记录在完整找到结果中的偏移位置,0开始,一般分页用。
  • rows - 指定返回结果最多有多少条记录,配合start来实现分页。
  • sort - 排序,格式:sort=+<desc|asc>[,+<desc|asc>]。示例:(inStock desc, price asc)表示先 "inStock" 降序, 再 "price" 升序,默认是相关性降序。
  • wt - (writer type)指定输出格式,可以有 xml, json, php, phps。

运行jar文件,并指定依赖的包的路径,多个路径使用冒号":"分割

java -jar -Djava.ext.dirs=./ xxx.jar  

date: 2020 年 04月 24日

参考文档:
[1] Solr中文网
[2] SolrJ8.5.1-官方API文档
[3] CSDN:通过solrj对solr进行开发
[4] CSDN: Solr 7.5.0 windows单机版
[5] Solr Ref Guide 8.5
[6] Uploading Data with Solr Cell using Apache Tika
[7] Indexing Existing Data with SolrJ in Apache Solr