00_Solr初识

Solr 是一个独立的企业级搜索应用服务器

Posted by silhouette on May 23, 2020

一、Solr 概述

1.1 Solr 简介

  • Solr:Solr 是一个高性能,采用 Java5 开发,基于 Lucene 的全文搜索服务器。同时对其进 行了扩展,提供了比 Lucene 更为丰富的查询语言,同时实现了可配置、可扩展并对查询性 能进行了优化,并且提供了一个完善的功能管理界面,是一款非常优秀的全文搜索引擎。
  • 下载:Solr 是 Apache 下的一个开源项目,由于是基于 Lucene 的一个实现产品,因此将 Solr 放在 Lucene 项目当中。可以在官方网站历史版本中下载我们需要的版本。此处以4.10.2为例
  • 目录结构
    • contrib/dist:存放Solr 插件依赖的 jar 包
    • example:存放Solr内置实例
      • Solr 目录用来存放 Solr 的 Core,主要包含索引和配置文件。
      • solr-webapp 目录中管理者 Jetty 运行的 Solr 服务去程序。
      • Webapps 目录中的 solr.war 为 solr 服务的文本应用程序。
      • start.jar:内置 Jetty 服务器,可以直接运行Solr 服务

1.2 运行 Solr 服务

Solr 实际上是一个 web 服务器应用程序,因此要启动 Solr 需要文本服务器,而在 Solr 实例代码中已经直接集成了 Jetty 服务器,我们可以直接使用, 也可以单独配置和使用 Tomcat 服务器来启动 Solr 服务。

  • 启动 Solr 服务的方式

    • 使用 Solr 内置的 Jetty 服务器启动 Solr

      1. 使用内置的 Jetty 来启动 Solr 服务器只需要在 example 目录下,执行 start.jar 程序即可,我们可以直接执行命令:

        $ java –jar start.jar
        
      2. 当服务启动后,默认发布在 8983 端口,所以可以访问该端口来访问 Solr 服务。

    • 将 Solr 部署到 Tomcat 中

      1. 在 Tomcat 中部署 Web 服务,将 solr-4.10.2/example/webapps/solr.war 复制到自 己的 tomcat/webapps 目录中,解压后移除war包(因为每次服务器重新启动后,会被覆盖)。

        $ cd /home/silhouette/solr-4.10.2/example/webapps
        $ cp solr.war /home/silhouette/apache-tomcat-8.5.38/webapps/ 
        $  unzip solr.war -d solr
        
      2. 解压后Solr还不能直接访问,因为缺少相应的jar包,所以需要将 Solr 用到的 jar 包拷贝到项目的 lib 目录下

        $ cd /home/silhouette/solr-4.10.2/example/lib/ext
        $ cp * /home/silhouette/apache-tomcat-8.5.38/webapps/solr/WEB-INF/lib/
        $ cd /home/silhouette/solr-4.10.2/example/resources
        $ cp log4j.properties /home/silhouette/apache-tomcat-8.5.38/webapps/solr/WEB-INF/classes
        
      3. 拷贝Solr 的索引目录。

      • 将 Solr 用到的 jar 包拷贝到项目的 lib 目录下或者只想所在目录,在启动前还需要设置 solr.solr.home 启动参数,在 catalina.bat 文件添加配置:

        $ mkdir solr_home
        $ cd solr_home
        $ cp -r ../example/solr ./
        $ vim catalina.sh
        $ set "JAVA_OPTS=-Dsolr.solr.home=/Users/silhouette/software/apache/solr/solr-4.10.2/solr_home/solr"
        ####Linux 环境下
        $ export "JAVA_OPTS=-Dsolr.solr.home=/Users/silhouette/software/apache/solr/solr-4.10.2/solr_home/solr"
        
      • 启动 Tomcat 则 Solr 的服务器发布就完成了。

1.3 Solr 管理界面

