What is Lucene

Jakarta Lucene은 고성능의 확장 가능한 검색 엔진 기술로서 완전히 자바로 작성되었다. Lucene API는 인덱스 구성에 관한 기능과 검색 기능으로 구성되어 있다. Jakarta Lucene은 텍스트 검색 엔진을 필요로 하는 모든 어플리케이션에 적합한 기술이다.

Lucene의 기본적인 용어와 설명

용어 설명
indexing 인덱싱이란 인덱스를 생성하는 처리 과정이다. 인덱스는 컴파일된 문서들의 버전을 포함하고 있는 특별한 데이터베이스이며, 특정 단어들(term)들을 포함하고 있는 문서 목록을 빠르게 찾을 수 있도록 최적화 되어 있다. 대개 이러한 인덱스들은 선택된 한 디렉토리에 Lucene이 생성한 일련의 파일들에 저장된다.
analyzer Analyzer는 인덱싱 동안 문서의 내용을 어떻게 term(단어들)들로 쪼개야 할지에 대해 제어하는 클래스이다. 예를 들면, 어떤 한 analyzer는 "The ill dogs"라는 텍스트를 소문자로 바꾸어 "the", "ill, dogs"의 형태로 쪼개는 반면, 다른 한 analyzer는 소문자로 변환하고 복수형태는 단수 형태로 정규화 하며 일반적인 단어인 "the"는 제거한 형태인 "ill", "dog"라고 쪼갤 수도 있다. 어떤 analyzer를 사용할지 선택하는 것이 중요하다.
searching 이는 원하는 내용을 포함하고 있는 문서의 일부나 몇몇 지정한 사항들과 일치하는 문서의 속성들을 찾는 과정(operation)이다. search 연산은 미리 컴파일된 문서들을 포함하고 있는 특수한 데이터인 '인덱스'에 대해 수행된다. 인덱스 데이터베이스는 "인덱싱" 과정 동안 생성된다.
query 일반적으로 query는 문서를 선택하기 위한 criteria를 지정하며, searching은 문서 인덱스와 query를 비교하고 query와 일치하는 것들을 찾음으로써 이루어진다. query의 결과로 해당 criteria와 일치하는 문서들의 목록(hits)을 얻게 된다.
document Lucene에서 사용하는 문서를 나타낸다. 문서를 나타내기 위해 Document라는 클래스가 제공된다. 이 클래스의 인스턴스는 인덱싱 과정 동안은 인덱스에 추가될 문서들의 정보를 나타내며 검색 동안은 검색의 명중율(hits)을 나타낸다.
field 문서와 hit 정보는 Document 클래스에 필드들의 목록으로 표현된다. 각각의 필드는 이름(String)과 값(문자열이 추출될 수 있는 String이나 Reader)를 포함하고 있다. 또한 각각의 필드는 세 가지 boolean 속성을 갖는데, 이들의 조합은 인덱싱 동안 필드가 사용되는 방법을 지정한다. 이들 속성은 isIndexed, isStored, isTokenized이다.
hit 이것은 query의 결과이며, 지정된 query와 일치하는 문서들의 목록을 말한다. hit 목록은 일반적으로 ranking이나 scoring라 불리는 몇몇 관련 정도의 단위로 정렬된다. hit 목록에는 질의와 일치하는 문서들(매우 높은 score를 가진 문서들)로 이루어진 것들 중의 일부분만을 포함할 수도 한다.
terms 일반적으로 terms는 단어(words)라고 생각할 수 있다. 예를 들면, 문자열 "yadda yadda yadda"는 세 개의 terms를 포함하고 있으며, 이들 각각의 terms는 "yadda"라는 값을 가지고 있다. Lucene이나 대부분의 검색 엔진에서 terms는 인덱싱과 검색을 위한 기본적인 단위이다. terms 값들은 대소문자를 구별한다. 영어나 라틴 언어가 아닌 다른 언어의 텍스트를 terms들로 쪼갤 필요가 있는 경우 규칙들을 커스터마이징 할 수 있다.
tokenizing 문자열을 terms들로 쪼개는 과정. tokenizing은 Analyzer 객체를 사용해 생성되는 TokenStream의 인스턴스들에 의해 수행된다. 만약 한 문서의 필드가 인덱싱 과정 동안 "tokenize하지 않도록" 지정되었다면, 필드의 전체 내용이 단일 term으로 간주된다(URL인 경우 유용).
term query Lucene에서의 매우 단순한 질의이며 한 단어와 비교하는 경우에 사용된다. TermQuery 클래스로 표현되며 term과 필드 이름을 포함하고 있다.
boost factor query 구조와 일치하는 문서의 랭킹을 증가시키거나 감소시키는데 사용될 수 있는 factor.
phrase query 필드에서 연속된 일련의 terms들에 대해 일치하는지를 비교하는 query를 나타낸다. 예를 들면, "winding road"는 "winding road"와는 일치해야 하지만 "road winding"와는 일치하지 않는다. PhraseQuery 클래스 인스턴스로 표현된다. 이 인스턴스는 일치하는 terms들을 표현하는 정렬된 Term 객체들로 이루어진 리스트를 포함한다.
boolean query "AND", "OR", "NOT"과 같은 규칙들을 사용한 복합 질의를 나타낸다. BooleanQuery 클래스의 인스턴스로 표현된다. 각각의 객체는 BooleanClause라는 어댑터 클래스의 인스턴스를 사용해 연결된 하위 질의들의 목록을 포함하고 있다.
filtering hit 목록에 대해 추가적인 제한을 가해 검색 결과에 영향을 주는 것을 말한다.
 
 
 
 

