- 浏览: 2146681 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (682)
- 软件思想 (7)
- Lucene(修真篇) (17)
- Lucene(仙界篇) (20)
- Lucene(神界篇) (11)
- Solr (48)
- Hadoop (77)
- Spark (38)
- Hbase (26)
- Hive (19)
- Pig (25)
- ELK (64)
- Zookeeper (12)
- JAVA (119)
- Linux (59)
- 多线程 (8)
- Nutch (5)
- JAVA EE (21)
- Oracle (7)
- Python (32)
- Xml (5)
- Gson (1)
- Cygwin (1)
- JavaScript (4)
- MySQL (9)
- Lucene/Solr(转) (5)
- 缓存 (2)
- Github/Git (1)
- 开源爬虫 (1)
- Hadoop运维 (7)
- shell命令 (9)
- 生活感悟 (42)
- shell编程 (23)
- Scala (11)
- MongoDB (3)
- docker (2)
- Nodejs (3)
- Neo4j (5)
- storm (3)
- opencv (1)
最新评论
-
qindongliang1922:
粟谷_sugu 写道不太理解“分词字段存储docvalue是没 ...
浅谈Lucene中的DocValues -
粟谷_sugu:
不太理解“分词字段存储docvalue是没有意义的”,这句话, ...
浅谈Lucene中的DocValues -
yin_bp:
高性能elasticsearch ORM开发库使用文档http ...
为什么说Elasticsearch搜索是近实时的? -
hackWang:
请问博主,有用solr做电商的搜索项目?
Solr中Group和Facet的用法 -
章司nana:
遇到的问题同楼上 为什么会返回null
Lucene4.3开发之第八步之渡劫初期(八)
允许转载,转载请注明原创地址:
http://qindongliang1922.iteye.com/blog/1927605
谢谢配合
最近在群里面(324714439)遇到几位朋友提出了一些特殊的分词需求,在此做个总结。本来的Lucene的内置的分词器,差不多可以完成我们的大部分分词工作了,如果是英文文章那么可以使用StandardAnalyzer标准分词器,WhitespaceAnalyzer空格分词器,对于中文我们则可以选择IK分词器,Messeg4j,庖丁等分词器。
我们先来看看下面的几个需求
编号 | 需求分析 |
1 | 按单个字符进行分词无论是数字,字母还是特殊符号 |
2 | 按特定的字符进行分词,类似String中spilt()方法 |
3 | 按照某个字符或字符串进行分词 |
仔细分析下上面的需求,会觉得上面的需求很没意思,但是在特定的场合下确实是存在这样的需求的,看起来上面的需求很简单,但是lucene里面内置的分析器却没有一个支持这种变态的"无聊的"分词需求,如果想要满足上面的需求,可能就需要我们自己定制自己的分词器了。
先来看第一个需求,单个字符切分,这就要不管你是the一个单词还是一个电话号码还是一段话还是其他各种特殊符号都要保留下来,进行单字切分,这种特细粒度的分词,有两种需求情况,可能适应这两种场景
(-)100%的实现数据库模糊匹配
(=)对于某个电商网站笔记本的型号Y490,要求用户无论输入Y还是4,9,0都可以找到这款笔记本
这种单字切分确实可以实现数据库的百分百模糊检索,但是同时也带来了一些问题,如果这个域中是存电话号码,或者身份证之类的与数字的相关的信息,那么这种分词法,会造成这个域的倒排链表非常之长,反映到搜索上,就会出现中文检索很快,而数字的检索确实非常之慢的问题。原因是因为数字只有0-9个字符,而汉字则远远比这个数量要大的多,所以在选用这种分词时,还是要慎重的考虑下自己的业务场景到底适不适合这种分词,否则就会可能出一些问题。
再来分析下2和3的需求,这种需求可能存在这么一种情况,就是某个字段里存的内容是按照逗号或者空格,#号,或者是自己定义的一个字符串进行分割存储的,而这种时候我们可能就会想到一些非常简单的做法,直接调用String类的spilt方法进行打散,确实,这种方式是可行的,但是lucene里面的结构某些情况下,就可能不适合用字符串拆分的方法,而是要求我们必须定义一个自己的分词器来完成这种功能,因为涉及到一些参数需要传一个分词器或者索引和检索时都要使用分词器来构造解析,所以有时候就必须得自己定义个专门处理这种情况的分词器了。
好了,散仙不在唠叨了,下面开始给出代码,首先针对第一个需求,单字切分,其实这个需求没什么难的,只要熟悉lucene的Tokenizer就可以轻松解决,我们改写ChineseTokenizer来满足我们的需求.
package com.piaoxuexianjing.cn; import java.io.IOException; import java.io.Reader; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; import org.apache.lucene.util.AttributeSource.AttributeFactory; public class China extends Tokenizer { public China(Reader in) { super(in); } public China(AttributeFactory factory, Reader in) { super(factory, in); } private int offset = 0, bufferIndex=0, dataLen=0; private final static int MAX_WORD_LEN = 255; private final static int IO_BUFFER_SIZE = 1024; private final char[] buffer = new char[MAX_WORD_LEN]; private final char[] ioBuffer = new char[IO_BUFFER_SIZE]; private int length; private int start; private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class); private final void push(char c) { if (length == 0) start = offset-1; // start of token buffer[length++] = Character.toLowerCase(c); // buffer it } private final boolean flush() { if (length>0) { //System.out.println(new String(buffer, 0, //length)); termAtt.copyBuffer(buffer, 0, length); offsetAtt.setOffset(correctOffset(start), correctOffset(start+length)); return true; } else return false; } @Override public boolean incrementToken() throws IOException { clearAttributes(); length = 0; start = offset; while (true) { final char c; offset++; if (bufferIndex >= dataLen) { dataLen = input.read(ioBuffer); bufferIndex = 0; } if (dataLen == -1) { offset--; return flush(); } else c = ioBuffer[bufferIndex++]; switch(Character.getType(c)) { case Character.DECIMAL_DIGIT_NUMBER://注意此部分不过滤一些熟悉或者字母 case Character.LOWERCASE_LETTER://注意此部分 case Character.UPPERCASE_LETTER://注意此部分 // push(c); // if (length == MAX_WORD_LEN) return flush(); // break; case Character.OTHER_LETTER: if (length>0) { bufferIndex--; offset--; return flush(); } push(c); return flush(); default: if (length>0) return flush(); break; } } } @Override public final void end() { // set final offset final int finalOffset = correctOffset(offset); this.offsetAtt.setOffset(finalOffset, finalOffset); } @Override public void reset() throws IOException { super.reset(); offset = bufferIndex = dataLen = 0; } }
然后定义个自己的分词器
package com.piaoxuexianjing.cn; import java.io.Reader; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Tokenizer; /** * @author 三劫散仙 * 单字切分 * * **/ public class MyChineseAnalyzer extends Analyzer { @Override protected TokenStreamComponents createComponents(String arg0, Reader arg1) { Tokenizer token=new China(arg1); return new TokenStreamComponents(token); } }
下面我们来看单字切词效果,对于字符串
String text="天气不错132abc@#$+-)(*&^.,/";
天 气 不 错 1 3 2 a b c @ # $ + - ) ( * & ^ . , /
对于第二种需求我们要模仿空格分词器的的原理,代码如下
package com.splitanalyzer; import java.io.Reader; import org.apache.lucene.analysis.util.CharTokenizer; import org.apache.lucene.util.Version; /*** * *@author 三劫散仙 *拆分char Tokenizer * * */ public class SpiltTokenizer extends CharTokenizer { char c; public SpiltTokenizer(Version matchVersion, Reader input,char c) { super(matchVersion, input); // TODO Auto-generated constructor stub this.c=c; } @Override protected boolean isTokenChar(int arg0) { return arg0==c?false:true ; } }
然后在定义自己的分词器
package com.splitanalyzer; import java.io.Reader; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.util.Version; /** * @author 三劫散仙 * 自定义单个char字符分词器 * **/ public class SplitAnalyzer extends Analyzer{ char c;//按特定符号进行拆分 public SplitAnalyzer(char c) { this.c=c; } @Override protected TokenStreamComponents createComponents(String arg0, Reader arg1) { // TODO Auto-generated method stub return new TokenStreamComponents(new SpiltTokenizer(Version.LUCENE_43, arg1,c)); } }
下面看一些测试效果
package com.splitanalyzer; import java.io.StringReader; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; /** * 测试的demo * * **/ public class Test { public static void main(String[] args)throws Exception { SplitAnalyzer analyzer=new SplitAnalyzer('#'); //SplitAnalyzer analyzer=new SplitAnalyzer('+'); //PatternAnalyzer analyzer=new PatternAnalyzer("abc"); TokenStream ts= analyzer.tokenStream("field", new StringReader("我#你#他")); // TokenStream ts= analyzer.tokenStream("field", new StringReader("我+你+他")); CharTermAttribute term=ts.addAttribute(CharTermAttribute.class); ts.reset(); while(ts.incrementToken()){ System.out.println(term.toString()); } ts.end(); ts.close(); } } 我 你 他
到这里,可能一些朋友已经看不下去了,代码太多太臃肿了,有没有一种通用的办法,解决此类问题,散仙的回答是肯定的,如果某些朋友,连看到这部分的耐心都没有的话,那么,不好意思,你只能看到比较低级的解决办法了,当然能看到这部分的道友们,散仙带着大家来看一下比较通用解决办法,这个原理其实是基于正则表达式的,所以由此看来,正则表达式在处理文本字符串上面有其独特的优势。下面我们要做的就是改写自己的正则解析器,代码非常精简,功能却是很强大的,上面的3个需求都可以解决,只需要传入不用的参数即可。
package com.splitanalyzer; import java.io.Reader; import java.util.regex.Pattern; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.pattern.PatternTokenizer; /** * @author 三劫散仙 * 自定义分词器 * 针对单字切 * 单个符号切分 * 多个符号组合切分 * * **/ public class PatternAnalyzer extends Analyzer { String regex;//使用的正则拆分式 public PatternAnalyzer(String regex) { this.regex=regex; } @Override protected TokenStreamComponents createComponents(String arg0, Reader arg1) { return new TokenStreamComponents(new PatternTokenizer(arg1, Pattern.compile(regex),-1)); } }
我们来看下运行效果:
package com.splitanalyzer; import java.io.StringReader; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; /** * 测试的demo * * **/ public class Test { public static void main(String[] args)throws Exception { // SplitAnalyzer analyzer=new SplitAnalyzer('#'); PatternAnalyzer analyzer=new PatternAnalyzer(""); //空字符串代表单字切分 TokenStream ts= analyzer.tokenStream("field", new StringReader("我#你#他")); CharTermAttribute term=ts.addAttribute(CharTermAttribute.class); ts.reset(); while(ts.incrementToken()){ System.out.println(term.toString()); } ts.end(); ts.close(); } }
输出效果:
我 # 你 # 他
传入#号参数
PatternAnalyzer analyzer=new PatternAnalyzer("#");
输出效果:
我 你 他
传入任意长度的字符串参数
PatternAnalyzer analyzer=new PatternAnalyzer("分割"); TokenStream ts= analyzer.tokenStream("field", new StringReader("我分割你分割他分割"));
输出效果:
我 你 他
允许转载,转载请注明原创地址:
http://qindongliang1922.iteye.com/blog/1927605
谢谢配合
至此,我们可以完成一些比较特殊的分词,当然,大千世界无奇不有,无论什么时候,适合自己业务就是最好的,我们要根据自己的需求来灵活变通。
评论
5 楼
北风norther
2014-07-23
qindongliang1922 写道
北风norther 写道
对我帮助甚多,瞬间顿悟,我可得道升仙了
加油吧
终于写出自己的分词器了,参考你的China代码,然后又对照着源码里的SentenceTokenizer~
功能和SplitAnalyzer类似,就多了个把大写转小写。。
[url] http://song571377.iteye.com/blog/2095261[/url]
4 楼
qindongliang1922
2014-07-22
北风norther 写道
对我帮助甚多,瞬间顿悟,我可得道升仙了
加油吧
3 楼
北风norther
2014-07-22
对我帮助甚多,瞬间顿悟,我可得道升仙了
2 楼
高军威
2013-08-27
不知楼主 对luncene4.3的索引 优化 有何见解,能否分享下
1 楼
fncj
2013-08-22
不错
发表评论
-
Lucene4.3开发之第十步之渡劫后期(十)
2014-01-15 20:23 4557转载请务必注明,原创 ... -
Lucene4.3开发之第九步之渡劫中期(九)
2014-01-13 21:41 4020转载请务必注明,原创地址,谢谢配合! http://qind ... -
Lucene4.3开发之插曲之落寞繁华
2013-11-07 19:22 4307转载请注明,原创地址,谢谢配合! http://qindon ... -
Lucene4.3开发之第八步之渡劫初期(八)
2013-10-08 19:30 6895转载请注明,原创地址,谢谢配合! http://qin ... -
Lucene4.3开发之第七步之合体后期(七)
2013-09-16 20:17 4426转载请注明原创地址: http://qindongliang1 ... -
Lucene4.3开发之插曲之烽火连城
2013-09-06 18:12 5897转载请注明,原创地址,谢谢配合! http://qindon ... -
Lucene4.3开发之第六步之分神中期(六)
2013-08-30 20:17 6196转载请注明,原创地址,谢谢配合! http://qindo ... -
Lucene4.3开发之插曲之斗转星移
2013-08-26 18:08 4535允许转载,转载请注明原创地址: http://qindo ... -
Lucene4.3开发之第五步之融丹筑基(五)
2013-08-14 17:57 8499本文章允许转载,转载请注明原创地址 http://qin ... -
Lucene4.3开发之第四步之脱胎换骨(四)
2013-08-09 18:40 9884为防止,一些小网站私自盗用原文,请支持原创 原文永久链 ... -
Lucene4.3开发之第三步之温故知新(三)
2013-08-07 18:30 4979前面几篇笔者已经把Lucene的最基本的入门,介绍完了,本篇就 ... -
Lucene4.3开发之第二步初入修真(二)
2013-07-29 18:23 7970上次笔者简单介绍下了,Lucene的入门搭建以及一个添加的De ... -
Lucene4.3开发之第一步小试牛刀(一)
2013-07-25 16:47 8235首页,本篇适合对于刚 ... -
lucene开发序幕曲之luke神器
2013-07-25 11:28 8325lucene是一款很优秀的全 ... -
lucene4.x的分组实现
2013-06-24 11:51 4505lucene在4.x之前,没有实现分组的功能,如果业务中有需要 ... -
solr4.2的入门部署
2013-06-24 11:00 2813solr 4.2的入门配置 第一步,从官网上下载下 ...
相关推荐
Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会...
lucene4.3增删改查的的一个工具类,对新手来说是一份不可多得的入门资料。
全文检索lucene 4.3 所用到的3个jar包,包含lucene-queryparser-4.3.0.jar、 lucene-core-4.3.0.jar、lucene-analyzers-common-4.3.0.jar。
lucene4.3 按坐标距离排序,里面写了个简单的例子。运行就行
lucene4.3源代码 censed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information ...
使用lucene需要检索时,需要导入jar包,下载全资源文件,进去找就可以
1.XunTa是在lucene4.3上创建的通过“知识点”来找人的搜人引擎。 输入一个关键词(或组合),XunTa返回一个排名列表,排在前面的人是与该关键词(组合)最相关的“达人”。 可访问 http://www.xunta.so立即体验...
基于lucene的开发JavaEE项目 基于lucene的开发JavaEE项目 基于lucene的开发JavaEE项目 基于lucene的开发JavaEE项目 基于lucene的开发JavaEE项目
关于lucene的IKAnalyzer分词器以及与lucene4.3共同使用时发生的问题解决包
lucene应用开发揭秘 第四讲
Lucene3.4开发入门.pdf
Lucene开发详解.pdf,详细介绍Lucene的开发
Lucene.net开发最全文档 包含英文原api + 中文api
lucene2.9开发指南是为了帮助初级开发人员而编写整理的资料,包括了所有的核心技术,希望对大家有帮助
lucene 的开发包6.6
Lucene开发手册
闲着无聊做的lucene的知识管理,基本的lucene的用法都有。
搜索引擎 lucene开发流程介绍,开发全文检索系统,
Lucene搜索引擎开发权威经典(附盘源码)【于天恩】.zip
教大家使用lucene进行简单的开发,贴出了主要的源码和注释.