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 ...
随机推荐
- JavaScript自定义求和函数
我爱撸码,撸码使我感到快乐!大家好,我是Counter,当看到这个标题到时候是不是感觉很简单,千万不要大意哦,你说0.1 + 0.2 = 0.3 ?有时候计算机并不是我们所说绝对精确,这个时候就要我们 ...
- urllib3
urllib3是一个功能强大.条理清晰.用于http客户端的python库,相对于urllib它所有的特点如下: 线程安全 连接池 客户端SSL/TLS验证 使用多部分编码上传文件 Helpers用于 ...
- luoguP1850 换教室
luoguP1850 换教室 链接 https://www.luogu.org/problemnew/show/P1850 思路 状态很显然就是f[n][k][0/1] 前i次,用了k次机会,当前是在 ...
- react-native android 运行命令
debug模式运行 sudo react-native run-android release模式运行 sudo react-native run-android --variant=release ...
- mysql utf8mb4 设置
[mysqld]collation-server=utf8mb4_general_ciinit-connect='SET NAMES utf8mb4'character-set-server=utf8 ...
- python+selenium,实现带有验证码的自动化登录功能
python+selenium的环境准备,请自行安装完成,这里直接贴代码,方便做项目时直接使用. import time from selenium import webdriver from PIL ...
- oracle数据库 concat 与 ||
1.简述: 通过几条sql语句来引入此问题 先新建一张表,插入几条数据,如下: CREATE TABLE tab1 (col1 VARCHAR2(6), col2 CHAR(6), col3 VARC ...
- 2019清明期间qbxt培训qwq
4.4上午:数学基础 (qwq整成word和cpp了,它居然不能直接把文档附上来) part 1:高精度运算 高精加和高精减就不说了,之前写过博客了qwq,讲一讲高精乘和高精除吧. 1.高精度乘法(不 ...
- python自动化测试入门篇-jemter连接mysql数据库
jmeter对数据库的操作主要包括以下几个步骤:1.导入mysqlde jdbc的jar包:2.创建数据库连接配置:3.线程组添加jdbc request;4.启动按钮,添加查看结果树 一.准备好驱动 ...
- 详解Oracle partition分区表
随着表中行数的增多,管理和性能性能影响也将随之增加.备份将要花费更多时间,恢复也将 要花费更说的时间,对整个数据表的查询也将花费更多时间.通过把一个表中的行分为几个部分,可以减少大型表的管理和性能问题 ...