Lucene의 설치

다운로드 받은 Lucene의 압축을 적절한 디렉토리에 푼 후, lucene-1.4-final.jar를 클래스패스에 추가하면 Lucene의 설치는 끝난다.

주요 클래스 설명

  • 인덱싱 관련 클래스
클래스 이름 설명
IndexWriter 인덱스를 만들고 유지한다. 생성자의 인자들은 위에서 설명한 것과 동일하다. Document들을 추가하는 작업이 종료된 후에는 반드시 이 객체의 close 메소드를 통해 닫아주어야 한다. 만약 잠시 동안 추가될 Document들이 더 이상 없고 검색 성능을 높이기 위한 최적화를 수행하고자 할 경우, close 메소드 호출 전에 optimize 메소드를 호출할 수도 있다.
Analyzer 텍스트를 분석하는 TokenStream들을 만들어준다. 즉, 문서를 인덱싱 하거나 검새갈 때 핵심이 되는 요소로서, 텍스트를 파싱 할 때 사용된다.
StandardAnalyzer 추상 클래스 Analyzer를 구현한 클래스이다. 아스키 코드, 라틴권 문자와 언어 문법을 기반으로 한 analyzer이다. stop word에 대해서는 인덱싱 처리를 하지 않는다.
WhitespaceAnalyzer 추상 클래스 Analyzer를 구현한 클래스이다. 공백 문자를 기준으로 인덱싱 작업을 한다.
SimpleAnalyzer 공백과 하이픈을 사용해 단어를 구분하여 인덱싱 작업을 한다.
Document Document는 한 문서에 대한 정보를 담고 있다. Document는 인덱싱과 검색의 단위이며, 필드들로 이루어져 있다. 각각의 필드는 이름과 텍스트 값을 가지고 있다. 대개 Document는 하나 이상의 stored 필드들을 갖는다.
Field 필드는 Document의 일부를 구성한다. 각각의 필드는 이름과 값이라는 두 가지 부분을 갖는다. 값들은 String이나 Reader로 제공되는 형태의 텍스트나 atomic한 keyword들일 수 있다. 필드들은 선택적으로 인덱스에 저장되어 검색된 문서와 함께 반환될 수 있다. Field에는 Keyword, UnIndexed, UnStored, Text라는 네 가지 타입이 있다.
Field.Text 텍스트 데이터를 토큰으로 구분하여 인덱싱 작업을 수행하며, 그 값을 인덱스에 저장한다.
Field.Keyword 텍스트 데이터를 토큰으로 나누지 않는다. 인덱싱 작업을 처리하며 텍스트 데이터 그대로 인덱스에 저장된다.
Field.UnIndexed 텍스트 데이터를 토큰으로 분리하지 않고, 인덱싱 작업도 처리하지 않는다. 텍스트 데이터는 그대로 저장한다.
Field.UnStored 텍스트 데이터를 토큰으로 분리하고 인덱싱 작업도 처리하지만, 텍스트 데이터는 저장하지 않는다.

  • 검색 관련 클래스
