美文网首页
全文检索Solr集成HanLP中文分词

全文检索Solr集成HanLP中文分词

作者: 惠洋热熔胶网膜 | 来源:发表于2018-09-26 15:24 被阅读123次

    以前发布过HanLP的Lucene插件,后来很多人跟我说其实Solr更流行(反正我是觉得既然Solr是Lucene的子项目,那么稍微改改配置就能支持Solr),于是就抽空做了个Solr插件出来,开源在Github上,欢迎改进。

    HanLP中文分词solr插件支持Solr5.x,兼容Lucene5.x。

    快速上手

    1、将hanlp-portable.jarhanlp-solr-plugin.jar共两个jar放入${webapp}/WEB-INF/lib下

    2、修改solr core的配置文件${core}/conf/schema.xml:

      <fieldType name="text_cn" class="solr.TextField">

          <analyzer type="index">

              <tokenizer class="com.hankcs.lucene.HanLPTokenizerFactory" enableIndexMode="true"/>

          </analyzer>

          <analyzer type="query">

              <!-- 切记不要在query中开启index模式 -->

              <tokenizer class="com.hankcs.lucene.HanLPTokenizerFactory" enableIndexMode="false"/>

          </analyzer>

      </fieldType>

      <!-- 业务系统中需要分词的字段都需要指定type为text_cn -->

      <field name="my_field1" type="text_cn" indexed="true" stored="true"/>

      <field name="my_field2" type="text_cn" indexed="true" stored="true"/>

    Solr5中文分词器详细配置

    对于新手来说,上面的两步可能太简略了,不如看看下面的step by step。本教程使用Solr5.2.1,理论上兼容solr5.x。

    放置jar

    将上述两个jar放到solr-5.2.1/server/solr-webapp/webapp/WEB-INF/lib目录下。如果你想自定义词典等数据,将hanlp.properties放到solr-5.2.1/server/resources,该目录也是log4j.properties等配置文件的放置位置。HanLP文档一直在说“将配置文件放到resources目录下”,指的就是这个意思。作为Java程序员,这是基本常识。

    启动solr

    首先在solr-5.2.1\bin目录下启动solr:

    1.solr start -f

    用浏览器打开http://localhost:8983/solr/#/,看到如下页面说明一切正常:

    创建core

    在solr-5.2.1\server\solr下新建一个目录,取个名字比如叫one,将示例配置文件solr-5.2.1\server\solr\configsets\sample_techproducts_configs\conf拷贝过来,接着修改schema.xml中的默认域type,搜索

     1.   <fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">

     2.       ...

     3.   </fieldType>

    替换为

    [if !supportLists]1. [endif]<!-- 默认文本类型: 指定使用HanLP分词器,同时开启索引模式。

    [if !supportLists]2. [endif]通过solr自带的停用词过滤器,使用"stopwords.txt"(默认空白)过滤。

    [if !supportLists]3. [endif]在搜索的时候,还支持solr自带的同义词词典。-->

    [if !supportLists]4. [endif]<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">

    [if !supportLists]5. [endif]<analyzer type="index">

    [if !supportLists]6. [endif]<tokenizer class="com.hankcs.lucene.HanLPTokenizerFactory" enableIndexMode="true"/>

    [if !supportLists]7. [endif]<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />

    [if !supportLists]8. [endif]<!-- 取消注释可以启用索引期间的同义词词典

    [if !supportLists]9. [endif]<filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>

    [if !supportLists]10. [endif]-->

    [if !supportLists]11. [endif]<filter class="solr.LowerCaseFilterFactory"/>

    [if !supportLists]12. [endif]</analyzer>

    [if !supportLists]13. [endif]<analyzer type="query">

    [if !supportLists]14. [endif]<tokenizer class="com.hankcs.lucene.HanLPTokenizerFactory" enableIndexMode="true"/>

    [if !supportLists]15. [endif]<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />

    [if !supportLists]16. [endif]<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>

    [if !supportLists]17. [endif]<filter class="solr.LowerCaseFilterFactory"/>

    [if !supportLists]18. [endif]</analyzer>

    [if !supportLists]19. [endif]</fieldType>

    意思是默认文本字段类型启用HanLP分词器,text_general还开启了solr默认的各种filter。

    solr允许为不同的字段指定不同的分词器,由于绝大部分字段都是text_general类型的,可以说这种做法比较适合新手。如果你是solr老手的话,你可能会更喜欢单独为不同的字段指定不同的分词器及其他配置。如果你的业务系统中有其他字段,比如location,summary之类,也需要一一指定其type="text_general"。切记,否则这些字段仍旧是solr默认分词器,会造成这些字段“搜索不到”。

    另外,切记不要在query中开启indexMode,否则会影响PhaseQuery。indexMode只需在index中开启一遍即可,要不然它怎么叫indexMode呢。

    如果你不需要solr提供的停用词、同义词等filter,如下配置可能更适合你:

    1. <fieldType name="text_cn" class="solr.TextField">

    2.      <analyzer type="index">

    3.          <tokenizer class="com.hankcs.lucene.HanLPTokenizerFactory" enableIndexMode="true"/>

    4.      </analyzer>

    5.      <analyzer type="query">

    6.          <!-- 切记不要在query中开启index模式 -->

    7.          <tokenizer class="com.hankcs.lucene.HanLPTokenizerFactory" enableIndexMode="false"/>

    8.      </analyzer>

    9.  </fieldType>

    10.  <!-- 业务系统中需要分词的字段都需要指定type为text_cn -->

    11.  <field name="my_field1" type="text_cn" indexed="true" stored="true"/>

    12.  <field name="my_field2" type="text_cn" indexed="true" stored="true"/>

    完成了之后在solr的管理界面导入这个core one:

    接着就能在下拉列表中看到这个core了:

    上传测试文档

    修改好了,就可以拿一些测试文档来试试效果了。hanlp-solr-plugin代码库中的src/test/resources下有个测试文档集合documents.csv,其内容如下:

    [if !supportLists]1. [endif]id,title

    [if !supportLists]2. [endif]1,你好世界

    [if !supportLists]3. [endif]2,商品和服务

    [if !supportLists]4. [endif]3,和服的价格是每镑15便士

    [if !supportLists]5. [endif]4,服务大众

    [if !supportLists]6. [endif]5,hanlp工作正常

    代表着id从1到5共五个文档,接下来复制solr-5.2.1\example\exampledocs下的上传工具post.jar到resources目录,利用如下命令行将数据导入:

    [if !supportLists]1. [endif]java -Dc=one -Dtype=application/csv -jar post.jar *.csv

    Windows用户的话直接双击该目录下的upload.cmd即可,Linux用户运行upload.sh。

    正常情况下输出如下结果:

    [if !supportLists]1. [endif]SimplePostTool version 5.0.0

    [if !supportLists]2. [endif]Posting files to [base] url http://localhost:8983/solr/one/update using content-

    [if !supportLists]3. [endif]type application/csv...

    [if !supportLists]4. [endif]POSTing file documents.csv to [base]

    [if !supportLists]5. [endif]1 files indexed.

    [if !supportLists]6. [endif]COMMITting Solr index changes to http://localhost:8983/solr/one/update...

    [if !supportLists]7. [endif]Time spent: 0:00:00.059

    [if !supportLists]8. [endif]请按任意键继续. . .

    同时刷新一下core one的Overview,的确看到了5篇文档:

    搜索文档

    是时候看看HanLP分词的效果了,点击左侧面板的Query,输入“和服”试试:

    发现精确地查到了“和服的价格是每镑15便士”,而不是“商品和服务”这种错误文档:

    这说明HanLP工作良好。

    要知道,不少中文分词器眉毛胡子一把抓地命中“商品和服务”这种错误文档,降低了查准率,拉低了用户体验,跟原始的MySQL LIKE有何区别?

    索引模式的功能

    索引模式可以对长词进行全切分,得到其中蕴含的所有词汇。比如“中医药大学附属医院”在HanLP索引分词模式下的切分结果为:

    [if !supportLists]1. [endif]中0 医1 药2 大3 学4 附5 属6 医7 院8

    [if !supportLists]2. [endif][0:3 1] 中医药/n

    [if !supportLists]3. [endif][0:2 1] 中医/n

    [if !supportLists]4. [endif][1:3 1] 医药/n

    [if !supportLists]5. [endif][3:5 1] 大学/n

    [if !supportLists]6. [endif][5:9 1] 附属医院/nt

    [if !supportLists]7. [endif][5:7 1] 附属/vn

    [if !supportLists]8. [endif][7:9 1] 医院/n

    开启indexMode后,无论用户搜索“中医”“中医药”还是“医药”,都会搜索到“中医药大学附属医院”:

    高级配置

    目前本插件支持如下基于schema.xml的配置:

    对于更高级的配置,HanLP分词器主要通过class path下的hanlp.properties进行配置,请阅读HanLP自然语言处理包文档以了解更多相关配置,如:

    1.停用词

    2.用户词典

    3.词性标注

    4.……

    代码调用

    在Query改写的时候,可以利用HanLPAnalyzer分词结果中的词性等属性,如

    [if !supportLists]1. [endif]String text = "中华人民共和国很辽阔";

    [if !supportLists]2. [endif]for (int i = 0; i < text.length(); ++i)

    [if !supportLists]3. [endif]{

    [if !supportLists]4. [endif]    System.out.print(text.charAt(i) + "" + i + " ");

    [if !supportLists]5. [endif]}

    [if !supportLists]6. [endif]System.out.println();

    [if !supportLists]7. [endif]Analyzer analyzer = new HanLPAnalyzer();

    [if !supportLists]8. [endif]TokenStream tokenStream = analyzer.tokenStream("field", text);

    [if !supportLists]9. [endif]tokenStream.reset();

    [if !supportLists]10. [endif]while (tokenStream.incrementToken())

    [if !supportLists]11. [endif]{

    [if !supportLists]12. [endif]    CharTermAttribute attribute = tokenStream.getAttribute(CharTermAttribute.class);

    [if !supportLists]13. [endif]    // 偏移量

    [if !supportLists]14. [endif]    OffsetAttribute offsetAtt = tokenStream.getAttribute(OffsetAttribute.class);

    [if !supportLists]15. [endif]    // 距离

    [if !supportLists]16. [endif]    PositionIncrementAttribute positionAttr = kenStream.getAttribute(PositionIncrementAttribute.class);

    [if !supportLists]17. [endif]    // 词性

    [if !supportLists]18. [endif]    TypeAttribute typeAttr = tokenStream.getAttribute(TypeAttribute.class);

    [if !supportLists]19. [endif]    System.out.printf("[%d:%d %d] %s/%s\n", offsetAtt.startOffset(), offsetAtt.endOffset(), positionAttr.getPositionIncrement(), attribute, typeAttr.type());

    [if !supportLists]20. [endif]}

    在另一些场景,支持以自定义的分词器(比如开启了命名实体识别的分词器、繁体中文分词器、CRF分词器等)构造HanLPTokenizer,比如:

    [if !supportLists]1. [endif]tokenizer = new HanLPTokenizer(HanLP.newSegment()

    [if !supportLists]2. [endif]                    .enableJapaneseNameRecognize(true)

    [if !supportLists]3. [endif]                    .enableIndexMode(true), null, false);

    [if !supportLists]4. [endif]tokenizer.setReader(new StringReader("林志玲亮相网友:确定不是波多野结衣?"));

    [if !supportLists]5. [endif]...

    反馈

    技术问题请在Github上发issue ,大家一起讨论,也方便集中管理。博客留言、微博私信、邮件不受理任何HanLP相关的问题,谢谢合作!

    反馈问题的时候请一定附上版本号、触发代码、输入输出,否则无法处理。

    版权

    Apache License Version 2.0

    转载子码农场

    相关文章

      网友评论

          本文标题:全文检索Solr集成HanLP中文分词

          本文链接:https://www.haomeiwen.com/subject/akzdoftx.html