저자 - Marko Asplund

Oracle Text
는 모든 오라클 데이터베이스 제품(Express Edition 포함)에 기본적으로 포함되어 제공되는 강력한 검색 테크놀로지입니다. Oracle Text에서 제공되는 개발 API를 이용하여 완성도 높은 컨텐트 검색 애플리케이션을 쉽게 구현할 수 있습니다.

Oracle Text는 SQL 와일드카드 매칭 기능을 보완하는 기능을 제공하며, 구조형/비구조형 문서를 검색하는 용도로 활용됩니다. Oracle Text는 기본적 불리언 연산자(AND, OR, NOT, NEAR 등)의 조합을 지원하며 사운덱스(soundex) 검색, 퍼지(fuzzy) 검색, 결과 랭킹과 같은 고급 기능을 함께 제공하고 있습니다. 또 마이크로소프트 오피스와 PDF를 포함하는 수백여 종의 파일 타입이 지원됩니다. Oracle Text는 다양한 검색 관련 유즈 케이스 및 스토리지 구조에 적합한 검색 모델을 제공합니다. Oracle Text가 적용될 수 있는 몇 가지 예로 e-비즈니스, 문서/기록 관리, 이슈 트랙킹 시스템 등을 들 수 있습니다. 텍스트 정보는 데이터베이스 내에 구조적인 형태로 저장되거나, 또는 로컬 파일 시스템이나 웹에 비구조적인 형태로 저장될 수 있습니다.

Oracle Text는 커스텀 쿼리 연산자, DDL 신택스 확장, PL/SQL 프로시저, 데이터베이스 뷰 등을 포함하는 포괄적인 SQL 기반 검색 API를 제공합니다. 애플리케이션 개발자는 Oracle Text API를 이용하여 인덱싱, 쿼리, 보안, 프리젠테이션, 소프트웨어 설정 등의 컨트롤을 확보할 수 있습니다. 이러한 기능은 커스텀 애플리케이션이 아닌 상용 애플리케이션을 개발하는 경우에 특히 유용합니다. 상용 소프트웨어 제품을 개발하는 경우에는 소프트웨어의 설정을 가능한 한 단순한 형태로 유지하는 것이 관건이 됩니다. 애플리케이션의 복잡성을 최소화하는 것은 기술 지원, 유지 보수, 업그레이드 등 향후 제품 라이프사이클 전반에 걸친 이점을 제공합니다.

Oracle Text는 또 고성능 환경에서 구현이 까다로운 문서 레벨 승인(document level authorization) 기능을 지원하고 있습니다. 관계형 데이터 및 비구조형 데이터에 대해 텍스트가 혼합된 쿼리를 사용하는 것 또한 가능합니다. 이는 단일 쿼리 내에서 전체 텍스트 검색과 승인 기능을 함께 사용할 수 있음을 의미합니다. 최종적인 결과를 얻기 위해 결과 셋을 분리하고 필터링을 적용하는 작업이 최소화되므로 애플리케이션 개발 업무의 단순화가 가능합니다. Oracle Text는 성능 최적화와 관련된 애플리케이션 개발자의 업무 부담을 줄여 줍니다.

Oracle Text는 또 프로그래밍 언어로부터 독립적이며 Java, PHP 애플리케이션과 유연하게 연동됩니다.

언젠가 필자는 엔터프라이즈 컨텐트 관리(ECM) 시스템의 검색 기능을 개선하는 작업에 참여한 일이 있습니다. 필자는 프로젝트를 시작하면서 제일 먼저 Oracle Text의 활용 가능성을 검토하는 작업에 착수했습니다. 검토 결과, Oracle Text가 애플리케이션의 검색 기능 구현에 매우 적합한 도구임이 판명되었습니다. Oracle Text는 고급 검색 기능을 포함하고 있으며, 다양한 파일 유형을 지원할 뿐 아니라, 커스터마이즈 및 확장이 매우 용이합니다. 기존 검색 테크놀로지들의 경우 데이터베이스 외부의 파일 컨텐트를 검색하고, 다시 데이터베이스 메타데이터를 검색하고, 결과를 승인하고, 마지막으로 서로 분리된 결과 셋을 병합하는 번거로운 절차를 요구한다는 문제가 있었습니다. Oracle Text는 이러한 모든 작업을 데이터베이스 내부에서 수행합니다. 이 프로젝트의 ECM 시스템은 이미 오라클 데이터베이스에 메타데이터를 저장하고 있었습니다. 이미 기반이 마련되어 있는 환경에서, 추가적인 비용을 들이지 않고 Oracle Text를 활용하는 것은 어찌 보면 당연한 선택이었습니다.