solr 服务器管理界面可以查看系统状态、solr设置、分词检测、查询索引、增减core、查看日志等

  • DashBoard(仪表盘)

    • 查看到solr运行时间、solr版本,系统内存、虚拟机内存的使用情况

  • Logging(日志)

    • 显示solr运行出现的异常或错误

  • Core Admin

    • 在 Solr 中,每一个 Core,代表一个索引库,里面包含索引数据及其信息。Solr 中可以 拥有多个 Core,也就同时管理多个索引库!就像在 MySQL 中可以有多个 database 一样。

      • name:给core起的名字;
      • instanceDir:与我们在配置solr索引库时的solr_home里新建的core文件夹名一致;
      • dataDir:确认Add Core时,会在new_core目录下生成名为data的文件夹
      • config:new_core下的conf下的config配置文件(solrconfig.xml)
      • schema: new_core下的conf下的schema文件(schema.xml)

      Tips:可以直接拷贝本地的Collection1然后重命名,再修改core.properties文件即可达到相同效果

  • Java Properties

    • 可查看到java相关的一些属性的信息
  • Core Selecter(core选择器)

    • 需要在Core Admin里添加了core后才有可选项,这里以默认的collection1为例。

    • 通过 Solr 管理界面查询索引数据

1.4 Solr中的Core详解

  • Core 的概述:在Solr 中一个 core就是一个索引库,里面包含索引信息和配置文件。

  • 目录结构

    • conf:配置文件目录

      • schema.xml:字段及字段约束信息,如:字段数据类型、字段是否索引、是否存储、是否分词等

        • 通过Field 字段定义字段的属性信息

          <!-- 配置Field,就是配置在Solr中可以使用的字段名称。属性及含义:
                  name:字段名称 【_version_、_root_是Solr系统使用的】
                  type:字段类型,指向的是本文件中的<fieldType>标签 
                  indexed:是否创建索引 
                  stored:是否被存储 
                  multiValued:是否可以有多个值,如果字段可以有多个值,设置为 true
          -->
          <fieldType name="string" class="solr.StrField" sortMissingLast="true" />
          
        • 通过 FieldType 字段指定数据类型

          <!--
          name:字段类型的名称,可以自定义,<field>标签的 type 属性可以引用该字段,来指定数据类型
          class:字段类型在 Solr 中的类。StrField 可索引不可分词。TextField 字段可索引,可以分词, 所以需要指定分词器
          <analyzer>:这个子标签用来指定分词器,
          -->
          <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
              <analyzer type="index">
                  <tokenizer class="solr.StandardTokenizerFactory"/>
                  <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
                  <filter class="solr.LowerCaseFilterFactory"/>
              </analyzer>
              <analyzer type="query">
                  <tokenizer class="solr.StandardTokenizerFactory"/>
                  <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
                  <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" 
                          ignoreCase="true" expand="true"/>
                  <filter class="solr.LowerCaseFilterFactory"/>
              </analyzer>
          </fieldType>
          
          • 配置使用IK分词器

            <!--先将IK分词器的jar包放入项目的lib目录中-->
            <fieldType name="text_general" class="solr.TextField">
                <analyzer type="index" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
                <analyzer type="query" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
            </fieldType>
            
        • 通过dynamicField字段来定义动态字段

          <!--表示只要满足特定的规则就可以使用-->
          <dynamicField name="*_i"  type="int"    indexed="true"  stored="true"/>
          
        • 通过uniqueKey字段来配置唯一主键

          <!--Lucene 中本来是没有主键的。删除和修改都需要根据词条进行匹配。
          而 Solr 却可以设 置一个字段为唯一主键,这样增删改操作都可以根据主键来进行。-->
          <uniqueKey>id</uniqueKey>
          
      • Solrconfig.xml:索引库的相关配置

        • 通过<lib/>标签来配置插件依赖的 jar 包

          <!--
          如果引入多个 jar 包,要注意包和包的依赖关系,被依赖的包配置在前面
          这里的 jar 包目录如果是相对路径,相对于 core索引库 所在目录
          -->
          <lib dir="../../../contrib/extraction/lib" regex=".*\.jar" />
          <lib dir="../../../dist/" regex="solr-cell-\d.*\.jar" />
          
        • 通过<requestHandler/>标签来配置 Solr 处理各种请求(搜索/select、更新/update、等)的各种参数

          <!--主要参数:
          			name:请求类型,例如:select、query、get、update
          			class:处理请求的类
          			initParams:可选。引用<initParams>标签中的配置
          			<lst name="defaults">:定义各种缺省的配置,比如缺省的 parser、缺省返回条数
          -->
          <requestHandler name="/select" class="solr.SearchHandler">
            <lst name="defaults">
                <str name="echoParams">explicit</str>
                <int name="rows">10</int>
                <str name="df">text</str>
            </lst>
          </requestHandler>
          
    • data:索引数据目录

    • Core.properties:core的自身属性。注意core的name属性与外面索引文件名保持一致

1.5 配置中文分词器与业务域

