마지막 예제 분석으로 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와 관련된 내용은 이전 블로그 내용을 참고하거나 다른 자료를 찾아보면 된다.

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

 

+ Recent posts