클래스 이름 설명
Searcher 검색 구현을 위한 abstract base 클래스이다. 몇몇 일반적인 유틸리티 메소드들을 구현하고 있다.
IndexSearcher 단일 IndexReader에 대한 검색을 구현한 클래스이다. 어플리케이션에서는 상속된 Searcher.search(Query)나 Searcher.search(Query, Filter) 메소드를 호출한다.
QueryParser 이 클래스는 JavaCC에 의해 생성된다. 클라이언트에서는 parse() 메소드를 호출하게 된다. 이 메소드는 질의 문자열을 파싱 하여 Query를 반환한다.
Query 질의를 위한 abstract base 클래스이다.
Hits 순위가 매겨진 Document들의 목록이며, 검색 결과를 저장하고 유지하는데 사용된다.






Query Syntax

Terms
질의는 term과 연산자(operator)로 나뉜다. term에는 Single terms라는 것과 Phrases라는 두 가지 타입이 term이 존재한다. Single Term은 "test"나 "hello"와 같이 한 단어로 이루어진다. Phrase는 "hello dolly"와 같이 인용 부호로 감싸여진 여러 단어로 이루어진다. 여러 term들은 보다 복잡한 질의를 나타내기 위해 Boolean 연산자와 함께 조합될 수 있다.

Fields
Lucene은 필드화 된 데이터를 지원한다. 검색을 수행할 때, 필드를 지정하거나 디폴트 필드를 사용할 수 있다. 필드 이름들과 디폴트 필드는 구현에 종속적이다. 필드 이름 다음에 콜론(:)을 적고 찾으려는 term을 적어 넣음으로써 어떠한 필드라도 검색할 수 있다.

예를 들면, Lucene 인덱스가 title과 text라는 두 개의 필드를 포함하고 있다고 가정해보자. 이 때 text는 디폴트 필드라고 가정한다. 만약 "don't go this way"라는 text를 포함하며 "The Right Way"라는 title을 갖는 문서를 검색하고자 한다면, 다음 둘 중 한 방식으로 입력하면 된다.

title:"The Right Way" AND text:go 또는 title:"Do it right" AND right 

text는 디폴트 필드이기 때문에 필드 지시어(indicator)는 반드시 입력될 필요가 없다.

필드는 바로 뒤에 따르는 term에 대해서만 유효하기 때문에, title:Do it right라고 질의를 입력하는 경우, 이는 title 필드에서 "Do"만을 찾게 된다. 나머지 "it"과 "right"는 디폴트 필드에서 찾게 된다.

Term Modifiers
Lucene은 보다 광범위한 검색 옵션을 제공하고 있다.

와일드카드 검색

Lucene은 단일 문자 와일드카드 검색과 다수 문자 와일드카드 검색을 지원하고 있다. 단일 문자 와일드카드 검색에서는 "?" 기호를 사용하며, 다수 문자 와일드카드 검색에서는 "*" 기호를 사용한다.

단일 문자 와일드카드 검색의 예: "text"나 "test"를 검색하려면, te?t의 형태로 검색할 수 있다. 다수 문자 와일드카드 검색의 예: "test", "tests", "tester"를 검색하려 한다면, test* 형태로 검색한다. 

와일드카드는 te*t의 형태와 같이 term의 중간에 사용할 수도 있다. 그러나, *나 ? 기호는 검색 첫 글자의 위치에 사용할 수 없다.


Fuzzy 검색

Lucene은 Levenshtein Distance 알고리즘이나 Edit Distance 알고리즘에 기반하여 fuzzy 검색을 지원한다. fuzzy 검색을 수행하려면, Single word Term의 뒤에 틸드 기호인 "~"를 사용한다. 예를 들어, "roam"과 유사한 스펠링을 가진 term을 검색하려는 경우, roam~ 과 같은 형태를 사용한다. 이렇게 검색하게 되면 foam, roams와 같은 term들을 검색하게 된다.


Proximity 검색

Lucene supports finding words are a within a specific distance away. proximity 검색을 수행하려면 Phrase의 뒤에 "~" 기호를 사용한다. 예를 들어, 한 문서 안에서 각각 10 단어 내에서 "apache"와 "jakarta"를 검색하려 한다면 "jakarta apache"~10과 같은 형태로 검색을 수행하면 된다.

Range 검색

Range 질의를 이용하면 필드의 값이 질의에 지정된 범위 내에 있는 값들과 일치하는 문서들을 찾아낼 수 있다. Range 질의에서는 최소값과 최대값을 포함하거나 제외한 검색 결과를 얻을 수 있다. 정렬은 사전 순서로 이루어진다.

 mod_date:[20020101 TO 20030101] 

