笔者项目中用到的solr环境为5.1版本,分词器不是使用得solr得text_general分词器,而是jcseg分词器。因为笔者需要使用同义词和停止词功能,项目中得solr环境本人无法随意测试,所以打算在本地部署一个Solr5.1得环境,从Oracle数据库取数,使用项目中使用得jcseg分词器做本地环境得分词器,用来测试jcseg分词器同义词和停止词如何配置及使用,好了,说完背景之后,下面开始进行测试。
本文档使用得软件版本:
软件 | 版本 |
---|---|
Solr | 5.1 |
Jcseg | 2.2 |
一、下载编译Jcseg包
1、下载Jcseg包
下载地址:
jcseg源码包
点击下载zip包,下载之前务必仔细研读Jcseg介绍,不然可能看不懂下面文章:
下载好的源码包解压
打开eclips,把解压后得到的 jcseg 文件夹放到 eclipse 的工作空间里,具体操作过程:
zip压缩包解压后得到的文件夹名称是jcseg,打开 eclipse,File → import → Existing Maven Projects → next → Root Directory 选择 jcseg 文件夹 → Projects 全部选中,Add project(s) to working set 复选框也要选中 → 复选框下面的下拉列表选择jcseg → 点击Finish按钮。之后就可以成功导入了jcseg 项目。
2、Jcseg参数文件配置及解释
项目导入完成后,先打开我上面截图指定的jcseg配置文件,修改参数如下:
# jcseg properties file.
# bug report chenxin <chenxin619315@gmail.com>
# Jcseg function
#maximum match length. (5-7)
jcseg.maxlen = 7
#recognized the chinese name.(1 to open and 0 to close it)
jcseg.icnname = 1
#maximum length for pair punctuation text.
jcseg.pptmaxlen = 7
#maximum length for chinese last name andron.
jcseg.cnmaxlnadron = 1
#Wether to clear the stopwords.(set 1 to clear stopwords and 0 to close it)
jcseg.clearstopword = 1
#Wether to convert the chinese numeric to arabic number. (set to 1 open it and 0 to close it)
# like '\u4E09\u4E07' to 30000.
jcseg.cnnumtoarabic = 1
#Wether to convert the chinese fraction to arabic fraction.
#@Note: for lucene,solr,elasticsearch eg.. close it.
jcseg.cnfratoarabic = 0
#Wether to keep the unrecognized word. (set 1 to keep unrecognized word and 0 to clear it)
jcseg.keepunregword = 1
#Wether to start the secondary segmentation for the complex english words.
jcseg.ensencondseg = 1
#min length of the secondary simple token. (better larger than 1)
jcseg.stokenminlen = 2
#thrshold for chinese name recognize.
# better not change it before you know what you are doing.
jcseg.nsthreshold = 1000000
#The punctuations that will be keep in an token.(Not the end of the token).
jcseg.keeppunctuations = @#%.&+
####about the lexicon
#abusolte path of the lexicon file.
#Multiple path support from jcseg 1.9.2, use ';' to split different path.
#example: lexicon.path = /home/chenxin/lex1;/home/chenxin/lex2 (Linux)
# : lexicon.path = D:/jcseg/lexicon/1;D:/jcseg/lexicon/2 (WinNT)
#lexicon.path=/Code/java/JavaSE/jcseg/lexicon
#lexicon.path = {jar.dir}/lexicon ({jar.dir} means the base directory of jcseg-core-{version}.jar)
#@since 1.9.9 Jcseg default to load the lexicons in the classpath
lexicon.path = /hadoop/solr/server/solr/core_one/lib/lexicon
#Wether to load the modified lexicon file auto.
lexicon.autoload = 1
#Poll time for auto load. (seconds)
lexicon.polltime = 30
####lexicon load
#Wether to load the part of speech of the entry.
jcseg.loadpos = 1
#Wether to load the pinyin of the entry.
jcseg.loadpinyin = 0
#Wether to load the synoyms words of the entry.
jcseg.loadsyn = 1
#wether to load the entity of the entry
jcseg.loadentity = 1
上面主要变动的几个重要参数为:
1、开启同义词功能
#Wether to load the synoyms words of the entry.
jcseg.loadsyn = 1
2、开启过滤停止词功能
#Wether to clear the stopwords.(set 1 to clear stopwords and 0 to close it)
jcseg.clearstopword = 1
3、指定词库路径
为什么要指定路径,如果你已经阅读了下载页面中的介绍(如果没读,请先看下载页面的Jcseg结构介绍)你就会知道Jcseg分词器有一个词库的概念,词库存放在:
我们日常Solr查询中,如果有一些同义词,比如想让Solr检索people这个词语时,zhaoyd也要在检索结果中,那么我们需要给people做一个同义词的映射,又因为Solr使用的Jcseg分词器,也就是我们要在Jcseg中的同义词库中添加我们的映射,中文同义词需要用到lex-chars.lex、lex-ynonyms.lex,英文同义词需要lex-en.lex、lex-ynonyms.lex,如果每次我们有了新的同义词,都以在源码中的同义词库中进行追加或则删除,那每次修改完词库后都要重新编译生成新的jar包替换原来的jar包并且重启整个solr应用,那这样修改词库的方式代价太高了,如果现在solr应用中有十几甚至更多个solr应用,每次因为某一个core的同义词变更去重启整个solr应用,生产环境中这是不可接受的,所以通过配置jcseg.properties中的lexicon.path指向一个文件夹路径作为词库存放路径,solr一次编译好jar包后,需要变更词库的时候,不需要再重新导出jar包做jar包替换了,直接在这个文件夹路径的词库中做更改,我这里直接写上了我本地环境词库的存放路径:
lexicon.path = /hadoop/solr/server/solr/core_one/lib/lexicon
那么问题来了,我们不用再重新导出导入jar包了,那词库变更了之后,Jcseg会自动发现词库变更的内容,在solr服务正在运行的过程中就能直接识别并加载吗?
答案是否定的,Jcseg默认做不到,那岂不是仍要在词库变更后再次重启整个solr服务?答案也是否定的,jcseg.properties配置文件中有两个参数能定时加载我们告诉它的发生变化的词库文件,先来看这两个参数:
#Wether to load the modified lexicon file auto.
lexicon.autoload = 1
#Poll time for auto load. (seconds)
lexicon.polltime = 30
就是我前面文件中配置的这两个参数,第一个参数功能说了是开启自动加载发生变化的词库文件,第二个参数是定时30s扫描一次用于存放发生变化的词库文件清单的一个文件,这个文件是前面截图中的lex-autoload.todo,当指定lexicon.autoload = 1后,会启动一个守护线程定时扫描这个文件中的内容,如果文件中有我们写入的发生变化的词库文件清单,那么Jcseg会自动加载变化的清单并在加载完成后将这个文件清空。当然,虽然变更后的词库已经能正确加载了,在Solr进行analyze时候能够正确解析,但是查询时候还是无法生效的,因为词库发生了变更,我们需要重建索引才能生效,后续有这部分解说的实际操作案例,继续向后看。
好了,到这里,Jcseg配置文件大致就配置好了,准备编译Maven导出吧。
3、编译项目,将Jar包上传到服务器
为了使用 jcseg 分词器,我们需要编译源代码。鼠标选中项目jcseg,右键打开快捷菜单。快捷菜单中选择 Run As,子菜单中点击 Maven Build ... ,随即打开了Maven命令的对话框。在 Goals 文本框中输入clean package,点击Run按钮。等待编译完成:
。。。。。。。。。。。。。。。。。。。。。。。。。
[INFO] jcseg .............................................. SUCCESS [ 0.232 s]
[INFO] jcseg-core ......................................... SUCCESS [ 15.863 s]
[INFO] jcseg-analyzer ..................................... SUCCESS [ 4.161 s]
[INFO] jcseg-elasticsearch ................................ SUCCESS [ 5.480 s]
[INFO] jcseg-server ....................................... SUCCESS [ 10.374 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 36.303 s
[INFO] Finished at: 2020-04-09T22:07:09+08:00
[INFO] ------------------------------------------------------------------------
进入子项目 jcseg-core 下的 target 文件夹下的jcseg-core-2.2.0.jar,jcseg-analyzer下的jcseg-analyzer-2.2.0.jar,以及jcseg.properties参数文件和lexicon词库都复制出来,如下:
接下来Xshell登录linux,进入到Solr安装目录,由于我本地有多个core,有几个core使用的Solr的text_general和IK分词器,而且也有几个core是用的Jcseg分词器,但是我只想对名为core_one的这个从数据库取数的core使用同义词和停止词的功能,所以为了避免影响其他几个core,我只把上面打包好的Jcseg分词器放在core_one下:
[root@hadoop ~]# cd /hadoop/solr/server/solr
[root@hadoop solr]# ls
conf configsets core_one core_three core_two jcg lib README.txt solr.xml testpdf zoo.cfg
[root@hadoop solr]# cd core_one/
[root@hadoop core_one]# ls
conf core.properties data lib
[root@hadoop core_one]# cd lib/
[root@hadoop lib]# ls
ojdbc14.jar ojdbc5.jar ojdbc6.jar solr-dataimporthandler-5.1.0.jar solr-dataimporthandler-extras-5.1.0.jar
将之前复制出来的jar包和文件都通过xsftp上传到这里:
[root@hadoop lib]# ls
jcseg-analyzer-2.2.0.jar jcseg-core-2.2.0.jar jcseg.properties lexicon ojdbc14.jar ojdbc5.jar ojdbc6.jar solr-dataimporthandler-5.1.0.jar solr-dataimporthandler-extras-5.1.0.jar
好了,接下来准备配置Solr和测试了。
二、Solr配置修改
修改前先看下之前文章对test_solr表的字段配置:
<field name="ename" type="string" indexed="true" stored="true" />
<field name="empno" type="string" indexed="true" stored="true" required="true" multiValued="false" />
之前type="string",所以只能查询类似数据库中的where ename='aa'这种查询,做不到模糊匹配,如果想用模糊匹配,可以使用前面说了多次的Solr的text_general分词器:
<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" />
<!-- in this example, we will only use synonyms at query time
<filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
-->
<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>
对于英问来说这个分词器能满足日常查询了,不过我们这里使用的是Jcseg分词器,所以我们要把原来empno,ename,dname字段的分词器改成Jcseg分词器。
前面已经将Jseg分词器及相关的词库和配置文件上传到服务器了,接下来要做的就是给Solr加入 jcseg 两个 jar 包的路径,那么就要修改solrconfig.xml 文件了,如下操作:
[root@hadoop conf]# pwd
/hadoop/solr/server/solr/core_one/conf
[root@hadoop conf]# vim solrconfig.xml
搜索lib单词,找到下面配置部分:
<lib dir="${solr.install.dir:../../../..}/contrib/extraction/lib" regex=".*\.jar" />
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-cell-\d.*\.jar" />
<lib dir="${solr.install.dir:../../../..}/contrib/clustering/lib/" regex=".*\.jar" />
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-clustering-\d.*\.jar" />
<lib dir="${solr.install.dir:../../../..}/contrib/langid/lib/" regex=".*\.jar" />
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-langid-\d.*\.jar" />
<lib dir="${solr.install.dir:../../../..}/contrib/velocity/lib" regex=".*\.jar" />
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-velocity-\d.*\.jar" />
<lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-dataimporthandler-.*\.jar" />
<lib dir="./lib/" regex="ojdbc14.jar" />
在之前加的路径后面再追加Jcseg的两个jar包:
<lib dir="./lib/" regex="jcseg-core-2.2.0.jar" />
<lib dir="./lib/" regex="jcseg-analyzer-2.2.0.jar" />
保存退出。
jar包引完了,接下来要修改schema.xml文件,先在配置文件最后追加新的fieldType标签,如下:
。。。。。。。。。。。。。。。。。。
<!-- Turkish -->
<fieldType name="text_tr" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.ApostropheFilterFactory"/>
<filter class="solr.TurkishLowerCaseFilterFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="false" words="lang/stopwords_tr.txt" />
<filter class="solr.SnowballPorterFilterFactory" language="Turkish"/>
</analyzer>
</fieldType>
<!-- 复杂模式分词: -->
<fieldtype name="textComplex" class="solr.TextField">
<analyzer>
<tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="complex"/>
</analyzer>
</fieldtype>
<!-- 简易模式分词: -->
<fieldtype name="textSimple" class="solr.TextField">
<analyzer>
<tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="simple"/>
</analyzer>
</fieldtype>
<!-- 检测模式分词: -->
<fieldtype name="textDetect" class="solr.TextField">
<analyzer>
<tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="detect"/>
</analyzer>
</fieldtype>
<!-- 检索模式分词: -->
<fieldtype name="textSearch" class="solr.TextField">
<analyzer>
<tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="search"/>
</analyzer>
</fieldtype>
<!-- NLP模式分词: -->
<fieldtype name="textSearch" class="solr.TextField">
<analyzer>
<tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="nlp"/>
</analyzer>
</fieldtype>
<!-- 空格分隔符模式分词: -->
<fieldtype name="textSearch" class="solr.TextField">
<analyzer>
<tokenizer class="org.lionsoul.jcseg.analyzer.JcsegTokenizerFactory" mode="delimiter"/>
</analyzer>
</fieldtype>
<!-- Similarity is the scoring routine for each document vs. a query.
A custom Similarity or SimilarityFactory may be specified here, but
the default is fine for most applications.
For more info: http://wiki.apache.org/solr/SchemaXml#Similarity
-->
<!--
<similarity class="com.example.solr.CustomSimilarityFactory">
<str name="paramkey">param value</str>
</similarity>
-->
<solrQueryParser defaultOperator="AND"/>
</schema>
然后再搜索ename,找到之前配置ename字段的地方,修改成如下配置:
<field name="ename" type="textComplex" indexed="true" stored="true" termVectors="true"/>
<field name="dname" type="textComplex" indexed="true" stored="true" termVectors="true"/>
<field name="empno" type="textComplex" indexed="true" stored="true" required="true" multiValued="false" />
textComplex即为Jcseg复杂分词算法,可选的analyzer名字:
jcseg : 对应Jcseg的检索模式切分算法
jcseg_complex : 对应Jcseg的复杂模式切分算法
jcseg_simple : 对应Jcseg的简易切分算法
jcseg_detect : 对应Jcseg的检测模式切分算法
jcseg_search : 对应Jcseg的检索模式切分算法
jcseg_nlp : 对应Jcseg的NLP模式切分算法
jcseg_delimiter : 对应Jcseg的分隔符模式切分算法
保存退出,重启solr应用。
[root@hadoop solr]# ./bin/solr stop
Sending stop command to Solr running on port 8983 ... waiting 5 seconds to allow Jetty process 62186 to stop gracefully.
[root@hadoop solr]# ./bin/solr start
Waiting to see Solr listening on port 8983 [/]
Started Solr server on port 8983 (pid=62878). Happy searching!
登录:http://192.168.1.66:8983/solr/#/页面,选择core_one,查看:
这里已经做好了配置,接下来进行测试。
三、同义词停止词测试
1、同义词简单测试
这里先介绍中文的同义词,英文的同义词在后面,继续往下看。
先来看下Jcseg词库中的同义词库里面有啥:
[root@hadoop ~]# cd /hadoop/solr/server/solr/core_one/
[root@hadoop core_one]# ls
conf core.properties data lib
[root@hadoop core_one]# cd lib/lexicon/
[root@hadoop lexicon]# ls
lex-admin.lex lex-cn-mz.lex lex-dname-2.lex lex-festival.lex lex-lang.lex lex-lname.lex lex-nation.lex lex-org.lex lex-stopword.lex lex-tourist.lex
lex-autoload.todo lex-company.lex lex-domain-suffix.lex lex-fname.lex lex-live.lex lex-main.lex lex-net.lex lex-place.lex lex-synonyms.lex lex-units.lex
lex-chars.lex lex-dname-1.lex lex-en.lex lex-food.lex lex-ln-adorn.lex lex-mixed.lex lex-number-unit.lex lex-sname.lex lex-time.lex
[root@hadoop lexicon]# vim lex-synonyms.lex
看到这里面已经有了一些中文的同义词映射配置案例,那么接下来,先给我的数据库表test_solr写入一些数据,写入完成后:
可以看到同义词库中的:研究,琢磨,研讨,已经被我分成三行写入到了数据库中,接下来我们重新更同步一下数据到Solr。已经导入完了,接下来用analyzes检验分词效果:
当我输入“研究”后,分词能后很好的把同义词库中的同义词映射分出来,好了,直接去查询页面做查询看看:
我们查询研究时候,另外写入数据库的两个同义词也能查询出来,而且得分都一样,如果我们查询普通的一个单词,并没有做同义词映射,比如数据库中那条empno=17,ename='decimal'的数据:
只有一条数据出来了。
2、不停Solr服务,在线追加停止词
如果我们设置一个停止词,比如decimal,那就需要修改词库中的lex-stopword.lex文件了,还记得我文章前面说的如果想要修改词库文件又不停服务的追加方式介绍么,这里做一个实际演示:
首先,编辑lex-stopword.lex文件,新增追加单词“decimal”:
[root@hadoop lexicon]# pwd
/hadoop/solr/server/solr/core_one/lib/lexicon
[root@hadoop lexicon]# vim lex-stopword.lex
追加单词decimal:
。。。。。。。。。
within
without
would
yet
you
your
yours
yourself
yourselves
#chenxin12
#other number
decimal
保存退出
其次,因为我们修改了lex-stopword.lex词库文件,而且前面jcseg.properties中配置了lexicon.autoload = 1,lexicon.polltime = 30,那么我们需要把发生了变动的lex-stopword.lex文件名写到lex-autoload.todo文件中,以便Jcseg每隔30秒从这里加载一次:
[root@hadoop lexicon]# vim lex-autoload.todo
写入:
lex-autoload.todo
保存退出
[root@hadoop lexicon]# cat lex-autoload.todo
lex-autoload.todo
好了,接下来等待30秒,再看:
[root@hadoop lexicon]# cat lex-autoload.todo
已经没有刚才写入的文件了,然后我们再去solr做前面的查询:
之前查询ename:decimal是有数据的,但是这里再查询查不到数据了,说明lex-stopword.lex中追加的:decimal生效了,而且我们并没有停止任何服务。
如果在按照我的这个操作正确设置这个功能后,后续发现没有效果,请更改org.lionsoul.jcseg.core.ADictionary#startAutoload方法(86行),加入测试输出代码,查看jcseg是否启动了词库加载守护进程。
3、不停Solr服务,在线追加英文同义词
先声明一点,中文和英文的停止词配置比较简单,只需要按照上面介绍配置就行了,而且中英文配置的方式都一样。
但是中英文同义词的配置有些出入,在做这个测试之前,我从网上看到了大量的中文同义词配置操作方式,这里不再介绍中文同义词的配置方法,只介绍英文的,接下来操作:
先看下数据库中的数据:
empno=20,21的两条数据,我们在solr查询:
都只能查出带各自关键词的结果,如果我们想在ename搜索people关键词的时候,带zhaoyd关键词的结果也能查询出来,那该怎么配置这个同义词?
首先,修改lex-en.lex文件:
[root@hadoop lexicon]# pwd
/hadoop/solr/server/solr/core_one/lib/lexicon
[root@hadoop lexicon]# vim lex-en.lex
追加实体定义:
people/n/null/peoples
保存退出
[root@hadoop lexicon]# cat lex-en.lex
CJK_WORD
#英文词条, 做英文词语同义词追加,实体定义用
#2.0.1版本后类别直接归属CJK_WORD,不再使用之前的EN_WORD
decimal/n/null/decimals,fraction
spirit/n/null/mind
admire/v/null/appreciate,like,love,enjoy
chenxin12/n/null/chenxin,lionsoul
kilometer/q/null/null/length.km
people/n/null/peoples
接下来,修改lex-synonyms.lex文件:
[root@hadoop lexicon]# vim lex-synonyms.lex
追加:
people,zhaoyd
保存退出
然后修改lex-autoload.todo:
[root@hadoop lexicon]# vim lex-autoload.todo
追加:
lex-en.lex
lex-synonyms.lex
等待30秒,去solr Analysis页面,检查分词效果:
我们对ename进行了分词,people能返回people和zhaoyd,这里看着没问题了,词库的变更已经动态追加进来了,接下来去查询数据:
这里查询数据发现是空,为什么?明明之前没加同义词的时候还能查询到数据,而且查询其他的词汇也能查到数据:
这是因为我们新增了同义词,需要重建索引再进行查询这时候就能查询到数据了,而且我们输入people关键词,带zhaoyd关键词的数据也出现在结果了,同样的,搜索zhaoyd结果也是一样的。暂时到这,有时间再继续测试