***********************
    SolrJ学习的一点总结
***********************

    写在开头的废话
================

    最近接手一个旧项目,原来因为赶工导致整个代码混乱不堪,无奈之下下定决心准备全部重写。这个系统大量用到全文索引,目前比较流行的有Lucene和Sphinx,且这两个框架在不同项目中都用到了。很久之前看过Lucene,但基本都忘记了,Sphinx在最近一个项目中用到,但是我没具体参与,用法也不清楚。原先系统中是用的对Lucene上层封装的SolrJ框架,目前用的也有很多问题。这真是一个不容易的抉择,按道理应该统一技术选型用Sphinx,但是我还是被SolrJ的易用性和强大功能吸引了,特别是用Java的哪能那么容易拒绝Java的开源产品呢。

    小试牛刀
===========

    既然定下来了,那就踏踏实实的开始学习的旅程吧。第一步当然先上官方网站看资料,apache下的产品文档还是非常齐备的。先看看有哪些版本,结果上去一看确实被晃点了一下,最新版本有两个,一个是3.1.0,一个是1.4.1。后来在一处文档中看到,最新的版本号已经不单独立了,而是根据Lucene的版本一起走,并且打算以后就和它一起打包发布了。看了一下change log,3.1.0的版本改动并不大,感觉稳定性应该还是有保障的。两个版本都下载下来看了一下,确实3.1.0的版本在注释、目录结构等细节上更清晰,所以就敲定用它了。

    第二步当然是弄个demo玩一下了,索性在包中直接就有一个example的例子,官方给的代码例子和这个都是对应的,后来看配置文件的时候才发现的,狂汗...!所以不要异想天开的自己乱写,先搞懂再动手。服务器启动很简单,直接在example目录下面执行java -jar start.jar就行了,在http://localhost:8983/solr中就可以进行查询了。但是添加数据的代码就要自己写了,否则啥都没有咋查啊。插入数据的例子官方网站就有,照着写就OK了,还是给一个例子好了。其实我这里把插入、查询、删除的都写了个例子并调试通过,所以心里也基本有底了。

   
   
   
   
  1. public class AddClient { 
  2.      
  3.     public static void main(String[] args) { 
  4.         String url = "http://localhost:8983/solr"
  5.         SolrServer server; 
  6.         try { 
  7.             server = new CommonsHttpSolrServer(url); 
  8.             // add 
  9.             SolrInputDocument doc1 = new SolrInputDocument(); 
  10.             doc1.addField( "id""1"1.0f ); 
  11.             doc1.addField( "name""玛瑙宝石真漂亮"1.0f ); 
  12.             doc1.addField( "price"10 ); 
  13.             SolrInputDocument doc2 = new SolrInputDocument(); 
  14.             doc2.addField( "id""2"1.0f ); 
  15.             doc2.addField( "name""MM也很漂亮,天地真爱可见"1.0f ); 
  16.             doc2.addField( "price"20 ); 
  17.             SolrInputDocument doc3 = new SolrInputDocument(); 
  18.             doc3.addField( "id""3"1.0f ); 
  19.             doc3.addField( "name""simple is beauty"1.0f ); 
  20.             doc3.addField( "price"1000 ); 
  21.             Collection docs = new ArrayList(); 
  22.             docs.add( doc1 ); 
  23.             docs.add( doc2 ); 
  24.             docs.add( doc3 ); 
  25.             server.add( docs ); 
  26.             server.commit(); 
  27.         } catch (MalformedURLException e) { 
  28.             e.printStackTrace(); 
  29.         } catch (SolrServerException e) { 
  30.             e.printStackTrace(); 
  31.         } catch (IOException e) { 
  32.             e.printStackTrace(); 
  33.         } 
  34.     } 
  35.      

    然后就是在查询界面里面玩玩查询吧,突然发现中文的查询居然不能任意挑选一个词去查,当然用通配符可以,但是这不是索引的效果。

    深入探索
===========

    新鲜劲已经过去了,是时候好好看看官方文档了。其实官方文档写的还算比较简洁了,无奈这个功能太多,所以看起来还是花了不少时间。

    要用好这个系统,最重要的还是要搞清楚参数的含义。这里有三个重要的配置文件,分别是solr.xml,sorlconfig.xml和schema.xml。

  solr.xml
---------------

    这个比较简单,主要就是定义Core,我的理解就是一个索引实例,有自己独立的配置。在其他地方查阅资料的时候发现,其实就算一个实例也会在特定的操作中用到多核CPU,所以这里的Core与CPU没有直接的对应关系。以前用Sphinx测试的时候发现,建立索引的时候就只能用到单核。

    solrconfig.xml
-------------------------

    这个是最核心的配置文件了,系统参数都在这。其实默认配置文件里面的注释非常详细了,这里挑几个感觉比较重要的记录一下。

1. mainIndex:这是磁盘上索引的主要参数,其中mergeFactor表示多少个segment开始合并,maxMergeDocs表示单个segment的最大值,maxBufferDocs表示缓存的最大个数。后面还有一个移除缓存的策略参数,通常LRU足够用了。

2. updateHandler:这里可以设置自动提交的配置,有maxDocs和maxTime,这在特定场景下肯定会用到。

3. Caching Sections:这里有filterCache,queryResultCache,documentCache,策略就用LRU就行,size和autoWarmCount根据情况设置就行了。还有一些比较复杂的参数,具体看注释吧。

4. updateRequestProcessorChain:里面有个引起我注意的参数,就是overwriteDups的去重配置,或许在特定情况下能帮上忙。
  对于去重功能,系统提供了3种识别方式。MD5Signature就不用说了,为了生成唯一主键的;Lookup3Signature是64bit的加密方式,比MD5更快且存储空间更小,当然冲突会高一些;TextProfileSignature是对长文本提取关键特征的一种方式。最后就是把这个生成的唯一键标记成一个去重的标志位就可以了。

 

5. highlighting:用于高亮显示查询命中词的,这个是在实际使用中非常实用的功能,这个用到的时候要细细的研究一下。

    schema.xml
-------------------

1. field:就是定义具体参数的,indexed表示是否建索引,stored表示是否存内容;compressed表示是否压缩;omitNorms表示关闭length normalization,对全文字段加快一些索引速度,并节省一些内存;表示omitTermFreqAndPositions表示忽略一些参数来减少存储空间,但不能再进行频率和位置的查询了;multiValued表示一个文档中可以有多个值。

2. dynamicField:可以给name用模糊命名,比如"*_i"。

3. analyzer:分词的配置,默认的是按空白切分,这对于中文会没用,如果用一元分词就改成Standard模式。这里分词有两个地方会用到,一个是在建立索引的时候,另一个是对于查询条件。引入第三方中文词库也是在这里配置。

4. uniqueKey:这个是整个索引的全局唯一值,所以如果用主键,务必注意多个表数据不能同时插入。 

5. copyField:这个是可以让多个字段合并到一个字段进行查询。

    其他发现
===========

    1. 突然想到既然每次添加都要commit才行,那如果没有commit却中断通讯,会不会产生回滚呢。结果测试了一下,居然不会回滚,上次没有commit的数据会在下一次一起提交。

    2. Optimize操作是优化那一章提到的非常多,有一个可以直接配置的就是每次commit以后都做一次优化。官方介绍中提到1GB的索引优化一次需要约5分钟,所以这种频繁commit的场景是不能这么使用的,在另外一处也提到了建议一天优化一次。所以这个还是自己写个定时程序晚上优化一下即可。在Java代码中使用CommonsHttpSolrServer.optimize()方法应该就行了。