이것은 20020101과 20030101을 포함하여, 이 범위 안에 있는 값들을 가진 mod_date 필드들을 포함하고 있는 문서를 검색하게 된다. Range 질의는 날짜 필드 검색에만 국한되지 않는다. 다음과 같이 날짜가 아닌 필드들에 대해서도 가능하다.

title:{Aida TO Carmen} 

이것은 Aida와 Carmen 범위 내에 있는 title을 갖는 문서들을 찾는다. 이 때, Aida와 Carmen은 포함되지 않는다.

최소값과 최대값을 포함한 Range 검색은 [ ] 기호를 사용하며, 포함하지 않는 Range 검색은  { } 기호를 사용한다.


Boosting a Term

Lucene은 발견된 term들을 기반으로 문서가 일치하는 정도를 판단하는 기능을 제공한다. term을 boost하려면, 검색하려는 term의 끝에 boost factor (숫자)와 함께 캐럿 기호 "^"를 사용한다. boost factor가 높을수록, term과의 관련성이 더 높아진다.

Boosting을 사용하여 문서의 term을 boosting함으로써, 문서의 관련성을 제어할 수 있다. 예를 들어, jakarta apache를 검색하려 하는데, "jakarta"라는 term이 보다 더 중점을 두어 검색하려 한다면, jakarta4 apache와 같은 형태로 term 다음에 기호와 함께 boost factor를 사용하면 된다.

이는 jakarta라는 term을 가진 문서가 보다 더 연관성이 높은 것으로 만들어준다. "jakarta apache"^4 "jakarta lucene"과 같이 Phrase term에 대해서도 boost할 수 있다.

디폴트로 boost factor는 1이다. boost factor는 반드시 양수여야 하지만, 0.2와 같이 1보다 작을 수는 있다.

Boolean 연산자
Boolean 연산자를 사용하면 논리 연산자를 통해 term들을 조합할 수 있다. Lucene은 AND, "+", OR, NOT, "-"을 Boolean 연산자로 지원한다. Boolean 연산자들은 반드시 대문자여야 한다.

OR
OR 연산자는 디폴트 결합 연산자이다. 즉, 두 term 사이에 어떠한 Boolean 연산자도 존재하지 않는다면, OR 연산자가 사용된다. OR 연산자는 두 term들을 연결하여, 이 term들 중 하나라도 문서에 존재하는지에 대해 검색한다. 이는 합집합 연산과 동일하다. OR라는 단어 대신 기호 ||를 사용할 수도 있다.

"jakarta apache"나 "jakarta"만을 포함하고 있는 문서들을 검색하려면, "jakarta apache" jakarta나 "jakarta apache" OR jakarta와 같은 형태의 질의를 사용하면 된다.

AND
AND 연산자는 두 term을 모두 갖는 문서들을 검색한다. 이는 교집합 연산과 동일하다. AND라는 단어 대신 기호 &&를 사용할 수도 있다.

"jakarta apache"와 "jakarta lucene"을 포함하고 있는 문서들을 검색하려면, "jakarta apache" AND "jakarta lucene"과 같은 형태의 질의를 사용하면 된다.

+
"+"가 표시된 요소는 반드시 포함되어 있어야 한다는 것을 의미한다. "jakarta"는 반드시 포함해야 하며 "lucene"은 포함될 수도 있는 문서들을 검색하려면, +jakarta apache와 같은 형태의 질의를 사용하면 된다.

NOT
NOT 연산자는 NOT 다음에 나온 term을 포함한 문서들은 배제한다. 이는 차집합 연산과 동일하다. NOT 단어 대신 ! 기호를 사용할 수 있다.

"jakarta apache"는 포함하고 있지만 "jakarta lucene"은 포함하지 않는 문서들을 검색하려면, "jakarta apache" NOT "jakarta lucene"과 같은 형태의 질의를 사용하면 된다.

NOT 연산자는 한 term에만 사용될 수 없다. 예를 들면, NOT "jakarta apache"와 같이 사용할 수 없으며, 이는 어떠한 결과도 반환하지 않을 것이다.

-
"-" 연산자는 "-" 기호 다음에 있는 term을 포함하고 있는 문서들은 제외한다. "jakarta apache"는 포함하지만 "jakarta lucene"은 포함하지 않는 문서들을 검색하려면 "jakarta apache" -"jakarta lucene"과 같은 형태의 질의를 사용하면 된다.

