목요일, 3월 28
Shadow

#020 기본 색인 작업

1. 색인에 문서 추가하는 방법을 살펴 보자. Lucene 5.4기준의 예제를 살펴 보자.

package foo.bar;

import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory;

import java.io.IOException;

public class HelloApp {
    public static void main(String[] args) throws IOException, ParseException {
        RAMDirectory ramDirectory = new RAMDirectory();
        IndexWriter indexWriter = new IndexWriter(ramDirectory,
                new IndexWriterConfig(new WhitespaceAnalyzer())
                        .setOpenMode(IndexWriterConfig.OpenMode.CREATE));
        Document doc = new Document();
        doc.add(new TextField("title", "helloworld", Field.Store.YES));
        doc.add(new StringField("content", "1", Field.Store.YES));
        indexWriter.addDocument(doc);
        indexWriter.close();

        IndexReader indexReader = DirectoryReader.open(ramDirectory);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);

        String[] fields = {"title","content"};
        QueryParser parser = new MultiFieldQueryParser(fields,new WhitespaceAnalyzer());
        Query query = parser.parse("helloworld");
        TopDocs topDocs = indexSearcher.search(query, 100000);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for(int i=0;i<scoreDocs.length;i++) {
            Document document = indexSearcher.doc(scoreDocs[i].doc);
            System.out.println(document.getField("title").stringValue());
            System.out.println(document.getField("content").stringValue());
        }
        indexReader.close();

    }
}
1. RamDirectory 인스턴스를 생성한다. Ram에 색인하도록 하는 설정이다. 
2. IndexWriter 인스턴스를 생성한다. indexWriter로 실제 색인을 한다. 
3. Document객체에 여러 종류의 Field를 사용하여 필트를 추가한다. 
4. indexWriter에 생성된 하나의 Row를 저장한다. 
5. 생성된 indexWriter의 인스턴스를 종료한다. 

2. 색인에서 문서를 삭제하는 방법을 살펴 보자. 색인에서 문서를 삭제해야 할 일도 생긴다. 예를 들어 최근 일주일 데이터를 검색하게 제한 할 수도 있다. 아니면 경우에 따라 특정 검색에 질의에 해당하는 모든 문서를 삭제 할 수도 있고, 원본 문서의 내용이 변경되었을때 색인돼 있던 오래된 문서를 제거하고 새로운 내용을 추가해야 할 수도 있다.
색인된 문서를 삭제하는 방법에는 3가지 방법이 있다.

1. 모든 문서를 삭제하는 방법
2. Term을 이용하는 방법
3. Query를 이용하는 방법

첫번째 방식의 모든 문서를 삭제하는 방법에 대해서 알아보자. indexWriter의 deleteAll()을 이용하여 모든 색인된 데이터를 삭제 할 수 있다.

package foo.bar;

import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory;

import java.io.IOException;

public class HelloApp {
    public static void main(String[] args) throws IOException, ParseException {
        RAMDirectory ramDirectory = new RAMDirectory();
        IndexWriter indexWriter = new IndexWriter(ramDirectory,
                new IndexWriterConfig(new WhitespaceAnalyzer())
                        .setOpenMode(IndexWriterConfig.OpenMode.CREATE));
        Document doc = new Document();
        doc.add(new TextField("title", "helloworld", Field.Store.YES));
        doc.add(new StringField("content", "1", Field.Store.YES));
        indexWriter.addDocument(doc);
        indexWriter.close();

        indexWriter = new IndexWriter(ramDirectory,
                new IndexWriterConfig(new WhitespaceAnalyzer())
                        .setOpenMode(IndexWriterConfig.OpenMode.CREATE));
        indexWriter.deleteAll();
        indexWriter.close();

        IndexReader indexReader = DirectoryReader.open(ramDirectory);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        String[] fields = {"title","content"};
        QueryParser parser = new MultiFieldQueryParser(fields,new WhitespaceAnalyzer());
        Query query = parser.parse("helloworld");
        TopDocs topDocs = indexSearcher.search(query, 100000);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for(int i=0;i<scoreDocs.length;i++) {
            Document document = indexSearcher.doc(scoreDocs[i].doc);
            System.out.println(document.getField("title").stringValue());
            System.out.println(document.getField("content").stringValue());
        }
        indexReader.close();

    }
}

두번째 방법은 Term을 이용하여 색인된 파일을 삭제하는 방법이다.
이 메소드는 지정한 텀을 포함하는 모든 문서를 삭제한다.

package foo.bar;

import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory;

import java.io.IOException;

public class HelloApp {
    public static void main(String[] args) throws IOException, ParseException {
        RAMDirectory ramDirectory = new RAMDirectory();
        IndexWriter indexWriter = new IndexWriter(ramDirectory,
                new IndexWriterConfig(new WhitespaceAnalyzer())
                        .setOpenMode(IndexWriterConfig.OpenMode.CREATE));
        Document doc = new Document();
        doc.add(new TextField("title", "hello world", Field.Store.YES));
        doc.add(new StringField("content", "1", Field.Store.YES));
        indexWriter.addDocument(doc);
        indexWriter.close();

       indexWriter = new IndexWriter(ramDirectory,
                new IndexWriterConfig(new WhitespaceAnalyzer())
                        .setOpenMode(IndexWriterConfig.OpenMode.CREATE));
        indexWriter.deleteDocuments(new Term("title", "hello"));
        indexWriter.commit();
        indexWriter.close();

        IndexReader indexReader = DirectoryReader.open(ramDirectory);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        String[] fields = {"title","content"};
        QueryParser parser = new MultiFieldQueryParser(fields,new WhitespaceAnalyzer());
        Query query = parser.parse("hello");
        TopDocs topDocs = indexSearcher.search(query, 100000);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for(int i=0;i<scoreDocs.length;i++) {
            Document document = indexSearcher.doc(scoreDocs[i].doc);
            System.out.println(document.getField("title").stringValue());
            System.out.println(document.getField("content").stringValue());
        }
        indexReader.close();

    }
}

