자바 검색엔진 "아파치 루씬(lucene) 8.7.0" 적용기 - 2/2

2021. 1. 22. 23:04Development/[Java] 자바

728x90
1편 - kplog.tistory.com/285

 

지난 시간에 1개의 파일을 읽어서 해당 파일의 특정 단어의 위치에 대한 검색을 빠르게 하는 방법으로 "아파치 루씬" 도입을 고려했다.

테스트를 거듭한 결과, 역시 아파치 루씬의 단어 검색에 성공을 했고, 그 속도에 놀라게 되었다.

역시 코딩은 집에서 편하게 쉬는 시간(?)에 해야 해결이 잘되는 것 같은 것은 진리인가..

 

우선 아파치 루씬을 구현하기 위한 큰 구조는 다음과 같다.

*. 사전 필요 작업 (라이브러리 의존성 추가)

    implementation group: 'org.apache.lucene', name: 'lucene-core', version: '8.7.0'
    implementation group: 'org.apache.lucene', name: 'lucene-analyzers-common', version: '8.7.0'
    implementation group: 'org.apache.lucene', name: 'lucene-codecs', version: '8.7.0'
    implementation group: 'org.apache.lucene', name: 'lucene-queryparser', version: '8.7.0'

 

[ 인덱스 만들기 작업 ]

(1) 인덱싱할 대상 파일을 지정한다.

(2) 대상파일을 인덱싱할 인덱스 파일의 저장 디렉토리를 지정한다.

(3) 인덱스 Writer 작업 (중요) - "IndexWriter"

(4) 인덱싱을 위한 도큐먼트 추가 작업 (라인넘버와 해당 라인의 문자열)

 

[ 인덱스 찾기 작업 ]

(1) 인덱스 파일에서 인덱스 내용을 검색한다  - "IndexReader, IndexSearcher > QueryParser"

 

실제 순서에 따라 구현을 하자면 다음과 같다.

[ 인덱스 만들기 작업 ]

public static void luceneMakeIndex() throws Exception {

		//(1) 인덱싱할 대상 파일을 지정한다.
        File file = new File("sample.txt");
		
        
        //(2) 대상파일을 인덱싱할 인덱스 파일의 저장 디렉토리를 지정한다.
        File fileIndex = new File("sample.txt.index");
        
        //(3) 인덱스 Writer 작업
        FSDirectory dir = FSDirectory.open(Paths.get(fileIndex.toURI()));
        IndexWriterConfig config = new IndexWriterConfig(new StandardAnalyzer());
        IndexWriter writer = new IndexWriter(dir, config);

		//기존 정보 삭제
        writer.deleteAll();

		//파일을 라인 단위로 읽기
        try(LineNumberReader lineNumberReader = new LineNumberReader(Files.newBufferedReader(Paths.get(file.toURI())))) {

            lineNumberReader.lines().forEach(s -> {
                int line = lineNumberReader.getLineNumber();
                try {
                    //(4) 인덱싱을 위한 도큐먼트 추가 작업 (라인넘버와 해당 라인의 문자열)
                    Document document = new Document();
        			document.add(new StringField("id", String.valueOf(line) , Field.Store.YES));
        			document.add(new TextField("body", s , Field.Store.YES));
                    writer.addDocument(document);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });

            writer.commit();
            writer.close();

        }
        catch (IOException | SecurityException e) {
            e.printStackTrace();
        }
}

 

[ 인덱스 찾기 작업 ]

public static void luceneFindIndex() throws Exception {

		//인덱스 디렉토리 위치
        File fileIndex = new File("sample.txt.index");
        
        //인덱스 찾기 이니셜라이징
        Directory dir = FSDirectory.open(Paths.get(fileIndex.toURI()));
        IndexReader reader = DirectoryReader.open(dir);
        IndexSearcher searcher = new IndexSearcher(reader);

        //라인으로 찾아보기
        QueryParser qp = new QueryParser("id", new StandardAnalyzer());
        Query idQuery = qp.parse("1");
        TopDocs foundDocsIdx = searcher.search(idQuery, 10);

		//당연히 라인이니까 1개여야...
        System.out.println("총 찾은 갯수 (라인) :: " + foundDocsIdx.totalHits);

        for (ScoreDoc sd : foundDocsIdx.scoreDocs)
        {
            Document d = searcher.doc(sd.doc);
            //해당 라인의 내용보기
            System.out.println(String.format(d.get("body")));
        }


        //특정 단어로 찾아보기
        QueryParser qp = new QueryParser("body", new StandardAnalyzer());
        Query wordQuery = qp.parse(word);
        TopDocs foundDocsBody = searcher.search(wordQuery, 10);

        System.out.println("총 찾은 갯수 (단어) :: " + foundDocsBody.totalHits);

        for (ScoreDoc sd : foundDocsBody.scoreDocs)
        {
            Document d = searcher.doc(sd.doc);
            //해당 단어의 라인 위치
            System.out.println(String.format(d.get("id")));
        }
    }

 

사실 구현한 소스의 원리만 파악하면 크게 어려울 것이 없다. (함수를 외우는게 어렵지만..)

다시한번 정리하자면 아래의 과정으로 정리할 수 있을 것 같다.

 

인덱스할 파일 지정 > 인덱스 만들기(IndexWriter) > 인덱스 찾기 (IndexReader, IndexSearcher) 만들기 > 찾기 (
QueryParser)

 

Mission Accomplished!

728x90