Ace-T's Blog 내 검색 [네이버 커넥트 이웃 합니다~^-^/ 요청 大 환영~~]

[색인&검색] UI에 루씬 붙여보기!

OpenSource/Lucene 2014.06.21 18:22
[Good Comment!!, Good Discussion!!, Good Contens!!]
[ If you think that is useful, please click the finger on the bottom~^-^good~ ]
by ace-T

에고고...

환절기 감기 조심하세요...

갑자기 엊그제 선풍기 틀어놓고 창문 열어놓고 잤더니...

편도가 심하게 부어서...미열(37.5)도 나서 죽을 것 같네요...@.@;;;

고고 하지만 고고씽 해볼까용~약먹고 궁디에 주사도 맞아서 조금 나아졌네요 헤헤;


간단하게 spring을 사용하여 루씬을 붙여보겠습니다~


환경

springframework 3.1

maven

lucene 4.0알파

아파치 톰캣 6.0

Junit test - 색인 구현



검색바 부분

  검색어 : taeha로 검색을 하였을 때!~





검색결과 부분



수집기를 이제 한번 만들어봐야할 것 같습니다.

또한 매~~~우 기본적으로 검색을 구축 하였습니다.

아직 풀지 못한 친구와 통계와 정렬, 자동완성, 인기어, 유사어, 불용어, 결과내 재검색 등등 해봐야 할 것들이 많네요!~


일단은 여기까지! 


소스는 아래를 참고 하시면 됩니다. ^-^/

https://github.com/acetaeha/rndStartService/tree/luceneBasicSource03


       - END -


저작자 표시 비영리 변경 금지
신고

acet 박태하가 추천하는 readtrend 추천글!

설정

트랙백

댓글

:::: facebook을 이용하시는 분들은 로그인 후 아래에 코멘트를 남겨주세요 ::::

[색인&검색] 음..

OpenSource/Lucene 2014.06.19 11:51
[Good Comment!!, Good Discussion!!, Good Contens!!]
[ If you think that is useful, please click the finger on the bottom~^-^good~ ]
by ace-T




음....멍2


궁금한것이 생겼다..


루씬에서 doc01.scd 라는 수집되어진 문서가 있다고 하자.

그 안에는 다음과 같이 <searchNum><title><contents>  로 구성 되어져있다.

내가 원하는 것은 검색을할 때 문서를 찾고 그 안에서 offset을 통해 검색되어진

<searchNum><title><contents> 만 뽑아내고 싶다...

찾아보는데 잘 찾아지지 않는다..하하;

음...생각해보면 역시..오픈소스는 좋은 것 같다. 이런저런 고민도 하게 해준다ㅋㅋ

기존에 검색엔진을 가지고 개발은 많이 해봤지만 뭔가 주인의식이 없다고 할까?..

오픈소스 루씬은 설치부터 개발까지 그안에 있는 것들도 확장가능하니 이 얼마나 좋은 오픈소스인가!! +ㅁ+/

라고 생각을 해본다.


<searchNum> 001

<title> lucene books

<contents> goooood lucene

<searchNum> 002

<title> lucene dev

<contents> very goooood

<searchNum> 003

<title> lucene taeha

<contents> dev prd lucene

<searchNum> 004

<title> this is my project

<contents> taeha


title에 색인을 걸어주고 lucene으로 검색어를 날렸을 때 004 searchNum를 

제외한 001 002 003만 가져와서 뿌려주고 싶다.


Luke로 Search를 해봐도 검색범위가 해당 document 즉, data01.scd 전체를 대상으로 한다.

그렇게 되면...아래와 같이 해주어야할 것이다.. 이건 아니잖아! ㅠ ㅠ 

평화

data01.scd

<searchNum> 001

<title> lucene books

<contents> goooood lucene


data02.scd

<searchNum> 002

<title> lucene dev

<contents> very goooood


data03.scd

<searchNum> 003

<title> lucene taeha

<contents> dev prd lucene


data04.scd

<searchNum> 004

<title> this is my project

<contents> taeha



2014/06/16 - [OpenSource/Lucene] - [색인&검색]루씬 - FSDirectory() 사용


2014/06/04 - [OpenSource/Lucene] - [색인&검색] - 루씬 JUnit Test 해보기& RAMDirectory() 사용!


2014/06/03 - [OpenSource/Lucene] - 다시 시작하는 루씬!!!



음...더 고민해보고 찾아봐야겠다. ㅎㅎㅎ 



저작자 표시 비영리 변경 금지
신고

acet 박태하가 추천하는 readtrend 추천글!

설정

트랙백

댓글

:::: facebook을 이용하시는 분들은 로그인 후 아래에 코멘트를 남겨주세요 ::::

[색인&검색]루씬 - FSDirectory() 사용

OpenSource/Lucene 2014.06.16 16:16
[Good Comment!!, Good Discussion!!, Good Contens!!]
[ If you think that is useful, please click the finger on the bottom~^-^good~ ]
by ace-T

Ref url :  http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/

               2014/06/04 - [OpenSource/Lucene] - [색인&검색] - 루씬 JUnit Test 해보기& RAMDirectory() 사용!


에 이이서~! 이번 시간에는 RAMDiretory() => FSDirectory() 로 만들어 보겠습니다.

색인 후 Luke로 잘 되었는지 확인을 해보면 좋습니다. 


아래는 stackoverflow 에 답변 단 Test 코드에서 나온 Luke 내용 입니다. 머리털 나고 처음으로 답변을 달아봤습니다. ㅋㅋ;

http://stackoverflow.com/questions/18862600/how-to-use-lucene-indexreader-to-read-index-in-version-4-4/24237792#24237792



<< Documents >>


<< Search >>


source link : https://github.com/acetaeha/rndStartService/tree/luceneBasicSource02


소스에 보시면

doc.add(new TextField("acet", target ,Field.Store.YES));  // Field.Store.YES 이녀석이 NO면 색인내용이 없다.  

