본문 바로가기

Lucene

[about Lucene] 루씬으로 검색엔진 개발하기 - Analyzer (6) -

이번에는 많은 분들께서 기다리셨을(?) 명사 추출 필터입니다.
사전에 등록되어있는 명사를 기반으로 Tokenizer나 선행 TokenFilter로 부터 넘어온
Token을 탐색하여 명사를 추출해내는 구조입니다.

기대를 하셨다면 죄송스럽게도 굉장히 별거 없는 Filter입니다.
 
형태소 분석이 아닌 단순히 Token을 읽어 사전에 있는 단어를 추출하는 방식입니다.
Token탐색도 그냥 character단위로 쭉.... 합니다.

이걸 다른 자료구조형으로 만들면 속도를 빠르게 할 수도 있겠지만
여기서는 코드의 간결함을 위해서 복잡한 부분은 모두 제외를 시킬 예정입니다.

이 추출의 과정이 형태소분석을 기반으로 알고리즘이 만들어져 있다면 이 Filter가 형태소분석기가 됩니다.
하지만 이 형태소분석이라는 것이 쉬운 것이 아니고..요즘은 웹이 처음 태동 할 당시처럼 형태소 분석으로
명사만 추출해서 되는 상황도 아닙니다. 때문에, 여기서는 사전을 기반으로 Token을 탐색하여
매칭되는 명사들을 그냥 추출해는 정도로 구현을 하려고 합니다.

그리고 가장 중요한 것이 사전으로 형태소분석을 하던 지금과 같은 방식을 사용하던 이 사전은 계속 유지보수가 되어야합니다.
신조어가 계속 생겨나기 때문입니다.

때문에 기본이 되는 사전을 가지고 핵심적인 모듈을 구현하고 그외 신조어/불용어/동의어등 
사용자가 직접 커스터마이즈 할 수 있는 사전을 제공하여 그 rule을 추가적으로 적용하는 방법들도 많이 사용됩니다.

그럼 이 Filter의 테스트케이스부터 보겠습니다.

 

DevysNounFilterTest.java

우선 추출의 대상이 되는 문장이 있고 여기서 추출되어야 하는 단어들을 미리 List에 넣어두고
추출되는 단어들을 비교합니다. 동의어필터 테스트케이스와 마찬가지로 양방향으로 검증을 합니다.

명사가 추출되는 것이 보이시나요?

비록 아주 단순한 로직이지만 이렇게 문장으로부터 원하는 키워드가 추출되기 시작하면 뭔가 기분이 좋아집니다. :)

그럼 Filter의 코드를 보겠습니다.

 
DevysNounEngine.java
DevysNounFilter.java

기본적으로는 동의어필터의 SynonymEngine과 같은 인터페이스를 구현하고있습니다.
하나의 Token으로부터 여러개의 Token이 파생되는 경우에는 대부분 Stack<AttributeSource>로 활용이
가능하기 때문에 이를 이용해서 인터페이스를 만들고 이용합니다.

내부로직은 각 엔진이 가지고 있게 됩니다.

Token을 하나씩 인덱스를 증가시켜가며 단어를 추출하고
이를 사전과 비교하여 사전에 있는 단어라면 이를 Stack에 저장해 넘겨줍니다.

여기서 word 타입만을 명사 추출 대상으로 삼는데
동의어의 경우 기본적으로 명사를 추출 할 대상이 아니게 되고 기타 불필요한 로직을 타지 않게 하기 위합니다.
따라서 동의어필터와 명사필터의 우선순위를 봤을 때 추출된 명사의 동의어를
추출해야하므로 명사필터가 동의어필터보다 앞에 와야함을 알 수 있습니다.

사전/탐색/비교등의 로직을 더 빠르고 확실하게 구현한다면 그래도 꽤 쓸만한 명사 추출 필터가 되지 않을까 합니다.
다만 여러방법으로 명사를 추출하도록 하는 것 예를들면 사전과 매칭되는 가장 긴 명사를 추출한다던가 하는 부분이 추가되면
더 좋을 것 같습니다.

이 필터에서 한가지 중요한 것은 원문 토큰도 리턴을 해준다는 것 입니다.

즉 , "하둡을" 이라는 Token에서 "하둡"뿐만이 아니라 
"하둡"+"하둡을"Token이 추출되는 것 입니다.

이는 나중에 사용자가 "하둡을 사용하는 방법"이라고 검색을 했다고 하면
기본적으로 사용자가 입력한 키워드에 더 많은 가중치를 두어서
검색 결과의 우선순위를 높이는데 사용하기 위함입니다.

"하둡을" * BOOST! + "하둡" + "사용하는" + "방법" 와 같은 형태의 쿼리를 사용 할 수도 있을 것 입니다.

또한 위 로직에서는 빠져있지만 추출된 명사의 OFFSET을 제대로 계산하여
Attribute에 설정해주어야 합니다. 나중에 매치된 키워드에 하이라이팅을 해주기 위해서는
OFFSET정보가 필요하기 때문입니다.

이렇게해서 루씬을 사용한 개발에서 가장 중요하고 어려운 부분 중 하나인
Analyzer에 대한 이야기를 우선 마칠 수 있을 것 같습니다. 어렵고 중요한 부분인데 설명이나 코드의 부족함이
많았던 것 같아서 아쉬움이 남기도 하고 걱정도 됩니다.

공유해드린 소스를 사용하여 이런저런 테스트를 해보신다면
더 쉽게 Analyzer에 접근 하실 수 있을 것이라 생각됩니다.

이후에는 색인과 검색, 그리고 쿼리등에 대해서 간단한 코드를 사용하여
설명을 드리려고 합니다.

아직 코드를 작성해두지 않아 조금 시간이 걸릴지도 모르겠네요..^^
 
https://github.com/need4spd/aboutLucene
에서 체크아웃 받으 실 수 있습니다.