데이터베이스에서 텍스트 검색 쿼리를 구현하기 위한 일반적인 방법이 아래와 같습니다.

    

SELECT * FROM issues

WHERE LOWER(author) LIKE '%word1%' AND LOWER(author) LIKE '%word2%' ...    

 

여기서 각각의 키워드는 컬럼별로 제각각 매치되며, 키워드의 순서에 관계없이 매치가 가능합니다. 하지만 관계형 데이터베이스는 이러한 쿼리를 효과적으로 실행하도록 설계되지 않았으며, 따라서 이러한 접근법은 확장성 면에서 심각한 한계를 갖습니다. 물론 별도의 인덱싱/검색 솔루션을 고안해 낼 수도 있겠지만, 이미 검색 테크놀로지 구현 비용을 별도로 지출한 경우가 아니라면 비용효율적인 솔루션을 얻어내기가 매우 어려울 것입니다.

본 문서는 가상의 이슈 트래킹 애플리케이션에서 Oracle Text를 활용하는 방법을 소개하고 있습니다. 애플리케이션에서 사용자에 의해 생성된 '이슈'는 메타데이터와 첨부 파일로 구성됩니다. 애플리케이션은 Oracle Text를 이용하여 메타데이터 및 첨부 파일 컨텐트에 대한 전체 텍스트 검색을 수행합니다.

본 예제는 Linux 기반 Oracle Database XE 환경에서 테스트 되었으며, 다른 오라클 플랫폼에서도 문제 없이 동작할 것으로 예상됩니다.

인덱싱 프로세스와 검색

Oracle Text는 사용자가 컨텐트 검색을 수행하기 전에 조회 가능한 데이터 아이템에 대한 인덱스를 생성합니다. 이는 검색 성능의 개선을 위해 반드시 필요한 작업입니다. Oracle Text 인덱싱 프로세스는 파이프라인을 모델로 하며, 데이터 저장소에서 인출된 데이터 아이템은 일련의 변환 과정을 거친 후 인덱스에 그 키워드가 추가됩니다. 인덱싱 프로세스는 여러 단계로 나뉘며, 각 단계별로 처리되는 엔티티는 애플리케이션 개발자에 의해 설정이 가능합니다.

Oracle Text는 여러 가지 인덱스 타입을 지원하며, 인덱스 타입의 선택은 그 활용 목적에 따라 달라집니다. 대용량 문서에 대한 전체 텍스트 검색을 위해서는 CONTEXT 인덱스 타입이 가장 적합합니다. 인덱싱 프로세스는 다음과 같은 단계를 거쳐 진행됩니다.

  1. 데이터 인출: 데이터 저장소(웹 페이지, 데이터베이스 LOB, 로컬 파일 시스템 등)로부터 데이터가 인출되어 다음 단계의 처리를 위해 데이터 스트림으로 전달됩니다.
  2. 필터링: 서로 다른 파일 포맷의 데이터를 일반 텍스트로 변환하기 위해 필터링 프로세스가 적용됩니다. 인덱싱 파이프라인의 다른 컴포넌트들은 일반 텍스트 포맷만을 처리하며 원본 파일 포맷(Microsoft Word, Excel 등)에 대해 알지 못합니다.
  3. 섹셔닝(Sectioning): 섹셔너(sectioner)가 원본 데이터 아이템의 구조에 대한 메타데이터를 추가합니다.
  4. 렉싱(Lexing): 캐릭터 스트림이 아이템의 언어를 기준으로 단어(word)로 분할됩니다.
  5. 인덱싱: 키워드가 실제 인덱스에 추가됩니다.

인덱스가 생성되고 나면, 엔드 유저는 애플리케이션에서 SQL 쿼리를 이용하여 검색을 실행할 수 있습니다.

Oracle Text의 설정

Oracle Text는 기본적으로 Oracle Database XE와 함께 설치됩니다. 다른 데이터베이스 버전의 경우, Oracle Text를 별도로 설치해야 합니다. Oracle Text의 설치를 완료한 다음에는, 데이터베이스 사용자를 생성하고 이 사용자에게 CTXAPP 롤을 할당해 주기만 하면 됩니다. CTXAPP 롤을 할당 받은 사용자는 인덱스 관리 작업을 수행할 수 있는 권한을 가집니다.

CREATE USER ot1 IDENTIFIED BY ot1;

GRANT connect,resource, ctxapp TO ot1;

파일 인덱스

