[JAVA] 루씬 (Lucene 8.0.0)

[JAVA] 루씬 (Lucene 8.0.0)

오늘은 저녁/밤 내내 루씬을 공부했다.

0. 루씬(Lucene) 간략정리

– 강력한 색인과 검색 기능 지원

– 약 3MB 정도의 작은 라이브러리

– 창시자 더그 커팅은 하둡의 창시자이기도 함

– 루씬은 오직 색인과 검색에만 집중

— 루씬의 관심사 : 문서 텍스트 분석, 색인에 문서 추가, 색인, 질의 실행, 검색 질의 생성, 결과 출력

— 루씬이 관심 갖지 않는 것 : 검색 대상, 검색 대상 텍스트 확보, 분석 인터페이스, 관리 인터페이스, 검색 화면 인터페이스

– 루씬에서 발전/파생된 프로젝트에는 다음과 같은 것들이 있음

— 엘라스틱 서치, 솔라, 너치, 하둡

– 루씬 3점대 버전 책을 갖고 있는데, 3점대에서는 첫번째 글자에 와일드 카드(? 또는 *)를 넣을 수 없음.

— 이에 따라 가장 최신 버전인 8.0 버전으로 코드를 재작성함. 확인결과 첫번째 글자에도 와일드 카드 사용 가능.

1. 필요한 라이브러리 : lucene-core-8.0.0.jar

* 라이브러리 다운로드 주소 : https://mvnrepository.com/artifact/org.apache.lucene/lucene-core

2. 테스트 코드
특정 폴더(dataDir)에 txt 파일을 여러 개 넣어놓고, IndexManager.java 의 main 메서드를 실행한다.
그러면 색인 파일들이 결과폴더(indexDir)에 쌓인다.
이어서, SearchManager.java 의 main 메서드를 실행하여 파일 내용을 검색해본다.
2-1. IndexManager

package com.thkmon.lucene;

import java.io.File;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

public class IndexManager {

    public static void main(String[] args) {
        IndexManager indexMng = new IndexManager();
        indexMng.doIndex();
    }
    
    
    /**
     * 색인하기
     */

    public void doIndex() {
        
        // 색인 결과파일 폴더 지정
        String indexDir = “C:\\index”;
        
        // 색인할 txt 파일들이 있는 대상 폴더 지정
        String dataDir = “C:\\개인폴더\\개발작업\\개인사이트제작”;
        
        Directory dir = null;
        IndexWriterConfig config = null;
        IndexWriter indexWriter = null;
        
        try {
            dir = FSDirectory.open(new File(indexDir).toPath());
            config = new IndexWriterConfig(new StandardAnalyzer());
            indexWriter = new IndexWriter(dir, config);
            
            File[] fileArr = new File(dataDir).listFiles();
            int fileCount = fileArr.length;
            
            File file = null;
            for (int i=0; i<fileCount; i++) {
                file = fileArr[i];
                
                if (file.isDirectory()) {
                    continue;
                }
                
                if (file.isHidden()) {
                    continue;
                }
                
                if (!file.exists()) {
                    continue;
                }
                
                if (!file.canRead()) {
                    continue;
                }
                
                if (file.getName().toLowerCase().endsWith(“.txt”)) {
                    indexFile(indexWriter, file);
                }
            }
            
            System.out.println(indexWriter.numRamDocs());
            
        } catch (Exception e) {
            e.printStackTrace();
            
        } finally {
            try {
                if (indexWriter != null) {
                    indexWriter.close();
                }
            } catch (Exception e) {
                indexWriter = null;
            }
            
            try {
                if (dir != null) {
                    dir.close();
                }
            } catch (Exception e) {
                dir = null;
            }
        }
    }

    
    public void indexFile(IndexWriter indexWriter, File file) throws Exception {
        Document doc = getDocument(file);
        indexWriter.addDocument(doc);
    }
    
    
    private Document getDocument(File file) throws Exception {
        String fileContent = FileReadUtil.readFileToString(file, “MS949”, ” “);
        
        Document doc = new Document();
        
        doc.add(new TextField(“contents”, String.valueOf(fileContent), Field.Store.YES));
        doc.add(new TextField(“filename”, String.valueOf(file.getName()), Field.Store.YES));
        doc.add(new TextField(“fullpath”, String.valueOf(file.getCanonicalPath()), Field.Store.YES));
        
        System.out.println(“canonicalPath : “ + file.getCanonicalPath());
        System.out.println(“filename : “ + file.getName());
        System.out.println(“fileContent : “ + fileContent);
        System.out.println(“====================”);
        
        return doc;
    }
}

 

