Lucene 4.3 개발 에피소드 의 포용 만물
최근 그룹 내 (32471439) 에서 몇 명의 친 구 를 만 나 특별한 단어 수 요 를 제기 하여 이 를 정리 했다.원래 Lucene 에 내 장 된 단어 기 는 우리 의 대부분의 단어 작업 을 완성 할 수 있 는 차이 가 많 지 않 습 니 다. 영어 문장 이 라면 Standard Analyzer 표준 단어 기, WhitespaceAnalyzer 빈 칸 단어 기 를 사용 할 수 있 습 니 다. 중국어 에 대해 서 는 IK 단어 기, Messeg4j, 요리사 등 단 어 를 선택 할 수 있 습 니 다.
우리 먼저 아래 의 몇 가지 요 구 를 살 펴 보 자.
번호
수요 분석
1
하나의 문자 에 따라 단 어 를 나눈다. 숫자 든 자모 든 특수 기호 든.
2
문자열 의 spilt () 방법 과 같은 특정한 문자 로 단 어 를 나 눕 니 다.
3
어떤 문자 나 문자열 에 따라 단 어 를 나눈다
위의 수 요 를 자세히 분석 하면 위의 수요 가 재 미 없 지만 특정한 상황 에서 이런 수요 가 존재 한다. 위의 수 요 는 매우 간단 해 보이 지만 lucene 에 내 장 된 분석 기 는 이런 변태 적 인 '지루 한' 단어 수 요 를 지지 하지 않 는 다. 위의 수 요 를 만족 시 키 려 면우리 스스로 분사 기 를 맞 춰 야 할 지도 모른다.
먼저 첫 번 째 수 요 를 살 펴 보 겠 습 니 다. 한 글자 로 구분 해 야 합 니 다. 이것 은 당신 이 the 한 단어 든 전화번호 든 한 단락 의 말 이 든 다른 각종 특수 기호 든 모두 보류 하고 한 글자 로 구분 해 야 합 니 다. 이런 특수 한 입자 의 단 어 는 두 가지 수요 상황 이 있 습 니 다. 이 두 가지 장면 (-) 에 적응 하여 데이터 베 이 스 를 모호 하 게 일치 시 킬 수 있 습 니 다 (=)한 전자상거래 사이트 의 노트북 모델 Y490 에 대해 서 는 Y 를 입력 하 든 4, 9, 0 을 입력 하 든 모두 이 노트북 을 찾 을 수 있 도록 요구한다.
이러한 단어 절 점 은 확실히 데이터 뱅 크 의 100% 모호 한 검색 을 실현 할 수 있 지만 문제 도 가 져 왔 다. 만약 에 이 도 메 인 에 전화번호 나 신분증 같은 숫자 와 관련 된 정보 가 저장 되 어 있다 면 이런 단어 법 은 이 도 메 인의 후진 링크 가 매우 길 고 검색 에 나타 나 면 중국어 검색 이 매우 빠르다.숫자 검색 이 느 린 것 은 사실이다.숫자 가 0 - 9 글자 에 불과 하고 한 자 는 이 수량 보다 훨씬 많 기 때문에 이런 단 어 를 선택 할 때 자신의 업무 장면 이 이런 단어 에 적합 한 지 신중하게 고려 해 야 한다. 그렇지 않 으 면 문제 가 생 길 수 있다.
다시 2 와 3 의 수 요 를 분석 해 보면 이러한 수 요 는 이러한 상황 이 존재 할 수 있다. 즉, 특정한 필드 에 저 장 된 내용 은 쉼표 나 빈 칸, \ # 번호 또는 자신 이 정의 한 문자열 에 따라 분할 저장 한 것 이다. 이 럴 때 우 리 는 매우 간단 한 방법 을 생각 하고 String 류 의 spilt 방법 을 직접 호출 하여 분산 시 킬 수 있다. 확실 하 다.이러한 방식 은 실행 가능 하지만 lucene 안의 구 조 는 어떤 경우 에 문자열 로 나 누 는 방법 에 적합 하지 않 을 수 있 습 니 다. 우 리 는 반드시 자신의 단어 기 를 정의 하여 이런 기능 을 완성 해 야 합 니 다. 왜냐하면 일부 매개 변 수 는 단어 기 나 색인 과 검색 을 할 때 단어 기 를 사용 하여 구조 적 으로 해석 해 야 하기 때 문 입 니 다.그래서 가끔 은 이런 상황 을 전문 적 으로 처리 하 는 분사 기 를 스스로 정의 해 야 한다.
자, 산 선 이 잔 소 리 를 하지 않 습 니 다. 다음 에 코드 를 드 리 겠 습 니 다. 먼저 첫 번 째 수요 에 대해 한 글자 로 나 누 겠 습 니 다. 사실은 이 수 요 는 어렵 지 않 습 니 다. lucene 의 Tokenizer 를 잘 알 면 쉽게 해결 할 수 있 습 니 다. 우 리 는 Chinese Tokenizer 를 바 꾸 어 우리 의 수 요 를 만족 시 킬 수 있 습 니 다.
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 = "날씨 가 좋 습 니 다 132 abc @ \ # $+ -) (* & ^., /";
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();
}
}
여기 서 일부 친구 들 은 더 이상 볼 수 없 을 것 입 니 다. 코드 가 너무 많 고 비대 해서 통용 되 는 방법 이 있 습 니까? 이런 문 제 를 해결 하 는 방법 이 있 습 니까? 산 선의 대답 은 긍정 적 입 니 다. 만약 에 어떤 친구 들 이 이 부분 을 보 는 인내심 조차 없다 면 죄송합니다. 당신 은 비교적 저급한 해결 방법 만 볼 수 있 습 니 다. 물론 이 부분의 친구 들, 산 선 을 볼 수 있 습 니 다.여러분 을 데 리 고 비교적 통용 되 는 해결 방법 을 살 펴 보 겠 습 니 다. 이 원 리 는 사실 정규 표현 식 에 기초 한 것 입 니 다. 따라서 정규 표현 식 은 텍스트 문자열 을 처리 하 는 데 있어 서 독특한 장점 을 가지 고 있 습 니 다. 아래 에서 우리 가 해 야 할 일 은 바로 자신의 정규 해석 기 를 고 치 는 것 입 니 다. 코드 는 매우 간소화 되 고 기능 은 매우 강 합 니 다. 위의 세 가지 요 구 는 모두 해결 할 수 있 습 니 다. 입력 만 하면 됩 니 다.사용 하지 않 는 인자 만 있 으 면 됩 니 다.
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 협조 해 주 셔 서 감사합니다.
이로써 우 리 는 비교적 특수 한 단 어 를 완성 할 수 있다. 물론 대천세계 에는 별 것 이 다 있다. 언제든지 자신의 업무 에 적합 한 것 이 가장 좋다. 우 리 는 자신의 수요 에 따라 유연 하 게 변통해 야 한다.