// 뜻 그대로 Store 여부, 단 : 2번째 param에 Reader가 들어오게 되면 Field.Store.YES를 사용할 수가 없어서 검색 결과가 나오지 않는다.

라고 주석을 달아놨습니다.


2번째 파라미터에 Reader를 넣어서 할 경우에는 Field.Store.NO를 사용하셔야 하며, 그렇게 될 경우 검색이 되지 않는 현상이 나타납니다.

왜냐면 NO일 경우에 당연히 색인에 남지 않으니깐요! -0-;;


그리고 public int index(File indexDir, File dataTargetDir) 메소드에서 

iw.commit(); or iw.close(); 를 반드시 해주셔야 색인이 잘~~만들어 집니다.


흐름은 대충~소스를 보시면 알 수 있습니다.

앞으로 더욱 소스를 다듬고~업그레이드를 하고 다만들어지면 루씬 자체를 분석도 해보고 싶네요! ㅎㅎㅎ 


다음 시간에는 UI에다가 붙여보도록 하겠습니다.


Springframework 기반으로 하게 되어집니다. 

아래의 링크를 참고 하시면 됩니다.


참고 :


2013/11/03 - [OpenSource/Spring 강좌] - [Ace-T의 Spring강좌] Step 01. Spring 환경 구축 하기(Eclipse+Jdk)


2013/11/05 - [OpenSource/Spring 강좌] - [Ace-T의 Spring강좌] Step 02. Spring 환경 구축 하기(Maven+Spring Project)


2013/12/04 - [OpenSource/Spring 강좌] - [Ace-T의 Spring강좌] Step 03. Spring 환경 구축 하기(was)


2014/02/05 - [OpenSource/Spring 강좌] - [Ace-T의 Spring강좌] Step 04. Spring @MVC 분석-01


2014/05/05 - [OpenSource/Spring 강좌] - [Ace-T의 Spring강좌] Step 05. Spring @MVC 분석-02


2014/05/23 - [OpenSource/Spring 강좌] - [Ace-T의 Spring강좌] Step 06. Spring @MVC 분석-03


2014/05/26 - [OpenSource/Spring 강좌] - [Ace-T의 Spring강좌] Step 07. Spring @MVC 분석-04


그럼 개발 하러 이만..:D

고고


by ace-T






저작자 표시 비영리 변경 금지
신고

acet 박태하가 추천하는 readtrend 추천글!

설정

트랙백

댓글

:::: facebook을 이용하시는 분들은 로그인 후 아래에 코멘트를 남겨주세요 ::::

[색인&검색] - 루씬 JUnit Test 해보기& RAMDirectory() 사용!

OpenSource/Lucene 2014.06.04 16:28
[Good Comment!!, Good Discussion!!, Good Contens!!]
[ If you think that is useful, please click the finger on the bottom~^-^good~ ]
by ace-T

우선! https://github.com/macluq/HelloLucene/blob/master/pom.xml

pom.xml을 보시면 루씬 core말고도 여러가지가 있습니다! 일단은 그냥 복사해서 붙여넣기를 해봅시다!

차근차근! 알아가보도록 하겠습니다^-^/

오키

사이트에 가서 보기 싫으신 분은 아래를 클릭!

더보기


우선은 아래와 같이 테스트 코드를 하나 만들어봅니다. 빨간색 부분이 무엇인지 파악해야 할 친구 입니다. ㅎㅎ

소스는 루씬 튜토리얼 소스라고 하네요 ver은 4.0 입니다!

https://github.com/macluq/HelloLucene


public class IndexTest {


@Test

public void makeIndexTest() {

   // 0. Specify the analyzer for tokenizing text.

   //    The same analyzer should be used for indexing and searching

   StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);

}


StandardAnalyzer 이 친구는 아래와 같이 lucene-analyzers-common에 포함이 되어있습니다.

무엇을 하는 친구인지~~api를 살펴보겠습니다.^-^/

http://lucene.apache.org/core/4_0_0/analyzers-common/





위의 그림을 보시면 StopwordAnalyzerBase를 extends 받아서 구현을 한 것을 알 수 있습니다.

음..이부분을 이해하려면 먼저 색인에 대해서 조금 더 학습을 해야 할 것 같습니다.

용어를 대충 보면 Tokenizer, Filter 등등이 보이네요 그리고 Stopword가 확~~눈에 띄이네요 불용어?? 

그러면 생각해보면...

무엇을 Tokenizer하고 Filter한다는 걸까요????

제가 보기에는...수집(Crawler)되어 나온 SCD(전형화된 문서)를 Parsing 하고 그것을 형태소분석을 통해 색인파일을 만드는 것 같습니다.

제가 생각하는 것이 맞는지는 소스를 차근차근 따라해보면 알 수 있을 것 같네요 ㅎㅎㅎ 

테스트를 하면서 정리를 하는 것이라서..끝까지 읽어주세요 ㅋㅋㅋ;;;

방구뽕



2012/08/28 - [OpenSource/Lucene] - [Study_1회차] Lucene이란??


에 보시면..아래와 같이 주요 클래스들이 있습니다. 오래된 루씬 책에서 발췌한 것이라 틀릴수도있겠지만 검색엔진의 틀은 변하지 않을테니

그냥 요정도로 봐도 무관 할 것 같습니다. ㅎㅎㅎ


색인 주요 클래스

검색 주요 클래스 

 IndexWriter

IndexSearcher

 Directory

 Term

 Analyzer

 Query

 Document

 TermQuery

 Field

 Hits


 색인 주요 클래스에 대해서 알아보도록 하겠습니다. 

1) IndexWriter : 이 친구를 사용해야 새로운 색인을 만들 수 있고, 색인에 문서를 추가하는 작업을 합니다.

                        즉, 루씬의 색인에 문서를 추가하는 기능을 전담하는 친구입죠! 소중한 친구네요 ㅎㅎ; 더욱 친해져야겠어요ㅋㅋ

