3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SQL を用いた JSON 全文検索

Last updated at Posted at 2024-07-07

概要

SQL の JSON 関数 JSON_TEXTCONTAINS を用いると、任意の文字列が含まれている JSON データを検索することができます。

SELECT COUNT(*)
FROM wiki_en
WHERE JSON_TEXTCONTAINS(content_json, '$.text', 'データベース');

Oracle Database の場合には、次のような簡単な構文で JSON 列に対する全文索引を作成することができます。

CREATE SEARCH INDEX wiki_search_idx ON wiki (jsondata);

この全文索引が利用できると、RDBMS で大きなテキストデータを含む JSON を扱いやすくなるため便利ですが、一方で、データ量に対して性能や索引サイズが十分かわかっていないため、日本語 Wikipedia のデータを使って簡単に検証したいと思います。

日本語 Wikipedia の JSON データを作成して、データベースにロードするためのスクリプトとその解説は こちらの記事 に記載しています。

Oracle Database の準備

無料版の Oracle Database 23ai Free を利用しようと考えましたが、全文索引のサイズを含めるとユーザーデータの合計が 12GB の制限を超えてしまいました。そのため、以下の Oracle Cloud 環境を使用しました。

  • Oracle Base Database Service
    • DB Version: 23.4.1.24.06 (23ai)
    • Edition: Standard Edition
    • Shape: VM.Standard.E5.Flex
    • CPU Core Count: 1

sys でログインして、一般ユーザー myuser を作成します。

sqlplus sys/<password>@<ip_address>:1521/<service_name> as sysdba
sys
CREATE USER myuser IDENTIFIED BY <password> QUOTA UNLIMITED ON users;
GRANT CONNECT, RESOURCE TO myuser;

myuser でログインできることを確かめておきます。

sqlplus myuser/<password>@<ip_address>:1521/<service_name>

Wikipedia データのインポート

今回用いる wiki 表のダンプ(約 3.8 GB)をこちらに公開しています。2024年5月1日時点の日本語 Wikipedia データ(2,289,280 件)を JSON 列に格納しています。

OS ディレクトリを作成しておきます。

sudo mkdir /u01/dir01
sudo chown oracle:oinstall /u01/dir01/

ダンプファイルをダウンロードします。

sudo su - oracle
cd /u01/dir01/
wget https://objectstorage.ap-osaka-1.oraclecloud.com/n/ax2juefahle0/b/download/o/wiki.dmp

sys でログインして、myuser にディレクトリ作成権限を付与します。

sys
GRANT create any directory, drop any directory TO myuser;

myuser でログインして、ディレクトリを作成します。

myuser
CREATE DIRECTORY dir01 AS '/u01/dir01';

インポート用のコマンド impdp を使って表をインポートします。

impdp myuser/<password>@<ip_address>:1521/<service_name> \
tables=myuser.wiki directory=dir01 dumpfile=wiki.dmp logfile=wiki.log
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
. . imported "MYUSER"."WIKI"                              3.8 GB 2289280 rows

表には JSON データを格納する列のみが含まれています。

