搜索引擎 ~ Solr & SpringDataSolr

2018-11-10  本文已影响25人  帅可儿妞

如果你的项目中用到了搜索,现在的选择就奔上就是Solr或者Elasticsearch了,今天我们就来看看Solr,当然我一般是不会在博客中讲什么原理的,因为讲原理是我自己的理解,我怕误导大家,好了开始吧

一、简介

  1. 为什么要使用搜索呢?
    • 我们知道,一个大型网站,用户的搜索是一个及其消耗服务器性能的事情,如果在这点上处理不好,将会使得用户体验度大打折扣,甚至拖垮整个应用!
  2. Solr
    • 讲到Solr其实肯定就会提到Lucene,两者的区别在于Lucene是一个底层的API实现,而Solr是在应用的层面上封装了Lucene。
    • Solr是Apache旗下的一款快速的,高度可扩展,可提供高性能,以文本为中心的开源搜索服务器。它基于HTTP请求访问,也就是说跨平台性得到了保障;主要用于构建搜索应用程序。Yonik Seely于2004年创建,并于2006年1月成为Apache软件基金会下的一个开源项目;
    • Solr的另一大可以说是特性就是可以和Hadoop一起使用,也就是说不仅限于搜索,Solr也可以用于数据的存储和数据处理,是一种非关系数据存储和处理技术;
  3. 如果你想知道Elasticsearch和Solr的区别,可以查看这篇

二、Solr的安装

  1. 在Win上的安装

    • 下载Solr,常用版本你可以去国内的镜像源下载
    • 如果你下载的是Solr5之前的版本,那就需要把安装包下的solr.war复制到Tomcat中,而如果是5及其以后版本则直接运行,即独立部署,两者版本区别请查看这里;独立部署的文章也是一大堆,问度娘;
  2. Solr5之前的版本安装

    • 这里我选择的是4.9.1,即5之前的最后一个版本;
    • 解压之后把dist之中的solr-4.9.1.war复制到Tomcat的webapp下;
    • 启动,等待解压,完成后把下载包中的\example\lib\ext中所有的jar拷贝到Tomcat\webapps\solr\WEB-INF\lib中,主要是给solr提供一个日志方面的包;
    • 配置索引库:把解压包中的\example\solr到电脑中的一个位置,如我放在D:\Documents\SolrHome,我把solr重命名为SolrHome,把D:\Documents\SolrHome配置到Solr应用的web.xml中的env-entity中;
    • 启动Tomcat,访问:http://localhost:8080/solr,安装完成
  3. Solr5及其之后的版本安装

    • // TODO 有时间补

三、配置中文分词器

  1. 这里我们使用IKAnalyzer来做中文分词,单独使用请参考我的另一片文章中文分词器 ~ IK Analyzer
  2. 在Solr中使用的步骤如下:
    • 文章中文分词器 ~ IK Analyzer中的配置;
    • 修改 SolrHome 中的 schema.xml 文件,配置一个 FieldType,使Solr采用 IKAnalyzer作为默认的文本分词器:
      <fieldType name="text_ik" class="solr.TextField">
          <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
      </fieldType>
      
    • 配置完成后启动服务器

四、域

  1. 域,即字段,在Solr的配置schema.xml文件中对应的标签是field;还有一个就是fieldType,这个指的是域的数据类型;
  2. 我们一般是要在数据库中搜索数据的,尽管Solr的配置中已经为我们提供了相当多的域,但是我们在做项目的时候还是要根据我们的需求自定义添加一些域,并给这些域设置相应的属性,常见的属性如下:
    • name:指定域的名称,自定义,一般是数据表中的字段名
    • type:指定域的类型,即fieldType
    • indexed:是否索引,取值true/false
    • stored:是否存储,取值true/false
    • required:是否必须,取值true/false
    • multiValued:是否多值,取值true/false
  3. 自定义域
    • 分析需要搜索的字段;
    • 在SolrHome的schema.xml文件中添加字段field,根据业务需求添加这些域,同时设置上面的属性;
  4. 复制域
    • 我们在搜索的时候摁钉还会遇到一种情况,用户在搜索的时候可以填写很多信息,而这些信息对应数据库的很多字段,难道我们要把分词的结果全部在数据库中笛卡尔积式 'LIKE' 吗?Solr中为我们解决了这个问题,解决的途径就是使用复制域来解决;
    • 复制域schema.xml文件中对应的标签是copyField,含有两个属性:
      • source:来源域的name属性
      • dest:目标域,即:将source域复制到该目标域进行搜索)
  5. 动态域
    • 动态域主要用来解决项目表中存储的数据是是动态类型的情况,比如我们常用的字典表就是这一类型,某列的值是根据当前记录的类型决定的;
    • 动态域的配置与普通的字段域大部分相同,为疑似不同的就是在name属性上提供通配符 *;

