`
qindongliang1922
  • 浏览: 2182348 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
7265517b-f87e-3137-b62c-5c6e30e26109
证道Lucene4
浏览量:117470
097be4a0-491e-39c0-89ff-3456fadf8262
证道Hadoop
浏览量:125858
41c37529-f6d8-32e4-8563-3b42b2712a50
证道shell编程
浏览量:59865
43832365-bc15-3f5d-b3cd-c9161722a70c
ELK修真
浏览量:71254
社区版块
存档分类
最新评论

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
谢谢配合



至此,我们可以完成一些比较特殊的分词,当然,大千世界无奇不有,无论什么时候,适合自己业务就是最好的,我们要根据自己的需求来灵活变通。
11
5
分享到:
评论
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 按坐标距离排序

    在"lucene4.3 按坐标距离排序"这个主题中,我们将探讨如何在Lucene 4.3版本中利用地理位置信息进行文档排序,特别是在处理地理空间搜索时的应用。 首先,Lucene 4.3引入了对地理空间搜索的支持,这允许我们根据地理...

    lucene4.3源码

    Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会...

    lucene4.3工具类

    lucene4.3增删改查的的一个工具类,对新手来说是一份不可多得的入门资料。

    lucene 4.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.3src 源代码

    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-4.3.1资源

    Lucene是一个开源的全文搜索引擎库,由Apache软件基金会开发并维护。在Java编程环境中,它为开发者提供了强大的文本检索功能,使得在海量数据中快速查找相关信息变得简单易行。本篇文章将详细探讨Lucene 4.3.1版本的...

    基于lucene4.3的知识图谱搜索引擎XunTa(一种用"知识点"来找人的搜人引擎).zip

    1.XunTa是在lucene4.3上创建的通过“知识点”来找人的搜人引擎。  输入一个关键词(或组合),XunTa返回一个排名列表,排在前面的人是与该关键词(组合)最相关的“达人”。  可访问 http://www.xunta.so立即体验...

    lucene的IKAnalyzer以及兼容4.3

    本文将深入探讨IKAnalyzer的特性和其在Lucene 4.3中的兼容性问题及其解决方案。 **IKAnalyzer简介** IKAnalyzer( Intelligent Keyword Analyzer)是一款基于Java实现的中文分词工具,它主要针对中文的特性进行了...

    基于lucene的开发JavaEE项目

    《基于Lucene的JavaEE项目开发详解》 在信息技术领域,搜索引擎已经成为不可或缺的一部分,而Lucene作为开源全文搜索引擎库,以其高效、灵活的特点被广泛应用于各种JavaEE项目中。本项目将深入探讨如何利用Lucene...

    Lucene3.4开发入门.pdf

    Lucene3.4开发入门.pdf

    Lucene.net开发最全文档

    **Lucene.net 开发完全指南** Lucene.net 是一个开源的全文搜索引擎库,它是 Apache Lucene 的 .NET 版本,适用于 .NET 平台。Lucene.net 提供了高效、可扩展的搜索功能,广泛应用于网站内容检索、文档搜索、数据库...

    lucene2.9开发指南

    《Lucene2.9开发指南》是一份专为初级开发者准备的资料,旨在详细介绍如何使用开源全文搜索工具包Lucene2.9.1。Lucene作为一个强大的文本搜索库,其核心功能包括建立索引和执行搜索。以下是关于Lucene2.9开发的一些...

    基于lucene.net开发的个人知识库

    Lucene.Net的设计理念是将复杂的搜索引擎技术封装在简单易用的API之下,使得开发者可以专注于应用逻辑,而无需深入理解搜索引擎的底层原理。 在这个个人知识库项目中,开发者可能已经实现了以下关键功能: 1. **...

    Lucene开发手册

    【Lucene开发手册】 Lucene是一个开源的全文检索库,由Apache软件基金会开发,主要用Java编写。作为一套API,Lucene提供了构建高效、可扩展的搜索引擎所需的核心工具。它并非一个完整的搜索引擎产品,而是一个搜索...

    Lucene搜索引擎开发权威经典(附盘源码)【于天恩】.zip

    《Lucene搜索引擎开发权威经典》是由于天恩编著的一本深入探讨Lucene搜索引擎开发的专业书籍,这本书结合源码分析,旨在帮助读者掌握Lucene的核心技术和应用实践。Lucene是Apache软件基金会的一个开放源代码项目,它...

    Lucene开发详解.pdf

    ### Lucene开发详解 #### 一、Lucene简介 Lucene是一个高性能、全功能的文本搜索引擎库,由Doug Cutting创建并捐赠给Apache Software Foundation。它主要用于构建全文搜索应用程序,能够帮助开发者快速地在其应用...

    lucene开发流程

    Lucene 是一个高性能、全文本搜索库,广泛应用于开发全文检索系统。要理解Lucene的开发流程,我们首先需要了解其基本概念和步骤。以下是对Lucene开发流程的详细说明: 1. **数据获取**: 开发流程的第一步是从各种...

    lucene 的开发包6.6

    《Apache Lucene 6.6 开发详解》 Apache Lucene 是一个开源全文搜索引擎库,由Java编写,设计为高性能、可扩展的信息检索应用程序。它提供了丰富的搜索功能,包括索引、查询、排序等,被广泛应用于各种信息管理和...

    Lucene开发指南

    《Lucene开发指南》是一份综合性的学习资料,旨在帮助开发者深入理解和熟练运用Apache Lucene这一强大的全文搜索引擎库。Lucene是Java语言实现的开源项目,广泛应用于信息检索、数据分析和大数据处理等领域。本指南...

    Lucene开发包

    **Lucene 开发包详解** Lucene 是一个高性能、全文本搜索库,由 Apache 软件基金会开发并维护。这个开发包包含了两个版本:lucene-1.4.3 和 lucene-1.4.1,分别代表了 Lucene 的不同迭代阶段,它们为开发者提供了...

Global site tag (gtag.js) - Google Analytics