Grouping
Lucene에서는 서브 질의를 형성하기 위해 괄호를 사용하여 절들을 묶을 수 있다. 이는 boolean 로직을 제어하려는 경우 유용할 수 있다.

"jakarta" OR "apache" AND "website"를 검색하려면 (jakarta OR apache) AND website라는 형태의 질의를 사용하면 된다.

Field Grouping
Lucene에서는 여러 절들을 하나의 필드로 묶는데 괄호를 사용할 수 있다.

"return"이라는 단어와 "pink panther"이라는 phrase를 모두 포함하고 있는 title을 찾으려면, title:(+return +"pink panther")와 같은 질의를 사용하면 된다.

Escaping Special Characters
Lucene에서는 질의에 특수 문자를 escaping하는 기능을 지원해준다. 이들 특수 문자들은 다음과 같다.

+ - && || ! ( ) { } [ ] ^ " ~ * ? :

이들 특수 문자들을 escape 하려면, 문자 앞에 를 붙인다. 예를 들면, (1+1):2를 검색하려면 (1+1):2와 같은 질의를 사용하면 된다.

관련 아티클 및 자료

Lucene Home http://jakarta.apache.org/lucene/
Lucene FAQ http://lucene.sourceforge.net/cgi-bin/faq/faqmanager.cgi
Lucene Getting Started http://jakarta.apache.org/lucene/docs/gettingstarted.html
고수로 가는 지름길 Jakarta Project 가메출판사 최범균 저
QueryParser Rules http://today.java.net/pub/a/today/2003/11/07/QueryParserRules.html
Give your Web site its own search engine using Lucene http://builder.com.com/5100-6389-5054799.html
Lucene Intro http://today.java.net/pub/a/today/2003/07/30/LuceneIntro.html
Parsing, indexing, and searching XML with Digester and Lucene http://www-106.ibm.com/developerworks/library/j-lucene/
Advanced Text Indexing with Lucene http://www.onjava.com/pub/a/onjava/2003/03/05/lucene.html
Introduction to Text Indexing with Apache Jakarta Lucene http://www.onjava.com/pub/a/onjava/2003/01/15/lucene.html
The Lucene search engine Powerful flexible and free http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-lucene.html

다운로드

Lucene 바이너리 다운로드 http://jakarta.apache.org/site/binindex.cgi
Lucene 소스 다운로드 http://jakarta.apache.org/site/sourceindex.cgi






Lucene 데모 실행

Lucene 배포본을 다운로드 받으면 그 안에 데모가 들어 있다. 여기에서는 이 데모를 실행하는 방법에 대해 살펴보도록 하겠다.

1. 우선 Lucene의 데모를 실행하기 위해서는 압축이 풀린 디렉토리에 있는 lucene-1.4-final.jar 파일과 lucene-demos-1.4-final.jar 파일이 클래스패스에 잡혀 있어야 한다.

2. 인덱스를 생성한다. 데모의 인덱스를 구성하는 어플리케이션의 이름은 IndexFiles로서, 이는 org.apache.lucene.demo 패키지에 들어 있다. 다음 명령을 실행하게 되면 Lucene 배포본의 src라는 하위 디렉토리에 있는 Lucene에 대한 모든 소스 코드를 대상으로 인덱스를 구성하게 된다.
첫 번째 인자는 <Lucene 배포본의 압축을 푼 위치>src의 형태를 취한다.

java org.apache.lucene.demo.IndexFiles C:ApacheJakartalucene-1.4-finalsrc 

다음은 명령 프롬프트에서 클래스패스를 잡고 위 명령어를 실행한 결과 화면이다.




 
 
3. 위 과정이 성공적으로 이루어졌다면 인덱스 생성은 끝나게 된다. 위 예제를 실행 시킨 후 탐색기로 살펴보면 다음 화면과 같은 디렉토리와 파일이 생성된다.
 



 
 
4. 데모에서 생성된 인덱스를 검색하는 클래스는 SearchFiles로, org.apache.lucene.demo 패키지에 들어 있다.

java org.apache.lucene.demo.SearchFiles 

