solr实现动态加载分词
版本是5.3.0
在core(自己创建的模块)的schema.xml里面增加类型:
<fieldType name="text_lj" class="solr.TextField" positionIncrementGap="100" >
<analyzer type="index" >
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" conf="ik.conf"/> //同级目录下创建的ik.conf文件
</analyzer> <analyzer type="query">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" conf="ik.conf"/> //IKTokenizerFactory,这个是我们后面要改造的类
</analyzer> </fieldType>
<field name="desc" type="text_lj" indexed="true" stored="true" required="true" multiValued="false"/>
ik.conf:
lastupdate=1
files=extDic.txt
lastupdate:表示的是版本,比如我现在添加了新的分词,则将版本号加1。files表示分词的文件,后面可以是多个文件名,用英文的逗号分隔。在同级目录下创建文件extDic.txt
extDic.txt的内容:文件保存格式必须是utf-8
小红帽
华为手机
格力空调
给出一个目录:

配置已经完成,现在最主要的是修改ik分词器的源码,主要的思路是创建一个线程轮询更新分词
源码下载地址:https://codeload.github.com/EugenePig/ik-analyzer-solr5/zip/master
使用ideal打开工程:

主要设计这三个类:UpdateKeeper是新创建的,用于轮询读取配置文件
package org.wltea.analyzer.lucene; import java.io.IOException;
import java.util.Vector; //TODO optimize
public class UpdateKeeper implements Runnable{ public static interface UpdateJob{
public void update() throws IOException ; } final static int INTERVAL = 1 * 60 * 1000; private static UpdateKeeper singleton;
Vector<UpdateJob> filterFactorys;
Thread worker; private UpdateKeeper(){
filterFactorys = new Vector<UpdateJob>(); worker = new Thread(this);
worker.setDaemon(true);
worker.start();
} public static UpdateKeeper getInstance(){
if(singleton == null){
synchronized(UpdateKeeper.class){
if(singleton == null){
singleton = new UpdateKeeper();
return singleton;
}
}
}
return singleton;
} /*保留各个FilterFactory实例对象的引用,用于后期更新操作*/
public void register(UpdateKeeper.UpdateJob filterFactory ){
filterFactorys.add(filterFactory);
} @Override
public void run() {
while(true){
try {
Thread.sleep(INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(!filterFactorys.isEmpty()){
for(UpdateJob factory: filterFactorys){
try {
factory.update();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
} }
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wltea.analyzer.lucene; import java.io.IOException;
import java.io.InputStream;
import java.util.*; import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.util.ResourceLoader;
import org.apache.lucene.analysis.util.ResourceLoaderAware;
import org.apache.lucene.analysis.util.TokenizerFactory;
import org.apache.lucene.util.AttributeFactory;
import org.wltea.analyzer.dic.Dictionary; /**
* @author <a href="mailto:su.eugene@gmail.com">Eugene Su</a>
*/
public class IKTokenizerFactory extends TokenizerFactory implements
ResourceLoaderAware, UpdateKeeper.UpdateJob{
private boolean useSmart; private ResourceLoader loader; private long lastUpdateTime = -1;
private String conf = null; public boolean useSmart() {
return useSmart;
} public void setUseSmart(boolean useSmart) {
this.useSmart = useSmart;
} public IKTokenizerFactory(Map<String,String> args) {
super(args);
String useSmartArg = args.get("useSmart");
this.setUseSmart(useSmartArg != null ? Boolean.parseBoolean(useSmartArg) : false);
conf = get(args, "conf");
} @Override
public Tokenizer create(AttributeFactory factory) {
Tokenizer _IKTokenizer = new IKTokenizer(factory , this.useSmart);
return _IKTokenizer;
} @Override
public void update() throws IOException {
Properties p = canUpdate();
if (p != null){
List<String> dicPaths = SplitFileNames(p.getProperty("files"));
List<InputStream> inputStreamList = new ArrayList<InputStream>();
for (String path : dicPaths) {
if ((path != null && !path.isEmpty())) {
InputStream is = loader.openResource(path);if (is != null) {
inputStreamList.add(is);
}
}
}
if (!inputStreamList.isEmpty()) {
Dictionary.addDic2MainDic(inputStreamList); // load dic to MainDic
}
}
} @Override
public void inform(ResourceLoader resourceLoader) throws IOException {
System.out.println(":::ik:::inform::::::::::::::::::::::::" + conf);
this.loader = resourceLoader;
this.update();
if(conf != null && !conf.trim().isEmpty())
{
UpdateKeeper.getInstance().register(this);
}
} private Properties canUpdate() { try{
if (conf == null)
return null;
Properties p = new Properties();
InputStream confStream = loader.openResource(conf);
p.load(confStream);
confStream.close();
String lastupdate = p.getProperty("lastupdate", "0");
Long t = new Long(lastupdate); if (t > this.lastUpdateTime){
this.lastUpdateTime = t.longValue();
String paths = p.getProperty("files");
if (paths==null || paths.trim().isEmpty()) // 必须有地址
return null;
System.out.println("loading conf");
return p;
}else{
this.lastUpdateTime = t.longValue();
return null;
}
}catch(Exception e){
System.err.println("IK parsing conf NullPointerException~~~~~" + e.getMessage());
return null;
}
} public static List<String> SplitFileNames(String fileNames) {
if (fileNames == null)
return Collections.<String> emptyList(); List<String> result = new ArrayList<String>();
for (String file : fileNames.split("[,\\s]+")) {
result.add(file);
} return result;
}
}
Dictionary类里面新增方法:
Dictionary是单例模式
public static void addDic2MainDic(List<InputStream> inputStreams){
if(singleton == null)
{
Configuration cfg = DefaultConfig.getInstance();
Dictionary.initial(cfg);
}
for(InputStream is : inputStreams){
//如果找不到扩展的字典,则忽略
if(is == null){
continue;
}
try {
BufferedReader br = new BufferedReader(new InputStreamReader(is , "UTF-8"), 512);
String theWord = null;
do {
theWord = br.readLine();
if (theWord != null && !"".equals(theWord.trim())) {
//加载扩展词典数据到主内存词典中
//System.out.println(theWord);
singleton._MainDict.fillSegment(theWord.trim().toLowerCase().toCharArray());
}
} while (theWord != null);
} catch (IOException ioe) {
System.err.println("Extension Dictionary loading exception.");
ioe.printStackTrace();
}finally{
try {
if(is != null){
is.close();
is = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
最后将工程打成jar放到web-inf的lib目录里面。大功告成!
solr实现动态加载分词的更多相关文章
- 中文分词实战——基于jieba动态加载字典和调整词频的电子病历分词
分词是自然语言处理中最基本的一个任务,这篇小文章不介绍相关的理论,而是介绍一个电子病历分词的小实践. 开源的分词工具中,我用过的有jieba.hnlp和stanfordnlp,感觉jieba无论安装和 ...
- js动态加载css和js
之前写了一个工具类点此链接里面含有这段代码,感觉用处挺多,特意提出来 var loadUtil = { /* * 方法说明:[动态加载js文件css文件] * 使用方法:loadUtil.loadjs ...
- geotrellis使用(二十三)动态加载时间序列数据
目录 前言 实现方法 总结 一.前言 今天要介绍的绝对是华丽的干货.比如我们从互联网上下载到了一系列(每天或者月平均等)的MODIS数据,我们怎么能够对比同一区域不同时间的数据情况,采用 ...
- Ext JS 如何动态加载JavaScript创建窗体
JavaScript不需要编译即可运行,这让JavaScript构建的应用程序可以变得很灵活.我们可以根据需要动态从服务器加载JavaScript脚本来创建和控制UI来与用户交互.下面结合Ext JS ...
- Ext动态加载Toolbar
在使用Ext的GridPanel时候,有时候需要面板不用重新加载而去更新Store或者Toolbar,Store的方法有很多,例如官方api给我们提供的Store.load(),Store.reLoa ...
- Android动态加载框架汇总
几种动态加载的比较 1.Tinker 用途:热修复 GitHub地址:https://github.com/Tencent/tinker/ 使用:http://www.jianshu.com/p/f6 ...
- 为不同分辨率单独做样式文件,在页面头部用js判断分辨率后动态加载定义好的样式文件
为不同分辨率单独做样式文件,在页面头部用js判断分辨率后动态加载定义好的样式文件.样式文件命名格式如:forms[_屏幕宽度].css,样式文件中只需重新定义文本框和下拉框的宽度即可. 在包含的头文件 ...
- html中的图像动态加载问题
首先要说明下文档加载完成是什么概念 一个页面http请求访问时,浏览器会将它的html文件内容请求到本地解析,从窗口打开时开始解析这个document,页面初始的html结构和里面的文字等内容加载完成 ...
- 非常郁闷的 .NET中程序集的动态加载
记载这篇文章的原因是我自己遇到了动态加载程序集的问题,而困扰了一天之久. 最终看到了这篇博客:http://www.cnblogs.com/brucebi/archive/2013/05/22/Ass ...
随机推荐
- css的再深入6(更新中···)
background-position 雪碧图 我们的html和css中有三个属性可以向服务器发送请求,src href url. overflow (1) 值hidden 超出就隐藏 (2) 值s ...
- NVIDIA 驱动安装(超详细)
目录 1. 删除原有驱动 2. 安装依赖 3. 禁用nouveau驱动: 4. reboot 5. 获取kernel source (important) 6. 关掉x graphic 服务 7. 安 ...
- Linux 命令行下导入导出 .sql 文件
一.导出数据库用的是 mysqldump 命令 1.导出数据和表结构 /usr/bin/mysqldump -u 用户名 -p 数据库名 > 数据库名.sql 敲回车键后会提示输入密码 注意 m ...
- 2_Linux操作系统和基础命令行
非图形页面下执行程序GUI接口CLI接口 #输入命令,给出执行文件的路径 命令提示符,prompt(表示可以立即输入命令了) 命令提示符通过bash(shell)运行程序来展示的 #:root #就是 ...
- ES5 map循环一大坑:循环遍历竟然出现逗号!
一.map map大法好 这里需要解释一下Map和forEach的区别 一般来说需要返回值时使用Map,而只需要循环的使用forEach map循环常用的一些方法 /********* ES6 *** ...
- 使用java命令运行class文件提示“错误:找不到或无法加载主类“的问题分析
有时候我们需要直接用jdk提供的java命令来执行class文件让软件运行起来,特别是很多初学者,但经常会发现如下提示: 用eclipse或用ant则没有问题. 其实原因很简单,我们忽略了2个细节. ...
- 安装activemq和java代码实现生产和消费
Apache Active MQ的官网 http://activemq.apache.org/ 下载地址: 国外的网站,网速不好的时候,真的下不下来 http://activemq.apache.or ...
- try与except处理异常语句
ji 简介 与其他语言相同,在python中,try/except语句主要是用于处理程序正常执行过程中出现的一些异常情况,如语法错(python作为脚本语言没有编译的环节,在执行过程中对语法进行检测, ...
- LeetCode--038--报数(java)
报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数.其前五项如下: 1. 1 2. 11 3. 21 4. 1211 5. 111221 1 被读作 "one 1" ...
- LeetCode--019--删除链表的倒数第N个节点(java)
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点. 示例: 给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后,链表变为 ...