Apache Lucene 4 (Search Engine)
Apache Lucene 3 (Lucene Document, Field Class)
1. Document, Field 차이
- Document : 색인과 검색 과정에 사용되는 기본 단위로, 하나 이상의 필드로 구성된다. RDB에 비교하면 테이블의 행(ROW) 와 같은 의미
- Field : 도큐먼트의 구성요서로 RDB와 비교하면 Column이라고 할 수 있다.
2. Document Class
Document는 색인 생성과 검색의 기본단위
로 사용자가 찾고자 하는 내용이 들어가 있다.
텍스트 변환 과정에서 파싱된 원본 Document는 모두 루씬이 인식 할 수있게 변환된다.
Document는 담을 수 있는 데이터의 규모가 정해져 있지 않아 Doc 안에 Doc을 넣는 등 다양하게 활용이 가능하나 성능적 측면에서 이슈가 있을 수 있어 설계를 주의해서 해야 한다.
- Document 주요 클래스
- add(IndexableField field) : 도큐먼트에 필드를 추가 한다. 같은 이름 필드 여러개를 추가 할 수 있다.
- get(String name) : 도큐먼트의 필드 값을 String 타입으로 가져온다.
- getField(String name) : 도큐먼트의 필드를 가져온다.
- removeField(String name) : 도큐먼트에서 필드를 제거한다.
- 사용 예시
Document doc = new Document(); doc.add(new StringField("id", userInfo.getId(), Field.Store.YES)); doc.add(new TextField("info", userInfo.getInfo(), Field.Store.YES)); doc.add(new StoredField("age", Integer.parseInt(userInfo.getAge()))); Date modifiedDate = new Date(); doc.add(new StringField("time", DateTools.dateToString(modifiedDate, DateTools.Resolution.DAY), Field.Store.YES)); try { writer.addDocument(doc); } catch (Exception e) { e.printStackTrace(); }
- Field.Store.YES , NO
- 루씬에서 색인된 텍스트의 도큐먼트 전체 복사본을 저장할지 말지에 대한 여부 값을 정하는 것으로
YES
로 지정하면 필드의 전체 텍스트가 저장되고 검색 결과에 함께 반영된다. - 하지만,
NO
로 지정 할 경우 도큐먼트가 색인 과정을 거쳐 검색대상이 되긴 하지만 검색 결과에 반영할 수는 없다.
- 루씬에서 색인된 텍스트의 도큐먼트 전체 복사본을 저장할지 말지에 대한 여부 값을 정하는 것으로
- Document 삭제
- 루씬 2, 3 버전에서는 IndexReader 클래스에서 도큐먼트를 삭제 했으나 의미가 맞지 않아 이후 버전 부터 IndexWriter의 deleteDocument()가 도큐먼트의 삭제를 맡는다.
- 주의 사항으로는 deleteDocument()를 호출해도 바로 반영되는 것이 아닌 flush()가 수행될 때 실 색인에서 Document가 삭제된다.
-
deleteDocument() 로 도큐먼트를 삭제할 때는 검색된 모든 도큐먼트를 삭제하기 때문에 검색식 생성에 유의해야 한다.
try (IndexWriter w = new IndexWriter(index, config)){ System.out.println("Before Delete Document Cnt : " + w.numDocs()); // 수정할 대상을 검색하는 검색 식 쿼리 생성 Query qry = new QueryParser("id", analyzer).parse(userId); w.deleteDocument(qry); System.out.println("Before Flush Document Cnt : " + w.numDocs()); w.flush(); System.out.println("After Flush Document Cnt : " + w.numDocs()); w.close(); }
- Document 수정
- 루씬 2, 3 버전에서는 Update를 따로 지원하지 않아 delete()후 add() 하는 방식으로 진행 했지만, 이후 버전 부터는 IndexWirter의 updateDocument() 클래스를 통해 Document를 업데이트 할 수 있다.
- updateDocument()로 수정할 도큐먼트를 찾고(검색) 삭제한 후 추가하는 식으로 업데이트가 진행된다. 사실상 수정보다는 교체
-
따라서 Flush 이전에는 도큐먼트가 하나 늘어나나 flush 후에는 수정 대상 도큐먼트를 지우기 때문에 기존과 동일 해 짐.
try (IndexWriter w = new IndexWriter(index, config)){ System.out.println("Before Delete Document Cnt : " + w.numDocs()); // 수정할 도큐먼트를 설정한다. Term updateDocumentReview = new Term("id", id); // 수정할 도큐먼트를 매개변수로 받은 도큐먼트로 교체한다. w.updateDocument(updateDocumentReview, document); System.out.println("Before Flush Document Cnt : " + w.numDocs()); w.flush(); System.out.println("After Flush Document Cnt : " + w.numDocs()); w.close(); }
3. Field Class
필드는 도큐먼트의 일부분이다. 색인 하위에 도큐먼트가 있고, 도큐먼트는 여러 필드로 이뤄지고 각 필드의 값에는 텀이 포함된다.
루씬의 도큐먼트는 다양한 타입의 필드를 색인한다. Filed는 IndexableField 인터페이스의 구현체이다.
- Field의 서브 클래스
- Stored Field
- 색인에 필드 값을 저장하기 위한 필드 클래스
- TextFeild : 전체 텍스트를 검색하는데 사용하는 필드로 자바의 String 타입
- StringField : TextField와 마찬가지로 String 타입의 필드로 원본 데이터에서 토큰을 색인하는데 사용한다.
- StoredFiled : 요약결과를 보관하는 필드로, 값만 저장한다.
- RangeFiled
- 범위 검색 쿼리를 위한 필드 클래스로, 검색 결과를 필터링 시 사용한다.
- 예를 들어 숫자에 StoredField를 사용하면 범위 검색 쿼리나 정렬을 사용할 수 없다.
- 범위 검색과 정렬 모두 필요하면 색인 시 같은 이름의 StroedField와 기타 다른 필드를 같이 사용해야 한다.
- IntPoint : 범위 쿼리를 위한 필드 클래스로 Int 타입 색인
- LoingPoint : 범위 쿼리를 위한 필드 클래스로 long 타입 색인
- FloatPoint : 범위 쿼리를 위한 필드 클래스로 float 타입 색인
- DoublePoint : 범위 쿼리를 위한 필드 클래스로 double 타입 색인.
- DocValue(정렬, 분류를 위한 집계성 클래스)
- SortedDocValuesField : 정렬과 분류를 위해 byte[] 배열로 색인한다.
- SotredSetDocValuesField : 정렬과 분류를 위해
SortedSet<byte[]>
으로 색인한다. - NumericDocVlauesField : 정렬과 분류를 위해 long으로 색인한다.
- SortedNumericDocValuesField : 정렬과 분류를 위해 SortedSet으로 색인한다.
- Stored Field
- Field 의 메소드
- Field(String name, byte[] values, IndexableFiledType type) : 바이너리로 필드를 생성하는 생성자.
- Field(String name, Reader reader, IndexableFieldType type) : Reader로 필드를 생성하는 생성자.
- Field(String name, String value, IndexableFieldType type) : String으로 필드를 생성하는 생성자.
- Field(String name, TokenStream tokenStream, IndexableFieldType type) : TockenStream으로 필드를 생성하는 생성자.
- binaryValue() : 필드의 값을 ByteRef로 가져온다.
- fieldType() : 필드의 타입을 가져온다.
- name() : 필드의 이름을 가져온다.
- numericValue() : Numeric 타입의 값을 가져온다.
- readerValue() : Reader 타입의 값을 가져온다.
- StringValue() : String 타입의 값을 가져온다.
-
예시
Document doc = new Document(); doc.add(new StringField("id", info.getId(), Field.Store.YES)); doc.add(new TextField("title", info.getTitle(), Field.Store.YES));
- 여러 Data Type의 Field
- StringField : 가장 기본적인 문자열 타입으로 색인 시에 주로 사용된다. textField와 비슷하게 문자열을 색인하지만, 토큰화 과정이 없다는 것이 가장 큰 차이점이다. 즉,
hello world
를 StringField에 넣게 되면 hello 검색해도 노출이 안되고, hello world 모두 검색해야지만 검색가능하다. 따라서, 아이디 값 조회 처럼 필드 값 전체과 일치해야 하는 경우에 사용한다. - TextField : 텍스트 타입의 필드를 색인하는 클래스로, TextField로 타입을 지정하게 되면 분석기에 의해 토큰화 된다. 예를들어
Hello World
를 색인하면토크나이저
에 의해 Hello, World 로 분리되고,토큰필터
를 통해 토큰화된 단어의 대문자를 소문자로 바꾼다. 이후 도출된 결과를 토대로 역색인 구조에 저장된다. - StoredFiled : 숫자 타입 색인 시 사용, int, float, long, double 형이 있으며
Field f1 = new StoredFiled("age", 10);
과 같이 사용한다. - 날짜 형식 : 루씬이 제공하는 DateTools클래스의 여러 매소드를 사용해 적절히 String 또는 Numeric 타입으로 지정해야 한다.
java Date modifieldDate = new Date(); doc.add(new StringFiled("systemYear", DateTools.dateToString(modifiledDate, DateTools.Resolution.DAY), Field.Store.YES));
- StringField : 가장 기본적인 문자열 타입으로 색인 시에 주로 사용된다. textField와 비슷하게 문자열을 색인하지만, 토큰화 과정이 없다는 것이 가장 큰 차이점이다. 즉,
4. Term Class
텀은 색인의 내부 단어를 의미하는 기본단위 이다. 루씬은 텍스트분석으로 검색을 효과적으로 하고자 텀을 생성하며 텀을 기준으로 역색인을 구성한다.
도큐먼트의 필드에 담긴 값은 색인 과정에서 분석기 유형에 따라 분석되고, 텀으로 구성된다. 일반적으로 사용되는 분석기는 StnadardAnalyzer 이다.
해당 분석기로 Cute top! Love the brand
라는 문장을 분석하면 cute
, top
, love
, brand
와 같이 단어가 분리되고 단어 각각은 하나의 텀으로 저장된다.
색인 시 분석과정에서 필드 값을 가반으로 텀은 생성된다.
텀을 기준으로 도큐먼트를 검색할떄 텀을 직접 생성하기도 한다.
Term updateDocument = new Term("Id", id);
- Term 클래스의 메소드
Term(String fld)
: 필드명으로 빈 값을 가진 텀을 생성한다.Term(String fld, BytesRef bytes)
: 바이너리로 필드에 텀을 생성Term(String fld, String text)
: String으로 필드에 텀을 생성.bytes()
: 해당 텀의 바이너리 값을 가져온다.compareTo(Term other)
: 다른 텀과 값을 비교한다.field()
: 해당 텀의 필드명을 가져온다.text()
: 해당 텀의 String 값을 가져온다.
5. DocValues
루씬은 전문검색의 효율성을 이유로 색인을 역색인 구조로 저장한다. 전문검색 시에는 역색인이 유리하지만, 숫자 범위 검색이나 집계(Aggregation) 의경우 그렇지 않다. 이를 보완하고자 루씬은 숫자나 날짜검색, 집계에 DocValue란 독특한 구조를 사용한다.
- 루씬의 캐시
- 루씬은 다음 JVM의 힙메모리와 DocValues 이 두가지 방법으로 집계(Aggregation) 검색을 한다.
- JVM힙메모리 : 루씬의 집계 방식 중 하나로 JVN 힙메모리는 역색인 구조로 필드를 StoredxFiled로 저장하는 key-value 구조이다. 행 기반의 데이터 구조
- DocValue : 메모리를 효율적으로 사용하고자 JVM의 힙메모리가 아닌 운영체제의 파일시스템 캐시를 사용해 색인 시 디스크를, 검색 시 시스템 캐시를 사용한다. 역색인은 텀과 도큐먼트의 값으로 쿼리하기엔 유리하지만 정렬이나 그룹핑에는 효율적이지 않다.역색인은 세그먼트의 병합이 많을수록 입출력도 비례해 증가해 처리속도가 느려진다. 특히 집계와 정렬은 메모리 내에서 필드 데이터를 처리하는 것 보다 통상 20% 정도 느린 것으로 알려져 있다. DocValues 필드는 OS상에서 루씬에게 할당된 메모리에 직접 메핑되는 디스크 기반의 데이터 구조이다. 즉, 컬럼기반 구조를 채택해 힙 메모리 사용에 영향을 주지 않으면서 힙메모리를 사용하는 것과 같은 성능을 낸다. DocValues 의 필드는 필드 캐시를 사용해 정렬, Facet, 그룹핑을 빠르게 처리한다. DocValues는 압축된 Document 타입의 저장공간을 제공하며, 루씬 내에서 도큐먼트 ID에 기반한 값에 빠르게 접근 가능하다.
-
역색인 구조
텀 DocId(Document Id) banana 4, 8 candy 7 -
DocValues 구조
DocID 텀 1 banana 2 banana 3 cnady