회사에서 오픈 소스 검색엔진인 Lucene을 기반으로 하는 Elasticsearch 도입을 진행하면서,

이전에 조금 다루었던 Solr (Lucene을 기반으로 하는 또 다른 검색엔진)를 개인적으로 정리 하고 있다.

둘 다 조금 부족한 형태소 분석기 (정확하게는 사전)를 이용하는데,

부족한 부분을 채우기 위해 이것 저것 시도하면서 찾은 데이터들 중에서 다른 이들에게도 중요 할만한 데이터들을 공유한다. 

특히, 검색 엔진, 형태소 분석기 등의 프로그램에 대한 자료는 많이 공개되어 있어서 쉽게 구할 수 있는데, 

이들을 제대로 사용할 수 있게 하는 사전에 대한 자료가 부족한 것 같다 (못 찾아서?).

이하의 데이터들을 여러 가지로 적용해서 활용 방법을 정리할 계획으로, 언제 끝날지 알 수 없어 가치 있어 보이는 데이터 리스트부터 정리한다.

유사한 맥락에서 심심풀이로 진행하는 챗봇(Dialogflow와 Rivescript/ChatScript) 제작에 도움 될 것 같아서 찾은 한국어 대화 데이터를 구할 수 있는 정보도 공유한다.

 

한글형태소 사전(NIADic) 

  • 국립 국어원에서 제공하는 데이터로, 약 100만건의 단어 사전 제공
  • “중소기업, 연구자, 일반인 등이 쉽게 NIADic을 활용하여 텍스트 분석을 수행할 수 있도록 KoNLP의 기초 형태소 사전으로 추가하여 제공”한다고 하는데 자연어 처리쪽에서 많이 사용하고, 검색엔진에 적용된 것을 보지 못해서 방법을 찾고 있다 (정보가 있으신 분 공유를 부탁드립니다).
  • 최소한 검색엔진에서 사용자 사전용으로 사용할 수 있을 것 같다.
  • 빅데이터 사이트에서 직접 다운로드 받으면 된다.

 

유의어 사전

  • 한국언론재단(빅카인즈)에서 정리하여 제공하는 시소러스 및 텍사노미 사전
  • 2016년에 제작된 pdf 파일은 구글 검색으로 쉽게 구할 수 있지만, PDF 파일이라 활용하기 어렵고,
  • 홈페이지 하단에 있는 이메일(bigkinds@kpf.or.kr)로 연락하면, 친절한 안내와 함께 2017년에 개정된 엑셀 파일을 받을 수 있다.
  • 시소러스 사전은 조금 가공해서 유의어 사전으로 사용하고,
  • 텍사노미 사전은 복합어 사전으로 유용할 것 같아서 가공해서 사용할 계획이다.

[경희대]를 Lucene 기반 검색엔진에서 어떤 형태소 분석기로 분석하면 [경희]와 [대]로 색인.
[경희대]를 사용자 사전에 추가하면 [경희대]로 색인은 되지만, [경희대학교]가 [경희대]와 [학교]로 색인.
[경희대학교]를 사용자 사전에 추가하면, 공백 차이로 [경희 대학교]가 색인되지 않음(검색되지 않음)
따라서, [경희대]를 사용자 사전에 추가하고, [경희대학교]와 [경희 대학교]를 복합명사 사전에 등록해야
[경희대], [경희대학교], [경희 대학교](실제로는 [경희], [대학교])로 색인.
=> 모든 학교와 조직들을 이런식으로 사전에 등록할 수 없는데, 텍사노미 사전에 이러한 내용이 일부 포함되어 있어서 가공하여 사용할 계획이다.

 

불용어 사전

  • 검색엔진 설정(SEO)을 도와주는 업체에서 제공하는 사전으로
  • 40여개 언어의 불용어가 정리되어 있으며, 약 700개의 한국어 불용어가 있다.
  • 웹 페이지의 내용을 복사해서, 검색엔진 불용어 사전 파일에 그대로 붙여넣으면, 바로 사용 할 수 있다.
  • Geitgey라는 사람이 개인적으로 작업하여 23개 언어, 70개 단어로 정리한 한국어 불용어 사전도 있다.
  • 형태소 분석기에서 품사에 따라 불용어 처리 기능을 제공하지 않는 경우 유용하다.

 

한국어 대화 데이터

  • 챗봇 개발에 유용한 데이터로 어떻게 활용할 것인지 검토 중인 데이터로, 대화 시나리오 구성 등에 참고용으로도 가치가 있어 보인다.
  • "소상공인 및 공공민원 10개 분야에 대한 1만건 이상의 대화(Dialog) 데이터를 구축"했다고 하고 상세한 내용은 사이트를 참고하면 된다.
  • 회원 가입하고 데이터 다운로드를 신청하면 되는데, 연구 목적 등을 적지 않으면 반려되고, 안내사항이 메일로 수신된다.
  • 연구 목적이 아니라도 조금 상세한 이용 계획을 소속과 함께 적어서 신청하면 무난하게 데이터를 다운받을 수 있다.
  • 이외에도 AI허브(aihub.or.kr)를 지향하는 사이트 답게 온갖 데이터가 제공된다.

 

기타 사전

금융기관용 사용자 사전 구축에 용이한 금융용어사전, 국방과학에 유용한 국방과학기술용어사전 등이 있다. 

특히 국방과학기술용어를 제공하는 사이트에서는 (왼쪽 트리형식의 메뉴에)

기타 사전으로 세종과학기술전문용어집, 항공기술용어집, 민간물류용어집 등 

다양한 산업에 사용할 수 있는 사전을 검색할 수 있고 엑셀로 다운 받을 수 있도록 되어 있다.

마지막으로, 위키에 정리된 비속어 사전도 나름 유용하다.

 

오픈소스 루씬(Lucene)을 기반으로 하는 대표적인 검색엔진인 Solr와 Elastic Search 중에서 

Elastic Search(이하 ES)가 더 폭넓게 사용되고, Solr는 다소(?) 위축된 것 같다.

이 사이트에 따르면 2012년 이후로 ES가 대세가 되었다고 한다.

온라인 서점에서 검색해 보면, ES 관련 서적은 매년 출판되어 2019년에도 몇 권이 출판되었다.
(Elastic Search, 엘라스틱서치, 일래스틱서치 등으로 검색)

Solr는 Solr 4 (최신 버전은 8) 버전이 2014년에 세 권 출판된 것이 전부다 (못 찾은 것일 수 있음).

 

하지만 일반적인 문서 검색은 Solr가 쉽게 구현할 수 있고, 웹 로그와 같은 소형 데이터는 ES가 좋다고 생각한다.

새로 이직한 회사에서 ES로 검색 서버를 구축하면서

외부 데이터를 검색 서버에 넣어주는(색인하는-Indexing) DIH (Data Import Handler) 관련 기술이 필요했다.

ElasticSearch는 Logtash를 이용해서 웹 로그나 메일 같은 외부 데이터를 쉽게 색인 할 수 있는데,

RDBMS의 Parent / Child 데이터를 처리하는 방법을 찾기 어려웠다.

게시판을 예로 들면, 게시글(Parent)과 댓글(Child)을 색인 하기 어려웠다. 

더욱이 첨부 파일이 있는 경우에는 게시글에 맞춰서 색인해야 하기 때문에 더 어려웠다.

방법을 찾던 중에 몇 년 전 교육 받은 Solr에서 쉽게 처리했던 기억이 났다.

같이 교육 받았던 직원이 며칠 만에 구현했다며 나에게 준 소스를 찾았다.

Solr에서는 쉬운 것이, ES에서는 자료를 첮지 못해 결국 색인 프로그램을 개발해야 했다.

 

찾은 자료를 정리하는 차원과 이 좋은 Solr도 많이 사용되길 바라면서 정리한다.

자세한 장단점은 이 사이트를(영어) 읽어보면 되고,

여기에서는 Solr를 쉽게 익히기 위해  Solr에서 제공하는 예제를 중심으로 정리한다.

Solr 예제는 Solr 사이트의 시작하기에 정리되어 있는데,

개인적으로 처음 시작하는 사람에게는 어려운 것 같아서 이 내용에 개인적인 해석을 넣어서 정리한다.

  1. 설치
  2. 기술제품과 검색식
  3. 스키마(Schema)
  4. DIH (Data Import Handler)

먼저 Solr 사이트에서 압축 파일을 다운 받아 압축을 풀어서 설치를 완료한다.

         http://lucene.apache.org/solr/downloads.html

이 글을 정리하는 시점에는 8.4.0버전이 최신 버전이고,

운영체제에 맞는 압축 파일(tgz/zip)을 받아서 사용하면 된다.

좀더 상세한 설명은 이전 블로그 글을 참고해도 되고, Solr 설치 매뉴얼을 참고 해도 된다.

 

압축 파일을 푼 다음,

다음 그림과 같이 콘솔 창에서 실행 파일이 있는 bin 폴더로 이동해서 

solr start를 입력해서 Solr를 실행한다.


경고 메시지는 일단 무시하고,

마지막의 “Started Solr server on port 8983.” 문장이 출력되면 

Solr 검색 엔진 서버가 잘 실행된 것이다.

웹 브라우저에서 8983포트로 접속해서 Solr 관리자가 잘 실행되는지 확인한다.

       http://localhost:8983

관리자 페이지에는 서버만 실행한 것이기 때문에 별다른 내용이 없다.

예제를 실행하면,

이 관리자 페이지에서 다양한 Solr 설정이나 기능을 확인할 수 있다.

 

예제에는 클라우드 예제를 먼저 정리하고 있는데, 여기에서는 클라우드 관련 내용은 정리하지 않는다.

서버나 네트워크 전문가가 아니라서 잘 모르는 것도 있고, 개인적으로 클라우드의 필요성을 잘 모르기 때문이다.

어느 정도 규모에 어떻게 적용할지를 모르면 낭비가 발생하고 (서버 규모 === 돈)

그냥 중소 규모의 기업에서 발생하는 데이터는 단독모드로 사용해도 된다고 생각한다.

더우기 검색 엔진을 잘 모르는데, 클라우드 등 다양한 개념이 시작하는 사람에게 도움이 되지 않는다고 생각하기 때문이다.

 

두 번째 예제인 기술상품(Index the Techproducts Data)부터 정리한다.

모든 예제는 설치 경로의 예제(example) 폴더에 있고, Solr 예제에서는 이 폴더의 내용을 지정해서 설치한다.

Solr 예제에 있는 방식으로 실행하면 오류가 발생한다.

데이터를 저장할 techproducts 코어를 생성하라는 설명 없이, 데이터를 저장하는 (post) 명령어만 설명되어 있다.

        solr create -c techproducts

따라서 콘솔에서 위와 같은 명령어를 실행해서 코어를 생성하고, 