五、SolrJ

  1. SolrJ是Solr官方提供的Java客户端,它提供了增删改查Solr索引的Java接口。SolrJ针对Solr提供了Rest 的HTTP接口进行了封装, SolrJ底层是通过使用httpClient中的方法来完成Solr的操作的;
  2. Solr是一个应用,SolrJ是一个请求Solr应用的客户端,之间使用HTTP进行通讯;
  3. 使用参考这篇文章

六、SpringDataSolr

  1. SpringDataSolr其实就是Spring对SolrJ的一个封装;
  2. 使用步骤
    • 引入Maven库
      <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-solr</artifactId>
          <version>3.0.11.RELEASE</version>
      </dependency>
      
    • 添加配置文件:application-solr.xml
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="https://www.springframework.org/schema/beans" 
              xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" 
              xsi:schemalocation="https://www.springframework.org/schema/beans
                  https://www.springframework.org/schema/beans/spring-beans.xsd">
               
          <!-- Solr Server配置方式1  -->
          <solr:solr-server id = "solrServer" url = "<solr-access-url>" />
          
          <!-- Solr Server配置方式2 -->
          <bean id = "solrServer" class = "org.apache.solr.client.solrj.impl.HttpSolrServer">
              <constructor-arg index="0" value = "<solr-access-url>" />
          </bean>
      
          <!-- Solr Server集群配置方式 -->
          <bean id = "solrServer" class = "org.apache.solr.client.solrj.impl.CloudSolrServer">
              <constructor-arg index="0" value="<ip-port-comma>" />
              <property name="defaultCollection" value="<collection-name>" />
          </bean>
          
          <!-- Solr Template -->
          <bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
              <constructor-arg ref="solrServer" />
          </bean>
       
      </beans>
      
    • 关联数据库字段与Solr搜索字段
      • 关联直接使用注解@org.apache.solr.client.solrj.beans.Field作用在实体类上即可,如果数据库字段名和Solr搜索字段不相同则在参数中执行即可;
      • 动态域在关联实体类的时候需要添加注解@Dynamic,凡是需要关联的实体字段是使用@Field,注解@Field是SolrJ提供的,而@Dynamic是SpringDataSolr提供的,所以在pom引用的时候直接引用SpringDataSolr即可,底层的框架会自动引入;
    • 编码初始化Solr数据
      • 编码的时候直接使用solrTemplate的save/delete/deleteById/queryForPage/getById等方法即可实现数据的增删改查,但是要注意的是,增删改语句后必须添加事务的提交语句solrTemplate.commit();,否则数据不会受影响的;
      • 具体的请查看API提示即可;
      • Query:分页查询的封装类,请看代码:
        Query query = new Query("*:*");
        query.setOffset(<start>);
        query.setRows(<page-size>);
        query.setCriteria(<criteria>); // 封装查询条件
        ScoredPage page = solrTemplate.queryForPage(query, <Bean>.class);
        List<<bean>> beansOfThisPage = page.getContent();
        System.out.println("一页数据:" + beansOfThisPage);
        System.out.println("总条数:" + page.getTotalElements());
        System.out.println("总页数:" + page.getTotalPages());
        System.out.println("总页数:" + page.getSize());
        
      • 使用上面的方法结合MyBatis把数据库中的数据保存到Solr索引中;具体就不在赘述;
  3. 关键字搜索
    • 根据上面SolrTemplate的查询方法编写查询Solr的服务;
  4. Solr增量更新(参考这里这里)
    • 我们可以手动编写导入代码结合定时器完成,但是SpringDataSolr已经帮我们做了一部分工作,配置一下即可;如下:
    • 导入solr数据更新的pom:
      <dependency>
          <groupId>org.apache.solr</groupId>
          <artifactId>solr-dataimporthandler</artifactId>
          <version>6.6.5</version>
      </dependency>
      
    • 在SolrHome目录下新建一个文件夹conf,再在conf文件夹下新建dataimport.properties文件,其内容为:
      #################################################  
      #                                               #  
      #       dataimport scheduler properties         #  
      #                                               #  
      #################################################  
               
      #  to sync or not to sync  
      #  1 - active; anything else - inactive  
      syncEnabled=1
      #  which cores to schedule  
      #  in a multi-core environment you can decide which cores you want syncronized  
      #  leave empty or comment it out if using single-core deployment  
      syncCores=test,hotel
      #  solr server name or IP address  
      #  [defaults to localhost if empty]  
      server=localhost
      #  solr server port  
      #  [defaults to 80 if empty]  
      port=8083
      #  application name/context  
      #  [defaults to current ServletContextListener's context (app) name]  
      webapp=solr
      #  增量索引的参数   
      #  URL params [mandatory]  
      #  remainder of URL  
      params=/dataimport?command=delta-import&clean=false&commit=true
      #  重做增量索引的时间间隔  
      #  schedule interval  
      #  number of minutes between two runs  
      #  [defaults to 30 if empty]  
      interval=1
      #  重做全量索引的时间间隔,单位分钟,默认7200,即5天;  
      #  为空,为0,或者注释掉:表示永不重做索引  
      #reBuildIndexInterval=7200
      #  重做索引的参数  
      reBuildIndexParams=/dataimport?command=full-import&clean=true&commit=true
      #  重做索引时间间隔的计时开始时间,第一次真正执行的时间=reBuildIndexBeginTime+reBuildIndexInterval*60*1000;  
      #  两种格式:2012-04-11 03:10:00 或者  03:10:00,后一种会自动补全日期部分为服务启动时的日期  
      reBuildIndexBeginTime=03:10:00
      
    • 增加增量更新监听器。即在Solr服务的web.xml加入监听器:
      <listener>
          <listener-class>
              org.apache.solr.handler.dataimport.scheduler.ApplicationListener
          </listener-class>
      </listener>
      
    • 编写增量更新SQL:在Solr的conf\data-config.xml中<entity>标签加入两个属性:
      deltaImportQuery="<data-query-sql>'"
      deltaQuery="<primary-key-query-sql>"
      
      • 属性介绍:
        query是获取全部数据的SQL
        deltaImportQuery是获取增量数据时使用的SQL
        deltaQuery是获取主键的SQL
        parentDeltaQuery是获取父Entity的主键的SQL
      • Full Import工作原理:
        1. 执行本Entity的Query,获取所有数据;
        2. 针对每个行数据Row,获取主键,组装子Entity的Query;
        3. 执行子Entity的Query,获取子Entity的数据。
      • Delta Import工作原理:
        1. 查找子Entity,直到没有为止;
        2. 执行Entity的deltaQuery,获取变化数据的主键;
        3. 合并子Entity parentDeltaQuery得到的主键;
        4. 针对每一个主键 Row,组装父Entity的parentDeltaQuery;
        5. 执行parentDeltaQuery,获取父Entity的主键;
        6. 执行deltaImportQuery,获取自身的数据;
        7. 如果没有deltaImportQuery,就组装Query;
      • 限制:
        ✔ 子Entity的query必须引用父Entity的主键;
        ✔ 子Entity的parentDeltaQuery必须引用自己的主键;
        ✔ 子Entity的parentDeltaQuery必须返回父Entity的主键;
        ✔ deltaImportQuery引用的必须是自己的主键;
上一篇下一篇

猜你喜欢

热点阅读