2-2. SearchManager.java

package com.thkmon.lucene;

import java.io.File;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

public class SearchManager {

    public static void main(String[] args) {
        SearchManager searchMng = new SearchManager();
        searchMng.doSearch();
    }
    
    
    /**
     * 검색하기
     */

    public void doSearch() {
        
        // 색인 결과파일 폴더 지정
        String indexDir = “C:\\index”;
        
        // 검색 키워드
        String keyword = “*깨끗*만들자*”;
        
        Directory dir = null;
        IndexReader indexReader = null;
        IndexSearcher indexSearcher = null;
        
        try {
            dir = FSDirectory.open(new File(indexDir).toPath());
            indexReader = DirectoryReader.open(dir);
            indexSearcher = new IndexSearcher(indexReader);
            
            // 정확한 어절을 검색하려면 TermQuery 객체 사용.
            // Query query = new TermQuery(new Term(“filename”, keyword));
            
            // 와일드 카드(? 또는 *) 사용하여 검색하려면 WildcardQuery 객체 사용.
            Query query = new WildcardQuery(new Term(“filename”, keyword));
            
            TopDocs docs = indexSearcher.search(query, 10);
            
            int docCount = 0;
            if (docs.scoreDocs != null) {
                docCount = docs.scoreDocs.length;
                System.out.println(docCount);
                Document doc = null;
                for (int i=0; i<docCount; i++) {
                    doc = indexSearcher.doc(docs.scoreDocs[i].doc);
                    System.out.println(doc.get(“contents”));
                }
            }
            
        } catch (Exception e) {
            e.printStackTrace();
            
        } finally {
            try {
                if (indexReader != null) {
                    indexReader.close();
                }
            } catch (Exception e) {
                indexReader = null;
            }
            
            try {
                if (dir != null) {
                    dir.close();
                }
            } catch (Exception e) {
                dir = null;
            }
        }
    }
    
}

2-3. FileReadUtil.java

package com.thkmon.lucene;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class FileReadUtil {
    
    /**
     * 파일 읽어서 ArrayList 로 리턴
     *
     * @param file
     * @param encode
     * @return
     * @throws IOException
     * @throws Exception
     */

    public static ArrayList<String> readFile(File file, String encode) throws IOException, Exception {
        if (file == null || !file.exists()) {
            return null;
        }

        ArrayList<String> resultList = null;

        FileInputStream fileInputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;

        try {
            fileInputStream = new FileInputStream(file);
            inputStreamReader = new InputStreamReader(fileInputStream, encode);
            bufferedReader = new BufferedReader(inputStreamReader);

            String oneLine = null;
            while ((oneLine = bufferedReader.readLine()) != null) {
                if (resultList == null) {
                    resultList = new ArrayList<String>();
                }

                resultList.add(oneLine);
            }

        } catch (IOException e) {
            throw e;

        } catch (Exception e) {
            throw e;

        } finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (Exception e) {
                bufferedReader = null;
            }

            try {
                if (inputStreamReader != null) {
                    inputStreamReader.close();
                }
            } catch (Exception e) {
                inputStreamReader = null;
            }

            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (Exception e) {
                fileInputStream = null;
            }
        }

        return resultList;
    }
    
    
    /**
     * 파일 읽어서 String 으로 리턴
     *
     * @param file
     * @param encode
     * @param delimiter
     * @return
     * @throws IOException
     * @throws Exception
     */

    public static String readFileToString(File file, String encode, String delimiter) throws IOException, Exception {
        StringBuffer buff = new StringBuffer();
        
        ArrayList<String> strList = readFile(file, encode);
        if (strList != null && strList.size() > 0) {
            int listCount = strList.size();
            for (int i=0; i<listCount; i++) {
                buff.append(strList.get(i));
                buff.append(delimiter);
            }
        }
        
        return buff.toString();
    }
}