세번째 방법은 Query를 이용하는 방법이다. 이 메소드는 지정한 질의에 해당하는 모든 문서를 삭제한다.

package foo.bar;

import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory;

import java.io.IOException;

public class HelloApp {
    public static void main(String[] args) throws IOException, ParseException {
        RAMDirectory ramDirectory = new RAMDirectory();
        IndexWriter indexWriter = new IndexWriter(ramDirectory,
                new IndexWriterConfig(new WhitespaceAnalyzer())
                        .setOpenMode(IndexWriterConfig.OpenMode.CREATE));
        Document doc = new Document();
        doc.add(new TextField("title", "hello world", Field.Store.YES));
        doc.add(new StringField("content", "1", Field.Store.YES));
        indexWriter.addDocument(doc);
        indexWriter.close();

        indexWriter = new IndexWriter(ramDirectory,
                new IndexWriterConfig(new WhitespaceAnalyzer())
                        .setOpenMode(IndexWriterConfig.OpenMode.CREATE));
        QueryParser qp = new QueryParser( "title", new WhitespaceAnalyzer());
        Query query = qp.parse("hello");
        indexWriter.deleteDocuments(query);
        indexWriter.commit();
        indexWriter.close();
        IndexReader indexReader = DirectoryReader.open(ramDirectory);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        String[] fields = {"title","content"};
        QueryParser parser = new MultiFieldQueryParser(fields,new WhitespaceAnalyzer());
        Query querys = parser.parse("hello");
        TopDocs topDocs = indexSearcher.search(querys, 100000);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for(int i=0;i<scoreDocs.length;i++) {
            Document document = indexSearcher.doc(scoreDocs[i].doc);
            System.out.println(document.getField("title").stringValue());
            System.out.println(document.getField("content").stringValue());
        }
        indexReader.close();
    }
}

3. 마지막으로 색인의 문서 변경 방법에 대해서 알아보자.
검색 애플리케이션에서 문서를 최초에 색인 한 후 사용하다 보면 문서의 내용이 변경되는 경우가 발생하고, 그럴때는 해당문서를 삭제하고 다시 색인해야 한다. 하지만 상황에 따라서 색인된 문서의 특정 필드만 변경하고자 하는 경우가 생긴다. 예를 들어 문서의 본문은 그대로 지만 제목만 변경되는 경우가 있다. 이처럼 문서 내부의 특정 필드만 변경하고자 하는 필요성이 많지만, 루씬에서는 업데이트 기능을 지원하지 않는다 다만 기존해당 문서를 삭제 후 제 추가 하는 방법으로 기존데이터를 변경한다.
update에는 크게 2가지 종류의 메소드가 존재한다.

1. updateDocument(Term term, Iterable doc)
2. updateBinaryDocValue(Term term, String field, BytesRef value)
3. updateDocuments(Query... queries)
4. updateDocValues(Term term, Field... updates)
5. updateNumericDocValuee(Term term, String field, long value)

첫번쨰의 경우 인자로 지정한 Term객체에 해당하는 모든 문서를 삭제하고, 해당 indexWriter에 기본 설정된 분석기를 사용해 지정한 문서를 추가한다.
두번째의 경우는 특정 값에 대한 BinaryDocValues 문서를 업데이트 할때 사용한다. 존재하는 필드를 업데이트 할수 있다.
세번쨰의 경우는 쿼리로 검색된 내용의 도큐먼트를 모두 변경할 수 있다.
네번쨰의 경우는 특정 필드의 해당하는 값의 텀으로 도큐먼트를 업데이트 할수 있다.
다섯번재의 경우는 version의 id(도큐먼트id)로 필드의 내용을 변경 할 수 있다.

첫번쨰 예제만 수록한다.

package foo.bar;

import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory;

import java.io.IOException;

public class HelloApp {
    public static void main(String[] args) throws IOException, ParseException {
        RAMDirectory ramDirectory = new RAMDirectory();
        IndexWriter indexWriter = new IndexWriter(ramDirectory,
                new IndexWriterConfig(new WhitespaceAnalyzer())
                        .setOpenMode(IndexWriterConfig.OpenMode.CREATE));
        Document doc = new Document();
        doc.add(new TextField("title", "hello world", Field.Store.YES));
        doc.add(new StringField("content", "1", Field.Store.YES));
        indexWriter.addDocument(doc);
        indexWriter.close();
                indexWriter = new IndexWriter(ramDirectory,
                new IndexWriterConfig(new WhitespaceAnalyzer())
                        .setOpenMode(IndexWriterConfig.OpenMode.CREATE));
        Document docss = new Document();
        docss.add(new TextField("title", "hello", Field.Store.YES));
        docss.add(new StringField("content", "1", Field.Store.YES));
        indexWriter.updateDocument(new Term("content", "1"), docss);
        indexWriter.commit();
        indexWriter.close();
        IndexReader indexReader = DirectoryReader.open(ramDirectory);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);

        String[] fields = {"title","content"};
        QueryParser parser = new MultiFieldQueryParser(fields,new WhitespaceAnalyzer());
        Query query = parser.parse("hello");
        TopDocs topDocs = indexSearcher.search(query, 100000);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for(int i=0;i

이상 끝!

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.