创建对应的业务域,需要制定中文分析器,具体创建步骤如下:

  1. 把中文分析器的IKAnalyzer2012FF_u1.jar包添加到 solr 工程的lib目录下

    $ rz /home/silhouette/apache-tomcat-8.5.38/webapps/solr/WEB-INF/lib
    
  2. 把扩展词典、配置文件放到 solr 工程的 WEB-INF/classes 目录下

    $ cd /home/silhouette/apache-tomcat-8.5.38/webapps/solr/WEB-INF/lib
    $ mkdir classes
    $ cd classes
    $ rz ext_stopword.dic
    $ rz IKAnalyzer.cfg.xml
    $ rz mydict.dic
    

为了让分词器生效,配置一个 FieldType,制定使用 IKAnalyzer的分词规则,这个需要修改schema.xml文件,具体修改步骤如下:

  1. 修改schema.xml文件

    $ cd /home/silhouette/solr-index/solr/collection1/conf
    $ vim schema.xml
    # 修改 Solr 的 schema.xml 文件,添加 FieldType
    # 1.添加IK分词器的相关配置
        <!-- IKAnalyzer-->
        <fieldType name="text_ik" class="solr.TextField">
          <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
        </fieldType>
    # 2. 配置业务域,type制定使用自定义的 FieldType
        <field name="item_title" type="text_ik" indexed="true" stored="true"/>
        <field name="item_sell_point" type="text_ik" indexed="true" stored="true"/>
        <field name="item_price"  type="long" indexed="true" stored="true"/>
        <field name="item_image" type="string" indexed="false" stored="true" />
        <field name="item_category_name" type="string" indexed="true" stored="true" />
       
        <field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
        <copyField source="item_title" dest="item_keywords"/>
        <copyField source="item_sell_point" dest="item_keywords"/>
        <copyField source="item_category_name" dest="item_keywords"/>
    

重启tomcat,访问 solo 测试IK分词器是否生效。

二、SolrJ的使用

2.1 概述

SolrJ 是 Apache 官方提供的一套 Java 开发的,访问 Solr 服务的 API,通过这套 API 可 以让我们的程序与 Solr 服务产生交互,让我们的程序可以实现对 Solr 索引库的增删改查。

  • 添加依赖:

    <dependencies>
        <!-- Junit单元测试 -->
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
        </dependency>
        <dependency>
          <groupId>org.apache.solr</groupId>
          <artifactId>solr-solrj</artifactId>
          <version>4.10.2</version>
        </dependency>
        <!-- Solr底层会使用到slf4j日志系统 -->
        <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
          <version>1.7.22</version>
        </dependency>
        <dependency>
          <groupId>commons-logging</groupId>
          <artifactId>commons-logging</artifactId>
          <version>1.2</version>
        </dependency>
    </dependencies>
    

