SPARQL TextSearch
提供:TogoWiki
SPARQLでテキスト検索をする方法の調査
SPARQL仕様ではFilter regexを使用してテキスト検索をすることになっているが、データ量が増えると全く使用できないレベルで遅くなる。
そのため、各トリプルストアは独自に全文検索の仕組みを提供していることがある。
独自機能であるため記述方法や挙動が異なるため調査する。
目次 |
SPARQL仕様
テキスト完全マッチであればオブジェクトにそのまま指定する。
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . <http://example.com/test> rdfs:label "GI:16329170" .
SELECT ?s ?p WHERE { ?s ?p "GI:16329170" .} ------------------------------ s p http://example.com/test http://www.w3.org/2000/01/rdf-schema#label
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
<http://example.com/test> rdfs:label "Title"@en .
テキストの言語が指定してある場合には、言語タグまで指定しないとマッチしない。
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . <http://example.com/test> rdfs:label "Tokyo"@en .
SELECT ?s ?p WHERE { ?s ?p "Tokyo" . } ------------------------------ s p
SELECT ?s ?p WHERE { ?s ?p "Tokyo"@en . } ------------------------------ s p http://example.com/test http://www.w3.org/2000/01/rdf-schema#label
テキストのあいまい検索(部分一致)をする場合にはFilter regexを使用する。3番目の引数"i"は大文字小文字を区別しない。
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . <http://example.com/test> rdfs:label "Bacteria"@en .
SELECT ?text WHERE { ?s ?p ?text FILTER regex (?text, "bac", "i") . } ------------------------------ text "Bacteria"@en
単語の途中の文字列でもヒットする
SELECT ?text WHERE { ?s ?p ?text FILTER regex (?text, "cte", "i") . } ------------------------------ text "Bacteria"@en
Virtuoso
SPARQL文に加えて、フルテキスト検索機能としてVirtuoso独自のプレディケート"bif:contains"を使用する。
SELECT ?text WHERE { ?s rdfs:label ?text . ?text bif:contains '"Synechocystis"' . } ----------------------------- text "Synechocystis sp. PCC 6803 chromosome, complete genome."
インデックス生成のタイミング
フルテキスト検索用インデックスは、データのロードが終了した時点でスケジューラにより自動的に開始され、データ追加等でも自動的で生成される。
そのため、ロード直後でインデックスの生成が終わらない間は、bif:containsで検索してもヒットしない(100億トリプルでロード終了からインデックス生成に3時間掛った)。
インデックス自動生成設定の確認(参考)
スケジューラによって最後にインデック生成コマンドが実行された時刻(SE_LAST_COMPLETED)が確認できる。(SE_INTERVAL=)1分間隔の設定のように見えるが10分間隔で実行される(謎)。尚、このスケジューラは削除できない(削除しても再起動で復活する)
SQL> SELECT SE_SQL , SE_START, SE_LAST_COMPLETED, SE_INTERVAL FROM DB.DBA.SYS_SCHEDULED_EVENT WHERE SE_NAME='VT_INC_INDEX_DB_DBA_RDF_OBJ()'; SE_SQL SE_START SE_LAST_COMPLETED SE_INTERVAL _______________________________________________________________________________ "DB"."DBA"."VT_INC_INDEX_DB_DBA_RDF_OBJ"() 2020.6.16 11:39.1 761837000 2020.6.16 11:49.5 881444000 1 ※SE_LAST_COMPLETEDはインデックス生成コマンド開始時間であり、インデックス生成終了時間ではない
インデックス生成完了判定
インデックス生成の終了判定は、VTLOG_DB_DBA_RDF_OBJテーブルの件数で判定可能。
対象のデータは一旦VTLOG_DB_DBA_RDF_OBJテーブルに貯められるので、ロード直後はテーブルにデータがあり終了すると件数が0になる。
SQL> SELECT COUNT(*) FROM DB.DBA.VTLOG_DB_DBA_RDF_OBJ; _______________________________________________________________________________ 3369621 ※ ロード直後でスケジューラが実行されていない状態(データ量で値は変わる) SQL> SELECT COUNT(*) FROM DB.DBA.VTLOG_DB_DBA_RDF_OBJ; _______________________________________________________________________________ 1687230 ※ 10分間隔のスケジューラでインデックスが生成が実行され実行中の状態。徐々に件数が減っていく SQL> SELECT COUNT(*) FROM DB.DBA.VTLOG_DB_DBA_RDF_OBJ; _______________________________________________________________________________ 0 ※ インデックスが生成が終了した状態。この後別のデータがロードされると増えてスケジューラの実行を待つ
インデックス手動生成
スケジュールによる自動実行に頼りたくない場合には、ロード後に以下のコマンドを実行する。
使用例: 軽量なデータをロードして直後から検索インデックスを使用したい場合、インデックス生成が完了したデータでdbファイルを配布したい場合等
SQL> DB.DBA.VT_INC_INDEX_DB_DBA_RDF_OBJ(); ※ インデックス生成が終了するまで応答ない
インデックスを生成しない場合
"bif:contains"による検索をしない場合、インデックス生成をしない設定もできる。
ロード前に以下をコマンドを実行する。ドキュメントには'ALL'の部分が'All'と記載されているが大文字指定しないと効かない。
ロード後(あるいは途中)にこのコマンドを実行しても効かずにインデックスは生成されてしまう(VTLOG_DB_DBA_RDF_OBJテーブルにデータが貯まった後では止められない)
SQL> DB.DBA.RDF_OBJ_FT_RULE_DEL(null, null, 'ALL');
インデックスを生成しないモードでロードしたが後から生成したくなった場合には、RDF_OBJ_FT_RULE_ADDコマンドを実行する。
ロード済みデータ量が多いと実行に時間がかかるのでオススメしない。
SQL> DB.DBA.RDF_OBJ_FT_RULE_ADD (null, null, 'ALL'); ※ VTLOG_DB_DBA_RDF_OBJテーブルに一時データが貯まり、次回スケジューラでインデックス生成される SQL> DB.DBA.VT_INC_INDEX_DB_DBA_RDF_OBJ(); ※ 即座にインデックス生成を開始し同期処理で完了させたい場合にはこのコマンドも実行する。
AND検索
SELECT ?text WHERE { ?s rdfs:label ?text . ?text bif:contains '"Synechocystis" AND "PCC"' . } ----------------------------- text "Synechocystis sp. PCC 6803 chromosome, complete genome."
OR検索
SELECT ?text WHERE { ?s rdfs:label ?text . ?text bif:contains '"Synechocystis" OR "hoge"' . } ----------------------------- text "Synechocystis sp. PCC 6803 chromosome, complete genome."
単語単位の検索になる
- 単語単位に分解されインデックスが生成されるので(形態素解析)、単語の途中の文字列を指定してもヒットしない。
SELECT ?text WHERE { ?s rdfs:label ?text . ?text bif:contains '"Synech"' . } ----------------------------- text (ヒット無し)
ワイルドカード「*」は使えるが。。。
SELECT ?text WHERE { ?s rdfs:label ?text . ?text bif:contains '"chro*"' . } ----------------------------- text "Synechocystis sp. PCC 6803 chromosome, complete genome."
ワイルドカード「*」は形態素解析で区切られた単語のうち頭4文字は指定されなければならない。
SELECT ?text WHERE { ?s rdfs:label ?text . ?text bif:contains '"chr*"' . } ----------------------------- エラー:Virtuoso 22023 Error FT370: Wildcard word needs at least 4 leading characters
どういうものが単語の区切りになるかは資料がない。
空白、".", ",", ":" 等で区切られる様子だが、それ以外の記号もあるはず。
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . <http://example.com/test> rdfs:label "GI:16329170" .
"GI:16329170"は":"が区切り文字になり、"GI"と"16329170"に分かれる(と思われる)。
その上で頭4文字以上の指定が必要なので、"16329170"の頭4文字以上指定しなければエラー
SELECT ?text WHERE { ?s rdfs:label ?text . ?text bif:contains '"GI:163*"' . } ----------------------------- エラー:Virtuoso 22023 Error FT370: Wildcard word needs at least 4 leading characters
SELECT ?text WHERE { ?s rdfs:label ?text . ?text bif:contains '"GI:1632*"' . } ----------------------------- text "GI:16329170"
Jena
Jena 2.11.0から新たにテキスト検索用のモジュールがリリースされ、SPARQLに独自のプレディケートを指定する。
(動作未確認)
PREFIX text: <http://jena.apache.org/text#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT ?s { ?s text:query (rdfs:label 'word' 10) ; rdfs:label ?label }
Text searches with SPARQL
検索インデックス生成にはApache LuceneかApache Solrを使用できる様子。
Jena 2.11.0以前はLARQというテキスト検索機能が用意されていた。
同じくApache Solrと連携しており、独自のプレディケートを記述する形式だが、互換性はなくなった。
LARQ形式(?)
PREFIX pf: <http://jena.hpl.hp.com/ARQ/property#> SELECT ?doc { ?lit pf:textMatch '+text' . ?doc ?p ?lit }
Stardog
LARQ()と同等の記述で全文検索をサポートしている様子。 (動作未確認) http://docs.stardog.com/java/ (リンク切れ)
- 最新の仕様は以下のページに [yayamamo追記 2017/2/24]