2) Directory : 색인도 저장이 되어야 합니다. 그래서 이 친구의 역할은 루씬의 색인 파일 저장을 책임 집니다.

                    루씬은 기본적으로 RAMDirectory와 FSDirectory를 지원 합니다.

                    ㄴ  RAMDirectory : 컴퓨터의 메인 메모리를 색인 저장소로 사용 합니다.

                    ㄴ FSDirectory     : 디스크의 파일 시스템에 색인을 저장 합니다.

                   위의 2가지는 기본적으로 제공되어지는 것이며 DB를 색인저장소로 할 수도있습니다. 

                   하둡 교육을 들으면서 검색 생각이 났었습니다. 하둡같은 분산저장소에 수집 또는 색인 내용을 저장하지 않을까..라는 생각을 해보네요

                   혹은..Redis같은곳에 올릴수 있지 않을까요?? 음..해보고 싶은 것 해봐야할 것들이 생각나네요+ㅁ+/ 

                   아무튼..차근차근 해보도록 하겠습니다! ^-^/ 재밌는 개발 ㄱ ㄱ ㄱ ~~


3) Analyzer : 루씬은 문자열 즉, 일반 텍스트만을 색인하며, 분석하도록 지정한 모든 텍스트는 분석기(Analyzer)를 거칩니다.

                   색인이라는 것이 형태소분석기를 통해 나온 파일 이기때문이죠! 여기서 텍스트라는 것은 분석할 대상인 것 입니다.

                   예를 들어서 텍스트 즉, 수집된 내용이 <title>박태하 천재</titie> 에서 파싱을 통해 텍스트를 뽑아내 박태하 천재를 

                   형태소분석(ex. 복합명사 추출)을 해보면 박태하, 천재, 박태하 천재 이런식으로 분석기를 통해 뽑아 낼수 있습니다.


4) Document : 루씬은 색인에 추가할 데이터는 모두 문서, 즉 Document 단위로 처리 합니다.

                      간단히 말해 루씬의 색인에 데이터를 추가하는 방법은 Document 클래스를 사용하는 방법밖에 없다는 말입니다.


5) Field         : IndexWriter를 통해 색인에 추가하려는 내용은 Document 인스턴스에 Field의 형태로 추가해야 한다.

                      검색을 할경우에도 어느 필드에서 찾을지 지정하도록 되어있다.

                      4가지의 필드를 제공한다.

                         ㄴ Keyword, UnIndexed, UnStored, Text

 필드 생성 메소드

분석 여부 

 검색 가능 여부 

저장 여부 

 Field.Keyword(String, String)

N

 Y

 Y

 Field.Keyword(String, Date)

N

Y

 Y

 Field.UnIndexed(String, String)

 N 

 N 

 Y

 Field.UnStored(String, String)

 Y

 Y

 N

 Field.Text(String, String)

 Y

 Y

 Y

 Field.Text(String, Reader)

 Y

 Y

 N


     수집 된 어느 내용들(ex. title, contents, date, name, id) 중에 어떤 Field들을 색인 또는 검색 대상으로 잡아야할지를 결정해주는 역할을 한다.


우리가 테스트 해볼 소스는 인덱스 및 검색까지 하는 소스 입니다. 그러므로 검색(Search) 부분도 살펴보도록 하겠습니다. 

 검색 주요 클래스

1) IndexSearcher : IndexWriter로 만들어둔 색인file을 이용해 검색하려면 이 친구를 이용하면 됩니다! 

                            이 친구는 검색만을 담당하기 때문에 색인을 '읽기 전용' 으로 사용 합니다. 즉, 검색 중에도 IndexWriter를 통해 

                            색인에 문서를 얼마든지 추가할 수 있습니다.

                           가장 간단한 검색메소드는 질의(Query)를 입력 받아 결과를 도출하는 메소드 입니다.


2) Term : 텀..이 친구는 색인의 내부에서 단어를 의미하는 가장 기본적인 요소 입니다.

                하나의 텀은 하나의 이름과 하나의 단어로 이루어지며, 색인 과정에서 Analyzer가 분석해준 토큰을 이용해

                IndexerWriter가 텀을 만들어낸다. 색인파일 내부구조에서 사용되어짐을 알 수있다.  

                예를들어 어떤 단어를 검색하려고 했을 때, 다음과 같이 텀을 하나 만들어서 TermQuery로 IndexSearcher에서 

               검색할 수 있다.

                Query q = new TermQuery(new Term("contents", "parktaeha"));

                Hits hits = is.search(q);          

                위의 소스를 잠시보면 "parktaeha" 요녀석이 검색어가 되겠다. 

                &q=%EB%B0%95%ED%83%9C%ED%95%98 이런 형식으로다가 올 것이다. 

                그리고 contents라는 필드에 "parktaeha"라는 단어가 포함하는 모든 문서를 검색하라는뜻이다.


3) Query :  IndexSearcher를 통해 색인을 검색하려면 검색어가 필요하다. 루씬에서 검색어를 지정할 때는 Query를 사용한다.

                 차근차근 더 학습하면서 알아보도록 하겠습니다!


4) TermQuery : TermQuery는 루씬이 지원하는 Query의 하위 클래스 중 다른 모든 것들의 기본인 클래스이다.

                        이 친구는 특정 이름의 필드에 지정한 단어가 포함되어 있는 문서를 찾을 때 사용한다.


5) Hits : IndexSearcher에서 Query를 통해 검색한 결과는 Hits로 수집할 수 있다. Hits는 결과문서의 본문 내용을 

               모두 포함하지는 않으며, 결과 문서에 대한 ID 목록만을 갖는다. 즉, 필요할 때 결과에 해당하는 문서를 가져오는게 

               훨씬 효과적이기 때문이다.


여기까지 이론적으로 살펴 보았습니다.

소스를 직접 보고 고쳐보면서 더욱 더 자세히 보겠습니다.

