Oracle 23ai(23.6)에 하이브리드 벡터 인덱스 기능이 추가되었습니다. 하이브리드 벡터 인덱스를 사용하하면 전체 텍스트 검색(full-text search)과 벡터 유사도 검색(vector similarity search)을 조합하여 데이터를 조회할 수 있습니다.
테스트를 위해 예제 데이터를 다운로드하고 아래와 같이 압축을 해제하겠습니다.
$ unzip /home/oracle/wiki/vector_database_wikipedia_articles_embedded.zip
Archive: vector_database_wikipedia_articles_embedded.zip
inflating: vector_database_wikipedia_articles_embedded.csv
inflating: __MACOSX/._vector_database_wikipedia_articles_embedded.csv
아래와 같이 External 테이블를 사용하여 vector_database_wikipedia_articles_embedded.csv 파일을 wiki 테이블로 적재하겠습니다. 적재된 로우 수는 25,000 건입니다.
-- 1-1
DROP DIRECTORY dir_wiki;
CREATE DIRECTORY dir_wiki AS '/home/oracle/wiki';
-- 1-2
DROP TABLE ext_wiki PURGE;
CREATE TABLE ext_wiki (
id NUMBER
, url VARCHAR2(200)
, title VARCHAR2(100)
, text CLOB
, title_vector CLOB
, content_vector CLOB
, vector_id NUMBER
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY dir_wiki
ACCESS PARAMETERS (
RECORDS
SKIP 1
FIELDS CSV WITH EMBEDDED
MISSING FIELD VALUES ARE NULL (
id
, url
, title
, text CHAR(1000000000)
, title_vector CHAR(1000000000)
, content_vector CHAR(1000000000)
, vector_id
)
)
LOCATION ('vector_database_wikipedia_articles_embedded.csv')
);
-- 1-3
DROP TABLE wiki PURGE;
CREATE TABLE wiki AS SELECT id, url, title, text FROM ext_wiki;
-- 1-4
SELECT COUNT (*) AS cnt FROM wiki;
CNT
-----
25000
1 row selected.
하이브리드 벡터 인덱스는 벡터 검색, 텍스트 검색, 인덱스 유지 관리에 대한 설정을 할 수 있습니다. 아래와 같이 DBMS_VECTOR_CHAIN.CREATE_PREFERENCE 프로시저로 하이브리드 벡터 인덱스에 대한 vectorizer preference를 생성하고, CREATE HYBRID VECTOR INDEX 문으로 하이브리드 벡터 인덱스를 생성하겠습니다. (25,000 건에 대해 64 DOP로 7분이 소요됨) 예제에 사용한 all_minilm_l12_v2 모델은 ONNX Model 글을 참고하세요.
-- 2-1
-- CREATE HYBRID VECTOR INDEX wiki_x1 ON wiki (text) PARAMETERS ('MODEL all_minilm_l12_v2');
-- 2-2: set as default
BEGIN
DBMS_VECTOR_CHAIN.CREATE_PREFERENCE (
pref_name => 'WIKI_PREF'
, pref_type => DBMS_VECTOR_CHAIN.VECTORIZER
, params => JSON (
'{
"model": "all_minilm_l12_v2",
"by": "words",
"max": 100,
"split": "recursively",
"overlap": 0,
"distance": "cosine",
"accuracy": 95,
"vector_idxtype": "ivf"
}'
));
END;
/
-- 2-3
DROP INDEX wiki_x1 FORCE;
CREATE HYBRID VECTOR INDEX wiki_x1 ON wiki (text) PARAMETERS ('VECTORIZER wiki_pref');
하이브리드 벡터 인덱스는 도메인 인덱스로 생성되며 다수의 보조 테이블이 자동 생성됩니다.
-- 3-1
SELECT table_name, index_name, index_type, index_subtype
FROM user_indexes
WHERE index_name LIKE '%WIKI%';
TABLE_NAME INDEX_NAME INDEX_TYPE INDEX_SUBTYPE
------------- -------------- ---------- -----------------------
WIKI WIKI_X1 DOMAIN
DR$WIKI_X1$D DR$WIKI_X1$DI NORMAL
DR$WIKI_X1$G DR$WIKI_X1$H NORMAL
DR$WIKI_X1$I DR$WIKI_X1$X NORMAL
DR$WIKI_X1$K DR$WIKI_X1$KD NORMAL
DR$WIKI_X1$K DR$WIKI_X1$KR NORMAL
DR$WIKI_X1$N DR$WIKI_X1$NI NORMAL
DR$WIKI_X1$U DR$WIKI_X1$UI NORMAL
DR$WIKI_X1$VR DR$WIKI_X1$VI VECTOR NEIGHBOR_PARTITIONS_IVF
DR$WIKI_X1$VR DR$WIKI_X1$VRI NORMAL
10 rows selected.
-- 3-2
SELECT table_name, partitioned, secondary
FROM user_tables
WHERE table_name LIKE '%WIKI%';
TABLE_NAME PARTITIONED SECONDARY
--------------------------------------------------------------- ----------- ---------
EXT_WIKI NO N
WIKI NO N
DR$WIKI_X1$B NO Y
DR$WIKI_X1$C NO Y
DR$WIKI_X1$D NO Y
DR$WIKI_X1$G NO Y
DR$WIKI_X1$I NO Y
DR$WIKI_X1$K NO Y
DR$WIKI_X1$N NO Y
DR$WIKI_X1$Q NO Y
DR$WIKI_X1$U NO Y
DR$WIKI_X1$VR NO Y
VECTOR$DR$WIKI_X1$VI$65402_65413_0$IVF_FLAT_CENTROIDS NO N
VECTOR$DR$WIKI_X1$VI$65402_65413_0$IVF_FLAT_CENTROID_PARTITIONS YES N
14 rows selected.
-- 3-3
SELECT view_name
FROM user_views
WHERE view_name LIKE '%WIKI%';
VIEW_NAME
---------------
WIKI_X1$VECTORS
1 row selected.
wiki_x1$vectors 뷰를 조회하면 청크 정보를 조회할 수 있습니다.
-- 4-1
SELECT *
FROM wiki
WHERE id = 1;
ID URL TITLE TEXT
-- --------------------------------------- ----- --------------------------------------------------------------------------------
1 https://simple.wikipedia.org/wiki/April April April is the fourth month of the year in the Julian and Gregorian calendars, ...
1 row selected.
-- 4-2
SELECT *
FROM wiki_x1$vectors
WHERE DOC_ROWID = (SELECT ROWID FROM wiki WHERE id = 1);
DOC_ROWID DOC_CHUNK_ID DOC_CHUNK_COUNT DOC_CHUNK_OFFSET DOC_CHUNK_LENGTH DOC_CHUNK_TEXT DOC_CHUNK_EMBEDDING
------------------ ------------ --------------- ---------------- ---------------- -------------------------------- ---------------------
AAAP9lAAAAABAArAAA 1 40 1 437 April is the fourth month of ... [-8.13307762E-002,...
AAAP9lAAAAABAArAAA 2 40 438 205 April comes between March and... [2.67323982E-002,...
...
40 rows selected.
---------------------------------------------------------
| Id | Operation | Name |
---------------------------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | HASH JOIN RIGHT ANTI NA | |
| 2 | VIEW | VW_NSO_1 |
|* 3 | FILTER | |
| 4 | SORT GROUP BY | |
|* 5 | VIEW | |
| 6 | WINDOW SORT | |
| 7 | TABLE ACCESS STORAGE FULL| DR$WIKI_X1$C |
|* 8 | HASH JOIN | |
| 9 | TABLE ACCESS STORAGE FULL | DR$WIKI_X1$K |
| 10 | TABLE ACCESS STORAGE FULL | DR$WIKI_X1$VR |
---------------------------------------------------------
아래 예제는 DBMS_HYBRID_VECTOR.SEARCH 함수로 What have scientists discovered? 검색 키워드와 elements 텍스트가 포함된 상위 5개의 로우를 조회합니다. ROWID, 점수, 벡터 점수, 텍스트 점수가 포함된 JSON 값이 반환됩니다.
-- 5
SELECT JSON_SERIALIZE (DBMS_HYBRID_VECTOR.SEARCH (JSON (
'{
"hybrid_index_name": "wiki_x1",
"search_scorer": "rsf",
"search_fusion": "INTERSECT",
"vector": {
"search_text": "What have scientists discovered?",
"search_mode": "DOCUMENT",
"aggregator": "MAX",
"score_weight": 1
},
"text": {
"contains": "$elements",
"score_weight": 1
},
"return": {
"values": ["rowid", "score", "vector_score", "text_score"],
"topN": 5
}
}'
)) RETURNING CLOB PRETTY) AS result;
RESULT
-----------------------------------
[
{
"rowid" : "AAAP9lAAAAABAAuAAE",
"score" : 56.7,
"vector_score" : 64.4,
"text_score" : 49
},
{
"rowid" : "AAAP9lAAAAABAA0AAI",
"score" : 53.24,
"vector_score" : 65.48,
"text_score" : 41
},
{
"rowid" : "AAAP9lAAAAABAB3AAC",
"score" : 53.16,
"vector_score" : 65.32,
"text_score" : 41
},
{
"rowid" : "AAAP9lAAAAABAGUAAA",
"score" : 47.83,
"vector_score" : 71.66,
"text_score" : 24
},
{
"rowid" : "AAAP9lAAAAABABGAAH",
"score" : 45.82,
"vector_score" : 67.64,
"text_score" : 24
}
]
1 row selected.
아래 예제는 JSON_TABLE 함수를 사용하여 하이브리드 벡터 인덱스 검색 결과와 wiki 테이블을 조인한 결과를 보여줍니다. cnt 열은 text 칼럼에 elements 텍스트가 포함된 개수를 나타냅니다.
-- 6
SELECT b.score, b.vector_score, b.text_score, c.id, c.url, c.title, REGEXP_COUNT (c.text, 'elements') AS cnt
FROM (SELECT DBMS_HYBRID_VECTOR.SEARCH (JSON (
'{
"hybrid_index_name": "wiki_x1",
"search_scorer": "rsf",
"search_fusion": "INTERSECT",
"vector": {
"search_text": "What have scientists discovered?",
"search_mode": "DOCUMENT",
"aggregator": "MAX",
"score_weight": 1
},
"text": {
"contains": "$elements",
"score_weight": 1
},
"return": {
"values": ["rowid", "score", "vector_score", "text_score"],
"topN": 5
}
}'
)) AS result) a
, JSON_TABLE (a.result, '$[*]' COLUMNS (
rid VARCHAR2(18) PATH '$.rowid'
, score NUMBER PATH '$.score'
, vector_score NUMBER PATH '$.vector_score'
, text_score NUMBER PATH '$.text_score'
)) b
, wiki c
WHERE c.rowid = b.rid;
SCORE VECTOR_SCORE TEXT_SCORE ID URL TITLE CNT
----- ------------ ---------- ---- ------------------------------------------- --------- ---
56.70 64.40 49 47 https://simple.wikipedia.org/wiki/Atom Atom 10
53.24 65.48 41 108 https://simple.wikipedia.org/wiki/Chemistry Chemistry 8
53.16 65.32 41 858 https://simple.wikipedia.org/wiki/Universe Universe 6
47.83 71.66 24 2949 https://simple.wikipedia.org/wiki/Oxygen Oxygen 5
45.82 67.64 24 357 https://simple.wikipedia.org/wiki/Helium Helium 3
5 rows selected.
아래 예제는 What have scientists discovered? 검색 키워드만 포함된 상위 5개의 로우를 조회합니다.
-- 7
SELECT JSON_SERIALIZE (DBMS_HYBRID_VECTOR.SEARCH (JSON (
'{
"hybrid_index_name": "wiki_x1",
"search_scorer": "rsf",
"search_fusion": "INTERSECT",
"vector": {
"search_text": "What have scientists discovered?",
"search_mode": "DOCUMENT",
"aggregator": "MAX",
"score_weight": 1
},
"return": {
"values": ["rowid", "score", "vector_score"],
"topN": 5
}
}'
)) RETURNING CLOB PRETTY) AS result;
RESULT
-----------------------------------
[
{
"rowid" : "AAAP9lAAAAABAKoAAC",
"score" : 74.61,
"vector_score" : 74.61
},
{
"rowid" : "AAAP9lAAAAABBVqAAF",
"score" : 74.61,
"vector_score" : 74.61
},
{
"rowid" : "AAAP9lAAAAABAVTAAA",
"score" : 71.96,
"vector_score" : 71.96
},
{
"rowid" : "AAAP9lAAAAABBUCAAB",
"score" : 71.66,
"vector_score" : 71.66
},
{
"rowid" : "AAAP9lAAAAABAB3AAF",
"score" : 71.66,
"vector_score" : 71.66
}
]
1 row selected.
id가 882인 로우의 text 칼럼에만 elements 텍스트가 포함된 것을 볼 수 있습니다.
-- 8
SELECT b.score, b.vector_score, c.id, c.url, c.title, REGEXP_COUNT (c.text, 'elements') AS cnt
FROM (SELECT DBMS_HYBRID_VECTOR.SEARCH (JSON (
'{
"hybrid_index_name": "wiki_x1",
"search_scorer": "rsf",
"search_fusion": "INTERSECT",
"vector": {
"search_text": "What have scientists discovered?",
"search_mode": "DOCUMENT",
"aggregator": "MAX",
"score_weight": 1
},
"return": {
"values": ["rowid", "score", "vector_score"],
"topN": 5
}
}'
)) AS result) a
, JSON_TABLE (a.result, '$[*]' COLUMNS (
rid VARCHAR2(18) PATH '$.rowid'
, score NUMBER PATH '$.score'
, vector_score NUMBER PATH '$.vector_score'
)) b
, wiki c
WHERE c.rowid = b.rid;
SCORE VECTOR_SCORE ID URL TITLE CNT
----- ------------ ----- -------------------------------------------------- ---------------- ---
74.61 74.61 5757 https://simple.wikipedia.org/wiki/Virus Virus 0
74.61 74.61 57419 https://simple.wikipedia.org/wiki/Caecilian Caecilian 0
71.96 71.96 18125 https://simple.wikipedia.org/wiki/Phenomenon Phenomenon 0
71.66 71.66 55347 https://simple.wikipedia.org/wiki/Neurotransmitter Neurotransmitter 0
71.66 71.66 882 https://simple.wikipedia.org/wiki/Uranus Uranus 1
5 rows selected.
아래는 5번 예제와 7번 예제의 세션 통계를 비교한 결과입니다. 텍스트 검색의 유무에 따라 세션 통계에 차이가 발생한 것을 볼 수 있습니다.
-- 9
+----------------------+--------+--------+-------+------+------+
|NAME | #5| #7| DIFF| RATIO|CLASS |
+----------------------+--------+--------+-------+------+------+
|recursive calls | 23| 13| -10| 0.57|User |
|session logical reads | 2,694| 1,987| -707| 0.74|User |
|user calls | 27| 24| -3| 0.89|User |
|execute count | 22| 16| -6| 0.73|SQL |
|parse count (total) | 10| 6| -4| 0.60|SQL |
|sorts (rows) | 8,312| 6,500| -1,812| 0.78|SQL |
+----------------------+--------+--------+-------+------+------+
관련 링크