루씬(lucene)은 Java로 작성된 검색 엔진 라이브러리입니다. 검색 엔진을 구성하기 위한 기본적인 모듈을 포함하고 있으며 Elasticsearch, Solr 등 현재 여러 서비스에 사용되고 있는 오픈소스 검색 엔진이 루씬을 사용하여 개발되었습니다.
기본적으로 공식 문서에서는 핵심 용어에 대하여 다음과 같이 정의하고 있습니다.
- Index(인덱스)는 여러 documents로 구성되어 있고,
- Document(문서)는 fields의 집합이고,
- Field(필드)는 terms의 집합이고,
- Term(용어)는 byte의 집합이다. (같은 byte로 이루어져 있어도 field가 다르면 서로 다른 term으로 간주함)
인덱스는 실제로 데이터를 저장하고 색인 및 검색을 수행합니다.
1. Inverted Index Structure
인덱스에 다음과 같은 형태의 데이터를 입력하였다고 가정합니다.
ID | Text |
---|---|
doc1 | 우, 이번 주 금요일 |
doc2 | 금요일에 시간 어때요? |
doc3 | 주말까지 기다리긴 힘들어. 시간아 달려라 |
단순히 이런 형태로만 계속 저장한다면 데이터가 많아질수록 검색시 어떤 단어가 어디에 위치해 있는지 찾는데 시간이 점점 오래 걸릴것입니다. 따라서 인덱스는 데이터들을 저장할 때 추가적인 작업을 진행하는데, 문자열을 term 단위로 쪼개어 아래와 같은 역인덱스 구조(inverted index structure)에 term과 그 통계 정보를 저장합니다.
Term | Frequency | Documents |
---|---|---|
이번 | 1 | doc1 |
주 | 1 | doc1 |
금요일 | 2 | doc1, doc2 |
시간 | 2 | doc2, doc3 |
주말 | 1 | doc3 |
역인덱스 구조는 term을 key값으로 잡고 해당 term에 대한 통계 정보를 저장하고 있습니다. 이런 데이터 구조 덕분에 루씬은 term 기준으로 문서를 빠르게 검색할 수 있습니다. 두꺼운 책의 맨 뒤 “찾아보기” 와 비슷한 원리라고 생각하면 좋습니다.
2. Analyzer
Analyzer는 문자열을 검색 가능한 토큰으로 분해하는 형태소 분석을 수행합니다.
형태소 분석 과정에서 색인 및 검색 프로세스에서 사용되는 TokenStream
을 생성합니다.
하나의 tokenizer와 여러 filter로 이루어져 있는데, CharFilter → Tokenizer → TokenFilter 순으로 작업을 진행합니다.
- CharFilter : Tokenize 과정을 진행하기 전에 불필요한 문자들을 제거합니다. 대부분의 Analyzer는 Tokenizer를 처음 순서로 구성하지만, 필요에 따라 그 이전에 CharFilter를 두어 사전에 텍스트를 필터링하는 경우도 있습니다.
- Tokenizer : 입력받은 문자열을 토큰으로 분해합니다.
- TokenFilter : Tokenizer가 분해한 토큰을 추가적으로 조정하는 작업을 하는데 Analyzer의 필수 요소는 아닙니다. 필요한 경우 문자 제거, 어간 추출, 동의어 처리, 대소문자 처리 등을 수행합니다.
References