Junit으로 돌리기는 하지만 유닛테스트는 아닙니다..^^;;; 우선은 설계를 하지 않고 그냥 소스 가져온것을 돌려보겠습니다.

lucene 패키지를 하나 만드시고~아래의 소스를 돌려보도록 하죠! 

package lucene;

import static org.junit.Assert.*;


import java.io.IOException;


import org.apache.lucene.analysis.standard.StandardAnalyzer;

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.CorruptIndexException;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

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.TopScoreDocCollector;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.LockObtainFailedException;

import org.apache.lucene.store.RAMDirectory;

import org.apache.lucene.util.Version;

import org.junit.Test;


public class IndexTest {


@Test

public void makeIndexTest() throws CorruptIndexException, LockObtainFailedException, IOException {

// 0. Specify the analyzer for tokenizing text.

//    The same analyzer should be used for indexing and searching

StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_40);


// 1. create the index

Directory index = new RAMDirectory();


IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_40, analyzer);


IndexWriter w = new IndexWriter(index, config);

addDoc(w, "Lucene in Action", "193398817");

addDoc(w, "Lucene for Dummies", "55320055Z");

addDoc(w, "Managing Gigabytes", "55063554A");

addDoc(w, "The Art of Computer Science", "9900333X");

w.close();


// 2. query

String querystr = "Lucene";


// the "title" arg specifies the default field to use

// when no field is explicitly specified in the query.

Query q = null;

try {

q = new QueryParser(Version.LUCENE_40, "title", analyzer).parse(querystr);

} catch (org.apache.lucene.queryparser.classic.ParseException e) {

e.printStackTrace();

}


// 3. search

int hitsPerPage = 10;

IndexReader reader = DirectoryReader.open(index);

IndexSearcher searcher = new IndexSearcher(reader);

TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage, true);

searcher.search(q, collector);

ScoreDoc[] hits = collector.topDocs().scoreDocs;


// 4. display results

System.out.println("Found " + hits.length + " hits.");

for (int i = 0; i < hits.length; ++i) {

int docId = hits[i].doc;

Document d = searcher.doc(docId);

System.out.println((i + 1) + ". " + d.get("title") + "\t" + d.get("title"));

}


// reader can only be closed when there

// is no need to access the documents any more.

reader.close();

}


private static void addDoc(IndexWriter w, String title, String isbn) throws IOException {

Document doc = new Document();

doc.add(new TextField("title", title, Field.Store.YES));


// use a string field for isbn because we don't want it tokenized

doc.add(new StringField("isbn", isbn, Field.Store.YES));

w.addDocument(doc);

}

}


디렉토리 구조 - 아래와 같이 IndexTest.java를 Junit  기반으로 만들어 줍니다.



          


결과는 아래와 같습니다.


  

이제~~대망의 소스를 보도록 하겠습니다!!


소스를 그냥 보는것이 아니라main쪽으로 소스를 분리 해보겠습니다.  

1) 테스트 소스

package lucene;

import org.apache.lucene.search.Query;

import org.junit.Assert;

import org.junit.Test;


import kr.pe.constr.lucene.Indexer;


public class IndexTest {

@Test

   public void makeIndexerTest(){

      Indexer idx = new Indexer();

      Query q = idx.queryExcute();

      Assert.assertNotNull(idx);

      Assert.assertNotNull(q);

      System.out.println("query=>"+q.toString());

      idx.search(q);

    }

}


2) 메인쪽 소스 - 예외처리 요런것은 일단 패스~~

package kr.pe.constr.lucene;

import java.io.IOException;


import org.apache.lucene.analysis.standard.StandardAnalyzer;

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.CorruptIndexException;

import org.apache.lucene.index.DirectoryReader;

import org.apache.lucene.index.IndexReader;

import org.apache.lucene.index.IndexWriter;

import org.apache.lucene.index.IndexWriterConfig;

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.TopScoreDocCollector;

import org.apache.lucene.store.Directory;

import org.apache.lucene.store.RAMDirectory;

import org.apache.lucene.util.Version;