위 명령을 실행하게 되면 질의를 입력하도록 요청 받을 것이다. 여기에서 찾으려는 단어를 입력하고 엔터키를 누르면 검색된 결과를 보여준다. 결과 화면은 10개 단위로 보여주며, 결과를 보여준 후 다음 10개 항목을 볼 것인지 아닌지를 선택할 수 있다. 다음은 java와 vector에 대한 질의 실행 결과를 나타낸 화면이다.

 
 
 
 
 
 
 
 
 

Lucene 데모 소스 코드에 대한 설명

소스 코드의 위치
Lucene의 압축을 풀어 생성된 디렉토리에서 "src"라는 디렉토리에 "demo"라는 디렉토리가 있다. 이 디렉토리는 모든 Lucene 데모에 대한 루트 디렉토리이다. 모든 자바 소스는 이 디렉토리 아래의 org/apache/lucene/demo에 위치해 있다.

Indexing Files
앞에서 말한 디렉토리에 보면 인덱스를 생성하는데 사용된 IndexFiles.java를 볼 수 있을 것이다. 이것이 어떻게 동작하는지 살펴보도록 하자.

이 소스 코드에 있는 main 함수는 가장 먼저 IndexWriter의 인스턴스를 생성한다. 이 인스턴스는 "index"라는 문자열과 "StandardAnalyzer"라는 클래스의 새로운 인스턴스를 넘겨 받는다. "index"는 모든 인덱스 정보가 저장될 디렉토리 이름이다. 이 예제에서는 어떠한 경로 정보도 지정하지 않고 있기 때문에, 현재 디렉토리의 하위 디렉토리에 생성하는 것을 가정하고 있다.

IndexWriter는 인덱스들을 생성하는 역할을 담당하는 클래스이다. 이를 사용하려면, 이 클래스의 인스턴스를 생성할 때, 인덱스를 작성할 수 있는 경로 정보를 넘겨주어야한다. 이 예제의 경우, 이 경로에 인덱스가 존재하지 않는다면, 이를 생성하게 된다. 만약 해당 경로에 인덱스가 존재한다면, 그 인덱스를 갱신하게 된다. 또한, IndexWriter의 인스턴스를 생성할 때 org.apache.analysis.Analyzer의 인스턴스도 넘겨주어야 한다. 다음 코드는 이러한 작업을 수행한다.

IndexWriter writer = new IndexWriter("index", new StandardAnalyzer(), true); 

이 예제에서 사용한 Analyzer인 Stop Analyzer는 모든 문자열들을 소문자로 만들고 인덱스로부터 쓸모 없는(useless) 단어들을 걸러낸다. 여기에서 쓸모 없는 단어란, a, an, the와 같은 관사들과 검색에 필요없는 일반적인 단어들을 의미한다. 각각의 언어마다 서로 다른 규칙들이 존재하고 있다는 사실에 주의하며, 이들 각각의 언어에 적절한 analyzer를 사용해야 한다. Lucene는 현재 영어와 독일어를 위한 Analyzer들을 지원하고 있다.
(한국어, 중국어, 일본어와 같은 아시아권 언어에 대한 analyzer의 경우, Jakarta Lucene의 Sandbox에서 제공되는 analyzer를 사용하면 된다고 한다)

IndexFiles 소스 파일의 아래에 보면 indexDocs()라는 메소드가 있다. 이 메소드는 재귀적인 함수로, 지정한 하위 디렉토리를 돌아다니면서 FileDocument를 사용하여 Document 객체들을 생성한다. Document는 파일의 생성 일자, 위치 및 그 내용을 표현해주는 간단한 데이터 객체이다. 이들 Document의 인스턴스들은 IndexWriter에 추가된다. 다음은 indexDocs() 메소드에 대한 소스 코드이다.

public static void indexDocs(IndexWriter writer, File file) throws IOException { 
if (file.canRead()) {
if (file.isDirectory()) {
String[] files = file.list();
if (files != null) {
for (int i = 0; i < files.length; i++) {
indexDocs(writer, new File(file, files[i]));
}
}
} else {
System.out.println("adding " + file);
try {
writer.addDocument(FileDocument.Document(file));
} catch (FileNotFoundException fnfe) {
}
}
}
}

indexDocs()는 인자로 받은 File을 읽을 수 있는지 판별하고, 이것이 디렉토리인지 파일인지 검사한다. 만약 디렉토리라면 그 하위 디렉토리를 재귀적으로 검사해 나가며, 파일인 경우 IndexWriter의 addDocument() 메소드를 통해 인덱스에 Document를 추가한다. 이 때, addDocument() 메소드는 인자로 Document를 받는데, 이 예제에서는 FileDocument라는 유틸리티 클래스를 사용하고 있다. 이 유틸리티 클래스는 File로부터 Lucene Document 객체를 만들어준다.