2.2 使用 SolrJ 添加或修改索引库数据

  1. 创建索引

    1. 以 Document 形式添加或修改数据

      @Test
      public void createIndex() throws Exception{
        	//定义Solr的URL路径
          String baseURL = "http://192.168.0.111:8080/solr/collection1";
          //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
          HttpSolrServer server = new HttpSolrServer(baseURL);
            
          //添加索引,需要Document对象
          SolrInputDocument document = new SolrInputDocument();
          document.addField("id", "20");
          document.addField("title", "今天星期二,是一周的第三天。");
            
          //将Document添加到索引库
          server.add(document);
            
          //提交请求
          server.commit();
      }
      
    2. 使用注解和 JavaBean 添加或修改数据

      @Test
      public void testInsertIndexByBean() throws Exception {
          //定义Solr的URL路径
          String baseURL = "http://192.168.0.111:8080/solr/collection1";
          //创建Java对象
          Book book = new Book("12345", "Solr初识","silhouette","Solr 是一个独立的企业级搜索应用服务器");
          HttpSolrServer httpSolrServer = new HttpSolrServer(baseURL);
            
          //将对象插入索引库
          UpdateResponse updateResponse = httpSolrServer.addBean(book);
          int status = updateResponse.getStatus();
          System.out.println(status);
          //提交
          httpSolrServer.commit();
      }
      
  2. 删除索引

    1. 根据 ID 删除

      @Test
      public void testDleteIndexById() throws Exception{
        	//定义Solr的URL路径
            String baseURL = "http://192.168.0.111:8080/solr/collection1";
            //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
            HttpSolrServer server = new HttpSolrServer(baseURL);
            
        	//server.deleteById("10");
            List<String> list = new ArrayList<String>();
            list.add("10");
            list.add("12");
            //删除索引
            server.deleteById(list);
            
            //提交请求
            server.commit();
      }
      
    2. 通过制定条件删除

      @Test
      public void testDleteIndexByQuery() throws Exception{
        	//定义Solr的URL路径
          String baseURL = "http://192.168.0.111:8080/solr/collection1";
          //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
          HttpSolrServer server = new HttpSolrServer(baseURL);
          //Solr提供的是web服务,在接收用户传递的参数是就是request参数
          String query="title:solr";
            
          //删除索引
          server.deleteByQuery(query);
            
          //提交请求
          server.commit();
      }
      
  3. 查询索引

    1. 以 Document 形式返回查询结果

      @Test
      public void testQueryIndex() throws Exception{
        	//定义Solr的URL路径
          String baseURL = "http://192.168.0.111:8080/solr/collection1";
          //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
          HttpSolrServer server = new HttpSolrServer(baseURL);
            
          String query = "*:*";
          //进行查询操作
          SolrParams params = new SolrQuery(query );
          QueryResponse queryResponse = server.query(params);
            
          SolrDocumentList results = queryResponse.getResults();
            
          for (SolrDocument solrDocument : results) {
            String id = (String) solrDocument.get("id");
            String name = (String) solrDocument.get("name");
            
            System.out.println("id:"+id+" name:"+name);
          }
            
          //提交请求
          server.commit();
      }
      
    2. 以 JavaBean 形式返回查询结果

      @Test
      public void testQueryIndex() throws Exception{
      		//定义Solr的URL路径
            String baseURL = "http://192.168.0.111:8080/solr/collection1";
            //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
            HttpSolrServer server = new HttpSolrServer(baseURL);
            
            String query = "author:silhouette";
            //进行查询操作
            SolrParams params = new SolrQuery(query);
            QueryResponse queryResponse = server.query(params);
            
            List<Book> books = queryResponse.getBeans(Book.class);
            
            for (Book book : books) {
              System.out.println(book);
            }
            
            //提交请求
            server.commit();
      }
      

      注意:1.JavaBean的属性要和Solr界面管理展示的属性要一致,如果缺少字段则补上,如果属性不同,就修改JavaBean或者scheme.xml配置文件。

    3. SolrQuery 对象的高级查询

      1. 匹配所有文档:*:* (通配符?和*:“*”表示匹配任意字符;“?”表示匹配出现的位置)

      2. 布尔操作:AND、OR 和 NOT 布尔操作(推荐使用大写,区分普通字段)

      3. 子表达式查询(子查询):可以使用“()”构造子查询。 比如:(query1 AND query2) OR(query3 AND query4)

      4. 相似度查询:

        1. 默认相似度查询:title:appla~ ,此时默认编辑距离是2。~表示模糊查询,默认编辑距离为2
        2. 指定编辑举例的相似度查询:对模糊查询可以设置编辑举例,可选 0~2 的整数。
      5. 范围查询(Range Query):Lucene 支持对数字、日期甚至文本的范围查询。结束的范 围可以使用“*”通配符。

        1. 日期范围(ISO-8601 时间 GMT):a_begin_date:[1990-01-01T00:00:00.000Z TO 1999-12-31T24:59:99.999Z]
        2. 数字:salary:[2000 TO *]
        3. 文本:entryNm:[a TO a]
      6. 日期匹配:YEAR, MONTH, DAY, DATE (synonymous with DAY) HOUR, MINUTE, SECOND, MILLISECOND, and MILLI (synonymous with MILLISECOND)可以被标志成 日期。

        1. r_event_date:[* TO NOW-2YEAR]:2 年前的现在这个时间
        2. r_event_date:[* TO NOW/DAY-2YEAR]:2 年前前一天的这个时间
        @Test
        public void testQueryIndex() throws Exception{
          	//定义Solr的URL路径
              String baseURL = "http://192.168.0.111:8080/solr/collection1";
              //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
              HttpSolrServer server = new HttpSolrServer(baseURL);
                   	
        		SolrParams params = new SolrQuery("description:战国 AND id:3");
        		QueryResponse queryResponse = server.query(params);
                 		
        		List<Book> books = queryResponse.getBeans(Book.class);
                 		
        		for (Book book : books) {
        			System.out.println(book);
        		}
                 		
        		//提交请求
        		server.commit();
        }
        
  4. 排序实现

    @Test
    public void testQueryIndex() throws Exception{
    		//定义Solr的URL路径
          String baseURL = "http://192.168.0.111:8080/solr/collection1";
          //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
          HttpSolrServer server = new HttpSolrServer(baseURL);
       
          String query = "author:*";
          //进行查询操作
          SolrParams params = new SolrQuery(query);
    		params.addSort("id", ORDER.asc);
          QueryResponse queryResponse = server.query(params);
       
          List<Book> books = queryResponse.getBeans(Book.class);
       
          for (Book book : books) {
            System.out.println(book);
          }
       
          //提交请求
          server.commit();
    }
    
  5. 分页实现

    @Test
    public void testQueryIndex() throws Exception{
    		//定义Solr的URL路径
          String baseURL = "http://192.168.0.111:8080/solr/collection1";
          //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
          HttpSolrServer server = new HttpSolrServer(baseURL);
       
          //要查询的页数
          int pageNum = 2;
          //每页显示条数
          int pageSize = 2;
          //当前页的起始条数
          int start = (pageNum - 1) * pageSize;
          //当前页的结束条数
          int end = start + pageSize;
          SolrQuery query = new SolrQuery("*:*");
       
          //设置起始条数
          query.setStart(start);
          //设置每页条数
          query.setRows(pageSize);
          //进行查询操作
          QueryResponse queryResponse = server.query(query);
          SolrDocumentList results = queryResponse.getResults();
          System.out.println("当前第" + pageNum + "页,本页共" +results.size() + "条数据。");
       
          for (SolrDocument solrDocument : results) {
            System.out.println(solrDocument.get("id"));
            System.out.println(solrDocument.get("title"));
          }
       
          //提交请求
          server.commit();
    }
    
  6. 高亮实现

    @Test
    public void testQueryIndex() throws Exception{
    		//定义Solr的URL路径
          String baseURL = "http://192.168.0.111:8080/solr/collection1";
          //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
          HttpSolrServer server = new HttpSolrServer(baseURL);
          //定义查询条件
          String query = "title:*";
       
          //进行查询操作
          SolrQuery params = new SolrQuery(query);
       
          //设置高亮
          params.setHighlightSimplePre("<em>");
          params.setHighlightSimplePost("</em>");
          //设置高亮显示的字段
          params.addHighlightField("title");
       
          QueryResponse queryResponse = server.query(params);
          SolrDocumentList results = queryResponse.getResults();
       
          //在map中首先使用每一条数据的id作为键,将数据作为值(只有高亮字段)
          //Map<String, List<String>>存放的是一条数据的所有高亮字段,键就是字段的名称,值就是高亮显示的数据
          //List<String>里面就是经过处理的高亮的内容,主要是因为Solr中的字段可以存储多个值,对于单个数据而言,list的长度都为1
          Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
       
          System.out.println("找到了"+results.size()+"条记录");
       
          //那么获取高亮数据可以获取对应的id,在指定高亮字段的名称,在获取list的第0个元素
          for (SolrDocument solrDocument : results) {
            String id = solrDocument.get("id").toString();
            String title = highlighting.get(id).get("title").get(0);
       
            System.out.println("id:"+id+"--- title:"+title);
          }
       
          //提交请求
          server.commit();
    }
    
  7. 设置权重

    @Test
    public void testQueryIndex() throws Exception{
    		//定义Solr的URL路径
          String baseURL = "http://192.168.0.111:8080/solr/collection1";
          //要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
          HttpSolrServer server = new HttpSolrServer(baseURL);
       
          SolrInputDocument doc = new SolrInputDocument();
       		
    		//通过boost设置字段的权重
    		doc.addField("id", "20");
    		doc.addField("description", "Spring从入门到精通是一本好书",2.0f);
    		server.add(doc);
       		
    		//提交请求
    		server.commit();
    }
       
    @Test
    public void testCreateIndex() throws Exception {
      	 //定义Solr的URL路径
    		String baseURL = "http://192.168.0.111:8080/solr/collection1";
    		//要使用SolrJ首先创建服务对象,Solr实际上是提供web服务,需啊哟创建web服务的对象,支持HTTP请求
    		HttpSolrServer server = new HttpSolrServer(baseURL);
       		
    		SolrParams params = new SolrQuery("description:Spring");
    		QueryResponse queryResponse = server.query(params);
    		SolrDocumentList results = queryResponse.getResults();
       		
    		for (SolrDocument solrDocument : results) {
    			System.out.println(solrDocument.get("id")+" : "+solrDocument.get("description"));
    		}
       		
    		//提交请求
    		server.commit();
    }