Lucene4.3开发之插曲之包容万物

2024-05-15 04:58

本文主要是介绍Lucene4.3开发之插曲之包容万物,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[b][color=red][size=x-large]允许转载,转载请注明原创地址:
[url]http://qindongliang1922.iteye.com/blog/1927605[/url]
谢谢配合
[/size][/color][/b]

[b][size=x-large][color=green]最近在群里面(324714439)遇到几位朋友提出了一些特殊的分词需求,在此做个总结。本来的Lucene的内置的分词器,差不多可以完成我们的大部分分词工作了,如果是英文文章那么可以使用StandardAnalyzer标准分词器,WhitespaceAnalyzer空格分词器,对于中文我们则可以选择IK分词器,Messeg4j,庖丁等分词器。[/color][/size][/b]

[b][color=green][size=x-large]我们先来看看下面的几个需求

[table]
|编号|需求分析
|1|按单个字符进行分词无论是数字,字母还是特殊符号
|2|按特定的字符进行分词,类似String中spilt()方法
|3|按照某个字符或字符串进行分词
[/table][/size][/color][/b]
[b][size=x-large]仔细分析下上面的需求,会觉得上面的需求很没意思,但是在特定的场合下确实是存在这样的需求的,看起来上面的需求很简单,但是lucene里面内置的分析器却没有一个支持这种变态的"无聊的"分词需求,如果想要满足上面的需求,可能就需要我们自己定制自己的分词器了。[/size][/b]


[b][color=violet][size=x-large]先来看第一个需求,单个字符切分,这就要不管你是the一个单词还是一个电话号码还是一段话还是其他各种特殊符号都要保留下来,进行单字切分,这种特细粒度的分词,有两种需求情况,可能适应这两种场景
(-)100%的实现数据库模糊匹配
(=)对于某个电商网站笔记本的型号Y490,要求用户无论输入Y还是4,9,0都可以找到这款笔记本
[/size][/color][/b]

[b][size=x-large]这种单字切分确实可以实现数据库的百分百模糊检索,但是同时也带来了一些问题,如果这个域中是存电话号码,或者身份证之类的与数字的相关的信息,那么这种分词法,会造成这个域的倒排链表非常之长,反映到搜索上,就会出现中文检索很快,而数字的检索确实非常之慢的问题。原因是因为数字只有0-9个字符,而汉字则远远比这个数量要大的多,所以在选用这种分词时,还是要慎重的考虑下自己的业务场景到底适不适合这种分词,否则就会可能出一些问题。[/size][/b]

[b][size=x-large][color=green]再来分析下2和3的需求,这种需求可能存在这么一种情况,就是某个字段里存的内容是按照逗号或者空格,#号,或者是自己定义的一个字符串进行分割存储的,而这种时候我们可能就会想到一些非常简单的做法,直接调用String类的spilt方法进行打散,确实,这种方式是可行的,但是lucene里面的结构某些情况下,就可能不适合用字符串拆分的方法,而是要求我们必须定义一个自己的分词器来完成这种功能,因为涉及到一些参数需要传一个分词器或者索引和检索时都要使用分词器来构造解析,所以有时候就必须得自己定义个专门处理这种情况的分词器了。[/color][/size][/b]

[b][color=green][size=x-large]好了,散仙不在唠叨了,下面开始给出代码,首先针对第一个需求,单字切分,其实这个需求没什么难的,只要熟悉lucene的Tokenizer就可以轻松解决,我们改写ChineseTokenizer来满足我们的需求.[/size][/color][/b]
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;
}

}


[b][size=x-large]然后定义个自己的分词器[/size][/b]

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);
}





}



[b][size=x-large]下面我们来看单字切词效果,对于字符串
String text="天气不错132abc@#$+-)(*&^.,/";[/size][/b]





1
3
2
a
b
c
@
#
$
+
-
)
(
*
&
^
.
,
/



[b][size=x-large]对于第二种需求我们要模仿空格分词器的的原理,代码如下[/size][/b]
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 ;
}




}

[b][color=olive][size=x-large]然后在定义自己的分词器[/size][/color][/b]
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));
}


}

[b][size=x-large]下面看一些测试效果[/size][/b]
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();

}

}





[b][size=x-large][color=blue]到这里,可能一些朋友已经看不下去了,代码太多太臃肿了,有没有一种通用的办法,解决此类问题,散仙的回答是肯定的,如果某些朋友,连看到这部分的耐心都没有的话,那么,不好意思,你只能看到比较低级的解决办法了,当然能看到这部分的道友们,散仙带着大家来看一下比较通用解决办法,这个原理其实是基于正则表达式的,所以由此看来,正则表达式在处理文本字符串上面有其独特的优势。下面我们要做的就是改写自己的正则解析器,代码非常精简,功能却是很强大的,上面的3个需求都可以解决,只需要传入不用的参数即可。[/color][/size][/b]



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));
}



}



[b][size=x-large]我们来看下运行效果:[/size][/b]
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();

}

}

[b][size=x-large]输出效果:[/size][/b]

#

#


[b][size=x-large]传入#号参数[/size][/b]
PatternAnalyzer analyzer=new PatternAnalyzer("#");

[b][size=x-large]输出效果:[/size][/b]





[b][size=x-large]传入任意长度的字符串参数[/size][/b]
 PatternAnalyzer analyzer=new PatternAnalyzer("分割");
TokenStream ts= analyzer.tokenStream("field", new StringReader("我分割你分割他分割"));

[b][size=x-large]输出效果:[/size][/b]







[b][color=red][size=x-large]允许转载,转载请注明原创地址:
[url]http://qindongliang1922.iteye.com/blog/1927605[/url]
谢谢配合
[/size][/color][/b]


[b][color=red][size=x-large]至此,我们可以完成一些比较特殊的分词,当然,大千世界无奇不有,无论什么时候,适合自己业务就是最好的,我们要根据自己的需求来灵活变通。[/size][/color][/b]

这篇关于Lucene4.3开发之插曲之包容万物的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/990870

相关文章

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

基于Python开发批量提取Excel图片的小工具

《基于Python开发批量提取Excel图片的小工具》这篇文章主要为大家详细介绍了如何使用Python中的openpyxl库开发一个小工具,可以实现批量提取Excel图片,有需要的小伙伴可以参考一下... 目前有一个需求,就是批量读取当前目录下所有文件夹里的Excel文件,去获取出Excel文件中的图片,并

基于Python开发PDF转PNG的可视化工具

《基于Python开发PDF转PNG的可视化工具》在数字文档处理领域,PDF到图像格式的转换是常见需求,本文介绍如何利用Python的PyMuPDF库和Tkinter框架开发一个带图形界面的PDF转P... 目录一、引言二、功能特性三、技术架构1. 技术栈组成2. 系统架构javascript设计3.效果图

基于Python开发PDF转Doc格式小程序

《基于Python开发PDF转Doc格式小程序》这篇文章主要为大家详细介绍了如何基于Python开发PDF转Doc格式小程序,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用python实现PDF转Doc格式小程序以下是一个使用Python实现PDF转DOC格式的GUI程序,采用T

使用Python开发一个图像标注与OCR识别工具

《使用Python开发一个图像标注与OCR识别工具》:本文主要介绍一个使用Python开发的工具,允许用户在图像上进行矩形标注,使用OCR对标注区域进行文本识别,并将结果保存为Excel文件,感兴... 目录项目简介1. 图像加载与显示2. 矩形标注3. OCR识别4. 标注的保存与加载5. 裁剪与重置图像

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安