13.2.1 複数の表およびビューを使用したDBMS_SEARCH索引の作成および問合せ
この例では、ユビキタス検索索引の作成方法、複数の表およびビューの追加方法、CONTAINS
、JSON_TEXTCONTAINS
およびJSON_EXISTS
演算子を使用した索引に対する問合せ方法を確認できます。
- ローカル・ユーザーとしてOracle Databaseに接続します。
- SQL*Plusに
SYS
ユーザーとしてログインし、SYSDBA
として接続します。conn sys/password as sysdba
CREATE TABLESPACE tbs1 DATAFILE 'tbs5.dbf' SIZE 20G AUTOEXTEND ON EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO;
SET ECHO ON SET FEEDBACK 1 SET NUMWIDTH 10 SET LINESIZE 80 SET TRIMSPOOL ON SET TAB OFF SET PAGESIZE 10000 SET LONG 10000
- ローカル・ユーザー(
docuser
)を作成し、必要な権限を付与します。DROP USER docuser cascade;
GRANT DB_DEVELOPER_ROLE, DEFAULT TABLESPACE tbs1 quota unlimited on tbs1 TO docuser IDENTIFIED BY password;
- ローカル・ユーザー(
docuser
)として接続します:CONN docuser/password
- SQL*Plusに
customers
表、items
表、orders
表およびlineitems
表を作成して移入します。これらの表は、後でユビキタス検索索引に追加します。customers
:ここでは、スキーマ所有者名
DOCUSER
が明示的に指定されています。CREATE TABLE docuser.customers ( cust_id number PRIMARY KEY, first varchar2(30), last varchar2(30));
insert into customers values (1, 'Robert', 'Smith'); insert into customers values (2, 'John', 'Doe'); insert into customers values (3, 'James', 'Martin'); insert into customers values (5, 'Jane', 'Y');
items
:CREATE TABLE items ( item_id number PRIMARY KEY, name varchar2(30), price number(5,2), stock_quantity number);
insert into items values (122, 'Potato Gun', 29.99, 10); insert into items values (232, 'Rubber Christmas Tree', 65.00, 0); insert into items values (345, 'Border Patrol Costume', 19.99, 20); insert into items values (845, 'Meteor Impact Survival Kit', 299.00, 0); insert into items values (429, 'Air Guitar', 9.99, 14);
orders
:CREATE TABLE orders ( order_id number PRIMARY KEY, cust_id number REFERENCES customers(cust_id) ON DELETE CASCADE);
insert into orders values (1, 1); insert into orders values (2, 1); insert into orders values (3, 3); insert into orders values (4, 2);
lineitems
:CREATE TABLE lineitems ( order_id number REFERENCES orders(order_id) ON DELETE CASCADE, item_id number REFERENCES items(item_id) ON DELETE CASCADE, quantity number, PRIMARY KEY(order_id, item_id));
insert into lineitems values(1, 845, 1); insert into lineitems values(2, 232, 1); insert into lineitems values(2, 429, 4); insert into lineitems values(3, 122, 1); insert into lineitems values(4, 345, 1);
- 作成した表に基づいて、
search_view
という名前のビューを作成します。このビューは、後でユビキタス検索索引に追加します。CREATE OR REPLACE VIEW search_view(cust_id, business_object, CONSTRAINT search_view_pk PRIMARY KEY(cust_id) RELY DISABLE NOVALIDATE, CONSTRAINT search_view_fk FOREIGN KEY(cust_id) REFERENCES customers(cust_id) DISABLE NOVALIDATE) AS SELECT c.cust_id, JSON_OBJECT( 'id' VALUE c.cust_id, 'name' VALUE (c.first || ' ' || c.last), 'num_orders' VALUE ( SELECT COUNT(*) FROM orders o WHERE o.cust_id = c.cust_id), 'orders' VALUE ( SELECT JSON_ARRAYAGG( JSON_OBJECT( 'order_id' VALUE o.order_id, 'items' VALUE ( SELECT JSON_ARRAYAGG ( JSON_OBJECT( 'id' VALUE l.item_id, 'name' VALUE i.name, 'quantity' VALUE l.quantity, 'single_item_price' VALUE i.price, 'total_price' VALUE (i.price * l.quantity))) FROM lineitems l, items i WHERE l.order_id = o.order_id AND i.item_id = l.item_id))) FROM orders o WHERE o.cust_id = c.cust_id) ABSENT ON NULL) business_object FROM customers c;
MY_SEARCH_INDEX
という名前のユビキタス検索索引を作成します。EXEC DBMS_SEARCH.CREATE_INDEX('DOCUSER.MY_SEARCH_INDEX', NULL, 'JSON');
次のように、
tablespace
およびdatatype
(デフォルトはJSON
)を省略できます:exec dbms_search.create_index('MY_SEARCH_INDEX');
次のコマンドを実行して、
DOCUSER
スキーマに作成される索引の構造を決定します:DESC MY_SEARCH_INDEX;
Name Null? Type ---------------- -------- ---------------------------- METADATA NOT NULL JSON DATA JSON OWNER VARCHAR2(128) SOURCE VARCHAR2(128) KEY VARCHAR2(1024)
CUSTOMERS
表をデータ・ソースとしてMY_SEARCH_INDEX
に追加します。EXEC DBMS_SEARCH.ADD_SOURCE('DOCUSER.MY_SEARCH_INDEX', 'DOCUSER.CUSTOMERS');
他のすべての表ソースをこの索引に追加できますが、このシナリオ例を完了する必要はありません。
- 次のステップに示すように、索引の
DATA
列およびMETADATA
列をDBMS_SEARCH
ディクショナリ・ビューとともに確認します:METADATA
列を問い合せます:SELECT JSON_SERIALIZE(METADATA FORMAT JSON) META FROM DOCUSER.MY_SEARCH_INDEX ORDER BY META;
METADATA
列は、DBMS_SEARCH
索引で、索引付けされた表またはビューの各行を一意に識別するために役立ちます。METADATA
列には、customers
表の索引付き行ごとに、次の形式のJSON表現が格納されていることを確認できます:META -------------------------------------------------------------------------------- {"OWNER":"DOCUSER","SOURCE":"CUSTOMERS","KEY":{"CUST_ID":1}} {"OWNER":"DOCUSER","SOURCE":"CUSTOMERS","KEY":{"CUST_ID":2}} {"OWNER":"DOCUSER","SOURCE":"CUSTOMERS","KEY":{"CUST_ID":3}} {"OWNER":"DOCUSER","SOURCE":"CUSTOMERS","KEY":{"CUST_ID":5}} 4 rows selected.
DATA
列を問い合せます:SELECT DATA FROM DOCUSER.MY_SEARCH_INDEX;
DATA
列に実際のデータは格納されないことに注意してください。かわりに、データは元の実表に存在します。この索引は、データ・ソース表を参照して、索引付けされた仮想JSONドキュメントをその場で作成します。データがフェッチおよび索引付けされた後、この列は事実上空になり、重複が回避されます。DATA -------------------------------------------------------------------------------- 4 rows selected.
- 索引付けされた仮想ドキュメントを取得し、
customers
表ソースから抽出された内容を確認します。SELECT JSON_SERIALIZE( DBMS_SEARCH.GET_DOCUMENT('DOCUSER.MY_SEARCH_INDEX', METADATA) FORMAT JSON) DOC FROM DOCUSER.MY_SEARCH_INDEX ORDER BY JSON_SERIALIZE(METADATA FORMAT JSON);
このドキュメントには、索引にデータ・ソースとして追加される
customers
表の索引付き行ごとにJSON表現が含まれています:DOC -------------------------------------------------------------------------------- {"DOCUSER":{"CUSTOMERS":{"CUST_ID":1,"FIRST":"Robert","LAST":"Smith"}}} {"DOCUSER":{"CUSTOMERS":{"CUST_ID":2,"FIRST":"John","LAST":"Doe"}}} {"DOCUSER":{"CUSTOMERS":{"CUST_ID":3,"FIRST":"James","LAST":"Martin"}}} {"DOCUSER":{"CUSTOMERS":{"CUST_ID":5,"FIRST":"Jane","LAST":"Y"}}} 4 rows selected.
USER_DBMS_SEARCH_INDEXES
ビューを問い合せて、索引のメタデータ値を表示します。SELECT IDX_NAME FROM USER_DBMS_SEARCH_INDEXES ORDER BY IDX_NAME;
このビューには、ユーザー・スキーマ
DOCUSER
に追加された索引名が表示されます:IDX_NAME -------------------------------------------------- MY_SEARCH_INDEX 1 row selected.
USER_DBMS_SEARCH_INDEX_SOURCES
ビューを問い合せて、データ・ソースのメタデータ値を表示します。SELECT IDX_NAME,SRC_OWNER,SRC_NAME,SRC_TYPE FROM USER_DBMS_SEARCH_INDEX_SOURCES ORDER BY IDX_NAME, SRC_OWNER,SRC_NAME;
このビューには、ユーザー・スキーマ(
DOCUSER
)から、索引に関連付けられているデータ・ソースの詳細が表示されます。ここで、ソース・タイプT
は「表」ソースを意味します:IDX_NAME ------------------------------------------------ SRC_OWNER ------------------------------------------------ SRC_NAME ------------------------------------------------ SRC_TYPE - MY_SEARCH_INDEX DOCUSER CUSTOMERS T 1 row selected.
- 索引にビューを追加し、
METADATA
列およびディクショナリ・ビューを再度調べて、変更が索引付きデータにどのように反映されるかを比較します。- 作成したビュー(
SEARCH_VIEW
)をデータ・ソースとして索引に追加します:EXEC DBMS_SEARCH.ADD_SOURCE('DOCUSER.MY_SEARCH_INDEX', 'DOCUSER.SEARCH_VIEW');
METADATA
列を問い合せます:SELECT JSON_SERIALIZE(METADATA FORMAT JSON) META FROM DOCUSER.MY_SEARCH_INDEX ORDER BY META;
METADATA
列には、索引付けされているビューの各行が追加で表示されます:META -------------------------------------------------------------------------------- {"OWNER":"DOCUSER","SOURCE":"CUSTOMERS","KEY":{"CUST_ID":1}} {"OWNER":"DOCUSER","SOURCE":"CUSTOMERS","KEY":{"CUST_ID":2}} {"OWNER":"DOCUSER","SOURCE":"CUSTOMERS","KEY":{"CUST_ID":3}} {"OWNER":"DOCUSER","SOURCE":"CUSTOMERS","KEY":{"CUST_ID":5}} {"OWNER":"DOCUSER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":1}} {"OWNER":"CUSTOMER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":2}} {"OWNER":"DOCUSER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":3}} {"OWNER":"DOCUSER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":5}} 8 rows selected.
USER_DBMS_SEARCH_INDEXES
ディクショナリ・ビューを問い合せます。SELECT IDX_NAME FROM USER_DBMS_SEARCH_INDEXES ORDER BY IDX_NAME;
このビューには、ユーザー・スキーマ
DOCUSER
に存在する索引名が表示されます:IDX_NAME -------------------------------------------------------------------------------- MY_SEARCH_INDEX 1 row selected.
USER_DBMS_SEARCH_INDEX_SOURCES
ディクショナリ・ビューを問い合せます。SELECT IDX_NAME,SRC_OWNER,SRC_NAME,SRC_TYPE FROM USER_DBMS_SEARCH_INDEX_SOURCES ORDER BY IDX_NAME, SRC_OWNER,SRC_NAME;
出力には、データ・ソースとして索引に追加された
SEARCH_VIEW
の追加行が表示されます。ここで、ソース・タイプT
とV
は、それぞれ「表」ソースと「ビュー」ソースを意味します:IDX_NAME ---------------------------------------------------- SRC_OWNER ---------------------------------------------------- SRC_NAME ---------------------------------------------------- SRC_TYPE - MY_SEARCH_INDEX DOCUSER CUSTOMERS T MY_SEARCH_INDEX DOCUSER SEARCH_VIEW V 2 rows selected.
- 作成したビュー(
JSON_EXISTS
演算子を使用して、索引に対して問合せを実行します。DATA
列にJSON要素$.DOCUSER.SEARCH_VIEW
が含まれる索引のビュー・ソースで、ドキュメントを検索します。SELECT JSON_SERIALIZE(METADATA FORMAT JSON) META FROM DOCUSER.MY_SEARCH_INDEX WHERE JSON_EXISTS(DATA,'$.DOCUSER.SEARCH_VIEW') ORDER BY META;
出力では、
DOCUSER
スキーマから4行が返され、顧客IDが1
、2
、3
および5
として結合されます:META -------------------------------------------------------------------------------- {"OWNER":"DOCUSER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":1}} {"OWNER":"DOCUSER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":2}} {"OWNER":"DOCUSER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":3}} {"OWNER":"DOCUSER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":5}} 4 rows selected.
DATA
列にJSON要素$.DOCUSER.SEARCH_VIEW
が含まれる索引のビュー・ソースで、仮想ドキュメントとしてドキュメントを検索します:これは、前のステップに示したのと同じ問合せです。ただし、ここでは、すべてのメタデータ値のJSON表現を使用して、索引付けされた仮想ドキュメント全体を表示できます:
SELECT JSON_SERIALIZE( DBMS_SEARCH.GET_DOCUMENT('DOCUSER.MY_SEARCH_INDEX', METADATA) FORMAT JSON) DOC FROM DOCUSER.MY_SEARCH_INDEX WHERE JSON_EXISTS(DATA,'$.DOCUSER.SEARCH_VIEW') ORDER BY JSON_SERIALIZE(METADATA FORMAT JSON);
出力では、
MY_SEARCH_INDEX
で索引付けされたメタデータ値が結合されてJSONドキュメントが返されます:DOC -------------------------------------------------------------------------------- {"DOCUSER":{"SEARCH_VIEW":{"CUST_ID":1,"BUSINESS_OBJECT":{"id":1,"name":"Robert Smith","num_orders":2,"orders":[{"order_id":1,"items":[{"id":845,"name":"Meteor Impact Survival Kit","quantity":1,"single_item_price":299,"total_price":299}]}, {"order_id":2,"items":[{"id":232,"name":"Rubber Christmas Tree","quantity":1, "single_item_price":65,"total_price":65},{"id":429,"name":"Air Guitar","quantity":4 ,"single_item_price":9.99,"total_price":39.96}]}]}}}} {"DOCUSER":{"SEARCH_VIEW":{"CUST_ID":2,"BUSINESS_OBJECT":{"id":2,"name":"John Doe","num_orders":1,"orders":[{"order_id":4,"items":[{"id":345,"name":"Border Patrol Costume","quantity":1,"single_item_price":19.99,"total_price":19.99}] }]}}}} {"DOCUSER":{"SEARCH_VIEW":{"CUST_ID":3,"BUSINESS_OBJECT":{"id":3,"name":"James Martin","num_orders":1,"orders":[{"order_id":3,"items":[{"id":122,"name":"Potato Gun","quantity":1,"single_item_price":29.99,"total_price":29.99}]}]}}}} {"DOCUSER":{"SEARCH_VIEW":{"CUST_ID":5,"BUSINESS_OBJECT":{"id":5,"name":"Jane Y", "num_orders":0}}}} 4 rows selected.
JSON_TEXTCONTAINS
演算子を使用して、ターゲット・パスに対してテキスト検索問合せを実行します。- キーワード"
Anon or Jane
"について、DATA
列の$.DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.name
JSONパスを問い合せます。SELECT JSON_SERIALIZE( DBMS_SEARCH.GET_DOCUMENT('DOCUSER.MY_SEARCH_INDEX', METADATA) FORMAT JSON) DOC FROM DOCUSER.MY_SEARCH_INDEX WHERE JSON_TEXTCONTAINS(DATA,'$.DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.name', 'Anon or Jane') ORDER BY JSON_SERIALIZE(METADATA FORMAT JSON);
出力では、
DOCUSER
スキーマのSEARCH_VIEW
ソースから、顧客IDが5
で名前がJane Y
のJSONドキュメントが返されます:DOC --------------------------------------------------------------------------------- {"DOCUSER":{"SEARCH_VIEW":{"CUST_ID":5,"BUSINESS_OBJECT":{"id":5,"name":"Jane Y", "num_orders":0}}}} 1 row selected.
SCORE
演算子をJSON_TEXTCONTAINS
とともに使用して、検索結果の関連性スコアを取得します。SELECT METADATA, score(1) from DOCUSER.MY_SEARCH_INDEX WHERE JSON_TEXTCONTAINS( DATA,'$.DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.name','Anon or Jane',1);
出力では、顧客ID
5
、Jane Yの一致レコードについて、メタデータ値と関連性スコア5が返されます。METADATA -------------------------------------------------------------------------------- SCORE(1) ---------- {"OWNER":"DOCUSER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":5}} 5 1 row selected.
- キーワード"
CONTAINS
演算子を使用してスキーマ全体を検索します。- 索引を問い合せて、キーワード"
Anon or Jane
"に一致するレコードを取得します。SELECT JSON_SERIALIZE( DBMS_SEARCH.GET_DOCUMENT('DOCUSER.MY_SEARCH_INDEX', METADATA) FORMAT JSON) DOC FROM DOCUSER.MY_SEARCH_INDEX WHERE CONTAINS(DATA, 'Anon or Jane') > 0 ORDER BY JSON_SERIALIZE(METADATA FORMAT JSON);
出力では、
DOCUSER
スキーマから2つのJSONオブジェクトが返されます。1つは、CUSTOMERS
表ソースからの、顧客IDが5
、名前がJane Y
のオブジェクトです。もう1つのオブジェクトも、顧客IDが5
、名前がJane Y
ですが、SEARCH_VIEW
ビュー・ソースから返されます。ここで、ビジネス・オブジェクトに関連付けられたオーダーがゼロであることに注意してください。
DOC -------------------------------------------------------------------------------- {"DOCUSER":{"CUSTOMERS":{"CUST_ID":5,"FIRST":"Jane","LAST":"Y"}}} {"DOCUSER":{"SEARCH_VIEW":{"CUST_ID":5,"BUSINESS_OBJECT":{"id":5,"name":"Jane Y", "num_orders":0}}}} 2 rows selected.
SCORE
演算子をCONTAINS
とともに使用して、検索結果の関連性スコアを取得します。SELECT METADATA, score(1) as search_score from DOCUSER.MY_SEARCH_INDEX WHERE CONTAINS(DATA, 'Anon or Jane',1)>0;
ここで、出力では、表ソースとビュー・ソースの両方から、顧客ID
5
、Jane Yの一致レコードが返されます。また、両方のレコードの検索スコアが5と表示されます。METADATA -------------------------------------------------------------------------------- SEARCH_SCORE ------------ {"OWNER":"DOCUSER","SOURCE":"CUSTOMERS","KEY":{"CUST_ID":5}} 5 {"OWNER":"DOCUSER","SOURCE":"SEARCH_VIEW","KEY":{"CUST_ID":5}} 5 2 rows selected.
- 索引を問い合せて、キーワード"
DBMS_SEARCH.FIND
プロシージャを使用して、ヒットリストを取得します。また、指定された、例による問合せ(QBE)フィルタ条件に基づいて、JSONドキュメントの集計もファセット化します。SELECT JSON_SERIALIZE(DBMS_SEARCH.FIND('DOCUSER.MY_SEARCH_INDEX', JSON('{ "$query" : { "DOCUSER.SEARCH_VIEW.*" : { "$contains" : "Gun or patrol costume" } }, "$facet" : [ { "$sum" : "DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.orders.items.total_price"}, { "$count" : { "path" : "DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.orders.items.single_item_price", "bucket" : [ { "$lt" : 20 }, { "$gte" : 20 } ] } }, { "$uniqueCount" : "DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.name" } ] }')) FORMAT JSON PRETTY) AGG FROM DUAL;
出力には、JSON形式の集計結果が表示されます。
ここで、問合せでは、
DOCUSER.SEARCH_VIEW
内でGun or patrol costume
というフレーズを検索します。$count
は、問合せ基準に一致する2つのレコードを示します。$facet
キーは、複数の集計結果を個別のバケットにグループ化し、次の集計を実行します:-
DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.orders.items.total_price
:orders.items.total_price
フィールドのすべてのアイテムの合計価格を合計49.98
に集計します。 -
DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.orders.items.single_item_price
:single_item_price
フィールドの個々のアイテムの価格を集計し、価格範囲(バケット)にグループ化します。 -
DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.name
:BUSINESS_OBJECT.name
フィールドでの名前(John Doe
およびJames Martin
)の一意の出現をカウントします。両方の名前が1回出現します。
AGG -------------------------------------------------------------------------------- { "$count" : 2, "$facet" : [ { "DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.orders.items.total_price" : { "$sum" : 49.98 } }, { "DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.orders.items.single_item_price" : [ { "bucket" : { "$gte" : 19.99, "$lt" : 20 }, "$count" : 1 }, { "bucket" : { "$gte" : 20, "$lte" : 29.99 }, "$count" : 1 } ] }, { "DOCUSER.SEARCH_VIEW.BUSINESS_OBJECT.name" : [ { "value" : "John Doe", "$uniqueCount" : 1 }, { "value" : "James Martin", "$uniqueCount" : 1 } ] } ] } 1 row selected.
-