9.1 ユーザー定義の推論
RDFグラフの推論の拡張アーキテクチャによって、すでに提供されている推論のサポートに加えて、ユーザー定義の推論が使用可能になります。
9.1.1 ユーザー定義の推論によって解決される問題と得られる利点
Oracle Database 12cリリース(12.1)よりも前のOracle Database推論エンジンでは、OWL 2 RL、RDFS、SKOS、SNOMED (コアEL)およびユーザー定義ルールに対するネイティブ・サポートが提供され、広範なアプリケーションと要件に対応していました。一方で、新しいRDFリソースを作成してルール推論プロセスに追加することができないという制限がありました。
Oracle Database 12c リリース(12.1)よりも前の機能と制限の例として、次の簡単な推論ルールを考えてみます。
?C rdfs:subClassOf ?D . ?x rdf:type ?C . ==> ?x rdf:type ?D
前述のルールでは、インスタンスxのサブクラスCは、インスタンスCのスーパークラスDになる、としています。ルールに対応する部分は、2つの変数 ?xと?Dを示しています。ただし、これらの変数はルールの前件部分に存在している必要があり、さらにこれらのRDFリソースはナレッジ・ベース内に存在している必要があります。いいかえると、たとえばJohnがGraduateStudentとして存在しており、その公理によってGraduateStudentクラスはStudentクラスのサブクラスであると指定されている場合にのみ、JohnがStudentであると導出されます。
もう1つの制限の例は、Oracle Database 12cリリース(12.1)よりも前の推論関数では、推論プロセスの新しいRDFリソースとしてフルネームを生成するために姓と名を組み合せることがサポートされていなかったことです。この要件は、具体的には、次のようなルールとして取得されます。
?x :firstName ?fn ?x :lastName ?ln ==> ?x :fullName concatenate(?fn ?ln)
Oracle Database 12cリリース1 (12.1)では、RDFグラフ推論拡張アーキテクチャによって、推論プロセスでユーザーが独自の推論拡張関数を実装し、これをネイティブの推論プロセスに統合できるようになりました。このアーキテクチャにより、次のことが実現されています。
-
新しいRDFリソースの生成を必要とするルールをサポートします。
たとえば、文字列または他の文字列操作の連結、数学的な計算およびWebサービス・コールアウトがあります。
-
特定の既存のルールをカスタマイズされた最適化を使用して実装することができます。
ネイティブOWL推論エンジンには、多数のルールのための最適化があり、これらのルールは様々な大規模なオントロジには効率的に動作しますが、試されてないいくつかの新しいオントロジについては、特定の推論コンポーネントをカスタマイズした最適化の方が効果的に動作する場合があります。この場合は、SEM_APIS.CREATE_INFERRED_GRAPHコールの特定の推論コンポーネントを無効にし、新しい最適化を実装する、カスタマイズした推論拡張関数を(
inf_ext_user_func_nameパラメータを使用して)指定できます。 -
洗練された推論機能を使用して推論エンジンを拡張することができます。
たとえば、地理空間推論、時間間隔推論、テキスト解析機能のネイティブのデータベース推論プロセスへの統合があります。
親トピック: ユーザー定義の推論
9.1.2 ユーザー定義の推論のAPIサポート
ユーザー定義参照のための主要なアプリケーション・プログラミング・インタフェース(API)は、SEM_APIS.CREATE_INFERRED_GRAPHプロシージャの特に最後のパラメータです。
inf_ext_user_func_name IN VARCHAR2 DEFAULT NULL
inf_ext_user_func_nameパラメータを指定すると、使用したい特定のロジックを実装する、1つ以上のユーザー定義の推論関数が指定されます。
親トピック: ユーザー定義の推論
9.1.2.1 ユーザー定義の推論関数の要件
SEM_APIS.CREATE_INFERRED_GRAPHプロシージャへのコールで、inf_ext_user_func_nameパラメータに指定される各ユーザー定義の推測関数は、次のとおりである必要があります。
-
名前は文字列
SEM_INF_で始まります。 -
起動者の権限ではなく定義者の権限で作成されます。(定義者権限と起動者権限については、『Oracle Databaseセキュリティ・ガイド』を参照してください。)
ユーザー定義の推論関数の形式は、次のSEM_INF_EXAMPLEという架空の関数の例に示すような形式です。
create or replace function sem_inf_example(
src_tab_view in varchar2,
resource_id_map_view in varchar2,
output_tab in varchar2,
action in varchar2,
num_calls in number,
tplInferredLastRound in number,
options in varchar2 default null,
optimization_flag out number,
diag_message out varchar2
)
return boolean
as
pragma autonomous_transaction;
begin
if (action = SDO_SEM_INFERENCE.INF_EXT_ACTION_START) then
<... preparation work ...>
end if;
if (action = SDO_SEM_INFERENCE.INF_EXT_ACTION_RUN) then
<... actual inference logic ...>
commit;
end if;
if (action = SDO_SEM_INFERENCE.INF_EXT_ACTION_END) then
<... clean up ...>
end if;
return true; -- succeed
end;
/
grant execute on sem_inf_example to <network_owner>;
ユーザー定義関数の形式で、optimization_flag出力パラメータは、数値に対応する1つ以上のOracle定義の名前を指定できます。次の1つ以上を指定できます。
-
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_NONEは、推論エンジンが拡張関数の最適化を有効にしないことを示します。(optimization_flagパラメータが設定されていないとき、これが推論エンジンのデフォルトの動作です。) -
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_ALL_IDSは、拡張関数によって推論されたすべてのトリプル/クワッドがリソースIDのみを使用することを示します。つまり、output_tab表にはリソースID(列gid、sid、pidおよびoid)のみが含まれ、語彙の値(列g、s、pおよびoはすべてのNULLです)は含まれません。この最適化フラグを有効化すると、推論エンジンは、リソースID参照をスキップすることができます。 -
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_NEWDATA_ONLYは、拡張関数によって推論されるすべてのトリプル/クワッドが新規であり、src_tab_view内に存在しないことを示します。この最適化フラグを有効化すると、推論エンジンでoutput_tab表とsrc_tab_viewとの間の重複チェックをスキップすることができます。src_tab_viewには、拡張関数から推論されたトリプル/クワッドと、前回の推論ラウンドからのトリプル/クワッドが含まれることに注意してください。 -
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_UNIQDATA_ONLYは、拡張関数によって推論されるすべてのトリプル/クワッドが一意であり、output_tab表内に存在しないことを示します。この最適化フラグを有効化すると、推論エンジンでoutput_tab表内の重複チェックをスキップすることができます(たとえば拡張関数で2回推論された同じトリプルをチェックする必要がありません)。拡張関数の各推論ラウンドの最初のoutput_tab表は空であり、データの一意性を保つ必要があるのは現在の推論ラウンドでのみであることに注意してください。 -
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_IGNORE_NULLは、主語、述語または目的語のリソースがNULLのとき、推論されたトリプルまたはクワッドが推論エンジンで無視されることを示します。推論エンジンは、output_tab表で両方の列がNULLのとき、リソースをNULLとみなします (たとえば、s列とsid列が両方ともNULLの場合、主語はNULL)。この最適化フラグを有効化すると、推論エンジンでoutput_tab表の無効なトリプル/クワッドをスキップすることができます。推論エンジンは、NULLのグラフ列(gとgid)をデフォルトのグラフとして解釈することに注意してください。
optimization_flag出力パラメータに複数の値を指定するには、プラス記号(+)を使用して値を連結します。たとえば、次のようにします。
optimization_flag := SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_ALL_IDS +
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_NEWDATA_ONLY +
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_UNIQDATA_ONLY;
optimization_flag出力パラメータの使用の詳細は、「例3: パフォーマンスの最適化」を参照してください。
親トピック: ユーザー定義の推論のAPIサポート
9.1.3 ユーザー定義の推論拡張関数の例
次の例に、ユーザー定義参照拡張関数を使用して推論グラフを作成する方法を示します。
-
例1: 静的トリプルの追加、例2: 動的トリプルの追加および例3: パフォーマンスの最適化 は、ユーザー定義の推論の拡張に関する基本を扱っています。
例1: 静的トリプルの追加および例2: 動的トリプルの追加は、推論されたトリプルの新規追加にフォーカスしています。
例3: パフォーマンスの最適化は、パフォーマンスの最適化にフォーカスしています。
-
例4: 時間推論(いくつかの関連する例)と例5: 空間推論は、Oracleのネイティブの型と演算子を利用することによって、特殊なデータ型を効率的に処理する方法を示します。
例4: 時間推論(いくつかの関連する例)は、
xsd:dateTimeデータ型にフォーカスしています。例5: 空間推論は、地理空間データ型にフォーカスしています。
-
例6: Webサービスのコールは、Oracle GeocoderサービスへのWebサービス・コールを行います。
最初の3つの例では、RDFグラフEMPLOYEESが存在し、次のRDFデータがTurtle形式で表示されていることを前提としています。
:John :firstName "John" ;
:lastName "Smith" .
:Mary :firstName "Mary" ;
:lastName "Smith" ;
:name "Mary Smith" .
:Alice :firstName "Alice" .
:Bob :firstName "Bob" ;
:lastName "Billow" .
ユーザー定義の推論拡張ファンクションの作成に関する要件とガイドラインについては、「ユーザー定義推論のAPIサポート」を参照してください。
親トピック: ユーザー定義の推論
9.1.3.1 例1: 静的トリプルの追加
ユーザー定義の推論の拡張関数で新しいデータを推論する最も基本的な方法は、静的データの追加です。静的データは、RDFグラフの既存のデータに依存しません。これは、ユーザー定義の推測拡張関数にとって一般的な例ではなく、推論グラフへのトリプルの追加に関する基本を示しています。既存のオントロジを拡張するための準備フェーズ(つまりaction='START')では、静的データの挿入の方が、より一般的に実行されます。
次のユーザー定義の推論拡張関数(sem_inf_static)は、推論グラフに3つの静的トリプルを追加します。
-- this user-defined rule adds static triples
create or replace function sem_inf_static(
src_tab_view in varchar2,
resource_id_map_view in varchar2,
output_tab in varchar2,
action in varchar2,
num_calls in number,
tplInferredLastRound in number,
options in varchar2 default null,
optimization_flag out number,
diag_message out varchar2
)
return boolean
as
query varchar2(4000);
pragma autonomous_transaction;
begin
if (action = 'RUN') then
-- generic query we use to insert triples
query :=
'insert /*+ parallel append */ into ' || output_tab ||
' ( s, p, o) VALUES ' ||
' (:1, :2, :3) ';
-- execute the query with different values
execute immediate query using
'<http://example.org/S1>', '<http://example.org/P2>', '"O1"';
execute immediate query using
'<http://example.org/S2>', '<http://example.org/P2>', '"2"^^xsd:int';
-- duplicate quad
execute immediate query using
'<http://example.org/S2>', '<http://example.org/P2>', '"2"^^xsd:int';
execute immediate query using
'<http://example.org/S3>', '<http://example.org/P3>', '"3.0"^^xsd:double';
-- commit our changes
commit;
end if;
-- return true to indicate success
return true;
end sem_inf_static;
/
show errors;
sem_inf_static関数は、SQL挿入問合せを実行(挿入のターゲット表としてoutput_tabを使用)することで、新しいデータを挿入します。output_tab表には、現在のコール(num_callsパラメータを参照)中にsem_inf_static関数によって追加されるトリプルのみが含まれることになります。推論エンジンは、ユーザー定義の推論拡張関数を常に3回以上(アクション・パラメータ('START'、'RUN'および'END')で取り得る値ごとに1回)コールします。sem_inf_staticは準備またはクリーンアップを実行する必要がないため、関数ではRUNフェーズでデータが追加されるのみです。拡張関数では、現在の推論ラウンド中に推論されるデータに応じて、RUNフェーズの間に複数回コールできます。
sem_inf_static関数では、既存のトリプルに対する(重複トリプルを防ぐための)チェックは行いませんが、推論エンジンが結果の推論グラフに重複トリプルを生成することはありません。推論エンジンは、output_tab表(拡張関数によって挿入されるデータ)から、および最終推論グラフ(RDFグラフまたは他の推論データ)から、重複をフィルタ処理して除外します。適切な最適化フラグを設定する(optimization_flagパラメータを使用する)ことで、この便利な機能は使用されず、パフォーマンスが向上します。(最適化フラグの詳細は、「例3: パフォーマンスの最適化」を参照してください。)
output_tabの表定義にはグラフ名の列が表示されていますが、グローバル推論(SEM_APIS.CREATE_INFERRED_GRAPHのデフォルトの動作)と名前付きグラフ・グローバル推論(NGGI)を実行すると、推論エンジンは、拡張関数によって追加されるトリプルですべてのグラフ名を無視して、オーバーライドします。ユーザー定義の拡張ファンクションで、特定の名前付きグラフにトリプルを追加するには、NGLI (名前付きグラフ・ローカル推論)を使用します。NGLIでは、すべてのトリプルが名前付きグラフに属している必要があります(output_tabのgid列とg列の両方がNULLではないことが必要です)。
ネットワーク所有者は、sem_inf_static関数を推論に使用するため、この関数に関する実行権限を持っている必要があります。次の例は、sem_inf_static関数に適切な権限を付与し、(OWLPRIME推論ロジックとともに)関数を使用して推論グラフを作成する方法を示しています。
-- grant appropriate privileges
grant execute on sem_inf_static to RDFUSER;
-- create the inferred graph
begin
sem_apis.create_inferred_graph(
'EMPLOYEES_INF'
, sem_models('EMPLOYEES')
, sem_rulebases('OWLPRIME')
, passes => SEM_APIS.REACH_CLOSURE
, inf_ext_user_func_name => 'sem_inf_static'
, network_owner=>'RDFUSER'
, network_name=>'NET1'
);
end;
/
次の例は、新しい伴意データを示しています。
-- formatting
column s format a23;
column p format a23;
column o format a23;
set linesize 100;
-- show results
select s, p, o from table(SEM_MATCH(
'select ?s ?p ?o where { ?s ?p ?o } order by ?s ?p ?o'
, sem_models('EMPLOYEES')
, sem_rulebases('OWLPRIME')
, null, null, null
, 'INF_ONLY=T'
, network_owner=>'RDFUSER'
, network_name=>'NET1'));
前述の問合せは、sem_inf_staticによって追加される3つの一意の静的トリプルを重複なしで戻します。
S P O ---------------------- ---------------------- ----------------------- http://example.org/S1 http://example.org/P2 O1 http://example.org/S2 http://example.org/P2 2 http://example.org/S3 http://example.org/P3 3E0
親トピック: ユーザー定義の推論拡張関数の例
9.1.3.2 例2: 動的トリプルの追加
静的データの追加は有用ですが、通常は準備(action='START')フェーズの間に実行されます。動的データを追加するには、RDFグラフの既存のデータを参照し、その既存のデータに基づいて新しいデータを生成します。これは、ユーザー定義の推論拡張関数の最も一般的なケースです。
次のユーザー定義の推論拡張関数(sem_inf_dynamic)は、従業員のフルネームを表す新しいトリプルを作成するために、姓と名を連結します。
-- this user-defined rule adds static triples
create or replace function sem_inf_dynamic(
src_tab_view in varchar2,
resource_id_map_view in varchar2,
output_tab in varchar2,
action in varchar2,
num_calls in number,
tplInferredLastRound in number,
options in varchar2 default null,
optimization_flag out number,
diag_message out varchar2
)
return boolean
as
firstNamePropertyId number;
lastNamePropertyId number;
fullNamePropertyId number;
sqlStmt varchar2(4000);
insertStmt varchar2(4000);
pragma autonomous_transaction;
begin
if (action = 'RUN') then
-- retrieve ID of resource that already exists in the data (will
-- throw exception if resource does not exist). These will improve
-- performance of our SQL queries.
firstNamePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/firstName');
lastNamePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/lastName');
fullNamePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/name');
-- SQL query to find all employees and their first and last names
sqlStmt :=
'select ids1.sid employeeId,
values1.value_name firstName,
values2.value_name lastName
from ' || resource_id_map_view || ' values1,
' || resource_id_map_view || ' values2,
' || src_tab_view || ' ids1,
' || src_tab_view || ' ids2
where ids1.sid = ids2.sid
AND ids1.pid = ' || to_char(firstNamePropertyId,'TM9') || '
AND ids2.pid = ' || to_char(lastNamePropertyId,'TM9') || '
AND ids1.oid = values1.value_id
AND ids2.oid = values2.value_id
/* below ensures we have NEWDATA (a no duplicate optimization flag) */
AND not exists
(select 1
from ' || src_tab_view || '
where sid = ids1.sid AND
pid = ' || to_char(fullNamePropertyId,'TM9') || ')';
-- create the insert statement that concatenates the first and
-- last names from our sqlStmt into a new triple.
insertStmt :=
'insert /*+ parallel append */
into ' || output_tab || ' (sid, pid, o)
select employeeId, ' || to_char(fullNamePropertyId,'TM9') || ', ''"'' || firstName || '' '' || lastName || ''"''
from (' || sqlStmt || ')';
-- execute the insert statement
execute immediate insertStmt;
-- commit our changes
commit;
-- set our optimization flags indicating we already checked for
-- duplicates in the RDF graph (src_tab_view)
optimization_flag := SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_NEWDATA_ONLY;
end if;
-- return true to indicate success
return true;
end sem_inf_dynamic;
/
show errors;
sem_inf_dynamic関数は、2つの主要なステップを使用して新しいデータを挿入します。最初に関数は、既存のデータからすべての姓と名を収集するSQL問合せを構築します。このSQL問合せをsqlStmt変数に格納します。次に関数は、収集する姓と名を使用して新しいトリプルを挿入し、従業員ごとにフルネームを生成します。このSQL問合せをinsertStmt変数に格納します。副問合せでINSERTを実行しているため、insertStmt問合せにはsqlStmt問合せが含まれることに注意してください。
sqlStmt問合せは、リソース・ビュー(resource_id_map_view)と既存のデータ・ビュー(src_tab_view)の2つの主なビュー全体で結合を実行します。既存のデータ・ビューにはすべての既存のトリプルが含まれますが、それらのトリプルの値は、字句の値ではなく数値IDを使用して格納されます。sqlStmt問合せは従業員の姓と名の字句の値を抽出する必要があるため、リソース・ビューで2回結合を実行します(1回は姓でもう1回は名)。
sqlStmt問合せには、パフォーマンスの向上に役立てるためのPARALLEL SQLヒントが含まれます。均等なハードウェア構成でのパラレル実行は、パフォーマンスを大幅に向上させることができます。(詳細は、「例3: パフォーマンスの最適化」を参照してください。)
insertStmt問合せでは、既存のデータ・ビュー(src_tab_view)にすでに存在するトリプルが追加されることのないように、重複チェックも実行します。関数は、このチェックをINF_EXT_OPT_FLAG_NEWDATA_ONLY最適化フラグを有効にすることで実行したことを示しています。拡張関数内部でチェックを実行すると、推論の全体的なパフォーマンスが向上します。既存のデータ・ビューには、sem_inf_dynamic関数によって現在追加されている新しいトリプルが含まれないため、output_tab表内にはまだ重複が存在している場合があることに注意してください。sem_inf_dynamic関数でoutput_tab表の重複を詳細にチェックする場合、INF_EXT_OPT_FLAG_UNIQUEDATA_ONLY最適化フラグを有効にすることもできます。
両方のSQL問合せは、結合と挿入を実行するためにRDFリソースの数値IDを使用します。字句の値ではなくIDを使用すると、問合せのパフォーマンスが向上します。sem_inf_dynamic関数では、使用する予定の字句の値のIDを調べることによるパフォーマンスの利点を活用します。この場合関数は、名、姓およびフルネーム・プロパティを表す3つのURIを調べます。sem_inf_dynamic関数が、すべての新しいトリプルを完全にIDとして挿入した場合、INF_EXT_OPT_FLAG_ALL_IDS最適化フラグを有効にできます。ただし、この場合、新しいトリプルにはそれぞれ単一の新しい字句値(従業員のフルネーム)が含まれます。
sem_inf_dynamic関数で推論グラフを作成するには、次のように、ネットワーク所有者に実行権限を付与し、SEM_APIS.CREATE_INFERRED_GRAPHプロシージャに関数名を渡します。
-- grant appropriate privileges
grant execute on sem_inf_dynamic to RDFUSER;
-- create the inferred graph
begin
sem_apis.create_inferred_graph(
'EMPLOYEES_INF'
, sem_models('EMPLOYEES')
, sem_rulebases('OWLPRIME')
, passes => SEM_APIS.REACH_CLOSURE
, inf_ext_user_func_name => 'sem_inf_dynamic'
, network_owner=>'RDFUSER'
, network_name=>'NET1'
);
end;
/
推論グラフには、sem_inf_dynamicによって追加される次の2つの新しいトリプルを含める必要があります。
S P O ------------------------ ------------------------ ----------------------- http://example.org/Bob http://example.org/name Bob Billow http://example.org/John http://example.org/name John Smith
前述の例で、sem_inf_dynamic関数がMary Smithのフルネームを推論しなかったことに注意してください。Mary Smithは、既存のデータですでにフルネームが指定されていたためです。
親トピック: ユーザー定義の推論拡張関数の例
9.1.3.3 例3: パフォーマンスの最適化
いくつかの技術によって、推論拡張関数のパフォーマンスを向上させることができます。そのような技術の1つは、問合せにおいて、リソースの字句の値ではなく数値IDを使用することです。リソースIDのみを使用することにより、拡張関数はリソース・ビュー(resource_id_map_view)を使用する結合を回避し、問合せパフォーマンスを大幅に向上させることができます。推論拡張関数で、新しいトリプルをoutput_tab表に追加する際にリソースIDを使用する(つまり、output_tab表のgid列、sid列、pid列およびoid列のみを使用する)ことによっても、さらにパフォーマンスを向上できます。
次のユーザー定義の推論拡張関数(sem_inf_related)は、同じ姓を持つ従業員について、新規プロパティ:possibleRelativeを推論します。そのような従業員を見つけるためのSQL問合せは、リソースIDのみを使用 (字句の値、リソース・ビューとの結合を使用しない)します。また、この例の推論拡張関数は、リソースIDのみを使用して新しいトリプルを挿入し、関数でINF_EXT_OPT_FLAG_ALL_IDS最適化フラグを有効にすることができます。
-- this user-defined rule adds static triples
create or replace function sem_inf_related(
src_tab_view in varchar2,
resource_id_map_view in varchar2,
output_tab in varchar2,
action in varchar2,
num_calls in number,
tplInferredLastRound in number,
options in varchar2 default null,
optimization_flag out number,
diag_message out varchar2
)
return boolean
as
lastNamePropertyId number;
relatedPropertyId number;
sqlStmt varchar2(4000);
insertStmt varchar2(4000);
pragma autonomous_transaction;
begin
if (action = 'RUN') then
-- retrieve ID of resource that already exists in the data (will
-- throw exception if resource does not exist).
lastNamePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/lastName');
-- retreive ID of resource or generate a new ID if resource does
-- not already exist
relatedPropertyId := sdo_sem_inference.oracle_orardf_add_res('http://example.org/possibleRelative');
-- SQL query to find all employees that share a last name
sqlStmt :=
'select ids1.sid employeeId,
ids2.sid relativeId
from ' || src_tab_view || ' ids1,
' || src_tab_view || ' ids2
where ids1.pid = ' || to_char(lastNamePropertyId,'TM9') || '
AND ids2.pid = ' || to_char(lastNamePropertyId,'TM9') || '
AND ids1.oid = ids2.oid
/* avoid employees related to themselves */
AND ids1.sid != ids2.sid
/* below ensures we have NEWDATA (a no duplicate optimization flag) */
AND not exists
(select 1
from ' || src_tab_view || '
where sid = ids1.sid
AND pid = ' || to_char(relatedPropertyId,'TM9') || '
AND oid = ids2.sid)
/* below ensures we have UNIQDATA (a no duplicate optimization flag) */
AND not exists
(select 1
from ' || output_tab || '
where sid = ids1.sid
AND pid = ' || to_char(relatedPropertyId,'TM9') || '
AND oid = ids2.sid)';
-- create the insert statement that only uses resource IDs
insertStmt :=
'insert /*+ parallel append */
into ' || output_tab || ' (sid, pid, oid)
select employeeId, ' || to_char(relatedPropertyId,'TM9') || ', relativeId
from (' || sqlStmt || ')';
-- execute the insert statement
execute immediate insertStmt;
-- commit our changes
commit;
-- set flag indicating our new triples
-- 1) are specified using only IDs
-- 2) produce no duplicates with the RDF graph (src_tab_view)
-- 3) produce no duplicates in the output (output_tab)
optimization_flag := SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_ALL_IDS +
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_NEWDATA_ONLY +
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_UNIQDATA_ONLY;
end if;
-- return true to indicate success
return true;
end sem_inf_related;
/
show errors;
sem_inf_related関数には、前述の例と少数の重要な違いがあります。1つ目に、sem_inf_related関数は、リソースIDのみを使用して問合せを行い、リソースIDのみを使用して新しいトリプルを挿入します。output_tab表に追加されたすべてのトリプルはリソースIDのみを使用するため、関数はINF_EXT_OPT_FLAG_ALL_IDS最適化フラグを有効にできます。パフォーマンスを最適化するため、ファンクションは字句の値全体にリソースIDを使用する必要があります。ただし、これができない場合があります。例2: 動的トリプルの追加のように、新しい字句の値を生成するために字句の値を連結する場合です。例2: 動的トリプルの追加のような場合、SQL問合せでoracle_orardf_res2vidへのコールを埋め込むよりも、リソース・ビュー(resource_id_map_view)を使用して結合することを、通常はお薦めします。これは、別の表を使用した結合とは対照的に、取り得る値ごとにファンクションをコールするオーバーヘッドによります。
sem_inf_related関数のもう1つの大きな違いは、oracle_orardf_add_res関数(oracle_orardf_res2vidと比較して)の使用です。res2vid関数とは異なり、リソースが存在しない場合は、add_res関数がリソース・ビュー(resource_id_map_view)にリソースを追加します。リソース・ビューにリソースを追加することが重要でない場合、推論拡張機能関数はadd_res関数を使用する必要があります。関数を複数回コールしても、重複エントリはリソース・ビューに生成されません。
最後の重要な違いは、SQL問合せに追加されたNOT EXISTS 句です。最初のNOT EXISTS句は、RDFグラフにすでに存在するか、他のルール(src_tab_view)によって推測されるトリプルが重複する可能性のあるトリプルの追加を回避しています。このような重複をチェックすることで、sem_inf_relatedでINF_EXT_OPT_FLAG_NEWDATA_ONLY最適化フラグを有効にすることができます。2番目のNOT EXISTS句は、現在の推論ラウンド中(num_callsパラメータを参照)の間にsem_inf_related関数によってoutput_tab表に追加されたトリプルが重複するかもしれないトリプルを追加することを避けます。このような重複をチェックすることで、sem_inf_relatedでINF_EXT_OPT_FLAG_UNIQDATA_ONLY最適化フラグを有効にすることができます。
sem_inf_dynamicの例と同様に、sem_inf_relatedの例は、その挿入文でPARALLEL SQL問合せヒントを使用します。均等なハードウェア構成でのパラレル実行は、パフォーマンスを大幅に向上させることができます。データ集中型のアプリケーションの場合、通常、適切な入出力サブシステムは、システム全体のパフォーマンスに対する重要な構成要素です。
sem_inf_dynamic関数で推論グラフを作成するには、次のように、ネットワーク所有者に実行権限を付与し、SEM_APIS.CREATE_INFERRED_GRAPHプロシージャに関数名を渡します。
-- grant appropriate privileges
grant execute on sem_inf_related to RDFUSER;
-- create the inferred graph
begin
sem_apis.create_inferred_graph(
'EMPLOYEES_INF'
, sem_models('EMPLOYEES')
, sem_rulebases('OWLPRIME')
, passes => SEM_APIS.REACH_CLOSURE
, inf_ext_user_func_name => 'sem_inf_related'
, network_owner=>'RDFUSER'
, network_name=>'NET1'
);
end;
/
推論グラフには、sem_inf_relatedによって追加される次の2つの新しいトリプルを含める必要があります。
S P O ------------------------ ------------------------------------ ------------------------ http://example.org/John http://example.org/possibleRelative http://example.org/Mary http://example.org/Mary http://example.org/possibleRelative http://example.org/John
親トピック: ユーザー定義の推論拡張関数の例
9.1.3.4 例4: 時間推論(いくつかの関連する例)
ユーザー定義の拡張関数は、特定のデータ型(xsd:dateTimeなど)をトリプルでより利用しやすくすることができます。たとえばユーザー定義の拡張関数では、2つのxsd:dateTime値の違いに基づいてトリプル間の関係を推論することが可能です。この項の3つの例は、2つの異なる時間推論ルールと、これらを1つの推論グラフに結合する方法を調べます。例では、モデルEVENTとEVENT_ONTが存在し、次のRDFデータが含まれているとします。
EVENT_ONT
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix : <http://example.org/event/> .
# we model two types of events
:Meeting rdfs:subClassOf :Event .
:Presentation rdfs:subClassOf :Event .
# events have topics
:topic rdfs:domain :Event .
# events have start and end times
:startTime rdfs:domain :Event ;
rdfs:range xsd:dateTime .
:endTime rdfs:domain :Event ;
rdfs:range xsd:dateTime .
# duration (in minutes) of an event
:lengthInMins rdfs:domain :Event ;
rdfs:range xsd:integer .
# overlaps property identifies conflicting events
:overlaps rdfs:domain :Event ;
rdf:type owl:SymmetricProperty .
:noOverlap rdfs:domain :Event ;
rdf:type owl:SymmetricProperty .EVENT_TBOX
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix : <http://example.org/event/> .
:m1 rdf:type :Meeting ;
:topic "Beta1 launch" ;
:startTime "2012-04-01T09:30:00-05:00"^^xsd:dateTime ;
:endTime "2012-04-01T11:00:00-05:00"^^xsd:dateTime .
:m2 rdf:type :Meeting ;
:topic "Standards compliance" ;
:startTime "2012-04-01T12:30:00-05:00"^^xsd:dateTime ;
:endTime "2012-04-01T13:30:00-05:00"^^xsd:dateTime .
:p1 rdf:type :Presentation ;
:topic "OWL Reasoners" ;
:startTime "2012-04-01T11:00:00-05:00"^^xsd:dateTime ;
:endTime "2012-04-01T13:00:00-05:00"^^xsd:dateTime .
例は次のとおりです。
9.1.3.4.1 例4a: 期間ルール
次のユーザー定義の推論拡張関数(sem_inf_durations)は、イベントの開始時刻と終了時刻を指定して、イベント期間を分単位で推論します。たとえば、午前9時30分に開始し、午前11時00分に終了するイベントの期間は90分です。次の拡張関数は、イベントごとに開始時刻と終了時刻を抽出し、xsd:dateTime値をOracleタイムスタンプに変換し、タイムスタンプの差を計算します。この拡張関数は、タイムゾーンを処理できます。
create or replace function sem_inf_durations(
src_tab_view in varchar2,
resource_id_map_view in varchar2,
output_tab in varchar2,
action in varchar2,
num_calls in number,
tplInferredLastRound in number,
options in varchar2 default null,
optimization_flag out number,
diag_message out varchar2
)
return boolean
as
eventClassId number;
rdfTypePropertyId number;
startTimePropertyId number;
endTimePropertyId number;
durationPropertyId number;
xsdTimeFormat varchar2(100);
sqlStmt varchar2(4000);
insertStmt varchar2(4000);
pragma autonomous_transaction;
begin
if (action = 'RUN') then
-- retrieve ID of resource that already exists in the data (will
-- throw exception if resource does not exist).
eventClassId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/event/Event',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
startTimePropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/event/startTime',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
endTimePropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/event/endTime',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
durationPropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/event/lengthInMins',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
rdfTypePropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
-- set the TIMESTAMP format we will use to parse XSD times
xsdTimeFormat := 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM';
-- query we use to extract the event ID and start/end times.
sqlStmt :=
'select ids1.sid eventId,
TO_TIMESTAMP_TZ(values1.value_name,''YYYY-MM-DD"T"HH24:MI:SSTZH:TZM'') startTime,
TO_TIMESTAMP_TZ(values2.value_name,''YYYY-MM-DD"T"HH24:MI:SSTZH:TZM'') endTime
from ' || resource_id_map_view || ' values1,
' || resource_id_map_view || ' values2,
' || src_tab_view || ' ids1,
' || src_tab_view || ' ids2,
' || src_tab_view || ' ids3
where ids1.sid = ids3.sid
AND ids3.pid = ' || to_char(rdfTypePropertyId,'TM9') || '
AND ids3.oid = ' || to_char(eventClassId,'TM9') || '
AND ids1.sid = ids2.sid
AND ids1.pid = ' || to_char(startTimePropertyId,'TM9') || '
AND ids2.pid = ' || to_char(endTimePropertyId,'TM9') || '
AND ids1.oid = values1.value_id
AND ids2.oid = values2.value_id
/* ensures we have NEWDATA */
AND not exists
(select 1
from ' || src_tab_view || '
where sid = ids3.sid
AND pid = ' || to_char(durationPropertyId,'TM9') || ')
/* ensures we have UNIQDATA */
AND not exists
(select 1
from ' || output_tab || '
where sid = ids3.sid
AND pid = ' || to_char(durationPropertyId,'TM9') || ')';
-- compute the difference (in minutes) between the two Oracle
-- timestamps from our sqlStmt query. Store the minutes as
-- xsd:integer.
insertStmt :=
'insert /*+ parallel append */ into ' || output_tab || ' (sid, pid, o)
select eventId,
' || to_char(durationPropertyId,'TM9') || ',
''"'' || minutes || ''"^^xsd:integer''
from (
select eventId,
(extract(day from (endTime - startTime))*24*60 +
extract(hour from (endTime - startTime))*60 +
extract(minute from (endTime - startTime))) minutes
from (' || sqlStmt || '))';
-- execute the query
execute immediate insertStmt;
-- commit our changes
commit;
end if;
-- we already checked for duplicates in src_tab_view (NEWDATA) and
-- in output_tab (UNIQDATA)
optimization_flag := SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_NEWDATA_ONLY +
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_UNIQDATA_ONLY;
-- return true to indicate success
return true;
-- handle any exceptions
exception
when others then
diag_message := 'error occurred: ' || SQLERRM;
return false;
end sem_inf_durations;
/
show errors;
sem_inf_durations関数は、イベント期間を計算するために、組込みのOracle時間関数を使用します。最初にTO_TIMESTAMP_TZ関数を使用してxsd:dateTimeリテラル値をOracle TIMESTAMPオブジェクトに変換します。2つのOracle TIMESTAMPオブジェクトの差分を取ることで、時間間隔を表すINTERVALオブジェクトを生成します。sem_inf_durations関数は、EXTRACT演算子を使用して、期間間隔から日、時間および分を抽出することによって、各イベントの期間を分単位で計算します。
sem_inf_durations関数は、既存のモデル(src_tab_view)のデータとoutput_tab表のデータの両方に対して重複をチェックするため、INF_EXT_OPT_FLAG_NEWDATA_ONLYとINF_EXT_OPT_FLAG_UNIQDATA_ONLYの最適化フラグを有効にできます。(最適化フラグの詳細は、「例3: パフォーマンスの最適化」を参照してください。)
前述の例とは異なり、sem_inf_durationsには例外ハンドラが含まれていることに注意してください。例外ハンドラは、ユーザー定義の推論拡張関数での問題をデバッグする場合に役立ちます。有用なデバッグ・メッセージを生成するには、拡張関数の例外を捕捉し、エラーを反映するdiag_messageパラメータを設定して、拡張関数の実行中にエラーが発生したことを示すFALSEを戻します。sem_inf_durations関数は、すべての例外を捕捉して、diag_messageの値を例外メッセージに設定します。
sem_inf_durations関数で推論グラフを作成するには、次のように、RDFUSERに実行権限を付与し、SEM_APIS.CREATE_INFERRED_GRAPHプロシージャに関数名を渡します。
-- grant appropriate privileges
grant execute on sem_inf_durations to RDFUSER;
-- create the inferred graph
begin
sem_apis.create_inferred_graph(
'EVENT_INF'
, sem_models('EVENT', 'EVENT_ONT')
, sem_rulebases('OWLPRIME')
, passes => SEM_APIS.REACH_CLOSURE
, inf_ext_user_func_name => 'sem_inf_durations'
, network_owner=>'RDFUSER'
, network_name=>'NET1'
);
end;
/
推論グラフには、OWLPRIMEによって推論されるトリプルに加えて、sem_inf_durationsによって追加される次の3つの新しいトリプルを含める必要があります。
S P O ---------------------------- -------------------------------------- --------- http://example.org/event/m1 http://example.org/event/lengthInMins 90 http://example.org/event/m2 http://example.org/event/lengthInMins 60 http://example.org/event/p1 http://example.org/event/lengthInMins 120
親トピック: 例4: 時間推論(いくつかの関連する例)
9.1.3.4.2 例4b: オーバーラップ・ルール
次のユーザー定義の推論拡張関数(sem_inf_overlap)は、2つのイベントが重なるかどうかを推測します。一方のイベントの進行中に、一方のイベントが開始する場合、2つのイベントは重なります。関数はイベントのペアごとに開始時刻と終了時刻を抽出し、xsd:dateTimeの値をOracleタイムスタンプに変換して、1つのイベントが他方の進行中に開始するかどうかを計算します。
create or replace function sem_inf_overlap(
src_tab_view in varchar2,
resource_id_map_view in varchar2,
output_tab in varchar2,
action in varchar2,
num_calls in number,
tplInferredLastRound in number,
options in varchar2 default null,
optimization_flag out number,
diag_message out varchar2
)
return boolean
as
eventClassId number;
rdfTypePropertyId number;
startTimePropertyId number;
endTimePropertyId number;
overlapsPropertyId number;
noOverlapPropertyId number;
xsdTimeFormat varchar2(100);
sqlStmt varchar2(4000);
insertStmt varchar2(4000);
pragma autonomous_transaction;
begin
if (action = 'RUN') then
-- retrieve ID of resource that already exists in the data (will
-- throw exception if resource does not exist).
eventClassId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/event/Event',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
startTimePropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/event/startTime',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
endTimePropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/event/endTime',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
overlapsPropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/event/overlaps',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
noOverlapPropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/event/noOverlap',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
rdfTypePropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
-- set the TIMESTAMP format we will use to parse XSD times
xsdTimeFormat := 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM';
-- query we use to extract the event ID and start/end times.
sqlStmt :=
'select idsA1.sid eventAId,
idsB1.sid eventBId,
TO_TIMESTAMP_TZ(valuesA1.value_name,''YYYY-MM-DD"T"HH24:MI:SSTZH:TZM'') startTimeA,
TO_TIMESTAMP_TZ(valuesA2.value_name,''YYYY-MM-DD"T"HH24:MI:SSTZH:TZM'') endTimeA,
TO_TIMESTAMP_TZ(valuesB1.value_name,''YYYY-MM-DD"T"HH24:MI:SSTZH:TZM'') startTimeB,
TO_TIMESTAMP_TZ(valuesB2.value_name,''YYYY-MM-DD"T"HH24:MI:SSTZH:TZM'') endTimeB
from ' || resource_id_map_view || ' valuesA1,
' || resource_id_map_view || ' valuesA2,
' || resource_id_map_view || ' valuesB1,
' || resource_id_map_view || ' valuesB2,
' || src_tab_view || ' idsA1,
' || src_tab_view || ' idsA2,
' || src_tab_view || ' idsA3,
' || src_tab_view || ' idsB1,
' || src_tab_view || ' idsB2,
' || src_tab_view || ' idsB3
where idsA1.sid = idsA3.sid
AND idsA3.pid = ' || to_char(rdfTypePropertyId,'TM9') || '
AND idsA3.oid = ' || to_char(eventClassId,'TM9') || '
AND idsB1.sid = idsB3.sid
AND idsB3.pid = ' || to_char(rdfTypePropertyId,'TM9') || '
AND idsB3.oid = ' || to_char(eventClassId,'TM9') || '
/* only do half the checks, our TBOX ontology will handle symmetries */
AND idsA1.sid < idsB1.sid
/* grab values of startTime and endTime for event A */
AND idsA1.sid = idsA2.sid
AND idsA1.pid = ' || to_char(startTimePropertyId,'TM9') || '
AND idsA2.pid = ' || to_char(endTimePropertyId,'TM9') || '
AND idsA1.oid = valuesA1.value_id
AND idsA2.oid = valuesA2.value_id
/* grab values of startTime and endTime for event B */
AND idsB1.sid = idsB2.sid
AND idsB1.pid = ' || to_char(startTimePropertyId,'TM9') || '
AND idsB2.pid = ' || to_char(endTimePropertyId,'TM9') || '
AND idsB1.oid = valuesB1.value_id
AND idsB2.oid = valuesB2.value_id
/* ensures we have NEWDATA */
AND not exists
(select 1
from ' || src_tab_view || '
where sid = idsA1.sid
AND oid = idsB1.sid
AND pid in (' || to_char(overlapsPropertyId,'TM9') || ',' ||
to_char(noOverlapPropertyId,'TM9') || '))
/* ensures we have UNIQDATA */
AND not exists
(select 1
from ' || output_tab || '
where sid = idsA1.sid
AND oid = idsB1.sid
AND pid in (' || to_char(overlapsPropertyId,'TM9') || ',' ||
to_char(noOverlapPropertyId,'TM9') || '))';
-- compare the two event times
insertStmt :=
'insert /*+ parallel append */ into ' || output_tab || ' (sid, pid, oid)
select eventAId, overlapStatusId, eventBId
from (
select eventAId,
(case
when (startTimeA < endTimeB and
startTimeA > startTimeB) then
' || to_char(overlapsPropertyId,'TM9') || '
when (startTimeB < endTimeA and
startTimeB > startTimeA) then
' || to_char(overlapsPropertyId,'TM9') || '
else
' || to_char(noOverlapPropertyId,'TM9') || '
end) overlapStatusId,
eventBId
from (' || sqlStmt || '))';
-- execute the query
execute immediate insertStmt;
-- commit our changes
commit;
end if;
-- we only use ID values in the output_tab and we check for
-- duplicates with our NOT EXISTS clause.
optimization_flag := SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_ALL_IDS +
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_NEWDATA_ONLY +
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_UNIQDATA_ONLY;
-- return true to indicate success
return true;
-- handle any exceptions
exception
when others then
diag_message := 'error occurred: ' || SQLERRM;
return false;
end sem_inf_overlap;
/
show errors;
sem_inf_overlap関数は、「例4b: オーバーラップ・ルール」のsem_inf_durations関数と類似しています。2つの主な違いは、sem_inf_overlapの問合せには、新しい字句の値を生成する必要がないために、より多くの結合が含まれており、INF_EXT_OPT_FLAG_ALL_IDS最適化フラグが有効であることです。(最適化フラグの詳細は、「例3: パフォーマンスの最適化」を参照してください。)
sem_inf_overlap関数で推論グラフを作成するには、次のように、RDFUSERに実行権限を付与し、SEM_APIS.CREATE_INFERRED_GRAPHプロシージャに関数名を渡します。
-- grant appropriate privileges
grant execute on sem_inf_overlap to RDFUSER;
-- create the inferred graph
begin
sem_apis.create_inferred_graph(
'EVENT_INF'
, sem_models('EVENT', 'EVENT_ONT')
, sem_rulebases('OWLPRIME')
, passes => SEM_APIS.REACH_CLOSURE
, inf_ext_user_func_name => 'sem_inf_overlap'
, network_owner=>'RDFUSER'
, network_name=>'NET1'
);
end;
/
推論グラフには、OWLPRIMEによって推論されるトリプルに加えて、sem_inf_overlapによって追加される次の6つの新しいトリプルを含める必要があります。
S P O ---------------------------- ----------------------------------- ---------------------------- http://example.org/event/m1 http://example.org/event/noOverlap http://example.org/event/m2 http://example.org/event/m1 http://example.org/event/noOverlap http://example.org/event/p1 http://example.org/event/m2 http://example.org/event/noOverlap http://example.org/event/m1 http://example.org/event/m2 http://example.org/event/overlaps http://example.org/event/p1 http://example.org/event/p1 http://example.org/event/noOverlap http://example.org/event/m1 http://example.org/event/p1 http://example.org/event/overlaps http://example.org/event/m2
親トピック: 例4: 時間推論(いくつかの関連する例)
9.1.3.4.3 例4c: 期間ルールとオーバーラップ・ルール
この項の例は、例4a: 期間ルール (sem_inf_durations)および例4b: オーバーラップ・ルール (sem_inf_overlap)の拡張機能関数を一緒に使用して、単一の推論グラフを生成します。拡張ファンクションは、この例では変更されないままです。
複数の拡張関数を使用して推論グラフを作成するには、SEM_APIS.CREATE_INFERRED_GRAPHのinf_ext_user_func_nameパラメータに渡される各拡張関数をカンマを使用して区切ります。次の例では、拡張関数に関する適切な権限がRDFUSERユーザーに付与されていることを前提とします。
-- use multiple user-defined inference functions
begin
sem_apis.create_inferred_graph(
'EVENT_INF'
, sem_models('EVENT', 'EVENT_ONT')
, sem_rulebases('OWLPRIME')
, passes => SEM_APIS.REACH_CLOSURE
, inf_ext_user_func_name => 'sem_inf_durations,sem_inf_overlap'
, network_owner=>'RDFUSER'
, network_name=>'NET1'
);
end;
/
推論グラフには、OWLPRIMEによって推論されるトリプルに加えて、sem_inf_durationsおよびsem_inf_overlapによって追加される次の9つの新しいトリプルを含める必要があります。
S P O ---------------------------- -------------------------------------- ---------------------------- http://example.org/event/m1 http://example.org/event/lengthInMins 90 http://example.org/event/m1 http://example.org/event/noOverlap http://example.org/event/m2 http://example.org/event/m1 http://example.org/event/noOverlap http://example.org/event/p1 http://example.org/event/m2 http://example.org/event/lengthInMins 60 http://example.org/event/m2 http://example.org/event/noOverlap http://example.org/event/m1 http://example.org/event/m2 http://example.org/event/overlaps http://example.org/event/p1 http://example.org/event/p1 http://example.org/event/lengthInMins 120 http://example.org/event/p1 http://example.org/event/noOverlap http://example.org/event/m1 http://example.org/event/p1 http://example.org/event/overlaps http://example.org/event/m2
拡張関数のsem_inf_durationsおよびsem_inf_overlapで、同じ最適化フラグを使用する必要はありません。矛盾している最適化フラグで拡張関数を使用する(たとえば、INF_EXT_OPT_FLAG_ALL_IDSを使用する関数と、すべての新しいトリプルを字句の値として挿入する別の関数)ことは可能です。
親トピック: 例4: 時間推論(いくつかの関連する例)
9.1.3.5 例5: 空間推論
ユーザー定義の推論拡張関数では、WKT (Well-Known Text)のような地理空間データ型を使用して空間推論を実行することもできます。たとえばユーザー定義の拡張関数を使用して、州と市のような形状エンティティ間の包含関係を推論することができます。
この項の例では、あるジオメトリ(米国のある州)に、あるポイント(米国のある市)が含まれるかどうかを推論する方法を示します。この例では、RDFネットワークに空間索引(1.6.6.2項を参照)があることを前提としています。この例では、RDFグラフSTATESが存在し、次のRDFデータが含まれていることも前提としています。
@prefix orageo: <http://xmlns.oracle.com/rdf/geo/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix : <http://example.org/geo/> .
:Colorado rdf:type :State ;
:boundary "Polygon((-109.0448 37.0004, -102.0424 36.9949, -102.0534 41.0006, -109.0489 40.9996, -109.0448 37.0004))"^^orageo:WKTLiteral .
:Utah rdf:type :State ;
:boundary "Polygon((-114.0491 36.9982, -109.0462 37.0026, -109.0503 40.9986, -111.0471 41.0006, -111.0498 41.9993, -114.0395 41.9901, -114.0491 36.9982))"^^orageo:WKTLiteral .
:Wyoming rdf:type :State ;
:boundary "Polygon((-104.0556 41.0037, -104.0584 44.9949, -111.0539 44.9998, -111.0457 40.9986, -104.0556 41.0037))"^^orageo:WKTLiteral
:StateCapital rdfs:subClassOf :City ;
:Denver rdf:type :StateCapital ;
:location "Point(-104.984722 39.739167)"^^orageo:WKTLiteral .
:SaltLake rdf:type :StateCaptial ;
:location "Point(-111.883333 40.75)"^^orageo:WKTLiteral .
:Cheyenne rdf:type :StateCapital ;
:location "Point(-104.801944 41.145556)"^^orageo:WKTLiteral .
次のユーザー定義の推論拡張関数(sem_inf_capitals)は、WKTジオメトリを使用して各州内の首都を検索します。関数が首都を検出すると、その市が含まれている州の首都であると推測します。
create or replace function sem_inf_capitals(
src_tab_view in varchar2,
resource_id_map_view in varchar2,
output_tab in varchar2,
action in varchar2,
num_calls in number,
tplInferredLastRound in number,
options in varchar2 default null,
optimization_flag out number,
diag_message out varchar2
)
return boolean
as
stateClassId number;
capitalClassId number;
boundaryPropertyId number;
locationPropertyId number;
rdfTypePropertyId number;
capitalPropertyId number;
defaultSRID number := 8307;
xsdTimeFormat varchar2(100);
sqlStmt varchar2(4000);
insertStmt varchar2(4000);
pragma autonomous_transaction;
begin
if (action = 'RUN') then
-- retrieve ID of resource that already exists in the data (will
-- throw exception if resource does not exist).
stateClassId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/geo/State',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
capitalClassId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/geo/StateCapital',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
boundaryPropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/geo/boundary',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
locationPropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://example.org/geo/location',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
rdfTypePropertyId := sdo_sem_inference.oracle_orardf_res2vid(
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
-- retreive ID of resource or generate a new ID if resource does
-- not already exist
capitalPropertyId := sdo_sem_inference.oracle_orardf_add_res(
'http://example.org/geo/capital',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
-- query we use to extract the capital cities contained within state boundaries
sqlStmt :=
'select idsA1.sid stateId,
idsB1.sid cityId
from ' || resource_id_map_view || ' valuesA,
' || resource_id_map_view || ' valuesB,
' || src_tab_view || ' idsA1,
' || src_tab_view || ' idsA2,
' || src_tab_view || ' idsB1,
' || src_tab_view || ' idsB2
where idsA1.pid = ' || to_char(rdfTypePropertyId,'TM9') || '
AND idsA1.oid = ' || to_char(stateClassId,'TM9') || '
AND idsB1.pid = ' || to_char(rdfTypePropertyId,'TM9') || '
AND idsB1.oid = ' || to_char(capitalClassId,'TM9') || '
/* grab geometric lexical values */
AND idsA2.sid = idsA1.sid
AND idsA2.pid = ' || to_char(boundaryPropertyId,'TM9')|| '
AND idsA2.oid = valuesA.value_id
AND idsB2.sid = idsB1.sid
AND idsB2.pid = ' || to_char(locationPropertyId,'TM9')|| '
AND idsB2.oid = valuesB.value_id
/* compare geometries to see if city is contained by state */
AND SDO_RELATE(
SDO_RDF.getV$GeometryVal(
valuesA.value_type,
valuesA.vname_prefix,
valuesA.vname_suffix,
valuesA.literal_type,
valuesA.language_type,
valuesA.long_value,
' || to_char(defaultSRID,'TM9') || '),
SDO_RDF.getV$GeometryVal(
valuesB.value_type,
valuesB.vname_prefix,
valuesB.vname_suffix,
valuesB.literal_type,
valuesB.language_type,
valuesB.long_value,
' || to_char(defaultSRID,'TM9') || '),
''mask=CONTAINS'') = ''TRUE''
/* ensures we have NEWDATA and only check capitals not assigned to a state */
AND not exists
(select 1
from ' || src_tab_view || '
where pid = ' || to_char(capitalPropertyId,'TM9') || '
AND (sid = idsA1.sid OR oid = idsB1.sid))
/* ensures we have UNIQDATA and only check capitals not assigned to a state */
AND not exists
(select 1
from ' || output_tab || '
where pid = ' || to_char(capitalPropertyId,'TM9') || '
AND (sid = idsA1.sid OR oid = idsB1.sid))';
-- insert new triples using only IDs
insertStmt :=
'insert /*+ parallel append */ into ' || output_tab || ' (sid, pid, oid)
select stateId, ' || to_char(capitalPropertyId,'TM9') || ', cityId
from (' || sqlStmt || ')';
-- execute the query
execute immediate insertStmt;
-- commit our changes
commit;
end if;
-- we only use ID values in the output_tab and we check for
-- duplicates with our NOT EXISTS clauses.
optimization_flag := SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_ALL_IDS +
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_NEWDATA_ONLY +
SDO_SEM_INFERENCE.INF_EXT_OPT_FLAG_UNIQDATA_ONLY;
-- return true to indicate success
return true;
-- handle any exceptions
exception
when others then
diag_message := 'error occurred: ' || SQLERRM;
return false;
end sem_inf_capitals;
/
show errors;
sem_inf_capitals関数と例4a: 期間ルールのsem_inf_durations関数は、どちらの関数もネイティブのOracle演算子を利用するために、いくつかのトリプルの字句の値をOracle型に変換する必要があるという点で類似しています。sem_inf_capitalsの例では、SDO_RDF.getV$GeometryVal関数を使用して、WKT字句値のエンコーディングされたポリゴンとポイントを、Oracle SpatialのSDO_GEOMETRY型に変換します。getV$GeometryVal関数は、ほとんどがリソース・ビュー(resource_id_map_view)と追加の引数によって提供される引数、空間参照システム(SRID)へのIDを必要とします。getV$GeometryVal関数は、ジオメトリをSRIDによって指定される空間参照システムに変換します。sem_inf_capitals関数は、値8307のSRIDによって指定される、デフォルトのOracle Spatial参照システム(WGS84 Longitude-Latitude)を使用します。(RDFグラフの空間参照システムのサポートの詳細は、「空間のサポート」を参照してください。)
getV$GeometryVal関数を使用して、WKT値をSDO_GEOMETRY型に変換した後、sem_inf_capitals関数は、その州のジオメトリを市のジオメトリと比較して、その州にその市が含まれているかどうかを調べます。SDO_RELATE演算子がこの比較を実行し、州に市が含まれる場合は、リテラル値'TRUE'を戻します。SDO_RELATE演算子は、様々な異なる種類の比較を実行できます。(SDO_RELATEおよびその他の空間演算子の詳細は、Oracle Spatial開発者ガイドを参照してください。)
sem_inf_capitals関数で推論グラフを作成するには、次のように、RDFUSERに実行権限を付与し、SEM_APIS.CREATE_INFERRED_GRAPHプロシージャに関数名を渡します。
-- grant appropriate privileges
grant execute on sem_inf_capitals to RDFUSER;
-- create the inferred graph
begin
sem_apis.create_inferred_graph(
'STATES_INF'
, sem_models('STATES')
, sem_rulebases('OWLPRIME')
, passes => SEM_APIS.REACH_CLOSURE
, inf_ext_user_func_name => 'sem_inf_capitals'
, network_owner=>'RDFUSER'
, network_name=>'NET1'
);
end;
/
推論グラフには、OWLPRIMEによって推論されるトリプルに加えて、sem_inf_capitalsによって追加される次の3つの新しいトリプルを含める必要があります。
S P O -------------------------------- ------------------------------- -------------------------------- http://example.org/geo/Colorado http://example.org/geo/capital http://example.org/geo/Denver http://example.org/geo/Utah http://example.org/geo/capital http://example.org/geo/SaltLake http://example.org/geo/Wyoming http://example.org/geo/capital http://example.org/geo/Cheyenne
親トピック: ユーザー定義の推論拡張関数の例
9.1.3.6 例6: Webサービスのコール
この項では、Oracle GeocoderサービスへのWebサービス・コールを可能にする、ユーザー定義の推論拡張関数(sem_inf_geocoding)と関連するヘルパー・プロシージャ(geocoding)について説明します。ユーザー定義の推論拡張関数は、述語<urn:streetAddress>を使用してトリプルのオブジェクト値を検索し、Oracleの公開Geocoderサービスのエンドポイント(http://maps.oracle.com/geocoder/gcserver)でコールアウトを実行して、経度と緯度の情報を2つの別々のトリプルとして挿入します。
たとえば、RDFグラフに次の表明が含まれているとします。
<urn:NEDC> <urn:streetAddress> "1 Oracle Dr., Nashua, NH"
この場合、sem_inf_geocodingを使用した推論コールは、次の新しい表明を生成します。
<urn:NEDC> <http://www.w3.org/2003/01/geo/wgs84_pos#long> "-71.46421" <urn:NEDC> <http://www.w3.org/2003/01/geo/wgs84_pos#lat> "42.75836" <urn:NEDC> <http://www.opengis.net/geosparql#asWKT> "POINT(-71.46421 42.75836)"^^<http://www.opengis.net/geosparql#wktLiteral> <urn:NEDC> <http://xmlns.oracle.com/rdf/geo/asWKT> "POINT(-71.46421 42.75836)"^^<http://xmlns.oracle.com/rdf/geo/WKTLiteral>
sem_inf_geocoding関数は、次のように定義されます。
create or replace function sem_inf_geocoding(
src_tab_view in varchar2,
resource_id_map_view in varchar2,
output_tab in varchar2,
action in varchar2,
num_calls in number,
tplInferredLastRound in number,
options in varchar2 default null,
optimization_flag out number,
diag_message out varchar2
)
return boolean
as
pragma autonomous_transaction;
iCount integer;
nLong number;
nLat number;
nWKT number;
nOWKT number;
nStreetAddr number;
sidTab dbms_sql.number_table;
oidTab dbms_sql.number_table;
vcRequestBody varchar2(32767);
vcStmt varchar2(32767);
vcStreeAddr varchar2(3000);
type cur_type is ref cursor;
cursorFind cur_type;
vcLong varchar2(100);
vcLat varchar2(100);
begin
if (action = 'START') then
nLat := sdo_sem_inference.oracle_orardf_add_res(
'http://www.w3.org/2003/01/geo/wgs84_pos#lat',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
nLong := sdo_sem_inference.oracle_orardf_add_res(
'http://www.w3.org/2003/01/geo/wgs84_pos#long',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
nWKT := sdo_sem_inference.oracle_orardf_add_res(
'http://www.opengis.net/geosparql#asWKT',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
nOWKT := sdo_sem_inference.oracle_orardf_add_res(
'http://xmlns.oracle.com/rdf/geo/asWKT',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
end if;
if (action = 'RUN') then
nStreetAddr := sdo_sem_inference.oracle_orardf_res2vid(
'<urn:streetAddress>',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
nLat := sdo_sem_inference.oracle_orardf_res2vid(
'http://www.w3.org/2003/01/geo/wgs84_pos#lat',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
nLong := sdo_sem_inference.oracle_orardf_res2vid(
'http://www.w3.org/2003/01/geo/wgs84_pos#long',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
nWKT := sdo_sem_inference.oracle_orardf_res2vid(
'http://www.opengis.net/geosparql#asWKT',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
nOWKT := sdo_sem_inference.oracle_orardf_res2vid(
'http://xmlns.oracle.com/rdf/geo/asWKT',
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
vcStmt := '
select /*+ parallel */ distinct s1.sid as s_id, s1.oid as o_id
from ' || src_tab_view || ' s1
where s1.pid = :1
and not exists ( select 1
from ' || src_tab_view || ' x
where x.sid = s1.sid
and x.pid = :2
) ';
open cursorFind for vcStmt using nStreetAddr, nLong;
loop
fetch cursorFind bulk collect into sidTab, oidTab limit 10000;
for i in 1..sidTab.count loop
vcStreeAddr := sdo_sem_inference.oracle_orardf_vid2lit(
oidTab(i),
p_network_owner=>'RDFUSER',
p_network_name=>'NET1');
-- dbms_output.put_line('Now processing street addr ' || vcStreeAddr);
geocoding(vcStreeAddr, vcLong, vcLat);
execute immediate 'insert into ' || output_tab || '(sid,pid,oid,gid,s,p,o,g)
values(:1, :2, null, null, null, null, :3, null) '
using sidTab(i), nLong, '"'||vcLong||'"';
execute immediate 'insert into ' || output_tab || '(sid,pid,oid,gid,s,p,o,g)
values(:1, :2, null, null, null, null, :3, null) '
using sidTab(i), nLat, '"'||vcLat||'"';
execute immediate 'insert into ' || output_tab || '(sid,pid,oid,gid,s,p,o,g)
values(:1, :2, null, null, null, null, :3, null) '
using sidTab(i), nWKT, '"POINT('|| vcLong || ' ' ||vcLat ||')"^^<http://www.opengis.net/geosparql#wktLiteral>';
execute immediate 'insert into ' || output_tab || '(sid,pid,oid,gid,s,p,o,g)
values(:1, :2, null, null, null, null, :3, null) '
using sidTab(i), nOWKT, '"POINT('|| vcLong || ' ' ||vcLat ||')"^^<http://xmlns.oracle.com/rdf/geo/WKTLiteral>';
end loop;
exit when cursorFind%notfound;
end loop;
commit;
end if;
return true;
end;
/
grant execute on sem_inf_geocoding to RDFUSER;
sem_inf_geocoding関数は、Geocoder Webサービス・エンドポイントで実際のHTTP通信を実行する、次のgeocodingというヘルパー・プロシージャを使用します。Webサーバーに接続するには、適切な権限が必要であることに注意してください。
create or replace procedure geocoding(addr varchar2,
vcLong out varchar2,
vcLat out varchar2
)
as
httpReq utl_http.req;
httpResp utl_http.resp;
vcRequestBody varchar2(32767);
vcBuffer varchar2(32767);
idxLat integer;
idxLatEnd integer;
begin
vcRequestBody := utl_url.escape('xml_request=<?xml version="1.0" standalone="yes"?>
<geocode_request vendor="elocation">
<address_list>
<input_location id="27010">
<input_address match_mode="relax_street_type">
<unformatted country="US">
<address_line value="'|| addr ||'"/>
</unformatted>
</input_address>
</input_location>
</address_list>
</geocode_request>
');
dbms_output.put_line('request ' || vcRequestBody);
-- utl_http.set_proxy('<your_proxy_here_if_necessary>', null);
httpReq := utl_http.begin_request (
'http://maps.oracle.com/geocoder/gcserver', 'POST');
utl_http.set_header(httpReq, 'Content-Type', 'application/x-www-form-urlencoded');
utl_http.set_header(httpReq, 'Content-Length', lengthb(vcRequestBody));
utl_http.write_text(httpReq, vcRequestBody);
httpResp := utl_http.get_response(httpReq);
utl_http.read_text(httpResp, vcBuffer, 32767);
utl_http.end_response(httpResp);
-- dbms_output.put_line('response ' || vcBuffer);
-- Here we are doing some simple string parsing out of an XML.
-- It is more robust to use XML functions instead.
idxLat := instr(vcBuffer, 'longitude="');
idxLatEnd := instr(vcBuffer, '"', idxLat + 12);
vcLong := substr(vcBuffer, idxLat + 11, idxLatEnd - idxLat - 11);
dbms_output.put_line('long = ' || vcLong);
idxLat := instr(vcBuffer, 'latitude="');
idxLatEnd := instr(vcBuffer, '"', idxLat + 11);
vcLat := substr(vcBuffer, idxLat + 10, idxLatEnd - idxLat - 10);
dbms_output.put_line('lat = ' || vcLat);
exception
when others then
dbms_output.put_line('geocoding: error ' || dbms_utility.format_error_backtrace || ' '
|| dbms_utility.format_error_stack);
end;
/
親トピック: ユーザー定義の推論拡張関数の例