이제 이슈 트래킹 시스템에 저장된 첨부 파일의 인덱싱을 위해 텍스트 테이블을 생성해야 합니다. 첨부 파일은 파일 시스템에 저장되어 있습니다. 애플리케이션의 데이터 모델이 요구하는 컬럼과 별도로, 텍스트 기반 테이블은 절대 파일 경로와 포맷 컬럼 정보를 포함하고 있습니다.

CREATE TABLE files (

id NUMBER PRIMARY KEY,

issue_id NUMBER,

path VARCHAR(255) UNIQUE,

ot_format VARCHAR(6)

);

 

INSERT INTO files VALUES (1, 1, '/tmp/oracletext/found1.txt', NULL);

INSERT INTO files VALUES (2, 2, '/tmp/oracletext/found2.doc', NULL);

INSERT INTO files VALUES (3, 2, '/tmp/oracletext/notfound.txt', 'IGNORE');

ot_format의 값은 인덱싱 과정에서 Oracle Text에 의해 계산됩니다. NULL 값은 파일을 위한 필터가 자동으로 선택됨을 의미하며, IGNORE는 파일을 완전히 무시함을 의미합니다.

텍스트 인덱스를 생성하기 위한 구문이 아래와 같습니다.

CREATE INDEX file_index ON files(path) INDEXTYPE IS ctxsys.context

PARAMETERS ('datastore ctxsys.file_datastore format column ot_format');

이 구문은 인덱싱 프로세스가 베이스 테이블에 저장된 경로 정보를 이용하여 파일 시스템에서 파일을 인출하고, 그 컨텐트에 대한 필터링, 인덱싱을 수행하도록 지시하고 있습니다. CONTEXT 인덱스는 대소문자를 구분하지 않으며 정확한 단어 단위 매칭을 지원합니다. 여기서 인덱싱 프로세스의 커스터마이즈 작업을 통해 접두어(prefix), 접미어(suffix) 매치 등을 지원할 수 있습니다.

필터링은 대부분의 경우 각 파일의 포맷을 명시하지 않은 상태에서도 올바르게 동작하지만, format 컬럼을 베이스 테이블에 포함시킴으로써 인덱싱 프로세스를 보다 정교하게 제어할 수 있다는 장점이 있습니다. 예를 들어, format 컬럼을 이용하여 특정 파일 타입이 인덱싱되지 않도록 설정하는 것이 가능합니다. 이 옵션은 Oracle Text에서 파일 포맷 중 일부만을 지원하고자 하는 경우에 특히 유용합니다.

Oracle Text는 메타데이터의 전체 텍스트 검색을 위해서도 이용될 수 있습니다. 샘플 애플리케이션에서는 각 이슈에 관련한 메타데이터를 저장하기 위해 issues 테이블이 사용되고 있습니다. 이 테이블은 아래와 같이 정의됩니다.

CREATE TABLE issues (

id NUMBER,

author VARCHAR(80),

summary VARCHAR(120),

description CLOB,

ot_version VARCHAR(10)

);

The ot_version column is the index column, which can be used to force reindexing for certain documents. The table can be populated with test data:

INSERT INTO issues VALUES (1, 'Jane', 'Text does not make tea',

'Oracle Text is unable to make morning tea', 1);

INSERT INTO issues VALUES (2, 'John', 'It comes in the wrong color',

'I want to have Text in pink', 1);

사용자 인덱스

Oracle Text는 서로 다른 데이터 소스로부터의 데이터 인덱싱을 지원합니다. 샘플 애플리케이션에서 이슈 메타데이터의 전체 텍스트 검색을 위해 Oracle Text를 활용할 수 있습니다. 디폴트 상태에서 인덱스 값은 단일 컬럼에 저장됩니다. 하지만 여러 테이블의 데이터를 조합하고자 하는 경우라면 커스텀 PL/SQL 필터 프로시저를 생성해야 합니다. 여기에서는 스토리지 추상화(storage abstraction)의 한 방법으로서 프로시저를 생성하여 활용하기로 합니다. 인덱싱 프로세스는 텍스트 테이블의 모든 로우를 스캔하고, 각 로우에 대해 필터 프로시저를 호출합니다. 그런 다음 필터 프로시저는 해당 이슈에 관련하여 인덱싱 되어야 하는 모든 텍스트를 반환합니다.

-- declare indexing procedure

CREATE PACKAGE ot_search AS

PROCEDURE issue_filter(rid IN ROWID, tlob IN OUT NOCOPY CLOB);

END ot_search;

/

 

-- define indexing procedure