FileDocument는 File 객체에 들어 있는 정보를 바탕으로 Document 객체를 만들어준다. Document 객체는 세 개의 필드로 File에 대한 정보를 표현한다. 이들 필드들은 다음과 같다.
  • path: 파일에 대한 경로명. 텍스트 값 저장(Stored), 토큰 처리(Tokenized) 필드이다.
  • modified: 파일의 최종 수정일. org.apache.jakarta.lucene.DateField에 의해 인코딩 된 Keyword 필드이다.
  • contents: 파일의 내용. Reader 필드이다.

Searching Files
SearchFiles는 검색을 수행한다. 이 클래스는 IndexSearcher, StandardAnalyzer, QueryParser와 상호 작용한다.
이 클래스의 main() 메소드에서는 먼저 검색을 수행하는 Searcher의 객체를 생성하고 질의를 분석하는데 사용될 Analyzer 객체를 생성한다. 다음은 이를 수행하는 코드이다.

Searcher searcher = new IndexSearcher("index"); 
Analyzer analyzer = new StandardAnalyzer();

query parser는 Index를 해석할 때 사용했던 analyzer와 동일한 analyzer를 사용한다. 다음 코드는 이러한 작업을 수행한다.

Query query = QueryParser.parse(line, "contents", analyzer); 

Query 객체는 QueryParser로부터의 결과를 포함하고 있는데, 이 Query 객체는 searcher로 넘겨진다. searcher의 결과들은 "Hits"라 불리는 Document들의 컬렉션에 담겨져 반환된다. Query를 지정하기 위한 syntax는 매우 다양하다. 이들에 대한 내용은 아래에서 간단하게 다룰 것이다.

Hits hits = searcher.search(query); 

그런 후, 이들 컬렉션을 순환하면서 그 안의 내용을 사용자에게 보여준다.

for (int start = 0; start < hits.length(); start += HITS_PER_PAGE) { 
int end = Math.min(hits.length(), start + HITS_PER_PAGE);
for (int i = start; i < end; i++) {
Document doc = hits.doc(i);

String path = doc.get("path");
if (path != null) {
System.out.println(i + ". " + path);
} else {
String url = doc.get("url");
if (url != null) {
System.out.println(i + ". " + url);
System.out.println(" - " + doc.get("title"));
} else {
System.out.println(i + ". " + "No path nor URL for this document");
}
}
}

if (hits.length() > end) {
System.out.print("more (y/n) ? ");
line = in.readLine();
if (line.length() == 0 || line.charAt(0) == 'n')
break;
}
}

요약

다음은 일반적으로 Lucene을 standalone 형태의 어플리케이션에서 사용하는 절차를 간단하게 정리한 것이다.

인덱스 만들기
인덱스를 만드는 절차는 다음과 같다.
  • IndexWriter 객체를 생성한다.
이 객체를 생성할 때는 인자로 세 개의 정보를 넘겨준다. 우선 첫 번째 인자는 인덱스를 생성할 경로를 설정한다. 두 번째 인자는 사용할 Analyzer의 인스턴스이다. Analyzer는 Lucene에서 다수 구현하여 제공하고 있다. 세 번째 인자는 기존의 인덱스가 존재한다면 갱신할 것인지, 아니면 다시 새로 만들 것인지에 대해 지정한다. 자세한 내용은 Lucene API 문서를 찾아보길 바란다.
  • 인덱싱 작업의 대상이 되는 경로로부터 디렉토리와 파일 정보를 읽어 들여, 디렉토리인 경우는 재귀적인 호출을 통해 하위 디렉토리까지 검색하도록 하며, 파일인 경우는 필요한 정보를 추출한 후, org.apache.lucene.Document 형태로 구성하고, IndexWriter의 addDocument() 메소드를 통해 이를 추가한다.

