8 ユーザー定義の推論と問合せ
RDFセマンティク・グラフの拡張アーキテクチャによって、ユーザー定義の機能を追加できるようになりました。
Oracle Database 12cリリース1 (12.1)で有効になった機能は次のとおりです。
-
推論の拡張機能アーキテクチャによって、すでに提供されている推論のサポートに加えて、ユーザー定義の推論が使用可能になります。
-
問合せの拡張アーキテクチャにより、SPARQL問合せで使用されるユーザー定義の関数と集計を追加できます。これはいずれも、SEM_MATCH関数およびSupport for Apache Jenaを介して実現されます。
ノート:
この章で説明する機能は、上級ユーザーを対象としています。「RDFセマンティク・グラフの概要」と「OWLの概要」で説明される主要な概念と技術について、十分に理解していることが前提です。
- ユーザー定義の推論
すでに提供されている推論のサポートに加えて、RDFセマンティク・グラフの推論の拡張アーキテクチャにより、ユーザー定義の推論を使用できます。 - ユーザー定義の関数と集計
RDFセマンティク・グラフ問合せの拡張アーキテクチャによって、ユーザー定義関数と集計が追加され、SPARQL問合せで使用できるようになります。これには、SEM_MATCH表関数と、support for Apache Jenaも使用されます。
親トピック: 概念および使用方法に関する情報
8.1 ユーザー定義の推論
RDFセマンティク・グラフの推論の拡張アーキテクチャによって、すでに提供されている推論のサポートに加えて、ユーザー定義の推論が使用可能になります。
8.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_ENTAILMENTコールの特定の推論コンポーネントを無効にし、 新しい最適化を実装する、カスタマイズした推論拡張機能関数を指定することができます(
inf_ext_user_func_name
パラメータを使用)。 -
洗練された推論機能を使用して推論エンジンを拡張することができます。
たとえば、地理空間推論、時間間隔推論、テキスト解析機能のネイティブのデータベース推論プロセスへの統合があります。
親トピック: ユーザー定義の推論
8.1.2 ユーザー定義の推論のAPIサポート
ユーザー定義の推論のための主要なアプリケーション・プログラミング・インタフェース(API)は、SEM_APIS.CREATE_ENTAILMENTプロシージャの特に最後のパラメータです。
inf_ext_user_func_name IN VARCHAR2 DEFAULT NULL
inf_ext_user_func_name
パラメータを指定すると、使用したい特定のロジックを実装する、1つ以上のユーザー定義の推論関数が指定されます。
親トピック: ユーザー定義の推論
8.1.2.1 ユーザー定義の推論関数の要件
SEM_APIS.CREATE_ENTAILMENTプロシージャへのコールで、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 MDSYS;
ユーザー定義関数の形式で、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サポート
8.1.3 ユーザー定義の推論拡張関数の例
次の例に、ユーザー定義の推論拡張関数を使用して伴意を作成する方法を示します。
-
例1: 静的トリプルの追加、例2: 動的トリプルの追加および例3: パフォーマンスの最適化 は、ユーザー定義の推論の拡張に関する基本を扱っています。
例1: 静的トリプルの追加および例2: 動的トリプルの追加は、推論されたトリプルの新規追加にフォーカスしています。
例3: パフォーマンスの最適化は、パフォーマンスの最適化にフォーカスしています。
-
例4: 時間推論(いくつかの関連する例)と例5: 空間推論は、Oracleのネイティブの型と演算子を利用することによって、特殊なデータ型を効率的に処理する方法を示します。
例4: 時間推論(いくつかの関連する例)は、
xsd:dateTime
データ型にフォーカスしています。例5: 空間推論は、地理空間データ型にフォーカスしています。
-
例6: Webサービスのコールは、Oracle GeocoderサービスへのWebサービス・コールを行います。
最初の3つの例では、モデルEMPLOYEES
が存在し、(Turtle形式で表示された)次のセマンティク・データが含まれていることを前提としています。
:John :firstName "John" ; :lastName "Smith" . :Mary :firstName "Mary" ; :lastName "Smith" ; :name "Mary Smith" . :Alice :firstName "Alice" . :Bob :firstName "Bob" ; :lastName "Billow" .
ユーザー定義の推論拡張ファンクションの作成に関する要件とガイドラインについては、「ユーザー定義推論のAPIサポート」を参照してください。
親トピック: ユーザー定義の推論
8.1.3.1 例1: 静的トリプルの追加
ユーザー定義の推論の拡張関数で新しいデータを推論する最も基本的な方法は、静的データの追加です。静的データは、モデルに既存のデータに依存しません。このことは、ユーザー定義の推論の拡張関数にとって一般的な例でありませんが、伴意へのトリプルの追加に関する基本を示しています。既存のオントロジを拡張するための準備フェーズ(つまり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
表(拡張関数によって挿入されるデータ)からフィルタ処理された後、最終的な伴意(モデルまたは他の推論データ)からもフィルタ処理されます。適切な最適化フラグを設定する(optimization_flag
パラメータを使用する)ことで、この便利な機能は使用されず、パフォーマンスが向上します。(最適化フラグの詳細は、「例3: パフォーマンスの最適化」を参照してください。)
output_tab
の表定義にはグラフ名の列が表示されていますが、グローバル推論( SEM_APIS.CREATE_ENTAILMENTのデフォルトの動作)と名前付きグラフ・グローバル推論(NGGI)を実行すると、推論エンジンは、拡張関数によって追加されるトリプルですべてのグラフ名を無視して、オーバーライドします。ユーザー定義の拡張ファンクションで、特定の名前付きグラフにトリプルを追加するには、NGLI (名前付きグラフ・ローカル推論)を使用します。NGLIでは、すべてのトリプルが名前付きグラフに属している必要があります(output_tab
のgid
列とg
列の両方がNULLではないことが必要です)。
MDSYSユーザーには、sem_inf_static
関数を推論に使用するため、この実行権限が必要です。次の例は、sem_inf_static
関数に適切な権限を付与し、(OWLPRIME推論ロジックとともに)関数を使用して伴意を作成する方法を示しています。
-- grant appropriate privileges grant execute on sem_inf_static to mdsys; -- create the entailment begin sem_apis.create_entailment( 'EMPLOYEES_INF' , sem_models('EMPLOYEES') , sem_rulebases('OWLPRIME') , passes => SEM_APIS.REACH_CLOSURE , inf_ext_user_func_name => 'sem_inf_static' ); 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'));
前述の問合せは、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
親トピック: ユーザー定義の推論拡張関数の例
8.1.3.2 例2: 動的トリプルの追加
静的データの追加は有用ですが、通常は準備(action='START'
)フェーズの間に実行されます。動的データを追加するには、モデル内の既存のデータを参照し、その既存のデータに基づいて新しいデータを生成します。これは、ユーザー定義の推論拡張関数の最も一般的なケースです。
次のユーザー定義の推論拡張関数(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 model (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
関数で伴意を作成するには、次のように、MDSYSユーザーに実行権限を付与し、SEM_APIS.CREATE_ENTAILMENTプロシージャに関数名を渡します。
-- grant appropriate privileges grant execute on sem_inf_dynamic to mdsys; -- create the entailment begin sem_apis.create_entailment( 'EMPLOYEES_INF' , sem_models('EMPLOYEES') , sem_rulebases('OWLPRIME') , passes => SEM_APIS.REACH_CLOSURE , inf_ext_user_func_name => 'sem_inf_dynamic' ); 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は、既存のデータですでにフルネームが指定されていたためです。
親トピック: ユーザー定義の推論拡張関数の例
8.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 model (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
句は、すでにモデルに存在するトリプルまたは他のルール(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
関数で伴意を作成するには、次のように、MDSYSユーザーに実行権限を付与し、SEM_APIS.CREATE_ENTAILMENTプロシージャに関数名を渡します。
-- grant appropriate privileges grant execute on sem_inf_related to mdsys; -- create the entailment begin sem_apis.create_entailment( 'EMPLOYEES_INF' , sem_models('EMPLOYEES') , sem_rulebases('OWLPRIME') , passes => SEM_APIS.REACH_CLOSURE , inf_ext_user_func_name => 'sem_inf_related' ); 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
親トピック: ユーザー定義の推論拡張関数の例
8.1.3.4 例4: 時間推論(いくつかの関連する例)
ユーザー定義の拡張関数は、特定のデータ型(xsd:dateTime
など)をトリプルでより利用しやすくすることができます。たとえばユーザー定義の拡張関数では、2つのxsd:dateTime
値の違いに基づいてトリプル間の関係を推論することが可能です。この項の3つの例は、2つの異なる時間推論ルールと、これらを1つの伴意に結合する方法を調査します。例では、モデルEVENT
とEVENT_ONT
が存在し、次のセマンティク・データが含まれることを前提とします。
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 .
例は次のとおりです。
8.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'); startTimePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/event/startTime'); endTimePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/event/endTime'); durationPropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/event/lengthInMins'); rdfTypePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); -- 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
関数で伴意を作成するには、次のように、MDSYSユーザーに実行権限を付与し、SEM_APIS.CREATE_ENTAILMENTプロシージャに関数名を渡します。
-- grant appropriate privileges grant execute on sem_inf_durations to mdsys; -- create the entailment begin sem_apis.create_entailment( 'EVENT_INF' , sem_models('EVENT', 'EVENT_ONT') , sem_rulebases('OWLPRIME') , passes => SEM_APIS.REACH_CLOSURE , inf_ext_user_func_name => 'sem_inf_durations' ); 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: 時間推論(いくつかの関連する例)
8.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'); startTimePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/event/startTime'); endTimePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/event/endTime'); overlapsPropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/event/overlaps'); noOverlapPropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/event/noOverlap'); rdfTypePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); -- 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
関数で伴意を作成するには、次のように、MDSYSユーザーに実行権限を付与し、SEM_APIS.CREATE_ENTAILMENTプロシージャに関数名を渡します。
-- grant appropriate privileges grant execute on sem_inf_overlap to mdsys; -- create the entailment begin sem_apis.create_entailment( 'EVENT_INF' , sem_models('EVENT', 'EVENT_ONT') , sem_rulebases('OWLPRIME') , passes => SEM_APIS.REACH_CLOSURE , inf_ext_user_func_name => 'sem_inf_overlap' ); 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: 時間推論(いくつかの関連する例)
8.1.3.4.3 例4c: 期間ルールとオーバーラップ・ルール
この項の例は、例4a: 期間ルール (sem_inf_durations
)および例4b: オーバーラップ・ルール (sem_inf_overlap
)の拡張機能関数を一緒に使用して、単一の伴意を生成します。拡張ファンクションは、この例では変更されないままです。
複数の拡張関数を使用して伴意を作成するには、SEM_APIS.CREATE_ENTAILMENT のinf_ext_user_func_name
パラメータに渡される各拡張関数をカンマを使用して区切ります。次の例では、拡張ファンクションでの適切な権限がMDSYSユーザーに付与されていることを前提とします。
-- use multiple user-defined inference functions begin sem_apis.create_entailment( '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' ); 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: 時間推論(いくつかの関連する例)
8.1.3.5 例5: 空間推論
ユーザー定義の推論拡張関数では、WKT (Well-Known Text)のような地理空間データ型を使用して空間推論を実行することもできます。たとえばユーザー定義の拡張関数を使用して、州と市のような形状エンティティ間の包含関係を推論することができます。
この項の例では、あるジオメトリ(米国のある州)に、あるポイント(米国のある市)が含まれるかどうかを推論する方法を示します。この例では、RDFネットワークに空間索引(1.6.6.2項を参照)があることを前提としています。また、モデルSTATES
が存在し、これには次のセマンティク・データが含まれているとします。
@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'); capitalClassId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/geo/StateCapital'); boundaryPropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/geo/boundary'); locationPropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://example.org/geo/location'); rdfTypePropertyId := sdo_sem_inference.oracle_orardf_res2vid('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'); -- 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'); -- 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 and Graphの SDO_GEOMETRY型に変換します。getV$GeometryVal
関数は、ほとんどがリソース・ビュー(resource_id_map_view
)と追加の引数によって提供される引数、空間参照システム(SRID)へのIDを必要とします。getV$GeometryVal
関数は、ジオメトリをSRIDによって指定される空間参照システムに変換します。sem_inf_capitals
関数は、値8307のSRIDによって指定される、デフォルトのOracle Spatial and Graph参照システム(WGS84 Longitude-Latitude)を使用します。(RDFセマンティク・グラフの空間参照システムのサポートの詳細は、「空間のサポート」を参照してください。)
getV$GeometryVal
関数を使用して、WKT値をSDO_GEOMETRY型に変換した後、sem_inf_capitals
関数は、その州のジオメトリを市のジオメトリと比較して、その州にその市が含まれているかどうかを調べます。SDO_RELATE
演算子がこの比較を実行し、州に市が含まれる場合は、リテラル値'TRUE'
を戻します。SDO_RELATE
演算子は、様々な異なる種類の比較を実行できます。(SDO_RELATE
およびその他の空間演算子の詳細は、『Oracle Spatial and Graph開発者ガイド』を参照してください。)
sem_inf_capitals
関数で伴意を作成するには、次のように、MDSYSユーザーに実行権限を付与し、SEM_APIS.CREATE_ENTAILMENTプロシージャに関数名を渡します。
-- grant appropriate privileges grant execute on sem_inf_capitals to mdsys; -- create the entailment begin sem_apis.create_entailment( 'STATES_INF' , sem_models('STATES') , sem_rulebases('OWLPRIME') , passes => SEM_APIS.REACH_CLOSURE , inf_ext_user_func_name => 'sem_inf_capitals' ); 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
親トピック: ユーザー定義の推論拡張関数の例
8.1.3.6 例6: Webサービスのコール
この項では、Oracle GeocoderサービスへのWebサービス・コールを可能にする、ユーザー定義の推論拡張関数(sem_inf_geocoding
)と関連するヘルパー・プロシージャ(geocoding
)について説明します。ユーザー定義の推論拡張関数は、述語<urn:streetAddress>
を使用してトリプルのオブジェクト値を検索し、Oracleの公開Geocoderサービスのエンドポイント(http://maps.oracle.com/geocoder/gcserver
)でコールアウトを実行して、経度と緯度の情報を2つの別々のトリプルとして挿入します。
たとえば、セマンティク・モデルに次の表明が含まれているとします。
<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'); nLong := sdo_sem_inference.oracle_orardf_add_res('http://www.w3.org/2003/01/geo/wgs84_pos#long'); nWKT := sdo_sem_inference.oracle_orardf_add_res('http://www.opengis.net/geosparql#asWKT'); nOWKT := sdo_sem_inference.oracle_orardf_add_res('http://xmlns.oracle.com/rdf/geo/asWKT'); end if; if (action = 'RUN') then nStreetAddr := sdo_sem_inference.oracle_orardf_res2vid('<urn:streetAddress>'); nLat := sdo_sem_inference.oracle_orardf_res2vid('http://www.w3.org/2003/01/geo/wgs84_pos#lat'); nLong := sdo_sem_inference.oracle_orardf_res2vid('http://www.w3.org/2003/01/geo/wgs84_pos#long'); nWKT := sdo_sem_inference.oracle_orardf_res2vid('http://www.opengis.net/geosparql#asWKT'); nOWKT := sdo_sem_inference.oracle_orardf_res2vid('http://xmlns.oracle.com/rdf/geo/asWKT'); 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)); -- 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 mdsys;
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; /
親トピック: ユーザー定義の推論拡張関数の例
8.2 ユーザー定義関数と集計
RDFセマンティク・グラフ問合せの拡張アーキテクチャによって、ユーザー定義の関数と集計を追加して、SPARQL問合せで使用できます。これには、SEM_MATCH表関数と、support for Apache Jenaが使用されます。
SPARQL 1.1標準には、主に、問合せによって取得されるデータをフィルタ処理して分類するために使用されるいくつかの関数が用意されています。ただし、この標準ではサポートされていない特殊な関数が必要な場合があります。
簡単な例として、特定のタイプに属する値の検索、平方和の値を使用した特定のしきい値よりも大きい値の取得があります。これは、関数を組み合せることで実行することはできますが、この計算を処理する単一の関数があると、問合せをより簡単に短くすることができて有用です。
RDFセマンティク・グラフ問合せの拡張機能によって、独自の問合せ関数と集計を含めることができます。このアーキテクチャにより、次のことが可能になっています。
-
組込みのSPARQL問合せ関数と同様に使用できるカスタム問合せ関数(「ユーザー定義関数のAPIサポート」を参照)
-
組込みのSPARQL集計と同様に使用できるカスタム集計(「ユーザー定義集計のAPIサポート」を参照)
8.2.1 ユーザー定義関数と集計のデータ型
ユーザー定義関数と集計を作成するとき、RDF語句を表現するためにSDO_RDF_TERMオブジェクト型が使用されます。
SDO_RDF_TERMには次の属性があり、MDSYS.RDF_VALUE$表の列に対応しています(これらの属性については、「文」の表1-3を参照してください)。CTX1属性は将来使用するために確保されているもので、対応する列はMDSYS.RDF_VALUE$にはありません。
SDO_RDF_TERM( VALUE_TYPE VARCHAR2(10), VALUE_NAME VARCHAR2(4000), VNAME_PREFIX VARCHAR2(4000), VNAME_SUFFIX VARCHAR2(512), LITERAL_TYPE VARCHAR2(1000), LANGUAGE_TYPE VARCHAR2(80), LONG_VALUE CLOB, CTX1 VARCHAR2(4000) )
SDO_RDF_TERMオブジェクトの作成には、次のコンストラクタが使用可能です。1つ目のコンストラクタは、単一の字句RDF語句文字列から、各属性を移入します。2番目と3番目のコンストラクタは、個々の属性値を入力として受け取ります。1番目のRDF語句文字列コンストラクタのみが、VNAME_PREFIXとVNAME_SUFFIXの値を設定します。これらの値は、他のコンストラクタによってNULLに初期化されます。
SDO_RDF_TERM ( rdf_term_str VARCHAR2) RETURN SELF; SDO_RDF_TERM ( value_type VARCHAR2, value_name VARCHAR2, literal_type VARCHAR2, language_type VARCHAR2, long_value CLOB) RETURN SELF; SDO_RDF_TERM ( value_type VARCHAR2, value_name VARCHAR2, literal_type VARCHAR2, language_type VARCHAR2, long_value CLOB, ctx1 VARCHAR2) RETURN SELF;
SDO_RDF_TERMオブジェクトのリストを格納するために、SDO_RDF_TERM_LIST型が使用され、VARRAY(32767) of SDO_RDF_TERM
として定義されます。
親トピック: ユーザー定義関数と集計
8.2.2 ユーザー定義関数のAPIサポート
ユーザー定義関数は、特定のシグネチャを使用したPL/SQL関数を実装することによって作成され、固有のURIを使用してSPARQL問合せパターンで起動されます。
推論拡張関数コールが正常に実行された後、推論拡張関数で行われた変更を保持するために、コミットが実行されます。pragma autonomous_transaction
を指定することによって、推論拡張関数が自律型として定義した場合、実装ロジックの最後で、コミットまたはロールバックする必要があります。推論エンジンでは、伴意の作成時(1ラウンドにつき1回)に、拡張関数を複数回コールする場合があることに注意してください。あるコールのコミットおよびロールバックは、他のコールには影響しません。
8.2.2.1 PL/SQL関数の実装
各ユーザー定義関数は、次の形式のシグネチャを持つPL/SQL関数によって実装される必要があります。
FUNCTION user_function_name (params IN SDO_RDF_TERM_LIST)
RETURN SDO_RDF_TERM
このシグネチャは任意の数のRDF語句引数(単一のSDO_RDF_TERM_LISTオブジェクトを使用する際に渡される)をサポートし、単一のRDF語句(単一のSDO_RDF_TERMオブジェクトとして表現される)を出力として戻します。これらのパラメータの型チェックまたは他の検証は、実行されません。関数の用途に従ってデータを検証するためのステップを実行する必要があります。
CやJavaなど、他のプログラミング言語で記述された関数に対して、PL/SQLからのコールアウトがサポートされているため、ユーザー定義の問合せ関数を実装するPL/SQL関数は、他のプログラミング言語で記述された関数のラッパーとしてのみ機能します。
親トピック: ユーザー定義関数のAPIサポート
8.2.2.2 SPARQL問合せパターンからのユーザー定義関数の起動
これは、ユーザー定義関数がPL/SQLで実装された後、接頭辞<http://xmlns.oracle.com/rdf/extensions/>
の後にschema.package_name.function_name
が続く関数URI(対応するPL/SQL関数がPL/SQLパッケージの一部である場合)、またはschema.function_name
が続く関数URI(関数がPL/SQLパッケージの一部でない場合)を使用して、SPARQL問合せパターンから起動することができます。次の2つは、関数URIの例です。
<http://xmlns.oracle.com/rdf/extensions/my_schema.my_package.my_function>(arg_1, …, arg_n) <http://xmlns.oracle.com/rdf/extensions/my_schema.my_function>(arg_1, …, arg_n)
親トピック: ユーザー定義関数のAPIサポート
8.2.2.3 ユーザー定義関数の例
この項では、ユーザー定義関数の実装例と、その関数のFILTER句、SELECT式およびBIND演算での使用について示します。
たとえば、ここではN-triple形式で示されている次のデータが、MYMODEL
と呼ばれるモデル内に存在しているとします。
<a> <p> "1.0"^^xsd:double . <b> <p> "1.5"^^xsd:float . <c> <p> "3"^^xsd:decimal . <d> <p> "4"^^xsd:string .
例8-1 2つの平方和を計算するユーザー定義関数
例8-1に、2つの値を受け取って各値の平方和を計算する、単純な関数の実装を示します。
CREATE OR REPLACE FUNCTION sum_squares (params IN MDSYS.SDO_RDF_TERM_LIST) RETURN MDSYS.SDO_RDF_TERM AS retTerm SDO_RDF_TERM; sqr1 NUMBER; sqr2 NUMBER; addVal NUMBER; val1 SDO_RDF_TERM; val2 SDO_RDF_TERM; BEGIN –- Set the return value to null. retTerm := SDO_RDF_TERM(NULL,NULL,NULL,NULL,NULL); –- Obtain the data from the first two parameters. val1 := params(1); val2 := params(2); –- Convert the value stored in the sdo_rdf_term to number. –- If any exception occurs, return the null value. BEGIN sqr1 := TO_NUMBER(val1.value_name); sqr2 := TO_NUMBER(val2.value_name); EXCEPTION WHEN OTHERS THEN RETURN retTerm; END; –- Compute the square sum of both values. addVal := (sqr1 * sqr1) + (sqr2 * sqr2); –- Set the return value to the desired rdf term type. retTerm := SDO_RDF_TERM('LIT',to_char(addVal), 'http://www.w3.org/2001/XMLSchema#integer','',NULL); – Return the new value. RETURN retTerm; END; / SHOW ERRORS;
なお、例8-1のsum_squares
関数では、受け取った値のデータ型は検証されません。これはあくまでも例であり、SDO_RDF_TERMのVALUE_NAMEフィールドに格納されている数値の取得には、TO_NUMBERを使用します。
例8-2 FILTER句で使用されるユーザー定義関数
例8-2に、FILTER句で使用されるsum_squares
関数(例8-1)を示します。
SELECT s, o
FROM table(sem_match(
'SELECT ?s ?o
WHERE { ?s ?p ?o
FILTER (<http://xmlns.oracle.com/rdf/extensions/schema.sum_squares>(?o,?o) > 2)}',
sem_models('MYMODEL'),null,null,null,null,''));
例8-2の問合せは、次の結果を戻します。
s o -------------------- -------------------- b 1.5 c 3 d 4
例8-3 SELECT式で使用されるユーザー定義関数
例8-3に、SELECT句の式で使用されるsum_squares
関数(例8-1)を示します。
SELECT s, o, sqr_sum FROM table(sem_match( 'SELECT ?s ?o (<http://xmlns.oracle.com/rdf/extensions/schema.sum_squares>(?o,?o) AS ?sqr_sum) WHERE { ?s ?p ?o }', sem_models('MYMODEL'),null,null,null,null,''));
例8-3の問合せは、次の結果を戻します。
s o sqr_sum -------------------- -------------------- -------------------- a 1 2 b 1.5 4.5 c 3 18 d 4 32
例8-4 BIND演算で使用されるユーザー定義関数
例8-4に、BIND演算で使用されるsum_squares
関数(例8-1)を示します。
SELECT s, o, sqr_sum FROM table(sem_match( 'SELECT ?s ?o ?sqr_sum WHERE { ?s ?p ?o . BIND (<http://xmlns.oracle.com/rdf/extensions/schema.sum_squares>(?o,?o) AS ?sqr_sum)}', sem_models('MYMODEL'),null,null,null,null,''));
例8-4の問合せは、次の結果を戻します。
s o sqr_sum -------------------- -------------------- -------------------- a 1 2 b 1.5 4.5 c 3 18 d 4 32
親トピック: ユーザー定義関数のAPIサポート
8.2.3 ユーザー定義の集計のAPIサポート
ユーザー定義の集計は、一連のインタフェース・メソッドを実装するPL/SQLオブジェクト型を定義することによって実装されます。ユーザー定義の集計を作成したら、固有のURIを使用してそれを起動します。
8.2.3.1 ODCIAggregateインタフェース
ユーザー定義の集計では、ODCIAggregate
PL/SQLインタフェースを使用します。このインタフェースの詳細は、Oracle Databaseデータ・カートリッジ開発者ガイドのユーザー定義集計関数に関する章を参照してください。
ODCIAggregate
インタフェースは、4つの主要な関数を実装するPL/SQLオブジェクト型によって実装されます。
-
ODCIAggregateInitialize
-
ODCIAggregateIterate
-
ODCIAggregateMerge
-
ODCIAggregateTerminate
ユーザー定義関数(「ユーザー定義関数のAPIサポート」を参照)と同様に、ユーザー定義の集計は、SDO_RDF_TERM_LISTオブジェクトとして渡される任意の数のRDF語句引数を受け取り、SDO_RDF_TERMオブジェクトとして表現される単一のRDF語句の値を返します。
このスキームによって、PL/SQL ODCIAggregate
インタフェース関数のシグネチャは、次のようになります(my_aggregate_obj_typeは実際のオブジェクト・タイプ名)。
STATIC FUNCTION ODCIAggregateInitialize( sctx IN OUT my_aggregate_obj_type) RETURN NUMBER MEMBER FUNCTION ODCIAggregateIterate( self IN OUT my_aggregate_obj_type ,value IN MDSYS.SDO_RDF_TERM_LIST) RETURN NUMBER MEMBER FUNCTION ODCIAggregateMerge( self IN OUT my_aggregate_obj_type ,ctx2 IN my_aggregate_obj_type) RETURN NUMBER MEMBER FUNCTION ODCIAggregateTerminate ( self IN my_aggregate_obj_type ,return_value OUT MDSYS.SDO_RDF_TERM ,flags IN NUMBER) RETURN NUMBER
親トピック: ユーザー定義集計のAPIサポート
8.2.3.2 ユーザー定義の集計の起動
これは、ユーザー定義の集計がPL/SQLで実装された後、接頭辞<http://xmlns.oracle.com/rdf/aggExtensions/>
の後にschema_name.aggregate_name
を続けて構築される集計URIを参照することによって、SPARQL問合せから起動することができます。集計URIの例は次のとおりです。
<http://xmlns.oracle.com/rdf/aggExtensions/schema.my_aggregate>(arg_1, …, arg_n)
DISTINCT修飾子は、ユーザー定義の集計で次の例のように使用できます。
<http://xmlns.oracle.com/rdf/aggExtensions/schema.my_aggregate>(DISTINCT arg_1)
この場合、distinct引数の値のみが集計に渡されます。ただしDISTINCT修飾子は、厳密に1つの引数の集計でのみ使用できることに注意してください。
親トピック: ユーザー定義集計のAPIサポート
8.2.3.3 ユーザー定義の集計の例
この項では、ユーザー定義の集計を実装して使用する例を示します。たとえば、ここではN-triple形式で示されている次のデータが、MYMODEL
と呼ばれるモデル内に存在しているとします。
<a> <p> "1.0"^^xsd:double . <b> <p> "1.5"^^xsd:float . <c> <p> "3"^^xsd:decimal . <c> <p> "4"^^xsd:decimal . <d> <p> "4"^^xsd:string .
例8-5 ユーザー定義の集計の実装
例8-5は、単純なユーザー定義の集計の実装(countSameType
)を示しています。この集計には2つの引数があり、1番目は任意のRDF語句、2番目は定数のデータ型URIです。集計は、1番目の引数の位置からのRDF語句にある、2番目の引数と等しいデータ型の数をカウントします。
-- Aggregate type creation CREATE OR REPLACE TYPE countSameType authid current_user AS OBJECT( count NUMBER, –- Variable to store the number of same-type terms. –- Mandatory Functions for aggregates STATIC FUNCTION ODCIAggregateInitialize( sctx IN OUT countSameType) RETURN NUMBER, MEMBER FUNCTION ODCIAggregateIterate( self IN OUT countSameType , value IN MDSYS.SDO_RDF_TERM_LIST) RETURN NUMBER, MEMBER FUNCTION ODCIAggregateMerge( self IN OUT countSameType ,ctx2 IN countSameType) RETURN NUMBER, MEMBER FUNCTION ODCIAggregateTerminate ( self IN countSameType ,return_value OUT MDSYS.SDO_RDF_TERM ,flags IN NUMBER) RETURN NUMBER ); / SHOW ERRORS; –- Interface function for the user-defined aggregate CREATE OR REPLACE FUNCTION countSameAs (input MDSYS.SDO_RDF_TERM_LIST) RETURN MDSYS.SDO_RDF_TERM PARALLEL_ENABLE AGGREGATE USING countSameType; / show errors; –- User-defined aggregate body CREATE OR REPLACE TYPE BODY countSameType IS STATIC FUNCTION ODCIAggregateInitialize( sctx IN OUT countSameType) RETURN NUMBER IS BEGIN sctx := countSameType (0); –- Aggregate initialization RETURN ODCIConst.Success; END; MEMBER FUNCTION ODCIAggregateIterate( self IN OUT countSameType , value IN MDSYS.SDO_RDF_TERM_LIST ) RETURN NUMBER IS BEGIN -- Increment count if the first argument has a literal type -- URI equal to the value of the second argument IF (value(1).literal_type = value(2).value_name) THEN self.count := self.count + 1; END IF; RETURN ODCIConst.Success; END; MEMBER FUNCTION ODCIAggregateMerge( self IN OUT countSameType ,ctx2 IN countSameType) RETURN NUMBER IS BEGIN –- Sum count to merge parallel threads. self.count := self.count + ctx2.count; RETURN ODCIConst.Success; END; MEMBER FUNCTION ODCIAggregateTerminate( self IN countSameType ,return_value OUT MDSYS.SDO_RDF_TERM ,flags IN NUMBER) RETURN NUMBER IS BEGIN -- Set the return value return_value := MDSYS.SDO_RDF_TERM('LIT',to_char(self.count), 'http://www.w3.org/2001/XMLSchema#decimal',NULL,NULL); RETURN ODCIConst.Success; END; END; / SHOW ERRORS;
例8-6 GROUP BY句なしで使用されるユーザー定義の集計
例8-6に、問合せ結果のグループ全体に使用されるcountSameType
集計(例8-5)を示します。
FROM o
from table(sem_match(
'SELECT
(<http://xmlns.oracle.com/rdf/aggExtensions/schema.countSameType>(?o,xsd:decimal)
AS ?o)
WHERE { ?s ?p ?o }',
sem_models('MYMODEL'),null,null,null,null,''));
例8-6の問合せは、次の結果を戻します。
o -------------------- 2
例8-7 GROUP BY句とともに使用されるユーザー定義の集計
例8-7に、GROUP BY句から形成される一連のグループに使用されるcountSameType
集計(例8-5)を示します。
select s, o from table(sem_match( 'SELECT ?s (<http://xmlns.oracle.com/rdf/aggExtensions/schema.countSameType>(?o,xsd:decimal) AS ?o) WHERE { ?s ?p ?o } GROUP BY ?s', sem_models('MYMODEL'),null,null,null,null,''));
例8-7の問合せは、次の結果を戻します。
s o -------------------- -------------------- a 0 b 0 c 2 d 0
親トピック: ユーザー定義集計のAPIサポート