CREATE PACKAGE BODY ot_search AS

PROCEDURE issue_filter(rid IN ROWID, tlob IN OUT NOCOPY CLOB) IS

BEGIN

FOR c1 IN (SELECT author, summary, description FROM issues WHERE rowid = rid)

LOOP

dbms_lob.writeappend(tlob, LENGTH(c1.summary)+1, c1.summary || ' ');

dbms_lob.writeappend(tlob, LENGTH(c1.author)+1, c1.author || ' ');

dbms_lob.writeappend(tlob, LENGTH(c1.description), c1.description);

END LOOP;

END issue_filter;

END ot_search;

/

 

-- define datastore preference for issues

BEGIN

ctx_ddl.create_preference('issue_store', 'user_datastore');

ctx_ddl.set_attribute('issue_store', 'procedure', 'ot_search.issue_filter');

ctx_ddl.set_attribute('issue_store', 'output_type', 'CLOB');

END;

/

 

-- index issues

CREATE INDEX issue_index ON issues(ot_version) INDEXTYPE IS ctxsys.context

PARAMETERS ('datastore issue_store');

검색

CONTAINS 연산자는 CONTEXT 인덱스의 검색을 위해 사용됩니다. 본 예제에서는 키워드의 조합을 위해 간단한 불리언 연산자만을 이용하고 있습니다. 하지만 CONTAINS 연산자는 사운덱스(soundex) 매치와 같은 고급 기능도 함께 제공하고 있음을 참고하시기 바랍니다. Oracle Text에 의해 지원되는 언어가 사용되고 있는 경우, 퍼지(fuzzy) 매칭과 스테밍(stemming)이 디폴트로 활성화됩니다. CONTAINS 연산자와 함께 fuzzy(), $ 등의 쿼리 연산자를 이용하여 이러한 고급 기능을 쉽게 활용할 수 있습니다. 접두어/접미어 매칭을 위해 CONTAINS 쿼리에 와일드카드 문자를 활용할 수도 있습니다. 간단한 검색 쿼리의 예가 아래와 같습니다.

SELECT id FROM issues WHERE CONTAINS(ot_version, 'color AND pink', 1) > 0;

SELECT id FROM issues WHERE CONTAINS(ot_version, 'jane OR john', 1) > 0;

인덱스의 유지 보수

베이스 테이블 데이터는 인덱스에 의해 복제되므로, 인덱스와 데이터를 주기적으로 동기화하는 작업이 필요합니다. 인덱스 유지 보수 프로시저는 CTX_DDL PL/SQL 패키지에 포함되어 있습니다. 베이스 테이블의 변경 사항을 반영하여 인덱스를 업데이트하는 예가 아래와 같습니다.

EXECUTE ctx_ddl.sync_index('issue_index', '2M');

동기화 프로시저는 인덱스 네임과 작업에 사용되는 메모리 사이즈를 입력 매개변수로 받아 들입니다. 데이터베이스에서 이 작업을 일정 간격으로 자동 실행하도록 설정하는 것도 물론 가능합니다. 또 운영 체제 또는 다른 스케줄링 기능을 통해 동기화 작업을 실행할 수도 있습니다. UNIX 시스템의 경우 아래와 같은 쉘 스크립트를 Cron 작업 스케줄링 시스템에 등록하여 동기화를 수행할 수 있습니다.

#!/bin/sh

export ORACLE_SID=orcl

export ORAENV_ASK=NO

source /usr/local/bin/oraenv

 

sqlplus ot1/ot1@XE > synch.log <<EOF

WHENEVER SQLERROR EXIT 5;

EXECUTE ctx_ddl.sync_index('issue_index', '2M');

EOF

그 밖에도 CTX_DDL 패키지에는 인덱스 최적화, 인덱스 조각 모음, 데이터 폐기 등에 관련한 유용한 프로시저들이 포함되어 있습니다.

문제가 발생한 경우에는 CTX_USER_INDEX_ERROR 뷰에서 인덱싱 에러를 추적할 수 있습니다.

데이터베이스는 인덱스 컬럼의 변경 결과를 기준으로 문서의 변경 여부를 추적합니다. 따라서 Oracle Text에서 특정 문서의 인덱스를 재생성해야 하는 경우, 해당 로우의 인덱스 컬럼을 아래와 같은 방법으로 업데이트할 수 있습니다.

UPDATE files SET path=path WHERE id = 4;

위 구문을 실행하면 인덱스가 동기화될 때 id가 4인 파일의 인덱스가 업데이트됩니다.

 

Posted by 서오석
,