Linux/Mac 일때는
        bin/post -c techproducts example/exampledocs/*

Windows는
        java -jar -Dc=techproducts -Dauto example\exampledocs\post.jar example\exampledocs\*

으로 실행하라고 한다.

여기에서 실행 경로는(example\exampledocs) Solr 설치 경로의 하위이지 bin 경로의 하위가 아니다.

각자 다르겠지만, 여기에서는 그림처럼 D:\dev\solr-8.4.0가 Solr 설치 경로이다.

앞서 코어를 생성한 것은 bin 폴더에서 실행한 것이니, 폴더 경로를 잘 맞추어 실행해야 한다.

Solr 설치 경로에서 작업할 경우에는 

        bin/solr create -c techproducts          <= Linux/Mac
        bin\solr create -c techproducts         <= Windows

처음에는 Solr 파일들을 보기 위해 bin에서 시작했고, 이후로는 편의를 위해 Solr 설치 경로를 기준으로 정리한다.

즉, solr start가 아닌 위와 같이 bin\solr start와 같이 정리한다.

그리고, 별도의 표시가 없으면 윈도우 기준으로 정리한 것이다.

 

코어를 생성하고, 데이터를 저장하는 (post) 명령어를 실행해면 뭔가 잘되는 것 같다가 오류가 발생한다.

example\exampledocs 폴더에는 XML 등 다양한 파일이 있는데, post.jar 등의 파일을 색인 할 수 없다는 오류 메시지가 출력된다.

명령어에서 모든 파일(example/exampledocs/*)을 색인 하라고 지정해서 발생한 것으로,

별도의 설정을 하면 되겠지만,

여기서는 빼고 example/exampledocs/*.xml로 수정해서 xml 파일만 색인 하도록 한다.

또는 example/exampledocs/*.json으로 바꿔서 한번 더 실행해도 된다.

 

문서에서 제시하는 방법으로 하지 말고, 다음과 같이 실행해도 된다.

먼저 실행 중 인 Solr가 있으면 다음 명령어로 중지한다.

            bin\solr stop –all

코어를 생성하지 않고 다음과 같이 예제를 실행하는 e 옵션으로 다시 실행한다.

           bin\solr start -e techproducts

기술 제품(techproducts) 예제를 실행하라는 명령어이다.

위 문장들을 읽어보면

techproducts 코어 (Created new core 'techproducts')를 생성하고

D:\dev\solr-8.4.0\example\exampledocs 폴더에 있는 XML 파일을 색인했다는 의미이다 (14 files indexed).

그리고, 마지막에 예제를 잘 실행했으니 관리자 페이지에서 결과를 확인하라고 출력한 것이다.

 

코어(core)는 색인한 데이터를 저장하는 장소로,

RDBMS로는 테이블, 엑셀로는 시트(sheet)와 비슷한 의미이다.

기술 제품을 저장할 코어(techproducts)를 생성하고, 14개의 XML 파일을 읽어서 이 저장소에 저장했다는 의미이다.

저장하다는 것을 검색 엔진에서는 색인 - Indexing 한다고 한다.

 

웹 브라우저로 관리자 페이지에 접속하면,

[Core Selector]에 생성한 techproducts 코어가 추가된 것을 확인할 수 있다.

이 코어를 선택하면,

다음 그림과 같이 하단에는 코어에서 사용할 수 있는 메뉴들이 나오고

우측에는 저장된 데이터 수(Num Docs), 저장된 데이터의 위치(data)등의 개요(Overview)가 출력된다.

기술 문서 데이터가 32건 저장된 것을 확인할 수 있다.

Replication(복제)은 데이터에 오류가 생겼을때 복구하기 위해 생성하는 것으로 기본 2개가 생성되어 있다.

복제도 클라우드와 마찬가지로 여기서 정리하지 않는다.

 

이 방법이 쉽고 간단한데, Solr를 껏다 켠 경우(solr start)  techproducts 코어가 없다고 나온다.

일반적으로 코어를 생성시키면 solr설치 경로\server\solr에 코어 이름으로 폴더가 생성된다.

bin\solr start -e techproducts로 코어를 생성한 경우, solr설치 경로\example에 코어가 생성된다.

따라서, 계속 bin\solr start -e techproducts로 실행해야 하는 단점이 있다.

어차피 예제는 몇번보고 말것이니 이렇게 사용해도 괜찮을 것 같다.

 

이상의 예제 설치 방법에서 처음 것은 Solr 예제 사이트에서 설명한 방식이고,

두 번째 간단한 것은 예제(example) 폴더의 README.txt 파일에 설명된 방식이다.

조금 부족하고 이상하지만 각자에 맞는 방식으로 연습하면 될 것 같다.

그리고, 선택한 방식에 따라 저장된 데이터의 개수에 차이가 있다.

여기서는 두 번째 방식(xml만 색인)으로 색인한 예제를 대상으로 정리한다.

 

코어를 선택하고 "Query" 메뉴를 클릭하면 다양한 데이터 조회(질의-Query)를 할 수 있는 화면(중앙부분)이 나타난다.

중앙부분 하단에 있는 파란 색 버튼 - Execute Query를 클릭하면,

다음 그림과 같이 우측에 저장된 기술 제품 예제 데이터가 Json 형태로 출력된다.

중앙 부분이 검색식을 입력하는 부분이고, 우측 부분은 실행 결과 화면이다.

우측 실행 결과 화면의 상단에 있는 URL을 클릭하면 

웹 브라우저의 주소가 해당 URL로 바뀌면서 다음 그림과 같이 실행된다.

              http://localhost:8983/solr/techproducts/select?q=*%3A*

이 결과는 이전 페이지의 JSon 결과와 같은 것으로,

Solr 검색을 URL로 실행할 수 있다는 것을 의미한다 (RESTful).

즉, URL의 내용을 수정해서 다른 웹브라우저나 탭, 프로그램curl, wget, PostMan등에서 사용할 수 있다.

 

URL의 내용을 정리하면,

http://localhost:8983/solr은 Solr 검색 엔진 서버 주소이고

techproducts는 데이터를 저장하는 코어

select는 데이터 조회를 의미한다.

q는 Query, 즉 검색식을 의미하며 *:* (%3A = : )

콜론(:) 앞의 *는 모든 필드를, 뒤의 *는 모든 값을 의미하는 것으로 모든 데이터를 조회한다는 의미가 된다.

(뒤의 * 대신에 찾고자 하는 값을 지정해서 실행하면, 모든 필드에서 지정한 값을 찾는 검색이 된다.)

저장된 데이터가 32건이니 모든 데이터는 32건이 출력될 것 같지만 10개만 출력된다.

전체 데이터를 조회하는 경우에는 알아서 10개만 반환된다.

 

이 URL은 앞서의 Query 화면에서 (http://localhost:8983/solr/#/techproducts/query)

Execute Query 버튼을 클릭하면서 자동으로 생성된 것으로,

"Query" 메뉴는 검색식을 잘 모르는 초보자들이 검색 조건을 쉽게 만들어서 테스트 해 볼 수 있는 메뉴이다.

개발자들이 각 검색 조건에 값을 지정하고 실행하면, 즉시 실행 결과를 확인할 수 있고

Java와 같은 개발 언어에서 RESTful로 호출해서 사용할 수 있는 URL을 알려주는 것이다.

위 그림에서 동그라미로 표시된 q *:*를 보면

좌측의 q *:*이 조건식을 입력한 것이고

우측 상단의 url이 Query 페이지에서 자동으로 생성된 실행 명령어(URL - q *:*)이고

우측 중앙에 있는 responseHeader에

Solr가 실행한 결과를 반환하면서 무엇을 실행했는지(params, q *:*)가 표시되어 있다.

 

response에 numFound가 찾은 전체 개수이고, start가 몇번째 것 부터 가지고 온 것인지 표시한 것이다.

좌측의 검색 조건 입력부분에서 start 값을 변경하면 response의 값도 동일하게 바뀐다.

즉, 검색한 데이터 중 몇 번째 부터(start), 몇 개(rows) 를 가지고 오라고 지정하는 것이다.

페이징(Paging)처리를 위한 것이다.

docs 다음의 배열( [ ] )은 찾은 데이터의 필드 이름과 필드 값들이 Json 형태로 출력된다.

id, name, features, price, price_c 등의 필드 값이 출력된다.

 

검색 조건들을 지정하는 부분에는 q, start, rows외에도 fq(Filter Query), Sort(정렬),  fl (반환할 필드 리스트), df (default search field 기본 검색 필드), wt (writer type 결과 표시 방법 Json, XML등) 등의 설정을 지정해서 검색 할 수 있다.

보다 자세한 내용은 Solr 문서를 읽어보길 바라고 (값 넣고 실행해 보면 대충 파악 가능), 다른 블로그에 정리된 내용을 참고 해도 된다.

여기에서는 Solr 예제와 관련된 내용을 중심으로 하나씩 정리할 예정이다.

 

 

앞서서 Solr의 기술 제품 예제를 실행시켜봤다.

여기서는 Solr 예제 사이트에 있는 몇 가지 검색(Search - Query) 방법을 정리한다.

 

  1. 설치
  2. 기술제품과 검색식
  3. 스키마(Schema)
  4. DIH (Data Import Handler)

 

 

앞서 정리한 것과 같이, 다음 명령어로 Solr 예제 중 기술 제품을 실행한다.

         bin\solr start -e techproducts

 

첫 번째 검색은 한 단어 검색(Search for a Single Term)이다.

Solr 관리자 페이지에서 techproducts 코어를 선택하고, Query 메뉴를 선택해서 검색을 할 수 있도록 한다.

익숙해지면 웹 브라우저, curl, Postman 등에서 URL로 지정해서 검색하는 것이 편리하다.

 

다음 그림과 같이 Query 메뉴를 실행한 화면에서

q 입력상자에 electronics을 입력하고 실행 버튼(Execute Query)을 클릭해서 검색을 실행한다.

q의 기본 입력값은 *:*로,  콜론(:) 앞의 *는 모든 필드, 뒤의 *는 모든 값을 의미한다.

따라서 모든 필드에 대해서 특정한 값을 조회하면 *:electronics 으로 입력해야 할 것 같지만, electronics 만 사용한다.

 

위 그림에서 id, name, menu, cat 등의 필드 명이 출력되는 것을 볼 수 있다.

이번에는 전체 필드가 아닌, 이 필드 중 cat 필드에 있는 값을 검색한다.

특정한 필드의 특정한 값을 검색할 경우(Field Searches)에는 cat:electronics와 같이 필드명을 콜론으로 구분해서 사용한다.

      curl http://localhost:8983/solr/techproducts/select?q=cat:electronics

electronics으로 검색했을 때는 14건이 검색되었지만,

cat:electronics으로 cat필드에 대해서 검색했을 때는 12건이 검색된다.

 

여러 개의 단어로 검색 하는 구절 검색(Phrase Search)은 쌍따옴표("")를 이용한다.

먼저, SDRAM Unbuffered 로 검색하면, 4건이 검색된다.

      http://localhost:8983/solr/techproducts/select?q=SDRAM%20Unbuffered

 

그리고, "SDRAM Unbuffered"로 검색하면 3건이 검색된다.

      http://localhost:8983/solr/techproducts/select?q=%22SDRAM%20Unbuffered%22

 

쌍따옴표("" %22) 없이 검색하면

구절내의 단어를 분리해서 입력한 단어 2개 중 하나라도 있으면 출력한다.

일종의 OR 검색이 실행된 것이다.

쌍따옴표를 사용할 경우에는 해당한 구절이 있는 문서만 출력한다.

 

Solr 예제의 마지막은 결합 검색(Combining Searches)이다.

electronics music 로 검색하면

      http://localhost:8983/solr/techproducts/select?q=electronics%20music

32건의 문서가 검색된다.

앞서의 예제처럼 electronics이 있거나(OR) music 이 있는 문서가 검색 된 것이다.

 

+electronics +music으로 검색하면 1건이 검색된다.

      http://localhost:8983/solr/techproducts/select?q=%2Belectronics%20%2Bmusic

electronics과 music이 있는 문서만 검색 된 것이다.

즉, electronics가 있고(AND)  music이 있는 문서가 검색된 것으로 일종의 AND 검색이 된 것이다.

 

이번에는 electronics -music 로 검색하면 13건이 검색된다.

electronics가 있지만 music 이라는 단어가 없는(-) 문서만 검색된다.

 

이상의 내용은 Solr의 기본 예제에 있는 내용으로,

전체 필드를 대상으로 한 결합 검색식을 필드에 적용해서 응용할 수 있다.

 

cat:electronics cat:music 으로 검색하면, 12건의 결과가 검색된다.

cat 필드에 electronics가 있거나 music 이 있는 데이터가 조회되는 것이다.

cat:electronics cat:musiccat:electronics OR cat:music 으로 사용할 수 있다.

      http://localhost:8983/solr/techproducts/select?q=cat%3Aelectronics%20cat%3Amusic

      http://localhost:8983/solr/techproducts/select?q=cat%3Aelectronics%20OR%20cat%3Amusic

 

즉 앞서서 필드를 지정하지 않고, 전체 필드로 사용했던 검색식을 필드를 대상으로로도 사용할 수 있다.

 

+cat:electronics +cat:music 은 cat 필드에 electronics가 있고 music 이 있는 데이터로 1건이 검색된다.

+cat:electronics +cat:musiccat:electronics AND cat:music 로도 사용할 수 있다.

      http://localhost:8983/solr/techproducts/select?q=%2Bcat%3Aelectronics%20%2Bcat%3Amusic

      http://localhost:8983/solr/techproducts/select?q=cat%3Aelectronics%20AND%20cat%3Amusic

 

cat:electronics -cat:music 은 cat 필드에 electronics가 있지만 music 이 없는 데이터로 11건이 검색된다.

      http://localhost:8983/solr/techproducts/select?q=cat%3Aelectronics%20-cat%3Amusic

 

이 외에 DMBS의 LIKE 문처럼 cat:elect* 을 사용해서 cat필드의 내용 중 elect으로 시작하는 단어가 있는 문서 14건을 검색할 수 있다.

이 예제는 와일드카드(*)로 부분 문자열 검색을 나타내기도 하지만, 앞서 설명에서 빠진 중요한 개념을 가진 예제이다.

앞서의 첫 이미지를 자세히 보면

cat 필드는 문자열 값을 여러 개 가진 배열 타입인 것을 알 수 있다.

측, cat 필드는 ["electronics", "hard drive"]로 두 개의 문자열(“”)을 가진 배열[]이다.

다른 필드는 필드에 하나의 값을 가지지만 cat과 features는 여러 개의 값을 가진 배열이다.

(필드 타입에 배열이 있다는 점을 기억해야 한다.)

 

그리고, 이상에서 사용한 검색식들은 cat 필드의 값 중에서 검색하고 하는 문자와 일치하는 것만 조회 했다.

cat: electronics은 배열에 저장된 값이 정확하게 electronics일 때만 조회 된 것이다.

cat:elect*으로 조회할 경우에는 "electronics and computer1", "electronics and stuff2"가 있는 것도 조회 되었다.

cat: electronics은 일치하는 값만 조회하기 때문에 이 결과가 조회되지 않았다.

{
        "id":"3007WFP",
        "name":"Dell Widescreen UltraSharp 3007WFP",
        "manu":"Dell, Inc.",
        "manu_id_s":"dell",
        "cat":["electronics and computer1"],
        "features":["30\" TFT active matrix LCD, 2560 x 1600, .25mm dot pitch, 700:1 contrast"],
        "includes":"USB cable",
        "weight":401.6,
        "price":2199.0,
        "price_c":"2199.0,USD",
        "popularity":6,
        "inStock":true,
        "store":"43.17614,-90.57341",
        "_version_":1654893957917704192,
        "price_c____l_ns":219900
},
{
        "id":"VA902B",
        "name":"ViewSonic VA902B - flat panel display - TFT - 19\"",
        "manu":"ViewSonic Corp.",
        "manu_id_s":"viewsonic",
        "cat":["electronics and stuff2"],
        "features":["19\" TFT active matrix LCD, 8ms response time, 1280 x 1024 native resolution"],
        "weight":190.4,
        "price":279.95,
        "price_c":"279.95,USD",
        "popularity":6,
        "inStock":true,
        "store":"45.18814,-93.88541",
        "_version_":1654893957930287104,
        "price_c____l_ns":27995}]
  }

 

Solr 예제에서는 설명되지 않았지만 루씬 검색식에 있는 예제에 정리된 범위(Range)가 있다.

범위는 많이 사용하는 검색식(Query)으로

price:[* TO 100]은 가격(price)이 100($) 이하인 것,

price:[100 TO 500]은 100~500 사이인 것,

price:[100 TO *] 100보다 비싼 것을 검색하는 검색식이다.

보다 자세한 것은 루씬 검색식을 참고 하면 된다.

 

이러한 검색식들은 구글의 검색식과 비슷하게 제공되고,

다양하게 제공되니 예제를 익히고 나서 Solr 문서의 내용을 잘 읽어보길 바란다.

 

Solr 예제를 설치해서 검색하는 방법에 대해서 정의했고,

이번에는 검색을 위해 데이터를 저장하는 방법 중 구조(Schema)를 구성하는 것에 대해서 정리한다.

Solr 예제에서 영화 정보(films)를 이용하여 구조에 대해서 설명하기 때문에, 여기에서도 영화 정보를 이용하여 정리한다.

  1. 설치
  2. 기술제품과 검색식
  3. 스키마(Schema)
  4. DIH (Data Import Handler)

데이터를 저장하기 위해서는 저장하는 장소, 즉 스키마(Schema)에 대해서 정의해야 한다.

대부분의 데이터는 다음 엑셀 그림처럼 name, directed_by, genre, type, id, initial_release_date 같은 컬럼이 있고, 각각의 데이터가 행으로 저장된다.

각각의 컬럼이 어떤 데이터를 가지는 지를 정의하는 것이 스키마로 데이터 베이스(DBMS)의 용어와 동일한 의미로 사용된다.

그림을 보면 배포일자(initial_release_date)는 년월일의 년도 형 데이터 구조를 가지고 있다.

이 외의 모든 컬럼은 일반 (텍스트, 문자열) 형 데이터 구조를 가지고 있다.

숫자가 있었다면 숫자형 데이터를 가지게 된다.

이렇게 구조를 미리 지정해서 저장하는데, Solr에서는 이 스키마를 미리 정의하지 않고도 사용할 수 있다. 

Elasticsearch도 동일한데, 이것을 Schemaless라고 한다.

하지만, 가급적 정의해서 사용하는 것이 좋고, 그 이유를 Solr 예제에서 제공하고 있다.


Solr를 실행하고(solr star), 다음 명령어로 films 코어를 생성한다.

      bin/solr create -c films             <- Linux, Mac

      bin\solr delete -c films           <- Windows

 

예제 폴더(example)에는 앞서 정리한 기술제품(techproducts)외에도 영화 정보(films) 예제 파일이 있다.

이 파일의 내용을 생성한 films 코어에 저장한다.

다음 명령어로 생성한 films 코어에 데이터를 저장한다.

      bin/post -c films example/films/films.json                                                         <- Linux, Mac

      java -jar -Dc=films -Dauto example\exampledocs\post.jar example\films\*.json     <- Windows

영화 정보(films) 폴더에는 films.csv, films.json, films.xml 3개의 파일이 있다.

모두 동일한 영화 정보 데이터를 가지고 있고, 이 중에서 json 파일의 내용을 films 코어에 저장하는 명령어이다.

films 코어를 생성하고, 데이터를 저장하면 위 그림과 같이 오류가 발생한다.

그리고, Solr 관리자 화면에서 데이터를 조회(Query)하면, 5개의 데이터가 저장되어 있다.

화면을 스크롤 해서 이름(name) 필드의 값을 보면, 모두 실수형이고 배열 ([])이다.

  {
    "id":"/en/45_2006",
    ~~ 생략
    "name":[0.45],
  {
    "id":"/en/9_2005",
    ~~ 생략
    "name":[9.0],
  {
    "id":"/en/69_2004",
    ~~ 생략
    "name":[69.0],
  {
    "id":"/en/300_2007",
    ~~ 생략
    "name":[300.0],
    "_version_":1655399262942396417},
  {
    "id":"/en/2046_2004",
    ~~ 생략
    "name":[2046.0],
    "_version_":1655399262943444992}]
  }

Solr 관리자 화면의 스키마(Schema) 화면은 선택된 코어의 필드 구조를 관리 하는 페이지로,

name 필드를 선택하면 type이 pdoubles(실수) 로 생성되어 있다.

(이 화면에서 데이터가 저장되면서 자동으로 생성된 모든 필드들을 볼 수 있다.)

영화 예제 폴더 중에서 csv 파일을 엑셀로 열면, name (A)컬럼의 앞에 있는 5개 데이터가 숫자이다.

그리고, 6번째 데이터부터 약 1000개의 데이터는 문자이다.

Solr (실재로는 Lucene)에서는 첫 데이터로 컬럼의 구조를 정의하는데,

위와 같이 첫 데이터가 숫자면 컬럼은 숫자(실수)로 정의한다.

즉, 앞서의 데이터 저장시 발생한 오류는 숫자 컬럼에 문자가 입력되어 발생한 오류이다.

 

name 필드를 삭제하고 새로 생성해서, 이 오류를 해결한다.

Schema 페이지(앞서의 이미지)에서 name 필드를 선택하면 활성화 되는 하단의 [delete field] 버튼을 클릭해서 name 필드를 삭제한다.

그리고, 상단의 [Add Field] 버튼을 클릭하여 name 필드를 다시 생성한다.

그림과 같이 생성할 필드명 name을 입력하고, field type에는 문자열(text_general)을 지정하고 필드를 생성한다 (Add Field).

name 필드가 생성된 것을 확인하고, 앞서 실행했던 데이터 저장을 다시 실행하면(post),

그림과 같이 오류 없이 실행되는 것을 확인 할 수 있다.

Solr 관리자 페이지 Query 에서 데이터를 조회하면, 1100개의 데이터가 저장되어 있다.

Solr나 Elasticsearch에서는 이 스키마를 미리 정의하지 않고도 사용할 수 있지만(Schemaless)

이와 같이 데이터의 구조를 제대로 고려하지 않고 사용하면 문제가 발생하기 때문에 스키마를 정의해서 사용하는 것이 좋다.

이 문제 외에도 긴 문장의 경우에는 형태소 분석 등을 하게 된다.

이런 필드와 그냥 저장하는 필드 등을 미리 정의해야 해서 스키마를 미리 정의해서 사용하는 것이 좋다.

또다른 필드 추가 방법

Solr 관리자 화면에서 필드를 지우거나 생성해도 되고, 다음과 같이 RESTful로 처리해도 된다.

Linux: curl -X POST -H 'Content-type:application/json' --data-binary '{"delete-field" : { "name":"name" }}' http://localhost:8983/solr/films/schema
Windows: curl -X POST -H "Content-type:application/json" --data-binary "{\"delete-field\" : { \"name\":\"name\" }}" http://localhost:8983/solr/films/schema

curl 사용시 리눅스는 홑따옴표(')를 사용하고, 윈도우는 쌍따옴표(")를 사용하는 차이가 있다

윈도우는 쌍따옴표(")를 사용하기 때문에 Json 키/값 표현에 사용되는 쌍따옴표(")는 \"로 표현해야 한다.

필드를 삭제(delete-field) 할 때에는 삭제할 필드명(name)을 name으로 지정한다.

 

Linux: curl -X POST -H 'Content-type:application/json' --data-binary '{"add-field": {"name":"name", "type":"text_general", "multiValued":false, "stored":true}}' http://localhost:8983/solr/films/schema
Windows: curl -X POST -H "Content-type:application/json" --data-binary "{\"add-field\": {\"name\":\"name\", \"type\":\"text_general\", \"multiValued\":false, \"stored\":true}}" http://localhost:8983/solr/films/schema

필드를 추가(add-field) 할 때에는 필드명(name), 필드 타입(type), 다중값 여부(multiValued, 배열), 저장여부(stored)등을 지정해서 생성한다.

이와 관련된 정리는 다른 블로그에 잘 정리되어 있다.

{
    "add-field": {
                    "name":"name", 
                    "type":"text_general", 
                    "multiValued":false, 
                    "stored":true
                }
}        

이상과 같이 RESTful 방식으로 스키마를 생성하는 자세한 방법은 Solr 도움말을 참고 하면 된다.

 

스키마에 대해서 조금 더 정리하면,

적당한 편집기로 example\techproducts\solr\techproducts\conf 폴더에 있는 managed-schema 파일을 연다.

example\techproducts\solr\techproducts 폴더는 앞서 정리한 기술제품(techproducts)의 코어가 있는 폴더이고

managed-schema 파일은 코어의 스키마에 대해서 정의하는 XML 파일이다.

   <field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" /> 

   <field name="pre" type="preanalyzed" indexed="true" stored="true"/>
   <field name="sku" type="text_en_splitting_tight" indexed="true" stored="true" omitNorms="true"/>
   <field name="name" type="text_general" indexed="true" stored="true"/>
   <field name="manu" type="text_gen_sort" indexed="true" stored="true" omitNorms="true" multiValued="false"/>
   <field name="cat" type="string" indexed="true" stored="true" multiValued="true"/>
   <field name="features" type="text_general" indexed="true" stored="true" multiValued="true"/>
   <field name="includes" type="text_general" indexed="true" stored="true" termVectors="true" termPositions="true" termOffsets="true" />

   <field name="weight" type="pfloat" indexed="true" stored="true"/>
   <field name="price"  type="pfloat" indexed="true" stored="true"/>
   <field name="popularity" type="pint" indexed="true" stored="true" />
   <field name="inStock" type="boolean" indexed="true" stored="true" />

   <field name="store" type="location" indexed="true" stored="true"/>

앞서 정리했던, 기술제품(techproducts)의 필드들이 정의되어 있다.

필드명(name), 색인여부(indexed), 저장여부(indexed), 필수 입력(required), 다중값여부(multiValued)등의 속성을 지정했다.

이와 관련된 정리는 다른 블로그에 잘 정리되어 있다.

영화 정보의 managed-schema 파일 (\server\solr\films)에는 이러한 정의가 없다 (Schemaless).

 

 

'서버 > 검색엔진' 카테고리의 다른 글

1. Solr 예제 분석 - 설치  (1) 2020.01.12
2. Solr 예제 분석 - 기술제품과 검색식  (3) 2020.01.12
4. Solr 예제 분석 - DIH  (0) 2020.01.12
1. Solr로 만드는 단순 게시판: 각종 설치  (18) 2017.08.24
2. SolrJ 사용법  (0) 2017.08.24

마지막 예제 분석으로 DIH (Data Import Handler)를 정리한다.

앞서의 기술제품과 영화 예제들은 정리된 데이터를 저장(색인)한 것이고,

DIH는 원본 데이터에서 데이터를 추출해서 저장하는 방법에 대한 예제이다.

  1. 설치
  2. 기술제품과 검색식
  3. 스키마(Schema)
  4. DIH (Data Import Handler)

 

Solr 예제 폴더(example\example-DIH\)에는 atom, db, mail 등의 데이터 예제가 있다.

이 중에서 db (hsqldb) 예제를 정리한다.

주의: SQL이나 데이터 베이스에 대한 개념이 없다면, 다음 내용을 이해하는데 어려울 수 있다.


실행 중인 Solr를 멈추고,

      bin\solr stop -all

다음 명령어로 DIH 예제를 실행한다.

      bin\solr -e dih

Solr 관리자 화면으로 접속하면 5개의 코어가 생성된 것을 확인할 수 있다. 

이 중에서 db 코어를 선택하고, Query 화면에서 데이터를 조회하면 비어 있다 (numFound=0).

Solr 관리자 화면의 Dataimport 메뉴를 선택한 후, 화면 오른쪽의 Configuration을 클릭해서 실행할 SQL문을 확인한다.

중앙의 실행(Execute) 버튼을 클릭해서, 이 SQL문을 실행한다.

실행(Execute) 버튼을 클릭하고, Auto-Refresh Status를 체크해서 자동으로 갱신되게 하거나,

Refresh Status를 클릭해서 수동으로 갱신해서 처리 결과를 확인한다.

녹색 배경으로 16개의 데이터(document)가 저장되었다는 메시지가 나타나면, 잘 실행된 것이다.

Query 메뉴에서 저장된 데이터를 확인 할 수 있다.

앞서 정리한 기술 제품의 내용과 동일한 데이터로,

기술 제품 예제는 이 데이터 베이스의 내용을 XML로 만든 파일을 저장한 것이다.

 

간단하게 db 예제 사용법을 정리했고, 상세한 내용을 정리한다.

예제로 사용된 db는 hsqldb이지만

Dataimport 메뉴에서 확인한 다음의 SQL문을 보면 기본적인 SQL문만 사용되었기 때문에 hsqldb라는 것에 부담을 가질 필요는 없다.

<dataConfig>
    <dataSource driver="org.hsqldb.jdbcDriver" url="jdbc:hsqldb:${solr.install.dir}/example/example-DIH/hsqldb/ex" user="sa" />
    <document>
        <entity name="item" query="select * from item"
                deltaQuery="select id from item where last_modified > '${dataimporter.last_index_time}'">
            <field column="NAME" name="name" />

            <entity name="feature"  
                    query="select DESCRIPTION from FEATURE where ITEM_ID='${item.ID}'"
                    deltaQuery="select ITEM_ID from FEATURE where last_modified > '${dataimporter.last_index_time}'"
                    parentDeltaQuery="select ID from item where ID=${feature.ITEM_ID}">
                <field name="features" column="DESCRIPTION" />
            </entity>
            
            <entity name="item_category"
                    query="select CATEGORY_ID from item_category where ITEM_ID='${item.ID}'"
                    deltaQuery="select ITEM_ID, CATEGORY_ID from item_category where last_modified > '${dataimporter.last_index_time}'"
                    parentDeltaQuery="select ID from item where ID=${item_category.ITEM_ID}">
                <entity name="category"
                        query="select DESCRIPTION from category where ID = '${item_category.CATEGORY_ID}'"
                        deltaQuery="select ID from category where last_modified > '${dataimporter.last_index_time}'"
                        parentDeltaQuery="select ITEM_ID, CATEGORY_ID from item_category where CATEGORY_ID=${category.ID}">
                    <field column="DESCRIPTION" name="cat" />
                </entity>
            </entity>
        </entity>
    </document>
</dataConfig>

SQL이 나열된 XML은 Dataimport 메뉴에서 확인 할 수도 있고,

db-data-config.xml 설정(example\example-DIH\solr\db\conf)파일에서도 확인/ 수정 할 수 있다.

참고: Solr의 기본 설정 파일인 solrconfig.xml에서 다음과 같이 db-data-config.xml 을 지정해야 db-data-config.xml 에서 위와 같은 내용을 작성해서 사용할 수 있다.

 <lib dir="${solr.install.dir:../../../..}/dist/" regex="solr-dataimporthandler-\d.*\.jar" />

생략

  <requestHandler name="/dataimport" class="solr.DataImportHandler">
    <lst name="defaults">
      <str name="config">db-data-config.xml</str>
    </lst>
  </requestHandler>

 

dataSource는 Java의 dataSource와 같은 것으로, 데이터베이스 접속에 필요한 driver, url과 id, password등을 입력한다.

예제에서는 sa 계정에 비밀번호없이 접속한다.

 

이상의 SQL문을 하나 하나 정리할 수는 없고, 개념으로 정리한다.

사용된 hsqldb의 테이블 구조를 알아야 사용된 SQL문을 이해하기 쉽다.

hsqldb의 테이블 구조와 데이터는 example\example-DIH\hsqldb 폴더에 있는 ex.script 파일에 정의되어 있다.

이 내용 중 테이블 정의와 관련된 내용을 ERD로 정리하면 다음 그림과 같다.

ITEM(제품) 테이블이 기술 제품의 핵심 테이블로, 제품의 특성(FEATURE) 및 종류(CATEGORY) 테이블과 1:n으로 연결된다.

제품 테이블의 ID가 기본키(Primary Key)로, 특성(FEATURE)과 종류(ITEM_CATEGORY) 테이블의 ITEM_ID 필드(Foregin Key)와 관계(Relation)를 가진다.

즉, 하나의 제품이 여러 개의 특성과 종류를 가진다.

ITEM_CATEGORY은 연관 테이블(Association Table)로 여러개의 종류를 가지기 위해 사용되었다

(데이터 베이스와 관계된 개념은 여기서는 정리하지 않는다.)

주의: 데이터베이스에서는 이와 같이 데이터를 정규화해서 저장하지만, 검색엔진에서는 하나의 데이터로 묶어서 저장한다.

 즉, 데이터베이스에서 1:n으로 구성한 데이터는 검색엔진에서 배열([ ]), 즉 multiValued로 저장한다.

 

dataSource 태그는 접속 정보를 입력하고,

document 태그는 처리할 데이터를 정의하는 태그로 기본 구조는 다음과 같다.

    <document>
        <entity name="item" query="select * from item"
                deltaQuery="select id from item where last_modified > '${dataimporter.last_index_time}'">
            <field column="NAME" name="name" />
        </entity>
    </document>		

문서(document)를 구성하는 것을 entity라고 한다.

이 entity는 처음에는 query에 지정된 데이터로 채우고, 이후에 증가된 데이터는 deltaQuery로 지정된 데이터로 채운다.

처음에는 기존에 있던 데이터에 대해서(query) 전체 색인을 진행하고(Full indexing), 이후에는 증가된 데이터만(deltaQuery) 색인하도록 작성한다.

즉, 처음에는 모든 제품을 가져오고 select * from item

마지막 색인을 한 시간(last_index_time) 이후에 추가된 제품(last_modified)만 조회해서 색인한다.

last_modified > '${dataimporter.last_index_time}'

 

이렇게 문서를 생성해서 저장(색인)하면 되지만,

앞서 ERD로 정리한 것처럼 기술 제품은 여러 개의 제품의 특성(FEATURE)과 종류(CATEGORY)를 가지고 있다.

이것을 그냥 Join으로 작성하면, 1:n의 관계를 가지기 때문에 하나의 제품이 여러 개 조회되는 문제가 생긴다.

 

제품 특성(FEATURE) 테이블을 예로 정리하면,

제품(item)을 가져오는 entity 태그 하위에(안에), 특성(feature)를 가져오는 entity 태그를 추가한다.

<entity name="item" query="select * from item"
    ~~ 생략 ~~
	<entity name="feature"  
			query="select DESCRIPTION from FEATURE where ITEM_ID='${item.ID}'"
			deltaQuery="select ITEM_ID from FEATURE where last_modified > '${dataimporter.last_index_time}'"
			parentDeltaQuery="select ID from item where ID=${feature.ITEM_ID}">
		<field name="features" column="DESCRIPTION" />
	</entity>
</entity>

ERD의 관계(Relation)를 태그의 하위 계층으로 표현하는 것이다.

 

특성(FEATURE) 테이블에서 데이터를 가지고 오는 것은 제품(ITEM) 테이블에서 가지고 오는 것과 동일하다.

처음에는(query) 테이블의 모든 데이터를 가지고 오고,

이후에는(deltaQuery) 증가된 데이터만 가지고 온다. last_modified > '${dataimporter.last_index_time} 

 

여기에 추가적인 것이 parentDeltaQuery로 부모가 누구인지 지정하는 부분이 있다.

가지고 온 데이터를 누구에게 넣어줄 것인지를 지정하는 것으로,

제품(ITEM) 테이블에서 ID 필드가 기본키(paimary key)로 특성(FEATURE)테이블의 ITEM_ID 필드로 관계가 설정되어 있어, 이 값으로 서로를 식별하게 된다.

        select ID from item where ID=${feature.ITEM_ID}

즉, 특성 테이블의 값을 가지고 온 후(feature), 제품 테이블의 해당하는 ID를 찾아서,

특성의 값(DESCRIPTION)을 features로 저장(색인)한다.

        <field name="features" column="DESCRIPTION" />

주의: item(최상위 entity)에서도 field 태그를 이용해서 NAME을 name으로 저장하도록 지정했다.

색인으로 저장할 컬럼들은 모두 field 태그로 지정해야 하고, managed-schema에 정의해야 한다.

DIH에서는 managed-schema에 지정되지 않은 컬럼은 저장되지 않는다.

앞서 정리한 Shemaless가 적용되지 않는 것 같다.

 

parentDeltaQuery로 표현된 방식은 중요한 개념이라 다시 정리하면,

일반적인 S/W 개발에서는 제품(ITEM) 테이블에서 데이터를 가지고 오고, 

각각의 행에 있는 제품의 특성(ID) 필드의 값에 맞는 것을 특성(FEATURE) 테이블에서 찾아서

해당하는 설명(DESCRIPTION)을 가지고 오게 작성한다. 

그리고 SQL로 표현하면 다음과 같다.

      select a.*, 
            (select DESCRIPTION from FEATURE where ITEM_ID=a.ID) as features
      from ITEM a

주의: 제품(ITEM)과 특성(FEATURE)의 관계는 1:n 이기 때문에 이 SQL문을 그대로 쓰면 오류가 발생해서 실제론 stragg 처리를 해야 한다.

일반적인 S/W 개발에서는 제품(ITEM) 데이터를 가지고 오면서, 필요한 구성원 데이터(특성-FEATURE)을 가지고 오도록 작성한다.

(모 상용 검색엔진이 이런식으로 사용한다.)

 

이 개념과 반대로, 예제에서는 제품(ITEM) 데이터를 가지고 와서 저장하고

필요한 구성원 데이터(FEATURE)를 가지고 와서 저장된 제품(ITEM)에 넣어주는 방식으로 작성되었다.

 

이렇게 하는 이유는 제품(ITEM)과 특성(FEATURE) 테이블로 설명하는 것은 부족한 것 같아서 게시판 예제로 정리한다.

게시판에 게시물이 있고(post), 하나의 게시물에 여러 개의 댓글(reply)이 달린다.

일반적인 S/W 개발 방식으로 데이터를 추출하면, 게시물이 색인 될 때의 댓글도 같이 색인하면 된다.

하지만, 색인 된 이후에 추가된 댓글들은 색인하기 어렵다.

따라서, 게시물은 게시물 데로 색인하고, 댓글은 댓글데로 색인해서

추가 댓글이 있으면 추출해서 해당 게시물에 넣어주는 것(parentDeltaQuery)이 더 좋은 방식일 것이다.


종류(category, item_category)도 동일한데, 연관 테이블(Association Table)이 있어서 좀 더 복잡하다.

원리는 동일하니 직접 확인하고, 여기에서는 정리 하지 않는다.

 

이상의 정리는 Solr DIH 예제를 정리한 것으로, 보다 상세한 내용은 Solr DIH 예제에 정리된 내용을 읽어보면 된다.

이상으로 기술 제품에 대해 검색하는 방법,

기술 제품과 영화 정보 구축을 위해 스카마를 구성하는 방법,

스키마에 맞춰서 데이터를 저장하는 방법(색인)을 Solr 예제로 정리하였다.

 

Solr 예제 문서에 빠진 내용은 색인을 진행하는 시간 처리로,

초기에 전체 데이터를 대상으로 진행하는 풀색인은 앞서 정리한 것처럼 Dataimport 메뉴에서 실행해도 되고, 다음과 같이 RESTful로 실행해도 된다.

       curl http://localhost:8983/solr/dih/dataimport?command=full-import

풀색인은 사람이 진행하지만,
증가되는 데이터는 실시간으로 색인하는 경우가 아니면, 지정된 시간에 증가된 데이터에 대해서만 진행한다(delta-import).

지정된 시간에 실행하기 위해 별도의 프로그램을 제작하거나 플러그인을(DataImportScheduler) 이용할 수 있는데,

Solr 문서에서는 운영체제의 스케쥴 기능을 이용하길 원하는 것 같다.

좋은 스케쥴 기능이 많다며, DataImportScheduler 같은 것을 사용하는 것은 바퀴를 다시 발명하는 것이라고 한다.

증분만 실행하는(delta-import) 다음 명령어를 crontab에 등록해서 지정된 시간에 실행되게 사용한다. 

        curl http://localhost:8983/solr/dih/dataimport?command=delta-import

 

이상의 내용 외에 사용자가 입력한 검색어를 Solr로 전송하고, 결과를 받아서 출력하는 프로그램(Java)에서 사용하는 SolrJ와

색인하는 방법에 대한 구체적인 내용은 정리하지 않았다.

SolrJ와 관련된 내용은 이전 블로그 내용을 참고하거나 다른 자료를 찾아보면 된다.

색인하는 방법은 한국어 등을 색인 하기 위해서 필요한 것으로 형태소 분석과 관련된 내용을 찾아보면 된다.

 

검색엔진인 루씬(Lucene)을

쉽게 사용할 수 있도록 만든 오픈 소스 Solr를 쉽게 익히기 위해,

Java에서 Solr를 쉽게 사용할 수 있도록 해주는 SolrJ를 이용하여

검색엔진을 데이터 베이스처럼 사용하여 웹용 게시판을 제작한다.

실제로는 이렇게 사용하지 않지만,

SolrJ의 사용법을 익히기 위해

Spring + SolrJ + Solr (Lucene) 기반으로 게시판을 구현하였다.

1. Solr로 만드는 단순 게시판: 각종 설치

2. SolrJ 사용법

3. Solr 단순 게시판 (CRUD)

4. Solr 페이징과 검색

구축 환경

- Solr 6.6.0

- SolrJ 6.6.0

- Eclipse Neon 2 + STS

- Tomcat 8.5, JDK 1.8


게시판을 제작하기 위해 필요한 설치를

다음과 같이 간단하게 정리하고, 상세한 자료는 찾아보길 바란다.

    1. Solr 설치
    2. Eclipse에 STS 설치 및 프로젝트 생성
    3. SolrJ 설치

이상의 설치가 되어 있거나 잘 아는 경우에는

solr에서 core(solrBoard)만 생성해서 실행하고,

gitHub의 코드를 다운 받아서 실행하면 된다.


먼저 Solr를 설치하기 위해 다음 주소에서 다운로드 받는다.

    http://lucene.apache.org/solr/

우측 상단의 [DOWNLOAD] 링크를 선택해서

각자의 운영체제에 맞추어 최신 버전을 다운로드 받는다.

여기서는 윈도우(10)를 기준으로 작성하기 때문에

solr-6.6.0.zip 파일을 다운받아서

그림과 각이 압축을 해제해서 사용하면 된다.

콘솔창(cmd)을 열어서

Solr의 압축 파일을 해제한 경로 하위에 있는 bin 폴더로 이동한 후에,

다음 명령어를 실행해서

Solr 서버를 실행하고 (start), solrBoard라는 코어(core)를 생성한다.

코어는 DBMS의 테이블(Table)과 비슷한 개념으로,

solrBoard 라는 데이터 저장 공간을 생성한 것이다.

> solr start  (Solr시작)

> solr create -c solrBoard  (코어생성)

> solr restart -p 8983 (Solr 재시작)


웹 브라우저에서 Solr 관리자 페이지에 접속하여,

그림과 같이 solrBoard가 생성되어 있으면

Solr 설치와 데이터를 저장할 코어 생성이 완료 된 것이다.

http://localhost:8983/solr

위 그림의 중앙 위쪽에 빨간색 박스안에 어떤 메시지가 있다면

오류가 발생한 것이니 수정하고 Solr를 재시작해야 한다.


solr에서 한글을 사용할 수 있도록 설정한다.

solr를 설치한 경로 하위에서 managed-schema 파일을 찾아서 수정한다.

코어를 solrBoard로 생성했기 때문에

solrBoard란 폴더가 다음과 같이 생기고 설정 폴더(conf)에서 찾을 수 있다.

solr-6.6.0\server\solr\solrBoard\conf


위 그림에서 _text_와 관련된 두 줄을 삭제하고

다음 코드를 적당한 위치(120 라인 앞)에 추가한다.


<dynamicField name="*_txt_ko" type="text_ko" indexed="true" stored="true"/>
<fieldType name="text_ko" class="solr.TextField">
    <analyzer type="index">
        <tokenizer class="org.apache.lucene.analysis.ko.KoreanTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
        <filter class="solr.ClassicFilterFactory"/>
        <filter class="org.apache.lucene.analysis.ko.KoreanFilterFactory" hasOrigin="true" hasCNoun="true" bigrammable="false"/>
        <filter class="org.apache.lucene.analysis.ko.HanjaMappingFilterFactory"/>
        <filter class="org.apache.lucene.analysis.ko.PunctuationDelimitFilterFactory"/>
        <filter class="solr.StopFilterFactory" words="lang/stopwords_ko.txt" ignoreCase="true"/>        
        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
    </analyzer>

    <analyzer type="query">
        <tokenizer class="org.apache.lucene.analysis.ko.KoreanTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
        <filter class="solr.ClassicFilterFactory"/>
        <filter class="org.apache.lucene.analysis.ko.KoreanFilterFactory" hasOrigin="true" hasCNoun="true" bigrammable="true"/>
        <filter class="org.apache.lucene.analysis.ko.WordSegmentFilterFactory" hasOrijin="true"/>
        <filter class="org.apache.lucene.analysis.ko.HanjaMappingFilterFactory"/>
        <filter class="org.apache.lucene.analysis.ko.PunctuationDelimitFilterFactory"/>
        <filter class="solr.StopFilterFactory" words="lang/stopwords_ko.txt" ignoreCase="true"/>
        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
    </analyzer>
</fieldType>


검색엔진을 제대로 구축하는 것이 아니기 때문에,

위 코드에서 지정된 불용어(stopwords_ko.txt)파일은

인터넷에서 다운로드받아서 지정된 폴더에 추가하거나

위 코드에서 해당 행을 제거하고 사용해도 된다.


다음으로 managed-schema 파일에서

다음과 같이 게시판의 필드를 추가한다.

  <field name="brdwriter" type="text_ko" multiValued="true" indexed="true" required="true" stored="true"/>
  <field name="brdtitle" type="text_ko" multiValued="true" indexed="true" required="true" stored="true"/>
  <field name="brdmemo" type="text_ko" multiValued="true" indexed="true" required="true" stored="true"/>
  <field name="brddate" type="string" multiValued="false" indexed="true" required="true" stored="true"/>


루씬 한글 형태소 분석기인 arirang analyzer (arirang.lucene-analyzer-x.x.jar, arirang-morph-1.0.x.jar)을

루씬 커뮤니티에서 다운받아서

Solr를 설치한 웹 폴더 (\solr-6.0.0\server\solr-webapp\webapp\WEB-INF\lib)에 복사한다.


이상의 설정이 적용되도록 Solr 서버를 재 가동한다.

solr restart -p 8983


이상의 설정들은 한글을 인텍싱하고 조회하는 설정을 처리한 것으로

이상의 내용들을 생략하고, Solr만 실행해도 테스트를 진행 할 수 있다.

게시판 예제는 쉽게 Solr를 실행하고 테스트 해 볼 수 있도록

한글 처리와 필드 정의 없이 사용하도록 제작되었다.


이상으로 Solr 설정을 간단하게 정리하였다.

자세한 설치와 설명은 검색을 하거나 여기를 참고하면 된다.


주의 1: 필드 정의에서 multiValued를 true로 지정했다. 필드 지정을 하지 않으면 기본값이 true 이기 때문이고, 따라서 게시판 예제에서 brdtitle[0]}, brdwriter[0]등과 같이 사용한다. false이면 brdtitle, brdwriter등으로 사용한다.

주의 2: 필드 정의에서 brddate를 문자열로 정의하였다. 필드 지정을 하지 않으면 tdates로 지정된다. 게시판 예제는 필드 지정없이 사용하는 것을 기본으로 하기 때문에 위와 같이 문자열로 지정한 경우 jsp 파일에서 formatDate를 사용한 것을 모두 다음과 같이 수정하여야 한다.

string으로 지정한 경우: <fmt:formatDate pattern = "yyyy-MM-dd" value = "${listview.brddate[0]}" />

tdates로 지정한 경우: <c:out value="${listview.brddate}"/>


다음으로 Eclipse에 STS (Spring Tool Suite)를 설치하고,

새로운 프로젝트(solrBoard)를 생성한다.


Eclipse의 Help > Eclipse Marketplace 메뉴에서 STS를 찾아서 설치한다.

File > New > Other 메뉴를 실행하고,

그림과 같이 Spring > Spring Legacy Project를 선택해서

새로운 Spring 프로젝트를 생성한다.


프로젝트 이름을 solrBoard로 입력하고,

템플릿을 Spring MVC Project로 지정해서 다음으로 넘어간다.

적당한 패키지 이름을 지정한다.

solrBoard로 프로젝트가 생성되었으면

Tomcat 서버에 등록해서 실행한다.

웹 브라우저에서

다음과 같이 입력하여 그림과 같이 실행되면

Spring 기반의 게시판을 제작할 준비가 된 것이다.

http://localhost:8080/solrBoard/

톰캣 실행시 많은 문제가 발생 할 수 있다.

Java 버전이 안 맞거나,

Tools.jar 파일이 없다거나,

Maven이 제대로 라이브러리를 설치하지 못하는 등의 문제가 발생할 수 있다.


실행은 되었지만 한글이 깨어지는 경우

solrBoard\src\main\webapp\WEB-INF\views 폴더에 있는

home.jsp의 첫 행에 다음 문장을 추가하면 된다.

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>


마지막으로 SolrJ를 설치한다.

다음 웹 사이트(Maven Repository)에 접속해서

설치할 SolrJ의 버전을 선택한다.

https://mvnrepository.com/artifact/org.apache.solr/solr-solrj

여기서는 현재 최신 버전인 6.6.0을 선택한다.


Maven 탭의 내용을 복사한다.



SolrJ를 사용할 프로젝트(solrBoard)의 pom.xml 파일을 열어서

적당한 위치에 붙여넣기하고 저장한다.

저장만 해도 SolrJ가 설치가 되지만

제대로 설치되지 않으면

solrBoard 프로젝트를 선택하고

컨택스트 메뉴에서 Run As > Maven Install로 설치하면 된다.







'서버 > 검색엔진' 카테고리의 다른 글

3. Solr 예제 분석 - 스키마(Schema)  (0) 2020.01.12
4. Solr 예제 분석 - DIH  (0) 2020.01.12
2. SolrJ 사용법  (0) 2017.08.24
3. Solr 단순 게시판 (CRUD)  (0) 2017.08.24
4. Solr 단순 게시판 - 페이징과 검색  (0) 2017.08.24

Solr의 사용법을 익히기 위해

Spring + SolrJ + Solr (Lucene) 기반으로 게시판을 구현하였다.

1. Solr로 만드는 단순 게시판: 각종 설치

2. SolrJ 사용법

3. Solr 단순 게시판 (CRUD)

4. Solr 페이징과 검색


Solr등 각종 설치와 관련된 상세한 내용은 이전 글을 참고하고,

여기서는 간단하게 Solr를 다운로드 받은 후

다음과 같이 실행하여 Solr 서버의 설정을 간단하게 마친다.

그리고, github에서 게시판 소스를 받아서 예제를 실행하면 된다.

> solr start
> solr crate -c sqlBoard
> solr restart -p 8983


게시판과 관련된 설명은

기존에 Spring 4 + MyBatis 3 + MariaDB로 개발했던 게시판의 내용을 읽어 보길 바라고

여기서는 게시판에 대한 설명보다는 SolrJ의 사용법을 중심으로 정리한다.


SolrJ는 Java에서 Solr 서버를 제어할 수 있도로 다양한 기능을 제공하는 클래스로,

사용법은 아주 간단하고, Solr 문서 사이트에 쉽게(?) 설명되어 있다.

이 내용을 토대로 SolrJ의 몇 가지 기능을 정리하였다.


먼저, Java에서 SolrJ를 이용하여 Solr 서버에 접속하는 코드이다.

String urlString = "http://localhost:8983/solr/solrBoard";

SolrClient solr = new HttpSolrClient.Builder(urlString).build();

HttpSolrClient 클래스를 이용하여 접속할 Solr 서버의 주소를 지정한다.

다만, 하나의 Solr 서버에는 많은 코어(core)가 있을 수 있기 때문에

사용하고자 하는 코어(solrBoard)를 지정해야 한다.


다음으로, Solr 서버에서 데이터를 조회하는(Query) 코드이다.

        SolrQuery query = new SolrQuery();
        query.setQuery("*:*");
        query.setRows(Integer.MAX_VALUE);
        query.addSort("id", ORDER.desc);

        QueryResponse rsp = solr.query(query);
        SolrDocumentList docs=rsp.getResults(); 
        for(int i=0;i<docs.getNumFound();i++){
            System.out.println(docs.get(i));
        }

SolrQuery클래스를 이용하여

조회(Query)할 데이터의 조건을 설정하고 solr.query로 실행한다.

solr 변수는 Solr 서버에 접속하면서 생성한 변수이다.


조회할 데이터의 조건은

루씬(Lucene) 문법으로 여기서는 자세하게 정리하지 않는다.


코드에 사용된 조건은

모든 데이터(*.*)를 

페이징 처리 없이 모두 가지고 와서(Integer.MAX_VALUE) 출력한다.

데이터 정렬(sort)은 id 필드의 값이 큰 순서(Descending)로 하였다.


조건에 맞추어 반환된 데이터(SolrDocumentList)는 Json 형식의 배열로 반환되고

getNumFound() 함수로 총 데이터의 개수를 알 수 있다.


데이터를 추가(수정)하는 방법은

다음 코드와 같이 SolrInputDocument를 이용한다.

        SolrInputDocument solrDoc = new SolrInputDocument();
        solrDoc.addField("id", 1);
        solrDoc.addField("title", "단순게시판");
        
        solr.add(solrDoc);
        solr.commit();

SolrInputDocument로 생성한 solrDoc변수에

필드명과 저장할 값을 지정해서 addField() 함수로 추가하면 된다.

필드명이 Solr 서버(실제로는 코어, managed-schema)에 있을 경우

지정된 데이터 형태로 저장되고,

필드명이 없을 경우 Solr 서버가 판단하여 적정한 데이터 형태로

지정해서 필드를 생성한 뒤 저장한다.


Solr에서 core를 생성할 때,

기본 설정으로 id 필드를 키 필드(Primary key, Unique Field)로 한다.

따라서, 하나의 글을 저장할 때,

이 키 필드(id)의 값이 있으면 수정, 없으면 신규로 저장한다.

즉, DBMS의 merge 문과 같은 방식으로 사용한다.


마지막으로 삭제는 다음과 같이

삭제할 필드명과 조건 값을 지정하면 된다.

        solr.deleteByQuery("id:1");
        solr.commit();


데이터를 추가하거나 삭제한 경우

항상 commit()을 호출하여 데이터 변경이 반영되도록 한다.




Solr의 사용법을 익히기 위해

Spring + SolrJ + Solr (Lucene) 기반으로 단순 게시판을 구현하였고,

소스는 Github에서 받을 수 있다.

1. Solr로 만드는 단순 게시판: 각종 설치

2. SolrJ 사용법

3. Solr 단순 게시판 (CRUD)

4. Solr 페이징과 검색


게시판을 만들기 위해서는

MariaDB 와 같은 DBMS에서 데이터를 저장하는

다음과 같이 테이블(TBL_BOARD)을 생성해야 한다.

CREATE TABLE TBL_BOARD (

    BRDNO int(11) NOT NULL AUTO_INCREMENT,         -- 글 번호

    BRDTITLE varchar(255),                                -- 제목

    BRDWRITER varchar(20),                              -- 작성자

    BRDMEMO   varchar(4000),                           -- 내용

    BRDDATE     datetime,                                -- 작성일자

    PRIMARY KEY (BRDNO)
) ;

기본적인 게시판을 구현하기 위해서는 이상의 5 개의 필드가 필요하고,

Solr에서 정의하는 방법은 설정 부분에서 정리하였다.

여기에서는 이러한 설정을 하지 않고 코어(solrBoard)만 생성해서 진행한다.

데이터를 저장할 때 Solr에서 자동으로 생성되는 기능을 이용하였다.

입력 값에 따라서 다음과 같이 자동으로 생성된다.

  <field name="brdtitle" type="strings"/>
  <field name="brdwriter" type="strings"/>
  <field name="brdmemo" type="strings"/>
  <field name="brddate" type="tdates"/>

대부분 위에서 정의한 필드 구조로 생성되지만

글 번호(BRDNO)는 키 필드(PRIMARY KEY)로

데이터 값을 유일하게(Unique) 유지하는 역할을 하는데 Solr에서 별도로 설정을 해야 한다.

여기에서는 설정을 생략하기 위해 코어 생성시 기본 지정되는 ID 필드를 이용한다.

HTML이나 VO 클래스에서는 BRDNO로 사용하고

SolrJ를 이용하여 저장하거나 조회 때에는 ID라는 이름으로 사용한다.



게시판의 기능 중 글 리스트를 먼저 구현한다.

Java에서 board1List 컨트롤을 다음과 같이 작성한다.

Board1Ctr.java

이상의 코드는 모두 SolrJ 사용법 중 데이터 조회 부분에 정리되어 있다.

여기에서는 기본 예제로

Solr 서버에 있는 모든 데이터를 가지고 와서 출력하도록 했기 때문에

설명에 사용된 코드를 그대로 사용하였다.

차이는 ModelMap을 이용하여

Solr 서버에서 가지고온 데이터(docs)를

listview라는 이름으로 Jsp로 넘겨 주는 것이다 [라인 10].


/board1/boardForm.jsp

대부분의 코드는 디자인을 위한 HTML 코드이고

주어진 데이터를 화면에 출력하는 코드는 18 라인부터 29라인이다.

주어진 데이터(listview)의 개수 만큼  반복(forEach)해서

각 행(tr)을 생성하여 출력한다.


listview는 ModelMap으로 컨트롤(Java)에서 주어진 데이터 이고

Jsp(HTML)에서는 [listview.변수]로 사용하면 된다 [라인 24~27].


다만 키인 id를 제외하고

나머지 필드는 모두 배열[]로 처리되어 있다.

필드 정의에서 multiValued가 true로 지정되었기 때문으로 [설치 참조]

별도로 지정하지 않으면 true가 기본값으로 사용된다.


작성일자(brddate)에 형식(formatDate) 태그를 사용한 것은 [라인 27]

Solr 서버가 주어진 데이터(yyy-mm-dd)를 날짜로 인식해서

tdates 형으로 지정해서 저장하기 때문이다.

tdates 형으로 지정하면 [Sat Aug 19 09:00:00 KST 2017]로 출력한다.

작성일자(brddate)를 managed-schema에서 string으로 지정하는 것이 좋지만

여기서는 형식(formatDate) 태그를 이용하여 처리하였다.


주의: 게시판 예제는 필드 정의를 하지 않았다는(solr만 시작) 전제로 작성되었다.

만약, 설치에서 정리한 데로 필드를 정의하였다면,

brddate를 날짜형(tdates )이 아닌 문자열(String)로 지정되어 있기 때문에

27 라인에서 오류가 발생한다.

27 라인을 다음과 같이 수정하여야 한다.

<td><c:out value="${listview.brddate}"/></td>

필드 정의 부분의 주의 사항을 읽어보길 바란다.


톰캣을 실행하고

웹 브라우저에서 다음과 같이 입력해서 실행을 확인한다.

http://localhost:8080/solrBoard/board1List


이번에는 글 쓰기 기능을 구현한다.

게시판 글 쓰기는 글을 작성하기 위한 폼(board1Form)과

작성된 내용을 저장하는 부분(board1Save)으로 구성된다.


Board1Ctr.java

여기에서는 글쓰기와 글 수정을 하나로 구성했기 때문에

조금 복잡하게 보이지만 단순한 개념이다.

글 쓰기 폼은 아무 것도 하지 않고 입력 받을 HTML을 화면에 출력한다.

글 수정 폼은 지정된 글 번호(brdno)에 해당하는 내용을 찾아서 (getOneboard) [라인 5]

입력 상자에 초기값으로 넣어준다.


Solr에서 데이터를 가지고 오는 것은

글 읽기와 동일하기 때문에 getOneboard() 함수로 작성하였다.

상세한 내용은 글 읽기에서 정리하였으니 넘어간다.

/board1/boardForm.jsp

글 내용을 작성한 뒤에

글 저장(board1Save)을 호출하기 위해

Form 태그에서 action으로 지정하였다 [라인 1].


컨트롤의 getOneboard()함수에서

지정된 글에 대한 정보를 boardInfo에 넣어서 반환했기 때문에

Jsp에서도 [boardInfo.변수]로 사용한다 [라인 11, 15, 19, 24, 25].


두 개의 HTML HIDDEN 태그가 사용하였다 [라인 21, 22].

글 번호(brdno)는 글쓰기 일때는 값이 없고

글 수정일때는 값이 있게 되어,

저장시(board1Save ) 신규 등록과 수정의 기준이 된다.


작성일자(brddate)를 HIDDEN 태그로 가지고 있는 것은 Solr의 특성때문이다.

글 내용을 저장할 때 지정된 필드만 수정하는 것이 아니고

행 자체를 수정하기 때문에

수정되지 않았어도 같이 필드 값을 주지 않으면

빠진 필드는 빈 값으로 채워진다.

따라서 입력 받지 않는 작성일자도 값을 가지고 있다가 [라인 22]

같이 board1Save 컨트롤로 넘겨 준다.

작성일자는 Solr에서 [Sat Aug 19 09:00:00 KST 2017]으로 반환되기 때문에

yyyy-mm-dd로 변환해서 가지고 있는다 [라인 22].


웹 브라우저에서 다음 주소를 입력하여 실행을 확인 할 수 있다.

http://localhost:8080/solrBoard/board1Form



[저장]을 선택하면 오류가 날 뿐 저장되지 않는다.

Solr 서버에 데이터를 저장하는 컨트롤(board1Save)을 작성해야 한다.

다음 코드 중 핵심 코드는 SolrJ 사용법에서 정리하였다.

Board1Ctr.java

Solr 서버에 데이터를 저장하기 위해

SolrJ로 SolrInputDocument 클래스를 사용하여 저장한다 [라인 9~18].


사용자가 입력한 값을 boardInfo로 받아서 [라인 2]

SolrInputDocument 에 넣어준다[라인 9~14].

다만, 키 필드를 id로 지정했기 때문에 글 번호(brdno)를 id로 지정하였다 [라인 10].


이상의 코드는 SolrJ 사용법에서 정리한 내용과 동일하고

차이나는 부분은 글 번호(brdno)와 작성일자(brddate)의 값을 생성하는 부분이다.

이 두 필드는 사용자가 입력하는 값이 아니고

프로그램에서 생성하는 값이기 때문이다.


먼저, 작성일자는 SimpleDateFormat 클래스를 사용하여

현재 서버의 시간을 yyyy-MM-dd형식으로 저장하도록 했다.


글번호는 조금의 트릭을 사용하였다.

글번호는 데이터 저장 공간(테이블) 내에서 유일한 값(Primary key, Unique)을 가져야 한다.

DBMS에서는 최대 글번호에 1씩 증가하여 유일한 값을 유지한다.

Solr에는 이러한 기능이 없기(?) 때문에 날짜를 이용하여 생성하였다 [라인 30].

서버의 시간을 밀리센컨드로 받아서 [라인 31]

1~10까지의 숫자 중 아무 숫자랑(random) 결합하여 유일한 값을 생성하였다 [라인 32].

서버 성능에 따라 중복 값이 발생할 수 있지만 테스트 용으로는 무난하게 사용할 수 있다.


다음으로 글 읽기(baord1Read) 기능을 구현한다.

Board1Ctr.java

글읽기는 글 수정과 같이

주어진 글 번호에 대한 상세 정보를 조회하여 화면에 출력한다.

따라서 getOneboard() 함수를 작성하고

이 함수를 각각의 컨트롤에서 호출하여 구현하였다.


주어진 글 번호에 대한 상세 정보를 조회하는 코드는

데이터를 찾는 것이기 때문에 글 리스트와 동일하다

차이점은 주어진 글 번호를 조건으로 한다는 점이다.

(실제로는 글 리스트는 글 본문을 조회하지 않고, 글읽기는 조회한다는 차이도 있다)


글 번호를 조건(Query)으로 하기 위해

setQuery 함수의 파라미터에 조건("id:" + brdno)을 지정하였다 [라인 11].

글 리스트에서는 조건을 전체 데이터("*:*")로 지정하였다.


조회 결과 반환된 데이터(docs)가 [라인 15]

1개 이상을 반환할 수 있기 때문에 배열로 반환된다.

글 읽기는 하나의 데이터(행)만 있으면 되기 때문에,

Jsp로 해당 글 정보를 넘기기 전에 docs의 get() 함수를 이용하여

첫(0) 번째 데이터를 boardInfo라는 이름으로 지정하였다 [라인 19].


/board1/boardForm.jsp

글 읽기 화면(jsp)에서는 컨트롤에서 주어진 값들을(boardInfo) 출력한다.

글 쓰기 폼과 유사하게 작성하고,

디자인 구성이나 입력받기 위한 태그(text, textarea)를 사용하는 차이가 있다.



마지막으로 글 삭제(baord1Delete) 기능을 구현한다.


Board1Ctr.java

글 삭제는 코드와 같이 매우 간단하다.

deleteByQuery() 함수를 이용하여

삭제할 때 조건으로 사용할 필드와 값을 지정하면 된다.


글 읽기에서 삭제 버튼을 눌러서 잘 실행되는지 확인한다.


이것으로 Solr를 이용한 단순 게시판을 구현하였다.

웹 브라우저에서 다음 주소를 입력하여 실행을 확인 할 수 있다.

http://localhost:8080/solrBoard/board1List




이상의 내용을 Solr의 입장에서 정리하면

게시판 글 쓰기(수정)와 삭제는 색인 작업(Indexing)이 되고,

글 리스트와 글 읽기는 검색 작업(Query)이 된다.

즉, 검색 엔진의 가장 중요한 기능은 색인과 검색으로

여기에서는 SolrJ로 Solr의 색인과 검색을 제어하는 간단한 방법들을 정리하였다.




SolrJ의 사용법을 익히기 위해

Spring + SolrJ + Solr (Lucene) 기반으로 단순 게시판을 구현하였고,

소스는 Github에서 받을 수 있다.

1. Solr로 만드는 단순 게시판: 각종 설치

2. SolrJ 사용법

3. Solr 단순 게시판 (CRUD)

4. Solr 페이징과 검색


이전 예제는 Solr 서버에 대한 데이터 입출력(CRUD)을 익히기 위한 예제로

간단한 단순 게시판을 구현하였고,

여기에서는 검색 엔진인 Solr 서버의 기능을 조금 더 익히기 위해

페이징과 검색 기능을 구현한다.


페이징과 검색은 게시판 예제 중 제법 고급 기능으로

게시판에 대한 조금은 깊은 이해가 필요하다.

다만 이 글은 Solr에 대한 사용법을 익히는 것이 목적이기 때문에

게시판과 관련된 설명은 간단하게 정리하니

Java로 구현된 게시판의 정리 내용을 읽어보면 도움이 될 것이다.


먼저 페이징(Paging) 기능을 구현한다.

페이징은 다음 그림의 하단에서 보듯이 주어진 데이터를 나누어서 출력하는 것을 의미한다.

많은 데이터를 하나의 웹 페이지 출력하면 시간과 메모리 등의 문제가 발생히 때문에,

지정된 개수(10, 20개 등) 만큼 조회해서 출력한다.

페이징 처리에 대한 구체적인 구현 방법은 제법 복잡해서 여기에서는 정리하지 않고,

두 가지 기본 개념만 간단하게 정리하면

주어진 데이터의 개수를 출력할 데이터 개수(10, 20개 등)로 나누어

전체 페이지 수를 계산해서 화면에 출력한다.

예로, 데이터가 100 개면 10 개의 페이지가 출력된다.


현재 페이지가 지정되면 (리스트에서 사용자가 클릭하면)

계산을 해서 조회할 데이터의 시작 번호를 찾게 되고

이 번호 이후의 데이터를 지정한 개수만큼 가져와 출력한다.


이외에 상세한 정보는 Java(Spring) 게시판 예제의 정리를 참고하면 된다.


Board2Ctr.java

기존에 사용한 코드 (board1List)에 몇 가지를 추가하여 구현하였다.

추가한 코드는 데이터의 개수를 세어 [라인 9~12]

페이지를 계산하고 [라인 13],

시작 데이터와 조회할 데이터 개수를 지정하는 코드이다 [라인 16, 17].


개수를 세는 조회(Query)에는 데이터 반환이 필요 없기 때문에

반환 할 데이터 개수를 0 으로 지정한다 (setRows(0)) [라인 9].

총 데이터의 개수(getNumFound)만 확인하여 [라인 12],

PageVO의 pageCalculate() 함수에 파라미터로 제공하여 계산한다 [라인 13].

pageCalculate() 함수와 페이징에 대한 상세한 설명은

Java(Spring) 게시판 예제의 정리를 참고 하면 된다.


이 함수를 실행하고 나면

PageVO 클래스의 rowStart에 시작번호가 지정되어 있기 때문에

getRowStart() 함수로 가져와서 -1 하였다 [라인 16].

이 클래스는 자바 게시판 예제에서 작성한 것으로

애초 개발이 Oracle 기반으로 제작되었고,

Oracle에서는 데이터 시작 번호를 1 부터 한다.

Solr 에서는 0 부터 시작하기 때문에 PageVO를 수정하지 않고 -1로 구현하였다.


setRows() 함수로 가져올 데이터 개수를 지정하였다 [라인 17].


이렇게 실행된 데이터(docs)와 페이지 정보(pageVO)를

Jsp로 넘겨서 화면에 출력한다 [라인 21, 22].

/board2/boardList.jsp


화면 출력은 페이지 번호를 출력하는 forEach 문만 이해하면 된다 [라인 3].

forEach문은 앞서 글 리스트의 화면(boardList.jsp)에서 사용하는 것처럼

지정된 데이터(배열)의 개수만큼 반복처리하는 것이 있고,

여기에서 사용한 것처럼 시작 값(pageStart)부터 종료 값(pageEnd)까지 반복하는 기능이 있다.


데이터가 10,000개이고 10개씩 출력할 경우,

페이지가 1,000개가 되기 때문에 모두 출력할 수 없어

페이지 번호(시작페이지와 종료페이지)에 대하여 페이징을 하게 된다.

PageVO 클래스에 있는 pageCalculate()함수에서

시작페이지(pageStart)부터 종료페이지(pageEnd)를 계산해 준다.

이 값을 하나씩 i 변수에 넣어서 페이지 번호를 출력한다 [라인 3].


부가적으로 새로운 글 번호를 출력하도록 했다.

앞서의 리스트 그림을 보면

날짜와 시간으로 된 긴 글 번호(brdno) 값 대신에

11 부터 2 까지의 새로운 번호가 출력된 것을 볼 수 있다.

내부적으로는 날짜와 시간으로 된 글 번호(brdno) 값으로 처리하지만

사용자에게 보여주는 값은 정리된 일련번호를 보여주는 것이다.

다음 코드를 사용해 보고 계산식은 각자 확인해 보길 바란다.

<c:forEach var="listview" items="${listview}" varStatus="status">   
    <tr>
        <td><c:out value="${pageVO.totRow-((pageVO.page-1)*pageVO.displayRowCount + status.index)}"/></td>
        <td><a href="${link}"><c:out value="${listview.brdtitle[0]}"/></a></td>
        <td><c:out value="${listview.brdwriter[0]}"/></td>
        <td><fmt:formatDate pattern = "yyyy-MM-dd" value = "${listview.brddate[0]}" /></td>
    </tr>
</c:forEach>

/board2/boardList.jsp


이번에는 실제 검색에서 사용되는 기능과 유사한 게시판의 검색 기능을 구현한다.

실제 검색은 더 많은 필드들을 복잡한 조건으로 구현하고,

여기서는 다음 그림의 아랫 부분에 있는 것처럼

게시판의 제목과 내용 필드에 대하여

검색하려고 하는 값(검색 키워드)을 이용하여 조회(Query)하는 기능을 구현한다.

구현 방법은 글 읽기에서 작성했던 것처럼

Solr의 setQuery() 함수에 조건을 지정하여 구현한다.

글 읽기에서는 검색할 필드와 값을 “:”으로 구분하여 지정하였다.

예: 글번호:1      =>     id:1


여기에서는 사용자가 선택한 필드와 값(검색키워드)를 이용하여

다음과 같은 문장을 생성하여 조건으로 사용하면 된다.

제목:검색키워드 AND 내용:검색키워드

=>   brdtitle:HTML AND brdmemo:HTML


/board3/boardList.jsp

이해를 위해 화면 처리 부분을 먼저 정리한다.

검색 결과에 대해서도 페이징 처리를 구현해야 하기 때문에

검색 필드(checkbox)와 키워드(text), 현재 페이지 번호(hidden)를 가지고 다녀야 한다.

이 각각의 값을 Get방식으로 전달하기 보다는

Post 방식이 더 쉽기 때문에 Form 태그를 사용하였고, [라인 1].

코드를 줄여서 보이기 위해, 앞서 구현한 페이징을 별도의 파일로 처리하였다 [라인 2].


HTML에서 두 개의 체크 박스를 같은 이름(searchType)으로 지정하면

Java에서 콤마(,)로 받게 된다.

첫 번째 체크 박스는 brdtitle [라인 5],

두 번째 체크 박스는 brdmemo [라인 7]

로 값(Value 속성)을 지정하고,

사용자가 둘다 선택하면 [brdtitle, brdmemo]로 Java에 전달된다.

이 값을 searchType 변수로 받아서

다시 화면에 출력할 때 선택된 상태로 보일 때 사용한다.

즉, searchType 변수 값에 brdtitle 문자열이 있으면(indexOf>-1)

해당 체크 박스가 체크 표시(checked)가 되도록 한다 [라인 5].

글 내용(brdmemo)도 동일하게 구현한다 [라인 7].


화면 처리와 관련하여 보다 상세한 설명은 Java 게시판 예제의 정리를 참고하면 된다.


Board3Ctr.java

컨트롤에서는 Solr 서버에서 원하는 데이터를 가지고 올 수 있도록 검색 조건을 생성한다.

4 라인부터15 라인까지의 내용이 조건을 위해 추가한 코드이다.


사용자가 체크박스에서 선택한 내용을 searchType으로 받아서 [라인 4]

콤마(,)로 분류하여 각 선택 값을 배열로 변환한다 [라인 7].

이 배열의 개수만큼 반복해서 [라인 8]

필드명과 검색 키워드(searchKeyword)를 콜론(:)으로 구분하여 조건을 생성한다.

조건과 조건은 AND로 묶었다 [라인 9]

(OR로 처리해도 된다. 개념상 OR가 더 적당하다)


이렇게 생성한 조건(queryStr)을

글읽기에서 사용한 것처럼 setQuery로 지정하면 된다 [라인 17].


주의 : 코어만 생성하고 Solr를 실행한 경우 한국어 검색은 제대로 실행 되지 않을 수 있다.

형태소 분석기 등을 추가하고, 필드 설정을 제대로 해야 한다.

자세한 내용은 설정 부분을 참고하면 된다.


이상으로 Solr와 SolrJ의 사용법을 익히기 위해

Spring 4 + MyBatis 3 + MariaDB로 제작한 게시판 예제 중 몇 가지를

MariaDB 대신에 Solr 서버를 데이터 베이스처럼 사용해서 구현했다.

게시판과 관련된 설명들은 자세하게 정리하지 않았으니

기존 게시판 예제의 정리를 읽어보길 바란다.


나머지 예제들 (총 8 가지의 게시판이 있음)도 구현해보면

Solr에 대한 이해를 높일 수 있고,

프로그래밍 실력을 향상시키는데 도움이 될 것이다.



'서버 > 검색엔진' 카테고리의 다른 글

3. Solr 예제 분석 - 스키마(Schema)  (0) 2020.01.12
4. Solr 예제 분석 - DIH  (0) 2020.01.12
1. Solr로 만드는 단순 게시판: 각종 설치  (18) 2017.08.24
2. SolrJ 사용법  (0) 2017.08.24
3. Solr 단순 게시판 (CRUD)  (0) 2017.08.24

+ Recent posts