Graph Developer's Guide for RDF Graph
先日、以下の記事にてSPARQLクエリをOracle REST Data Services APIより呼び出すアプリケーションについて紹介しています。
Oracle REST Data ServicesのRDF Graph APIを呼び出してSPARQLを実行する
今回の記事では、Matt Perryさんの以下の記事を参考にOracle DatabaseでGeoSPARQLの問合せを実行できる環境を作成し、APEXのアプケーションから実行してみます。
Exploring GeoSPARQL 1.1 Features in Oracle Database 23ai
Matt Perryさんの記事ではAutonomous DatabaseにRDF Graphを作成して、主にDatabase ActionsのSQLワークショップからGeoSPARQLの問い合わせを行っています。本記事ではAutonomous DatabaseにRDF Graphを作成するところは同じですが、ネットワークやモデルの作成、データのインポートおよびSPARQLクエリの実行を、Oracle REST Data Services APIを呼び出して実行します。
作成したAPEXアプリケーションは以下のように動作します。自然言語の問合せより生成AI(OpenAI GPT-4.1)を呼び出し、GeoSPARQLのクエリを生成しています。その後、生成されたクエリを実行しています。
クエリの実行結果をAPEXアプリケーションのマップ上に表示しています。
上記のAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sparql-operations-for-geo.zip
以前の記事「Oracle REST Data ServicesのRDF Graph APIを呼び出してSPARQLを実行する」で作成したアプリケーションに、以下の2つのページを追加しています。
- Upload - RDF GraphにN-Triplesをアップロードする画面
- Map - 生成AIでGeoSPARQLクエリを生成し、そのクエリの検索結果をマップに表示するページ(GIF動画のページ)
以下より、APEXアプリケーションに追加した機能とAPEXアプリケーションを使用するにあたって必要な作業を紹介します。
APEXアプリケーションをインポートすると作成されるリモート・サーバーおよびWeb資格証明を更新します。
ワークスペース・ユーティリティのリモート・サーバーを開くと、RDF Graph ORDS Endpointが作成されています。
これを開きます。
https://[ホスト名]/ords/[ワークスペース名]/_/db-api/stable
クライアントIDまたはユーザー名に、RDF Graphを作成するスキーマ名を設定します。クライアント・シークレットまたはパスワードは、そのスキーマのパスワードを設定します。
次にopenai api keyの資格証明を開きます。
資格証明シークレットにAuthorizationヘッダーに設定する値、つまりBearerに空白で区切ってOpenAIのAPIキーを繋げた値を設定します。以下の形式になります。
Bearer [OpenAIのAPIキー]
ホーム・ページを開いて、RDFネットワークを作成します。
Network NameはRDF_NETWORK、Network OwnerはRDFネットワークを保存するスキーマの名前です。Tablespace NameはRDFグラフを保存する表領域の名前です。Autonomous Databaseでは、通常これはDATAになります。
以上を設定し、ボタンCreate RDF networkをクリックします。
RDF networkが作成されました。と表示されれば、RDFネットワークRDF_NETWORKが作成されています。
Model NameにGEO_DATAを入力し、ボタンCreate RDF modelをクリックします。
RDF modelが作成されました。と表示されるとRDFモデルGEO_DATAがRDFネットワークRDF_NETWORKに作成されています。
RDFモデルGEO_DATAにロードするデータを手元にダウンロードします。このデータはMatt Perryさんの記事でロードしているデータと同じものを使用します。
ダウンロードしたファイル2015–11–02-SportThing.node.sorted.nt.bz2はbzip2で圧縮されているので、これを解凍します。macOSではbzip2コマンドを使用しました。
bzip2 -d 2015-11-02-SportThing.node.sorted.nt.bz2
Uploadページを開きます。Fileとして解凍したファイル2015-11-02-SportThing.node.sorted.ntを選択します。Batch Linesはデフォルトの1000から変更は不要です。
以上でUpload Fileをクリックします。
ボタンUpload Fileをクリックすると、以下のコードがプロセスとして実行されます。
APEX_APPLICATION_TEMP_FILESにアップロードされたファイルをBatch Linesで指定した行数毎にまとめて、APEXコレクションNTRIPLESのメンバーにCLOB(列としてはCLOB001)として保存します。
列N001(分割数=1000行単位の数)を降順でソートすると、N001の最大値が1226、列N002(これは行数)が338なので、ファイル2015–11–02-SportThing.node.sorted.ntに1225338の行が含まれていることが確認できます。
ボタンUpload Serverをクリックすると、APEXコレクションのメンバごとにN-TriplesをINSERT DATA { } で囲んでSPARQLのINSERT文にし、ORDS RDF Graph APIのSPARQL Updateを呼び出します。
大量のREST API(データのアップロードで1226回)を発行することになります。APEXの管理サービスのインスタンスの管理のセキュリティより、最大Webサービス・リクエストを十分大きい値に変更しておく必要があります。
ボタンUpload Serverをクリックし、N-Triplesのデータのアップロードを開始します。アップロード処理はバックグラウンドで実行します。
Totalworkはすべてのデータをアップロードするために必要なREST APIの発行回数、Sofarは現在までに成功したREST APIの呼び出し回数です。SofarがTotalworkの値に達するとデータのアップロードは完了です。
ボタンRefresh Reportをクリックすると、バックグラウンド処理の状況が更新されます。
ボタンUpload Serverが実行するプロセスに、以下のコードを記述しています。APEXコレクションに分割して保存されたN-TriplesのデータをINSERT DATA { }で囲んでREST APIを呼び出しています。REST APIの呼び出しが完了するごとに、APEX_BACKGROUND_PROCESS.SET_PROGRESSを呼び出して、進捗を更新しています。
環境に依存すると思いますが、REST API経由でのデータのアップロードに2時間超の時間がかかります。
Matt Perryさんの記事ではデータ・ファイルをオブジェクト・ストレージにアップロードし、それを外部表にしてsem_apis.load_into_staging_tableを呼び出してRDFモデルにN-Triplesをロードしています。多分、速度の面では圧倒的にアドバンテージがあると思います。大量にデータをロードする場合は、sem_apis.load_into_staging_tableの利用を検討することをお勧めします。
データのアップロードが途中で中断したときは、中断した時点でのSofarの値+1をStart Partに設定した上でボタンUpload Serverをクリックすると、中断した時点からアップロードが再開されます。
StatusがExecuted SuccessfullyになりSofarとTotalworkが同じ数値になると、データのロードは完了です。
データのアップロードが完了した後に索引を作成します。DBA権限のあるユーザーで以下のコマンドを実行します。
BEGIN
sem_apis.add_datatype_index(
'http://www.opengis.net/ont/geosparql#wktLiteral',
options=>'MATERIALIZE=T TOLERANCE=0.1 SRID=4326 DIMENSIONS=((LONGITUDE,-180,180) (LATITUDE,-90,90))',
network_owner=>'Network Ownerのスキーマ名',
network_name=>'RDF_NETWORK');
END;
/
Autonomous Databaseの場合、管理者ユーザーADMINにてDatabase ActionsのSQLワークシートで実行します。
BEGIN
sem_perf.gather_stats(network_owner=>'Network Ownerのスキーマ名', network_name=>'RDF_NETWORK');
END;
/
以上でGeoSPARQLの問合せができるようになりました。
Mapのページを開き、GeoSPARQLの問合せを発行します。
User Promptに「ニューヨーク市のテニスができる施設を一覧してください。」と入力し、ボタンGenerateをクリックします。SPARQL QueryにOpenAI GPT-4.1が生成した以下のレスポンスが設定されます。LLMのレスポンスなので、毎回同じになるとは限りません。
以下が「ニューヨーク市のテニスができる施設」を一覧するGeoSPARQLクエリです。
(与えられた例と同様、POLYGONはニューヨーク市の領域です。変更点はFILTER中の?sportの値のみです)
```sparql
PREFIX geovocab: <http://geovocab.org/geometry#>
PREFIX lgd: <http://linkedgeodata.org/ontology/>
PREFIX dbpedia: <http://dbpedia.org/resource/>
PREFIX ogc: <http://www.opengis.net/ont/geosparql#>
PREFIX ogcf: <http://www.opengis.net/def/function/geosparql/>
SELECT ?s ?sport ?label ?geom
WHERE {
?s geovocab:geometry/ogc:asWKT ?geom ;
lgd:featuresSport ?sport ;
rdfs:label ?label .
FILTER(ogcf:sfWithin(?geom,
"POLYGON((-74.2557 40.4961, -73.7004 40.4961, -73.7004 40.9153, -74.2557 40.9153, -74.2557 40.4961))"^^ogc:wktLiteral))
FILTER (?sport = dbpedia:Tennis)
}
ORDER BY ?label
```
このクエリは、ニューヨーク市ポリゴン内で、スポーツ種別が`dbpedia:Tennis`である施設(?s)・その種別(?sport)・ラベル(?label)・WKTジオメトリ(?geom)を一覧表示します。
ボタンGenerateでは、APEXが標準で提供しているアクションAIによるテキストの生成を呼び出しています。
生成AIの構成としてGenerate GeoSPARQL Queryを作成し、それを生成AIの構成に設定しています。入力値はページ・アイテムP4_USER_PROMPT、レスポンスの使用はページ・アイテムP4_SPARQL_QUERYです。
OpenAIのChat Completions APIの呼び出しとしては、構成Generate GeoSPARQL Queryにシステム・プロンプトが設定されていて、P4_SPARQL_QUERYがroleがuserのcontentになり、そのレスポンスであるcontentがP4_SPARQL_QUERYに設定されるという、単純な呼び出しが行われています。
生成AI構成のGenerate GeoSPARQL Queryでは、システム・プロンプトとして以下を設定しています。都市と運動施設の種別を検索条件とするGeoSPARQLクエリを生成するように指示しています。
問合せからGeoSPARQLのクエリを生成してください。PREFIXは以下です。
—
PREFIX geovocab: <http://geovocab.org/geometry#>
PREFIX lgd: <http://linkedgeodata.org/ontology/>
PREFIX dbpedia: <http://dbpedia.org/resource/>
PREFIX ogc: <http://www.opengis.net/ont/geosparql#>
PREFIX ogcf: <http://www.opengis.net/def/function/geosparql/>
—
選択するのは以下の?s ?geom ?sport ?labelです。
?s
geovocab:geometry/ogc:asWKT ?geom
lgd:featuresSport ?sport
rdfs:label ?label
GeoSPARQLのクエリの例です。ニューヨーク市の領域をPOLYGONで与えて、そのPOLYGONに含まれる野球ができる施設の一覧です。
PREFIX geovocab: <http://geovocab.org/geometry#>
PREFIX lgd: <http://linkedgeodata.org/ontology/>
PREFIX dbpedia: <http://dbpedia.org/resource/>
PREFIX ogc: <http://www.opengis.net/ont/geosparql#>
PREFIX ogcf: <http://www.opengis.net/def/function/geosparql/>
SELECT ?s ?sport ?label ?geom
WHERE {
?s geovocab:geometry/ogc:asWKT ?geom ;
lgd:featuresSport ?sport ;
rdfs:label ?label .
FILTER(ogcf:sfWithin(?geom,
"POLYGON((-74.2557 40.4961, -73.7004 40.4961, -73.7004 40.9153, -74.2557 40.9153, -74.2557 40.4961))"^^ogc:wktLiteral))
FILTER (?sport = dbpedia:Baseball)
}
ORDER BY ?label
ボタンUpdateをクリックし、生成AIのレスポンスに含まれているSPARQLを実行し、結果をマップと対話モード・レポートに表示します。
ボタンUpdateをクリックしたときに、プロセスとして以下のコードを実行します。SPARQLクエリのレスポンスとして受け取ったJSONを、APEXコレクションSPORTS_FACILITIESに入れています。マップおよび対話モード・レポートはこのAPEXコレクションSPORTS_FACILITIESをソースとして、マップのレイヤやレポートを表示しています。
今回の記事は以上になります。
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完