myuser
SELECT * FROM wiki FETCH FIRST 3 ROWS ONLY;
JSONDATA
--------------------------------------------------------------------------------
{"id":"60","revid":"2160598","url":"https://ja.wikipedia.org/wiki?curid=60","tit
{"id":"62","revid":"1788533","url":"https://ja.wikipedia.org/wiki?curid=62","tit
{"id":"63","revid":"516649","url":"https://ja.wikipedia.org/wiki?curid=63","titl

全文索引の作成

全文索引の作成には一時表領域を使用するため、一時表領域が十分に拡張できるよう、データファイルを追加しておきます。

sys でログインして、データファイルをひとつ追加します。

sys
ALTER TABLESPACE temp ADD TEMPFILE '+DATA';

myuser でログインして、全文索引を作成します。この環境では 15 分程度かかりました。

myuser
CREATE SEARCH INDEX wiki_search_idx ON wiki (jsondata);
INDEX WIKI_SEARCH_IDX created.
Elapsed: 00:15:08.640

sys でログインして、セグメントのサイズを確認します。

sys
SELECT
  SEGMENT_NAME
, SEGMENT_TYPE
, BYTES/1024/1024 AS MB
FROM DBA_SEGMENTS
WHERE segment_name LIKE '%WIKI%'
ORDER BY mb DESC;
SEGMENT_NAME		      SEGMENT_TYPE     MB
------------------------- ------------ ------
DR$WIKI_SEARCH_IDX$I	  TABLE 	     8758
DR$WIKI_SEARCH_IDX$X	  INDEX 	     5991
WIKI			          TABLE 	     3232
DR$WIKI_SEARCH_IDX$SN	  TABLE 	      316
DR$WIKI_SEARCH_IDX$SNI	  INDEX 	      216
DR$WIKI_SEARCH_IDX$KR	  INDEX 	       72
DR$WIKI_SEARCH_IDX$KD	  INDEX 	       72
DR$WIKI_SEARCH_IDX$K	  TABLE 	       54
DR$WIKI_SEARCH_IDX$H	  INDEX 	    .0625
DR$WIKI_SEARCH_IDX$STI	  INDEX 	    .0625
DR$WIKI_SEARCH_IDX$UI	  INDEX 	    .0625
DR$WIKI_SEARCH_IDX$NI	  INDEX 	    .0625
DR$WIKI_SEARCH_IDX$ST	  TABLE 	    .0625
DR$WIKI_SEARCH_IDX$B	  TABLE 	    .0625
DR$WIKI_SEARCH_IDX$C	  TABLE 	    .0625
DR$WIKI_SEARCH_IDX$Q	  TABLE 	    .0625
DR$WIKI_SEARCH_IDX$U	  TABLE 	    .0625
DR$WIKI_SEARCH_IDX$N	  TABLE 	    .0625
DR$WIKI_SEARCH_IDX$G	  TABLE 	    .0625

DR$WIKI_SERACH_IDX$.. という名前の表と索引が内部的に作成されていて、3.2 GB 程度の wiki 表に対して、数倍のデータ容量が必要になることがわかります。

JSON 内の全文検索

JSON_TEXTCONTAINS 関数を用いて、本文に「データベース」という単語を含む Wikipedia ページの数をカウントしてみると、0.1 秒以内に実行できることがわかります。

SELECT COUNT(*)
FROM wiki
WHERE JSON_TEXTCONTAINS(jsondata, '$.text', 'データベース');
  COUNT(*)
----------
       288

Elapsed: 00:00:00.028

試しに索引を使わないようにヒントをつけてみると実行時間は 26 秒になりました。

SELECT /*+ NO_INDEX(wiki wiki_search_idx) */ COUNT(*)
FROM wiki
WHERE JSON_TEXTCONTAINS(jsondata, '$.text', 'データベース');
  COUNT(*)
----------
       288

Elapsed: 00:00:26.320

JSON の値の内容を出力するときには JSON_VALUE 関数を用います。text 要素に含まれる長い文字列は CLOB にキャストします。

SELECT JSON_VALUE(jsondata, '$.id') AS id
     , JSON_VALUE(jsondata, '$.title') AS title
     , JSON_VALUE(jsondata, '$.text' RETURNING CLOB) AS text
FROM wiki
WHERE JSON_TEXTCONTAINS(jsondata, '$.text', 'データベース')
FETCH FIRST 3 ROWS ONLY;
ID      TITLE                 TEXT
------- --------------------- -------------------------------------------------------------------------------------------------------------------------
63      情報工学                情報工学(じょうほうこうがく、)とは、「計算機による情報処理に関連する科学技術の一分野」を指す言葉であり、「情報科学」や「計算機科学」ともいう。なお英語の は
1297    パーソナルコンピュータ    パーソナルコンピュータ()は、個人によって占有されて使用されるコンピュータのことである。略称はパソコンまたはPC(ピーシー)。ただし「PC」という略称は、特に
1244    バイオインフォマティクス   バイオインフォマティクス(英語:bioinformatics)とは、生命科学と情報科学の融合分野のひとつであり、DNAやRNA、タンパク質をはじめとする、生命が

Elapsed: 00:00:00.051

以上の通り、200 万件以上の日本語 JSON データに対して、全文索引が有効であることを確認できました。索引のデータサイズは大きいため注意が必要ですが、JSON の内容と表のメタデータを用いて検索する場合などに便利そうです。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?