Lucene 4.3 개발 에피소드 의 포용 만물

전재 허용, 전재 원본 주소 표시: http://qindongliang1922.iteye.com/blog/1927605 협조 해 주 셔 서 감사합니다.
최근 그룹 내 (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 협조 해 주 셔 서 감사합니다.
이로써 우 리 는 비교적 특수 한 단 어 를 완성 할 수 있다. 물론 대천세계 에는 별 것 이 다 있다. 언제든지 자신의 업무 에 적합 한 것 이 가장 좋다. 우 리 는 자신의 수요 에 따라 유연 하 게 변통해 야 한다.

좋은 웹페이지 즐겨찾기