본문 바로가기

Lucene

Lucene 3.0 TokenStream과 AttributeSource. - 2 -

앞에서 AttributeSource에 Attribute들 (term, offset, position, type 등) set 해주면
그 뒤에 오는 Filter나 Tokenizer가 해당 Attribute를 사용 할 수 있다고 했습니다.
각각의 Filter와 Tokenizer는 모두 TokenStream을 상속하고 있고 이 TokenStream은
AttributeSource를 상속받고 있습니다.

이 Filter와 Tokenizer가 서로 데코레이터 패턴으로 물려있기 때문에
앞에서 set한 결과를 뒤에 따라오는 Filter와 Tokenizer가 사용 할 수 있는 것 입니다.
(Java의 stream 관련 클래스들을 생각하시면 됩니다.)

소스를 보면
보통 맨 앞단에서 들어오는 String을 잘라내는 역할을 하는 Tokenizer는 아래와 같이
사용 됩니다.


보시면 생성자에서
TermAttribute등을 addAttribute 메서드를 사용해서 등록하고 있습니다
이 메서드는 attribute가 있으면 있는 attribute를 리턴하고, 없으면 새롭게 attribute를 AttributeSource에 set해주는
역할을 합니다.
그럼 그냥 setAttribute를 해주면 되지 왜 저런 역할 (있으면 return, 없으면 add)을 하는 메서드가 필요할까요?
루씬에서 사용되는 필터나 Tokenizer는 사실 맨 앞이나 맨 뒤나 아니며 중간이던 어느 위치에서도
사용 될 수 있습니다. 때문에 저런 메서드가 필요한 것입니다.
하나의 필터가 맨 앞에서 사용 될거라고 해서 setAttribute 메서드를 사용해서 구현을 했는데 다른 Analyzer에서
이 필터가 3번째 4번째에서 사용될 수도 있기 때문입니다.

그리고 boolean incrementToken 메서드를 보시면 결과 값을 attribute에 set해주고 있는 것을 보실 수 있습니다.

그러면 이 결과를 받아서 사용하는
다른 Filter를 한번 보겠습니다. 지금까지 Analyzer를 구현하면서 본 Filter들은 크게 두개의 패턴을 가지고 있었습니다.

하나는 Tokenizer 처럼 하나의 결과를 받아서 그 결과에 직접 작업을 하는 필터입니다.하나의 Term을 만들거나 받아서
하나의 Term을 리턴하는 경우죠. 자세한 방법은 나중에 Analyzer에 대해서 얘기 할 때 자세하게 작성하겠습니다.
이 경우 따로 결과를 저장해 놓을 필요 없이 그냥 바로 "결과 Attribute를 넣어주면 됩니다.

또 하나는 동의어 추출이나 복합명사 추출 처럼 하나의 결과를 받아서 그 결과로부터 다른 추가적인 term을 추출해내는
필터입니다.
"노트북" 이라는 term이 들어오면, 이 노트북이라는 단어와 동의어로 설정 된 notebook,노트PC등의 term을 추가로 리턴해주는
필터입니다. 보통, List나 Stack에 결과를 담아두고 값을 하나하나 리턴하게 됩니다.
모양새는 서로 비슷합니다.

incrementToken 메서드는 이전 버전에서의 next 메서드와 같은 역할을 합니다.

그런데, 이 메서드의 return type이 boolean입니다. 그럼 어떻게 후속 필터들이 이 결과를 받을 수 있을까요?

첫번째 경우는 앞어 있는 Tokenizer 소스를 보시면 되고
두번째 경우인 추가적인 term를 만들어내는 필터를 보겠습니다.
Stemming 필터로써 어미 사전을 사용해 앞 필터에서 만들어낸 Term에서 어미를 제거하여 리턴하는 것입니다.
그런데 왜 stack을 사용하냐면 원본 Term도 리턴을 해줘야 하기 때문입니다.
이것도 나중에 Analyzer에 대해서 작성 할 때 상세히 알아보겠습니다.

아무튼 아래 stemming 필터를 보겠습니다.

생성자에서 Attribute3개를 addAttribute 해주고 있습니다.
만약, 앞선 Filter나 Tokenizer가 있다면 이 3개의 Attribute에는 앞선 Filter와 Tokenizer의 결과가
들어 올 것입니다.

그리고, AttributeSource save라고 선언된 것이 있는데 현재 input (즉, 앞선 Filter와 Tokenizer)를 cloneAttributes 메서드를 사용해서 Attribute들을 복사하고 있습니다. (사실은 AttributeSource 한벌이 더 생기는 거겠죠..)
왜 이게 필요할까요..

boolean incrementToken() 메서드를 보면

이렇게 되어 있는데요..
우선 #4를 보시면 앞선 필터에서의 결과가 없으면 역시 false를 리턴해서 결과 없음을 표시합니다.
#5 는 term을 받아서 그것을 stem 메서드를 사용해 어미를 잘라내는 부분입니다.
#6 #5에서의 결과를 가지고 어미가 잘려나갔으면
#7 현재의 State를 capture하고 있는데요 , 이 부분이 중요합니다. AttributeSource는 state라는 것을 갖습니다.
그리고 이 State는 captureState 메서드를 통해 snapshot을 저장해 둘 수도 있고
restore메서드를 사용해 다시 되살릴 수도 있습니다. 왜 이 부분이 필요하냐면...
앞선 필터에서 받은 Term이 "사랑해서"라고 가정했을 때 이 Stem 필터에서는 "사랑" 이라고 어미를 잘라낸 후
AttributeSource에 해당 Attribute를 set 할 것 입니다. (#9 번)

그렇게 되면 앞선 필터로 받았던 "사랑해서"에 대한 Attribute가 모두 사라지게 됩니다.

나중에 이 "사랑해서"라는 Term을 가지고 작업을 하려면 다시 이 것을 retore 메서드를 사용해서 처음으로 돌려놓아야 하는데
이를 위해서 앞에서 AttributeSource를 clone해서 save라는 한벌을 더 가지고 있었고, 이 save AttributeSource의 State를
Attribute가 변하기 전에 capture해 놓는 것입니다.

#10을 보면
이전 버전에서는 결과 Token을 Stack에 넣어두고 Token을 리턴했지면
여기서는 해당 Attribute들이 저장되어 있는 AttributeSource의 State를 Stack에 넣어놓습니다.
이 AttributeSource로부터 stem된 결과 Attribute들을 받아낼 수 있기 때문입니다.

실제로 #1 에서 stack의 사이즈가 0 보다 크면
#2에서 state를 받아내고
#3에서 그 State를 restore한 후 true를 리턴해주고 있습니다.

어차피 다음 필터에서도
생성자에서 addAttribute 메서드를 사용해서 Attriute를 받아내고 있기 때문에
이렇게 restoreState메서드를 사용하면 이 Stem 필터에서 결과로 뽑아낸 Attribute들을
AttributeSource로부터 받아 쓸 수 있게 됩니다.