在J2EE项目中集成Solr

之前j2ee项目中实现全文索引一直是用Lucene,这次准备使用Solr来实现。

1、下载Solr,本文所用Solr版本是4.10.4,下载链接:solr-4.10.4.zipsolr-4.10.4.tgz

2、在Eclipse里新建一个web工程,将solr解压缩,把其中的dist/solr-4.10.4目录复制到工程目录下。

3、在本地硬盘上任意建立一个目录用作solr的工作目录(solr/home),在工程的web.xml里修改相应的配置项指向这个目录:

<env-entry>
   <env-entry-name>solr/home</env-entry-name>
   <env-entry-value>C:/my/solr/home</env-entry-value>
   <env-entry-type>java.lang.String</env-entry-type>
</env-entry>

上面的方式相当于“写死”了solr/home,对开发部署不是很友好。为了解决这个问题,我们可以使用jndi配置solr/home,在tomcat的server.xml里:

<Context docBase="com.my.solr" path="/solr" >
<Environment name="solr/home" type="java.lang.String" value="/my/solr/home"/>
</Context>

4、将解压缩目录里的example/multicore目录复制到solr的工作目录。

5、启动tomcat,用这个地址试试能否访问solr的管理控制台:http://localhost:8080/solr/

solr_admin

如果启动时报找不到类的错误,可能需要把slf4j和commons-logging这两个jar包添加到WEB-INF/lib下。

配置中文分词(mmseg4j)

1、Solr 4.10.4对应mmseg4j的版本是2.2(注意区分,mmseg4j 2.3版支持的是solr 5.0),下载并解压缩得到mmseg4j-core-1.10.0.jar和mmseg4j-solr-2.2.0.jar两个jar包文件,复制到工程WEB-INF/lib目录下。

2、修改solr的schema.xml文件,添加支持中文分词的字段。mmseg4j提供了三种分词模式:simple, complex和maxword,所以这里添加三个字段类型。

<!-- mmseg4j -->
<fieldtype name="textComplex" class="solr.TextField" positionIncrementGap="100">
    <analyzer>
        <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="complex" dicPath="dic"/>
    </analyzer>
</fieldtype>
<fieldtype name="textMaxWord" class="solr.TextField" positionIncrementGap="100">
    <analyzer>
        <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="max-word" />
    </analyzer>
</fieldtype>
<fieldtype name="textSimple" class="solr.TextField" positionIncrementGap="100">
    <analyzer>
        <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="simple" dicPath="n:/custom/path/to/my_dic" />
    </analyzer>
</fieldtype>

从1.9(?)版开始,mmseg4j的jar包里已经包含了基本词库。上面的配置里,dicPath指定的是扩展词库所在路径(不是必须项),可以用绝对路径,也可以用相对路径(相对solr home的路径)。

在Solr admin里验证一下分词是否生效。

solr_analysis_maxword

使用DataImportHandler与MySQL集成

参考:Data Import Request Handler - from Solr Wiki

1、把solr自带的solr-dataimporthandler-4.10.4.jar和solr-dataimporthandler-extras-4.10.4.jar这两个文件复制到j2ee工程的WEB-INF/lib目录下。

把mysql驱动mysql-connector-java-5.1.21-bin.jar也复制到WEB-INF/lib目录下(如果缺少了这个文件,执行全量导入时导入数量是0但不会报错,比较坑)。

2、修改solrconfig.xml文件,添加如下内容:

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

数据源信息可以直接定义在solrconfig.xml里,也可以定义在独立的data-config.xml文件里。下面是一个data-config.xml例子:

<dataConfig>
    <dataSource type="JdbcDataSource" 
        jndiName="java:comp/env/jdbc/rushy" encoding="UTF-8" />
    <document>
        <!-- Root entity -->
        <entity name="discount"
            query="select id, old_price, old_price_time, new_price, new_price_time, product_id from t_price_change">
            <!-- column: column name in MySQL; name: column name in Solr -->
            <field column="id" name="c_id"/>
            <field column="old_price" name="c_old_price"/>
            <field column="old_price_time" name="c_old_price_time" />
            <field column="new_price" name="c_new_price" />
            <field column="new_price_time" name="c_new_price_time" />

            <!-- Embed entity -->
            <entity name="product"
                query="select id, name, image_url, store_given_id, last_price, last_price_time, lowest_price, lowest_price_time, lowest_price_time_latest, store_id from t_product where id=${discount.product_id}">
                <field column="id"  />
                <field column="name" />
                <field column="image_url" />
                <field column="store_given_id" />
            </entity>
        </entity>
    </document>
</dataConfig> 

注意上面的例子里,dataSource也是jndi定义的,这需要在<Context>里配置数据源的信息(与solr/home的jndi定义并列),例如:

<Resource auth="Container" driverClassName="com.mysql.jdbc.Driver" logAbandoned="true" maxActive="100" maxIdle="30" maxWait="10000" name="jdbc/mydb" password="mypassword" removeAbandoned="true" removeAbandonedTimeout="60" type="javax.sql.DataSource" url="jdbc:mysql://localhost:3306/mydb?characterEncoding=utf8" username="root"/>

3、配置schema.xml

将需要导入solr的字段定义在schema.xml里,例如:

<!-- solr internal -->
fieldtype name="string"class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0"/>
fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" positionIncrementGap="0"/>
fieldType name="date" class="solr.TrieDateField" precisionStep="0" positionIncrementGap="0"/>

<!-- mmseg4j -->
...

<!-- my app -->
<field name="c_id" type="int" indexed="true"stored="true"multiValued="false" required="true"/> 
<field name="c_old_price" type="double" indexed="false"stored="true"multiValued="false" /> 
<field name="c_old_price_time"type="date" indexed="false"stored="true"multiValued="false" /> 
<field name="c_new_price" type="double" indexed="false"stored="true"multiValued="false" /> 
<field name="c_new_price_time"type="date" indexed="true"stored="true"multiValued="false" /> 
<field name="p_id" type="int" indexed="false"stored="true"multiValued="false" /> 
<field name="p_name" type="string" indexed="true"stored="true"multiValued="false" /> 

Solr内置的数据类型参考:Solr Field Types

注意:如果导入时发现failed记录很多,可能是schema.xml里定义了required字段但在dataConfig的entity里没有定义,请仔细检查。

注意2:如果要修改schema.xml,需要清除原有数据(提交下面的请求),然后重启solr:

<delete><query>*.*</query></delete>
<commit/>

4、将MySQL里的数据导入到Solr

DataImportHandler配置好以后,可以在solr admin界面里直接操作全量或增量导入数据:

solr_import

也可以在浏览器里访问solr提供的接口调用:

当前状态(例如:导入进度):访问http://localhost:8080/solr/core0/dataimport

全量索引:访问http://localhost:8080/solr/core0/dataimport?command=full-import

增量索引:访问http://localhost:8080/solr/core0/dataimport?command=delta-import

关于索引速度,普通配置的开发环境下,单表索引速度约为10000条/秒,有一个关联表的情况约为2000条/秒。