검색하기
  • Searcher 객체를 생성한다. 이 객체는 인덱스가 생성되어 있는 경로를 인자로 받아 추후 검색을 수행한다.
  • 분석에 사용할 Analyzer를 생성한다. 이 Analyzer는 인덱스를 생성할 때 사용했던 Analyzer의 종류와 같은 것이어야 한다. 만약 다른 것을 사용한다면 잘못된 검색 결과를 얻을 수 있다.
  • QueryParser의 parse() 메소드를 이용하여 Query 객체를 생성한다. parse() 메소드는 검색어, 인덱스 생성 시 작성했던 필드 명과 위에서 생성한 analyzer 객체를 인자로 받아 인덱스 정보를 검색한다.
  • QueryParser를 통해 얻어진 Query 객체로부터, 검색 결과를 담고 있는 Hits를 얻어온 후, Hits가 담고 있는 내용들을 보여준다.




예제 실행을 위한 절차

Indexing Files
이 단계에서는 웹 어플리케이션 예제에서 필요한 인덱스를 작성하게 된다. 우선은 lucene-1.4-final.jar 파일과 lucene-demos-1.4-final.jar 파일이 클래스패스에 잡혀 있어야 한다. 명령 프롬프트에서 {tomcat-home}/webapps의 하위 디렉토리 중 아무 곳으로나 이동하여 다음 명령어를 실행한다.

java org.apache.lucene.demo.IndexHTML -create -index C:ApacheJakartaTomcat-5.0luceneIndex .. 

이 명령어에서 뒤의 ..을 반드시 입력해야 한다. 그렇지 않으면 예외가 발생한다. 세번째 인자는 Tomcat이 읽고 기록할 수 있지만, 웹을 통해서는 접근이 불가능한 디렉토리이다. 이 디렉토리에는 생성된 인덱스가 저장된다. 데모 웹 어플리케이션에서는 이 위치에서 인덱스를 찾도록 설정하고 있다. 이를 실행하게 되면 톰캣의 webapps 디렉토리의 하위에 있는 모든 디렉토리에 존재하는 문서들에 대한 정보를 담고 있는 인덱스를 생성하여 지정된 인덱스 디렉토리에 작성하게 된다. 여기에서는 luceneIndex가 될 것이다.

다음은 이 명령을 실행한 결과와 앞에서 지정했던 인덱스 디렉토리의 모습을 담은 화면이다.

 




 
Deploying the Demos
Lucene을 다운로드 받아 압축을 푼 디렉토리에 보면 luceneweb.war라는 war 파일을 볼 수 있을 것이다. 이 파일을 {tomcat-home}/webapps 디렉토리로 복사하고 톰캣을 재시작한다. {tomcat-home}은 톰캣이 설치된 디렉토리이다. 이 과정을 마치고 나면 다음처럼 luceneweb이라는 웹 어플리케이션이 생성된 것을 볼 수 있을 것이다.
 


 
 
Configuration
톰캣 디렉토리에서 webapps/luceneweb 하위 디렉토리로 이동한다. 만약 존재하지 않는다면, 브라우저의 주소 창에서 "http://localhost:8080/luceneweb"을 입력하고 이동하면, 이 디렉토리가 생길 것이다.

이동한 디렉토리에 있는 configuration.jsp 파일을 편집한다. 이 파일 안에 보면 indexLocation이라는 String 형 변수가 있다. 이 변수는 인덱스가 생성된 디렉토리의 위치를 저장하게 된다. 이 값이 이전에 지정했던 위치와 동일한지 확인한다.

appTitle과 appFooter String 형 변수도 원하는 값으로 설정할 수 있다. configuration.jsp 설정을 끝마쳤다면 톰캣을 재시작한다.

Running the Demos
브라우저에서 http://localhost:8080/luceneweb 을 입력하고 "test"와 페이지 당 보여질 항목의 개수를 입력한 후 search를 누른다.

이렇게 하면 몇 개의 결과들을 볼 수 있게 될 것이다. 다른 terms들도 검색해 보아라. 설정된 페이지 당 항목의 개수 및 반환된 결과에 따라, 화면 아래 쪽에 "more results>>"라는 링크가 보일 수 있다. 이를 클릭하게 되면 다음 페이지로 이동하게 된다. 만약 인덱스를 열 때 에러가 발생한다면 이는 "configuration.jsp"를 올바르게 설정하지 않았거나, 톰캣에서 접근이 허용되지 않은 디렉토리를 인덱스 디렉토리로 잡았을 가능성이 크다.

다음은 "jsp"라는 단어를 검색한 결과 화면이다.



 

참고 사이트 : http://blog.naver.com/NBlogMain.nhn?blogId=okaydanky&Redirect=Dlog&Qs=%2Fokaydanky%2F5815700&


|

No7Do's Blog is powered by Daum & tistory