public class Indexer {

private StandardAnalyzer analyzer;

private Directory index;

// Step 01.

public Indexer(){

IndexWriter w = null;

IndexWriterConfig config; 

try {

// The same analyzer should be used for indexing and searching

analyzer = new StandardAnalyzer(Version.LUCENE_40);


// 1. create the index

index = new RAMDirectory();

config = new IndexWriterConfig(Version.LUCENE_40, analyzer);

w = new IndexWriter(index, config);

addDoc(w, "Lucene in Action", "193398817");

addDoc(w, "Lucene for Dummies", "55320055Z");

addDoc(w, "Managing Gigabytes", "55063554A");

addDoc(w, "The Art of Computer Science", "9900333X");

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

try {

w.close();

} catch (CorruptIndexException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}


}

private static void addDoc(IndexWriter w, String title, String isbn) throws IOException {

Document doc = new Document();

doc.add(new TextField("title", title, Field.Store.YES));


// use a string field for isbn because we don't want it tokenized

doc.add(new StringField("isbn", isbn, Field.Store.YES));

w.addDocument(doc);

}

// step 02.

public Query queryExcute(){

// 2. query

String querystr = "Lucene";


// the "title" arg specifies the default field to use

// when no field is explicitly specified in the query.

Query q = null;

try {

q = new QueryParser(Version.LUCENE_40, "title", analyzer).parse(querystr);

} catch (org.apache.lucene.queryparser.classic.ParseException e) {

e.printStackTrace();

}

return q;

}

// step 03.

public void search(Query q){

// 3. search

int hitsPerPage = 10;

IndexReader reader = null;

ScoreDoc[] hits = null; 

IndexSearcher searcher;

TopScoreDocCollector collector;

try {

reader = DirectoryReader.open(index);

searcher = new IndexSearcher(reader);

collector = TopScoreDocCollector.create(hitsPerPage, true);

searcher.search(q, collector);

hits = collector.topDocs().scoreDocs;

display(hits, searcher);

} catch (CorruptIndexException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

try {

reader.close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

private void display(ScoreDoc[] hits, IndexSearcher searcher){

// 4. display results

System.out.println("Found " + hits.length + " hits.");

for (int i = 0; i < hits.length; ++i) {

int docId = hits[i].doc;

Document d;

try {

d = searcher.doc(docId);

System.out.println((i + 1) + ". " + d.get("title") + "\t" + d.get("isbn"));

} catch (CorruptIndexException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}


}


다시 JUnit을 돌려봤을 때 아래와 같이 녹색불이 뜨며 결과는 같아야 하겠죵~!




결과 


이제 소스는 색인과 검색 둘다 있지만 우선! 색인에 대해서 알아보도록 합니다~


TEST소스에서 Indexer idx = new Indexer();를 수행하면 생성자가 발동하겠죠!


색인을 생성하는 부분 입니다. 굳이 생성자에서 할 필요는 없죵~


        // Step 01.

public Indexer(){

IndexWriter w = null;

IndexWriterConfig config; 

try {

// The same analyzer should be used for indexing and searching

analyzer = new StandardAnalyzer(Version.LUCENE_40);

                       // Filters StandardTokenizer with StandardFilter, LowerCaseFilter and StopFilter, using a list of English stop words.


// 1. create the index

index = new RAMDirectory();

config = new IndexWriterConfig(Version.LUCENE_40, analyzer);

w = new IndexWriter(index, config);

addDoc(w, "Lucene in Action", "193398817");

addDoc(w, "Lucene for Dummies", "55320055Z");

addDoc(w, "Managing Gigabytes", "55063554A");

addDoc(w, "The Art of Computer Science", "9900333X");

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

try {

w.close();

} catch (CorruptIndexException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}


} 


// 색인에 추가할 데이터 Document단위로 넣어주기 

private static void addDoc(IndexWriter w, String title, String isbn) throws IOException {

Document doc = new Document();

System.out.println("title->"+title+"=== isbn->"+isbn);

doc.add(new TextField("title", title, Field.Store.YES));


// use a string field for isbn because we don't want it tokenized

doc.add(new StringField("isbn", isbn, Field.Store.YES));

w.addDocument(doc);

}



// step 02. 질의분석!!

public Query queryExcute(){

// 2. query

String querystr = "Lucene";


// the "title" arg specifies the default field to use

// when no field is explicitly specified in the query.

Query q = null;

try {

q = new QueryParser(Version.LUCENE_40, "title", analyzer).parse(querystr); // 질의 분석

System.out.println("q="+q);

} catch (org.apache.lucene.queryparser.classic.ParseException e) {

e.printStackTrace();

}

return q;

} 



// step 03. 색인을 가져와 검색하기 

public void search(Query q){

// 3. search

int hitsPerPage = 10;

IndexReader reader = null;

ScoreDoc[] hits = null; 

IndexSearcher searcher;

TopScoreDocCollector collector;

try {

reader = DirectoryReader.open(index);  // index reading

searcher = new IndexSearcher(reader);  // Creates a searcher searching the provided index.

collector = TopScoreDocCollector.create(hitsPerPage, true); // 어떻게 가져올 것인가? 

searcher.search(q, collector); // 색인에 대해 search(), Lower-level search API. return void

hits = collector.topDocs().scoreDocs;  // docId를 꺼내 hits[]에 넣어준다. 

display(hits, searcher);

} catch (CorruptIndexException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

try {

reader.close();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

} 


// 출력하기 

private void display(ScoreDoc[] hits, IndexSearcher searcher){

// 4. display results

System.out.println("Found " + hits.length + " hits.");

for (int i = 0; i < hits.length; ++i) {

int docId = hits[i].doc;  // return docId number

System.out.println("docId="+docId);

Document d;

try {

d = searcher.doc(docId);  // return 실제 문서 document object return

System.out.println((i + 1) + ". " + d.get("title") + "\t" + d.get("isbn"));

} catch (CorruptIndexException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

} 


주석으로 설명을 하기는 하였으나..더욱 더 자세히 알아보시려면 직접 api를 찾아가면서 테스트를 해보시는게 좋을 것 같습니다^-^


여기까지~~~해보고 다음편으로! 색인 저장소를 파일시스템으로 바꿔보겠습니다. 배웠으니 이제는 익힐 차례!!! ㅎㅎㅎ 

다음편에 뵙겠습니다 (- - (_ _ * 꾸벅~


소스는 github에 올려놨습니다~

https://github.com/acetaeha/rndStartService/tree/luceneBasicSource01



참고 URL :

http://lucene.apache.org/core/4_0_0/

http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/search/class-use/ScoreDoc.html

http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/search/IndexSearcher.html#doc(int)

http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/search/Collector.html

http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/index/IndexReader.html

http://lucene.apache.org/core/4_0_0/queryparser/org/apache/lucene/queryparser/classic/QueryParser.html

http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/document/StringField.html

http://lucene.apache.org/core/4_0_0/core/org/apache/lucene/document/TextField.html

http://lucene.apache.org/core/4_0_0/analyzers-common/

http://lucene.apache.org/core/


                         - END -

저작자 표시 비영리 변경 금지
신고

acet 박태하가 추천하는 readtrend 추천글!

설정

트랙백

댓글

  • Favicon of http://blog.naver.com/asjgi BlogIcon david 2014.07.16 11:16 신고 답글 | 수정/삭제 | ADDR

    어떤게 진짜 소스인지 모르겟어요 ㅠㅠ ㅋ

    • Favicon of http://acet.pe.kr BlogIcon String Ace-T 2014.07.17 17:48 신고 수정/삭제

      소스는~!! RAMDirectory() code는 설명에 나온 것 처럼https://github.com/acetaeha/rndStartService/tree/luceneBasicSource01 로 보시면 되구요~

      다른 기능 들어갈때마다 브렌치를 다르게 땄어요~
      즉, luceneBasicSource01 , luceneBasicSource02, luceneBasicSource03 서로 다른 브랜치이고 소스가 달라요~
      전부 진짜 소스입니다~-0-ㅋㅋ;

:::: facebook을 이용하시는 분들은 로그인 후 아래에 코멘트를 남겨주세요 ::::

[Lucene 7회 차] about index

OpenSource/Lucene 2012.10.04 20:53
[Good Comment!!, Good Discussion!!, Good Contens!!]
[ If you think that is useful, please click the finger on the bottom~^-^good~ ]
by ace-T


2012/09/20 - [OpenSource/Lucene] - [Lecene 6회차] Welcome to New face & Analyze about Index.

6회차에서 아래와 같이

더보기


를 공부하였다.

더욱 더 오늘 파고 들어보자!^0^ good~

Directory : 루씬의 책에서는 Directory 클래스가 루씬의 색인 파일 저장을 책임 진다고 나와있다.
여러가지는 지원하지만 보통 이야기를 할 때  FSDirectory, RAMDirectory를 말한다.

특히,  FSDirectory를 많이 쓰는 편 이다. 이름에서 알 수 있듯이 RAMDirectory는 컴퓨터의 메인 메모리를 색인 저장소로 사용하게 해주는 Directory의 하위 클래스이다.

소스에서 보면

import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
이런식으로 import를 해주어야 한다.

이제 메소드들에 대해서 살펴 보자!

메소드를 살펴보기 위해 레퍼런스를 보면..
http://lucene.apache.org/core/old_versioned_docs/versions/3_5_0/api/all/index.html

static FSDirectory open(File path) 
          Creates an FSDirectory instance, trying to pick the best implementation given the current environment.
static FSDirectory open(File path, LockFactory lockFactory) 
          Just like open(File), but allows you to also specify a custom LockFactory.

위의 open 메소드를 살펴보자.
레퍼런스를 보면 " Creates an FSDirectory instance, trying to pick the best implementation given the current environment"  이러하다.

String idxPath = "D:\\IR\\index";
Directory idxDir = FSDirectory.open(new File(idxPath));
File dataDir = new File(dataPath);


위의 코드를 보면 Directory 즉, 우리가 말하는 루씬에서 색인파일의 저장을 책임진다는 Directory~!!
에다가 FSDirectory의 메소드 open을 이용하여 new File을 해준다는 것이다.(파일 open!!)

여기에서!!
책에서의 루씬 구버전에서는 아래와 같이 모두 File Class를 이용하여 idx와 data file을 만든다.
  1) File indexDir = new File(idxPath);
  2) File dataDir = new File(dataPath);

하지만 지금 보고 있는 소스코드에서는 Directory와 File로 구분되어진다.
Directory idxDir = FSDirectory.open(new File(idxPath));
File dataDir = new File(dataPath);

Why???

물론..Directory idxDir = FSDirectory.open(new File(idxPath));를 file로 해줘도 무관 할 것이다.
   1) File idxDir = new File(idxPath);
   2) numIndexed = index(idxDir, dataDir);
   3) public static int index(File idxDir, File dataDir) throws IOException {
   4) IndexWriter writer = new IndexWriter(FSDirectory.open(idxDir), conf);

위와같이..써놓으면 뭔지 잘 모를지도 모르지만..ㅋㅋ
설명을 하면, File로 받아서 index메소드를 호출을 할 때 file을 인자로 넣고,
호출되어진 메소드에서는 File로 받는다.
여기서 중요한건 => FSDirectory.open(idxDir)  이부분이다.
즉, IndexWriter가 구버전과 3.5버전이 달라진 것이다.

구버전에서는
IndexWriter writer = new IndexWriter(indexdir, new StandardAnalyzer(), true); 로 루씬책에 나와있다.
현재버전에서는
IndexWriter writer = new IndexWriter(FSDirectory.open(idxDir), conf);
와 같이 FSDirectory와 conf가 인자로 들어간다.
conf는 IndexWriterConfig conf = new IndexWriterConfig(Version.LUCENE_35, new KeywordAnalyzer() );
로 표현 할 수 있다.

여기서 느낌이 팍! 오셨는지 모르겠다.

다른점은 구버전에서는 File자체를! 3.5버전에서는 FSDirectory 를 사용한다는 것이다.

조금 더 정리하지 못한게 아쉽지만..조금씩 파고 들어보자!+ㅁ+/






저작자 표시 비영리 변경 금지
신고

acet 박태하가 추천하는 readtrend 추천글!

설정

트랙백

댓글

  • Favicon of http://er1ca.tistory.com/ BlogIcon er1ca 2012.10.04 23:25 신고 답글 | 수정/삭제 | ADDR

    아 오늘이거하셨구나 'ㅅ'/
    진짜 빨리, 보기좋게 정리하시네요 ~~ 짱ㅋㅋㅋ 더보기어케하는거짘ㅋㅋㅋㅋ

    • Favicon of http://acet.pe.kr BlogIcon String Ace-T 2012.10.05 00:25 신고 수정/삭제

      ㅎㅎㅎ 시간이 촉박해서 더 자세히 못했음..ㅠ.ㅠ
      다음 스터디에는 밥을 후딱 먹고! 정리하는 시간을 벌어보아요ㅋ

:::: facebook을 이용하시는 분들은 로그인 후 아래에 코멘트를 남겨주세요 ::::

[Lucene 5] - Talk and Analyze about Index with goodtac!

OpenSource/Lucene 2012.09.13 20:19
[Good Comment!!, Good Discussion!!, Good Contens!!]
[ If you think that is useful, please click the finger on the bottom~^-^good~ ]
by ace-T

oh my god....keyborad bug.....I can't write in Korean...T.T
1. optimize deprecated!
Good bye...

optimize

@Deprecated
public void optimize(boolean doWait)
              throws CorruptIndexException,
                     IOException
Deprecated. 

This method has been deprecated, as it is horribly inefficient and very rarely justified. Lucene's multi-segment search performance has improved over time, and the default TieredMergePolicy now targets segments with deletions.

Throws:
CorruptIndexException
IOException

2. maxDoc() and numDocs() diffrence~!!

    int numIndexed = writer.maxDoc();   
    int numIndexed2 = writer.numDocs();


maxDoc : total cout(deletion count)

public int maxDoc()
Returns total number of docs in this index, including docs not yet flushed (still in the RAM buffer), not counting deletions.

See Also:
numDocs()

numDocs

: include deletion count!, (total cout - deletion count)

public int numDocs()
            throws IOException
Returns total number of docs in this index, including docs not yet flushed (still in the RAM buffer), and including deletions. NOTE: buffered deletions are not counted. If you really need these to be counted you should call commit() first.

Throws:
IOException
See Also:
numDocs()


Delete Document in the index~!
Use..IndexReader Class and The Class don't delete right now! just change status to deletion.
But Do you want to delete..Use that grammer => "reader.close()"


About Field..
Lucene 1.4 : provide Keyword, UnIndexed, Unstored, Text
CHANGE~~
Lucene 3.5 : provide ANALYZED, ANALYZED_NO_NORMS, NO, NOT_ANALYZED, NOT_ANALYZED_NO_NORMS

Enum Constant Summary
ANALYZED
          Index the tokens produced by running the field's value through an Analyzer.
ANALYZED_NO_NORMS
          Expert: Index the tokens produced by running the field's value through an Analyzer, and also separately disable the storing of norms.
NO
          Do not index the field value.
NOT_ANALYZED
          Index the field's value without using an Analyzer, so it can be searched.
NOT_ANALYZED_NO_NORMS
          Expert: Index the field's value without an Analyzer, and also disable the indexing of norms.


Field

public Field(String name,
             String value,
             Field.Store store,
             Field.Index index,
             Field.TermVector termVector)
Create a field by specifying its name, value and how it will be saved in the index.

Parameters:
name - The name of the field
value - The string to process
store - Whether value should be stored in the index
index - Whether the field should be indexed, and if so, if it should be tokenized before indexing
termVector - Whether term vector should be stored
Throws:
NullPointerException - if name or value is null
IllegalArgumentException - in any of the following situations:
  • the field is neither stored nor indexed
  • the field is not indexed but termVector is TermVector.YES


AND..FIELD

1) About doc.add(new Field("contents", new FileReader(f)));

  /**
   * Create a tokenized and indexed field that is not stored. Term vectors will
   * not be stored.  The Reader is read only when the Document is added to the index,
   * i.e. you may not close the Reader until {@link IndexWriter#addDocument(Document)}
   * has been called.
   *
   * @param name The name of the field
   * @param reader The reader with the content
   * @throws NullPointerException if name or reader is <code>null</code>
   */
  public Field(String name, Reader reader) {
    this(name, reader, TermVector.NO);
  }

============================================================

2) About doc.add(new Field("filename", f.getCanonicalPath(),Field.Store.YES,Field.Index.NO));

 /**
   * <p>Adds a field to a document.  Several fields may be added with
   * the same name.  In this case, if the fields are indexed, their text is
   * treated as though appended for the purposes of search.</p>
   * <p> Note that add like the removeField(s) methods only makes sense
   * prior to adding a document to an index. These methods cannot
   * be used to change the content of an existing index! In order to achieve this,
   * a document has to be deleted from an index and a new changed version of that
   * document has to be added.</p>
   */
  public final void add(Fieldable field) {
    fields.add(field);
  }

Class Document  : http://lucene.apache.org/core/old_versioned_docs/versions/3_5_0/api/all/index.html
Documents are the unit of indexing and search. A Document is a set of fields. Each field has a name and a textual value. A field may be stored with the document, in which case it is returned with search hits on the document. Thus each document should typically contain one or more stored fields which uniquely identify it.

sub function

add

public final void add(Fieldable field)

Adds a field to a document. Several fields may be added with the same name. In this case, if the fields are indexed, their text is treated as though appended for the purposes of search.

Note that add like the removeField(s) methods only makes sense prior to adding a document to an index. These methods cannot be used to change the content of an existing index! In order to achieve this, a document has to be deleted from an index and a new changed version of that document has to be added.

index info



save this filenames that this is .. .fdt, .fdx, .fnm, .nrm, .prx, .tii, .tis

저작자 표시 비영리 변경 금지
신고

acet 박태하가 추천하는 readtrend 추천글!

설정

트랙백

댓글

:::: facebook을 이용하시는 분들은 로그인 후 아래에 코멘트를 남겨주세요 ::::

[Study_2회차] Lucene 셋팅

OpenSource/Lucene 2012.08.29 20:04
[Good Comment!!, Good Discussion!!, Good Contens!!]
[ If you think that is useful, please click the finger on the bottom~^-^good~ ]
by ace-T
금일 스터디는 OSM교육으로 인해..늦어져..루씬 셋팅을 목표로 해보자! ^0^good~

루씬..오픈소스! 개발을 위해~스따뚜~~

1] 루씬 소스를 받자!

URL : http://apache.mirror.cdnetworks.com/lucene/java/3.6.1/



환경 : 32bit window

윈도우다보니....zip을 받으면 되는 줄 알고...lucene-3.6.1.zip 을 받았는데 알고보니 배포판이였다^0^good~
위와 같이 lucene-3.6.1-src.tqz를 받으면 됩니다.^-^

받으신 뒤에 원하는 경로에 압축 풀기 및 저장을 합니다.

2) 이제!! 남은 것은 이클립스를 받으면 됩니다.

http://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/juno/R/eclipse-jee-juno-win32.zip



물론!! jdk는 깔려져있어야겠죠! jdk는...
http://www.oracle.com/technetwork/java/javase/downloads/jdk6-downloads-1637591.html

위의 링크를 타고 가셔서 아래의 그림처럼 32bit시면 windows x86을 다운받으시면 됩니다.

jdk는 exe파일입니다~눌러서 클릭클릭~하시면 됩니다^-^

자! 이클립스는 그냥 압축만 풀면 되는거 아시죠?^-^
셋팅을 다하시고 나면 폴더구성이 됩니다.^-^good~
(색깔 별로 압축을 푼 내용 입니다. lib와 source는 헷깔릴까봐 일부러 붙였다 ㅎㅎ)



자! 이제 필요한 소스와 라이브러리와 IDE툴(이클립스), JDK를 모두 다운 받고 압축을 풀었습니다.

3) 이클립스에 Lucene 소스와 라이브러리를 추가하자!
  - 이클립스를 구동 합니다. 

    


 - 루씬은 라이브러리!! 고로 이클립스에서 자바 프로젝트를 하나 만듭니다.   


 


- 이제 만들어진 프로젝트에 루씬 소스를 import 시킵니다.^-^good~~ 



-  File System을 선택 합니다.
-  아래와 같이 루씬 소스 디렉토리를 찾아줍니다.



- 모두 체크 한 뒤에 Finish 해줍니다.

- 결과는 아래와 같습니다.


- 라이브러리 추가!




이로써 셋팅은 완료이다~~~~^-^good~~~

참고사항]
한글분석기.jar    url : http://sourceforge.net/projects/lucenekorean/



루씬 카페 => http://cafe.naver.com/korlucene
루크 =>        http://www.getopt.org/luke/

- 끝 -

저작자 표시 비영리 변경 금지
신고

'OpenSource > Lucene' 카테고리의 다른 글

[Study_4회차(2)] Lucene 셋팅(3.5.0)  (0) 2012.09.07
[Study_4회차(1)] Luke 셋팅  (0) 2012.09.06
<안정적인 버전> 루씬과 루크  (0) 2012.09.06
[Study_3회차] Lucene 색인 분석  (0) 2012.08.30
[Study_2회차] Lucene 셋팅  (2) 2012.08.29
[Study_1회차] Lucene이란??  (0) 2012.08.28

acet 박태하가 추천하는 readtrend 추천글!

설정

트랙백

댓글

:::: facebook을 이용하시는 분들은 로그인 후 아래에 코멘트를 남겨주세요 ::::

[Study_1회차] Lucene이란??

OpenSource/Lucene 2012.08.28 23:21
[Good Comment!!, Good Discussion!!, Good Contens!!]
[ If you think that is useful, please click the finger on the bottom~^-^good~ ]
by ace-T


책 : Lucene in Action
오픈소스 자바 검색엔진

2012.08.28 스터디 시작!
인원 : 2명(with 굿택)
시간 : 회사 업무 이후(6시~9시)

1장. 루씬과의 첫 만남

책을 읽다가 보면 JUnit, 리팩토링,  mock 객체 등을 소개 한뒤 기본편 1부가 시작 된다.

이번 스터디는 루씬이 뭔가? 색인 api, 검색 api등을 알아보는게 중점 이다.

루씬??  한마디로 정보검색(IR, Information Retrieval) 라이브러리이다.

자바로 만들어진 오픈소스 소프트웨어이며 아파치 소프트웨어 재단의
자카르타 프로젝트에 속해있다^0^good~

그냥 자바 라이브러리라고 보면 되고, 흔히 말하는 비지니스 로직이라고 말하는 부분은
루씬에는 전혀 포함 되어있지 않다. 고로 작성해야한다^-^

그림으로 보면 더 쉬울 것 이다. 아래의 그림을 보면 사용자 애플리케이션 부분과 루씬 2개의 영역으로 나누어져있는데 사용자 애플리케이션쪽에 수집과 검색 부분이 있다.(요 부분을 작성!!)
루씬쪽 색인은 루씬에서 제공이 가능하다는 것이다.(글씨가 더러워도 양해를..ㅠ.ㅠ..ㅋ)



뭐 이런식으로 보면 이해가 잘 안갈 것이다. 검색엔진을 배웠던 사람은 이해가 이미 갔을 것이다.
그래서 준비했다! 정말 앙증맞게 잘 그려놓은 검색엔진 작동 순서~!!!

[작동 순서]


<펌 : http://blog.naver.com/bestkeyword?Redirect=Log&logNo=100025555196 >

사실 위의 그림만 이해해도 검색엔진의 반은 한거라 볼 수 있다!!!

마지막으로 금일 스터디한 내용 중에 색인 api와 검색 api들이 있다.

아직은 대충 이런것들이 있다고만 알고 넘어가도록 하자! 실제로 코딩이 들어가면 자연스레 알게 될 것이므로! ^0^good~

 색인 주요 클래스 검색 주요 클래스 
 IndexWriter IndexSearcher
 Directory  Term
 Analyzer  Query
 Document  TermQuery
 Field  Hits

앞으로 step by step 해나가면 멋진 검색엔진을 하나 만들 수 있을거라 믿는다^-^good~
수집 -> 색인 -> 검색 이 세가지가 정말 많은 것들과 결합 하여 멋지게 변하는 모습을 보고 싶다^-^ 힘내자!

- 끝 -
저작자 표시 비영리 변경 금지
신고

'OpenSource > Lucene' 카테고리의 다른 글

[Study_4회차(2)] Lucene 셋팅(3.5.0)  (0) 2012.09.07
[Study_4회차(1)] Luke 셋팅  (0) 2012.09.06
<안정적인 버전> 루씬과 루크  (0) 2012.09.06
[Study_3회차] Lucene 색인 분석  (0) 2012.08.30
[Study_2회차] Lucene 셋팅  (2) 2012.08.29
[Study_1회차] Lucene이란??  (0) 2012.08.28

acet 박태하가 추천하는 readtrend 추천글!

설정

트랙백

댓글

:::: facebook을 이용하시는 분들은 로그인 후 아래에 코멘트를 남겨주세요 ::::

티스토리 툴바