17.2 SQL/JSONパス式の構文

SQL/JSONパス式は、SQL/JSONファンクションまたは条件によってJSONデータと照合され、その一部が選択またはテストされます。パス式にはワイルドカードおよび配列範囲を使用できます。照合では大/小文字が区別されます。

SQL/JSONパス式およびJSONデータをSQL/JSONファンクションまたは条件に渡します。パス式はデータに対して照合され、一致するデータが特定のSQL/JSONファンクションまたは条件によって処理されます。この照合プロセスは、パス式が一致データを関数または条件に戻すという観点で検討することができます。

17.2.1 基本的なSQL/JSONパス式の構文

SQL/JSONパス式の基本的な構文を示します。これは、コンテキスト項目記号($)とその後のゼロ個以上のオブジェクト、配列および子孫ステップで構成されます。それぞれの後にフィルタ式を続けることができ、オプションでファンクション・ステップを指定できます。例を示します。

ただし、この基本的な構文は、非配列パターンに対する配列の照合および配列パターンに対する非配列の照合の緩和によって拡張されます。SQL/JSONパス式の構文の緩和を参照してください。

SQL/JSONパス式に対するデータの照合では、大/小文字が区別されます。

  • SQL/JSONの基本的なパス式(ここでは単にパス式とも呼びます)は、絶対パス式または相対パス式です。

  • 絶対パス式は、パス式のコンテキスト項目(つまり、照合するJSONデータ)を表すドル記号($)から始まります。一致するデータは、SQL/JSONファンクションに引数として渡されるSQL式を評価することによって見つかります。ドル記号の後にゼロ個以上の非ファンクション・ステップが続き、その後にオプションのファンクション・ステップが続きます。

  • 相対パス式は、アット記号(@)の後にゼロ個以上の非ファンクション・ステップが続き、その後にオプションのファンクション・ステップが続く形式です。ドル記号($)のかわりにアット記号を使用する点を除き、絶対パス式と同じ構文になります。

    相対パス式はフィルタ式(フィルタと短縮される)内で使用されます。アット・マークは、パス式のカレント・フィルタ項目を表します。つまり、相対パス式を含むフィルタの前にある(周囲の)パス式の部分に一致するJSONデータを表します。相対パス式は、絶対パス式がコンテキスト項目に対して照合されるのと同じように、カレント・フィルタ項目に対して照合されます。

  • 非ファンクション・ステップは、オブジェクト・ステップ配列ステップまたは子孫ステップの後にオプションのフィルタ式が続く形式です。

  • 単一のファンクション・ステップは、基本パス式(絶対または相対)ではオプションです。このステップがあったら、これがパス式の最後のステップになります。これは、ドットと呼ばれることもあるピリオド(.)の後ろにSQL/JSON項目メソッドが続き、その後に左カッコ(()が続き、おそらく引数リストがあり、その後ろに右カッコ())が続く形式です。これらのカッコの中にホワイトスペースを入れることができます(このようなホワイトスペースは重要ではありません)。

    項目メソッドは、(1)ファンクション・ステップのにある同じパス式の残りの部分によって対象とされるデータと、(2)カッコ内の引数(ある場合)に適用されます。項目メソッドは、対象データを変換するために使用されます。引数としてパス式が渡されるSQLファンクションまたは条件は、変換したデータを対象データのかわりに使用します。

    一部の項目メソッドのみに、カッコ間の引数リストを使用できます。引数はスカラーJSON値であり、カンマ(,)で区切ります。項目メソッドには、このような引数を1つ以上必要とするものがあります。その他のメソッドでは、このような引数が許容されますが、必須ではありません。

  • オブジェクト・ステップは、ピリオド(.)の後にオブジェクト・フィールド名またはすべてのフィールド(の値)を表すアスタリスク (*)ワイルドカードが続く形式です。フィールド名はにすることができます。この場合は、""(ホワイトスペースなし)と記述する必要があります。空でないフィールド名は、大文字または小文字の文字A-Zで始まり、このような文字または10進数字(0-9)のみを含む必要があり、それ以外の場合は、二重引用符(")で囲む必要があります。

    オブジェクト・ステップは、指定されたフィールドのを戻します。フィールドにワイルドカードが使用されている場合、すべてのフィールドの値が特に順序は指定されずに戻されます。

  • 配列ステップは、左大カッコ([)の後ろに、すべての配列要素を表すアスタリスク(*)ワイルドカードまたはカンマ(,)で区切られた1つ以上の特定の配列索引または範囲指定のどちらかが続き、その後ろに右大カッコ(])が付いた形式です。

    アスタリスクと配列索引または範囲指定の両方を使用すると、エラーが発生します。索引または範囲の指定がない場合、エラーが発生します。[]は有効な配列ステップではありません。

    配列索引は、単一の配列の位置(整数(0、1、2...))を指定します。このため、配列索引はリテラルの整数(012、…)であり、配列位置および索引付けは配列に関するJavaScript規則にあるようにゼロベースです。先頭の配列要素は、索引0 (位置0を指定)です。

    任意のサイズの空ではない配列の末尾の要素は、索引lastを使用して参照できます。

    配列索引には、last - Nという形式もあります。ここで、-はマイナス記号(ハイフン)で、Nは配列サイズ-1を超えないリテラルの整数(012、…)です。

    末尾の1つ前の配列要素は、索引last-1を使用して参照できます。末尾の2つ前の配列要素は索引last-2で、以下同様になります。マイナス記号(ハイフン)の前後の空白は無視されます。

    たとえば、配列["a", "b", 42]の場合、索引1 (位置1)の要素は文字列"b" (2番目の配列要素)です。索引2または索引lastの要素は、数値42です。索引0またはlast-2の要素は、"a"です。

    Oracle SQLファンクションjson_transformでは、last + Nという形式の索引も使用できます。ここで、Nは整数です。これにより、現在の配列サイズ-1を超える位置を指定することによって、既存の配列に新しい要素を追加できます。プラス記号の前後の空白は無視されます。この形式の索引は、範囲指定(次を参照)などの他の索引と組み合せて使用できません。その場合は、エラーが発生します。

    範囲指定の形式は、N to Mです。ここで、NMは配列索引であり、toの前後には1つ以上の空白文字を記述します。脚注1

    範囲指定のNからMと、MからNは同等です。それぞれ、NMおよびNMの間の索引をすべて昇順で明示的に指定することと同等です。

    つまり、NMの順序は重要ではありません。3番目から6番目の要素の範囲は、2 to 5または5 to 2として記述できます。6個の要素を持つ配列の場合は、同じ範囲を2 to lastまたはlast to 2として記述できます。範囲指定N to N (toの両側に同じ索引N)は、単一の索引Nと同じです([N, N]と同じではありません)。

    配列ステップに配列索引および配列範囲を指定する順序は重要であり、パス式を使用するファンクションで生成される配列に反映されます。

    同じ配列ステップ内の複数の範囲指定は、個別に処理されます。特に、重複する範囲では、重複している要素が繰り返されます。

    たとえば、配列ラッパー(複数の問合せ結果をラップして単一のJSON配列を返します)を指定したSQL/JSONファンクションjson_queryに、配列ステップ[3 to 1, 2 to 4, last-1 to last-2, 0, 0]を指定したパス式を渡して、問合せを行うとします。この問合せによって返されるデータには、問合せ対象のデータの配列の次の要素から生成された配列が含まれています。

    • 2番目から4番目の要素(範囲3 to 1)

    • 3番目から5番目の要素(範囲2 to 4)

    • 末尾の2つ前から末尾の1つ前までの要素(範囲last-1 to last-2)

    • 先頭の要素(索引0)

    • 再び先頭の要素(索引0)

    データの配列["1", "2", "3", "4", "5", "6", "7", "8", "9"]と照合すると、問合せ結果の配列は["2", "3", "4", "3", "4", "5", "7", "8", "1", "1"]になります。

    データの配列(0から配列サイズ-1まで)の範囲外の位置を指定する配列索引を使用しても、エラーは発生しません。指定されたパス式がデータと一致しないだけです。配列にそのような位置はありません。(配列ステップのみではなく、通常、SQL/JSONパス式の照合はこのルールに従っています。)

    これは、たとえば、要素が7個未満の配列に対して索引last-6を照合しようとした場合などです。6個の要素を持つ配列の場合、last5であるため、last-6は(0より小さい)無効な位置を指定することになります。

    また、任意の配列ステップをの配列に対して照合しようとする場合にも当てはまります。たとえば、配列ステップ[0][last]はどちらもデータ配列[]と一致しません。[]には先頭の要素がないため、ステップ[0]は一致しません。また、[]には索引-1 (配列の長さ-1)の要素がないため、ステップ[last]は一致しません。

    これは、ファンクションjson_transform以外で、索引last+N (Nはゼロ以外)を使用した場合にも当てはまります。json_transformの場合、これは、既存の配列要素と照合するためではなく、既存の配列の変更時に新しい要素を挿入する位置を指定するために使用します。

    範囲指定は明示的な昇順の配列索引と等価であるため、範囲外の暗黙的な索引はデータと一致しません。明示的な索引と同様に、それらの索引は無視されます。

    これについて考えるもう1つの方法は、範囲指定は実際には所与のデータ配列の最も近い境界(0またはlast)で切り捨てられるということです。たとえば、配列["a", "b", "c"]と照合する場合、範囲指定last-3 to 12 to last+1およびlast-3 to last+1は、実際にはそれぞれ0 to 12 to 2および0 to 2に切り捨てられます。これらの範囲の(暗黙的な)範囲外の索引last-3 (ここでは-1)およびlast+1 (ここでは3)は無視されます。

  • 子孫ステップでは、2つの連続したピリオド(..)(ドット・ドットとも呼ばれる)の後にフィールド名を指定します(オブジェクト・ステップの場合と同じ構文)。

    これは、直前のステップ(前のステップがない場合はコンテキスト項目)と一致するオブジェクトまたは配列で、再帰的に子孫を参照します。

    各子孫レベルでは、各オブジェクトおよびオブジェクトである各配列要素について、指定された名前を持つすべてのフィールドの値が収集されます。収集されたフィールド値をすべて返します。

    たとえば、次の問合せとデータについて考えます。

    json_query(some_json_column, '$.a..z' WITH ARRAY WRAPPER)
    { "a" : { "b" : { "z" : 1 },
              "c" : [ 5, { "z" : 2 } ],
              "z" : 3 },
      "z" : 4 }

    この問合せでは[1,2,3]のような配列が返され、その要素は12および3になります。ドット・ドット(..)の直前のステップ、つまりフィールドa内の各フィールドzの値が収集されます。値4の最上位のフィールドzは、フィールドaの値内にないため一致しません

    フィールドaの値は、子孫が参照されるオブジェクトです。

    • zというフィールドがあり、その値(3)が収集されます。また、値がオブジェクトであるフィールドbもあります。これは、フィールドzの値(1)を収集するために子孫が参照されます。

    • 値が配列であるフィールドcもあり、この配列には、値(2)が収集されるフィールドzを持つオブジェクトである要素があります。

    したがって、収集されるJSON値は31および2です。これらの値は未定義の順序で配列にラップされます。可能性のある戻り値の1つは、[1,2,3]です。

  • フィルタ式(フィルタと短縮される)は、疑問符(?)の後カッコ(())で囲まれたフィルタ条件を記述します。条件が満たされるとフィルタが満たされ、trueが戻されます。

  • フィルタ条件は、その引数に述語(ブール関数)を適用します。これは、condcond1およびcond2のそれぞれがフィルタ条件を表す、次のいずれかとして再帰的に定義されます。脚注2

    • ! cond: cond否定で、condが満たされてはならないことを意味します。!は接頭辞単項述語です。(パス式での否定を参照してください。)

    • ( cond ): カッコを使用してグループ化することで、前後にあるその他のフィルタ条件から、フィルタ条件condを1つのユニットとして区別します。

      コードが読みやすくなるのであれば、影響のない箇所でかっこを使用することもできます。たとえば、必要に応じてexistscondのみでなく、exists(cond)のように、述語の引数の周囲にそれらを配置できます。

      条件引数の先頭と末尾が不明瞭な場合は必ず括弧が必要です。たとえば、condcomparison条件である場合は常に、!(cond)に必要です(次を参照)。たとえば、!@.x > 5ではなく!(@.x > 5)を使用する必要があります。(ただし、!exists@.xまたは!(exists@.x)のいずれでも使用できます。)

    • cond1 && cond2: cond1cond2論理積(and)で、両方を満たす必要があります。&&は中置二項述語です。

    • cond1 || cond2: cond1およびcond2の包含的論理和(or)で、cond1cond2またはその両方を満たす必要があります。||は中置二項述語です。

    • exists相対パス式: ターゲット・データが存在する条件。existsは、接頭辞の単項述語です。

    • 相対パス式の後にinが続き、その後に値リストが続きます。これは、値が値リスト内の値の1つであることを意味します。inは中置二項述語です。

      複数の値リストの要素があるinフィルタ条件は、値リストの要素に対する等価(==)比較の論理和(||)に相当します。脚注3たとえば、次のものは同等です。

      @.z in ("a", "b", c")
      (@.z == "a") || (@.z == "b") || (@.z == "c")

      値リストは、(の後ろに、0個以上のJSONリテラル値またはカンマ(,)で区切られたSQL/JSON変数のリストの後に)が続いたものです。脚注4値リストは、inの後にのみ指定できます。それ以外の場合は、エラーが発生します。

      • リスト内の各変数JSONデータ型の場合、「JSONデータ型の値の比較とソート」に説明がある正規のソート順を使用して、リストされた各値(リテラルまたは変数の値)が、ターゲットJSONデータに対して等価比較されます。条件inは、リストされた値のいずれかがターゲット・データと等しい場合に満たされます。

      • それ以外の(少なくとも1つの変数JSONデータ型ではない)場合、リスト内のすべての値は(リテラルか変数かに関係なく)、同じJSON言語型のスカラー値であることが必要です。たとえば、これらはすべて文字列であることが必要で、そうでない場合はエラーが発生します。

        JSONのnull値は、この同じタイプの制限に対する例外です。nullは、常に値リストで使用できます。ターゲット・データのnull値(のみ)によって照合されます。

    • 比較(次のいずれか)

      • JSONスカラー値、その後に比較述語、その後に別のJSONスカラー値。

      • 相対パス式の後ろに比較述語が続き、その後に別の相対パス式が続きます。

      • JSONスカラー値またはSQL/JSON変数、その後に比較述語、その後に相対パス式

      • 相対パス式、その後に比較述語、その後にJSONスカラー値またはSQL/JSON変数

      • 相対パス式の後にキーワードのhas substringstarts withlikelike_regexregex likeregexeq_regexci_like_regexまたはci_regexが続き、その後にJSON文字列またはSQL文字列(データベース文字セットからUTF8に自動的に変換される)にバインドされているSQL/JSON変数が続きます。

        これらのすべての述語について、空の文字列("")のパターンは空の文字列のデータに一致します。また、like_regex以外のすべてで、空でない文字列であるパターンは空の文字列のデータと一致しませんlike_regexの場合、空でないパターンは空の文字列データと一致します

        • has substringは、一致するデータ値に部分文字列として指定された文字列が含まれていることを意味します。

        • starts withは、一致するデータ値に接頭辞として指定された文字列が含まれていることを意味します。

        • likeは、JSON文字列データ値が、SQL LIKE4文字セット・セマンティクスを使用するSQL LIKEパターンとして解釈される指定された文字列と一致することを意味します。パターン内のパーセント記号(%)は、ゼロ個以上の文字に一致します。アンダースコア(_)は1文字と一致します。

          ノート:

          SQLのLIKEの場合とは異なり、パス式の述語likeエスケープ文字は選択できません。常に文字「`」(グレイヴ・アクセント(U +0060)、バッククォートやバックティックとも呼ばれます)を使用します。

          21cより前のデータベース・リリースでは、パス式の述語likeにエスケープ文字はありません。そのようなリリースでは、likeパターンで文字「`」(グレイヴ・アクセント(U+0060))を使用しないようにすることをお薦めします。

        • like_regexまたはそのシノニムのregex like (下線なし)は、JSON文字列データ値が指定の文字列と一致することを意味します。この文字列は、SQL LIKE4文字セット・セマンティクスを使用するSQL REGEXP LIKE正規表現パターンとして解釈されます。

          ci_like_regexは、like_regexと同じですが、照合では大文字と小文字が区別されません

          like_regexci_like_regexは、パターン一致比較の例外であり、そのパターンは空のJSON文字列("")と一致します。

        • eq_regexとそのシノニムregex equals (下線なし)およびregexは、次の2つの違いを除いてlike_regexと同じです。

          • eq_regexは、JSON文字列データ値全体に対する正規表現パターンと一致します。完全な文字列は、比較対象のパターンと一致する必要があります。like_regexは、JSON文字列の一部がパターンと一致する場合に満たされます。

          • eq_regexパターンが空のJSON文字列("")と一致しません。

          ci_regexは、eq_regexと同じですが、照合では大文字と小文字が区別されません

    SQL/JSON変数は、ドル記号($)の直後に(空白を入れない)、PASSING句でバインドされている変数の名前が続いたものです。(SQL/JSON変数名の必要な構文は、「SQLファンクションおよび条件のPASSING句」を参照。)

    比較述語==<>!=<a id="fn_5" name="fn_5" href="#fn_5" onclick='footdisplay(5, "!= is an Oracle alias for the SQL/JSON standard comparison predicate <>.")'>脚注5<<=>=または>です(それぞれ等しい、等しくない、未満、以下、以上、より大きい)。(等しくない述語、<>またはそのOracle別名!=の使用の詳細は、パス式での否定を参照してください。)

    したがってフィルタ条件で使用できる述語は、&&||!exists==<>!=<<=>=>inhas substringstarts withlikelike_regexregex likeregexeq_regexci_like_regexおよびci_regexです。

    例を示します。次の基準の両方が満たされると、フィルタ条件(a || b) && (!(c) || d < 42)が満たされることになります。

    • abのフィルタ条件のうち、少なくともいずれか1つが満たされる: (a || b)

    • フィルタ条件cが満たされない、は数値dが42以下になる、またはその両方が当てはまる: (!(c) || d < 42)

    条件述語!&&||は、この順番で優先度が高くなります。カッコは、常にグループ化を制御するために使用できます。

    前述の例の(a || b) && (!(c) || d < 42)で、グループ化にカッコを使用することなく、単にa || b && !(c) || d < 42としていたとすると、少なくとも次の基準の1つが満たされている場合に、これが満たされるようになります。

    • 条件b && !(c)が満たされる。つまり、条件b!(c)がそれぞれ満たされることを意味する(つまり、条件cは満たされない)。

    • 条件aが満たされる。

    • 条件d < 42が満たされる。

比較の少なくとも一方をSQL/JSON変数にすることはできません

比較の対象となるデータがJSONデータ型であり、比較に使用されるすべてのSQL/JSON変数もJSON型の場合、比較では「JSONデータ型の値の比較およびソート」で説明した正規のソート順が使用されます。

それ以外の場合、比較のデフォルトのは、コンパイル時に非変数側の型に基づいて定義されます。型指定項目メソッドを使用すると、このデフォルトを別の型でオーバーライドできます。一致するデータの型は、比較のために自動的に変換され、決定された型(デフォルトまたは項目メソッドで指定)に適合します。たとえば、$.a > 5では5が数値であるため、数値の比較が課され、$.a > "5"では"5"が文字列であるため、文字列の比較が課されます。

ヒント:

頻繁に使用する問合せの場合には、PASSING句を使用してSQLバインド変数を定義します。これらは、パス式でSQL/JSON変数として使用されます。これにより、(変数)値の変更時に問合せの再コンパイルを回避することで、パフォーマンスが向上する可能性があります。

たとえば、この問合せは、バインド変数v1の値をSQL/JSON変数$v1として渡します。

SELECT po.data FROM j_purchaseorder po
  WHERE json_exists(po.data,                    
                    '$.LineItems.Part?(@.UPCCode == $v1)'
                    PASSING '85391628927' AS "v1");

ノート:

Oracle SQLファンクションjson_textcontainsを使用すると、JSONデータの強力な全文検索を実行できます。単純な文字列のパターン一致のみが必要な場合は、かわりにパス式フィルタ条件をhas substringstarts withlikelike_regexまたはeq_regexのいずれかのパターン一致比較とともに使用できます。

基本的なパス式の例

パス式の例を次に示しますが、その意味も詳述しています。

  • $ - コンテキスト項目。

  • $.friends - コンテキスト項目オブジェクトのフィールドfriendsの値。ドル記号($)の直後のドット(.)は、コンテキスト項目がJSONオブジェクトであることを示します。

  • $.friends[0] - コンテキスト項目オブジェクトのフィールドfriendsの値である配列の最初の要素であるオブジェクト。大カッコ表記法は、フィールドfriendsの値が配列であることを示します。

  • $.friends[0].name - コンテキスト項目オブジェクトのフィールドfriendsの値である配列の最初の要素であるオブジェクトのフィールドnameの値。2番目のドット(.)は、配列friendsの最初の要素が(フィールドnameを持つ)オブジェクトであることを示します。

  • $.friends[*].name - コンテキスト項目オブジェクトのフィールドfriendsの値である配列のオブジェクトのフィールドnameの値。

  • $.*[*].name - コンテキスト項目オブジェクトのフィールドの配列値に含まれる各オブジェクトのフィールドnameの値。

  • $.friends[3, 8 to 10, 12] — 配列friends(コンテキスト項目オブジェクトのフィールド)の4番目、9番目から11番目、13番目の要素。要素は指定された順序で返されます(4番目、9番目、10番目、11番目、13番目)。

    照合する配列の要素が13個未満の場合、索引12は一致しません。照合する配列の要素が10個のみの場合、索引12と一致しないのみではなく、範囲8 to 10は実際には位置8および9 (要素9および10)に切り捨てられます。

  • $.friends[12, 3, 10 to 8, 12] - 配列friendsの13番目、4番目、9番目から11番目、13番目の要素(このとおりの順序)。要素は指定された順序で返されます。範囲10 to 8は、範囲8 to 10と同じ要素を同じ順序で指定します。13番目の要素(位置12)は2回返されます。

  • $.friends[last-1, last, last, last] - 配列friendsの末尾の1つ前、末尾、末尾および末尾の要素(このとおりの順序)。末尾の要素は3回返されます

  • $.friends[last to last-1, last, last] - 前述の例と同じです。範囲last to last-1は、範囲last-1 to lastと同じであり、末尾の1つ前から末尾の要素を返します。

  • $.friends[3].cars - 配列friendsの4番目の要素であるオブジェクトのフィールドcarsの値。ドット(.)は、4番目の要素が(フィールドcarsを持つ)オブジェクトであることを示します。

  • $.friends[3].* - 配列friendsの4番目の要素であるオブジェクトのすべてのフィールドの値。

  • $.friends[3].cars[0].year - 配列friendsの4番目の要素であるオブジェクトのフィールドcarsの値である配列の最初の要素であるオブジェクトのフィールドyearの値。

  • $.friends[3].cars[0]?(@.year > 2016) - 配列cars (配列friendsの4番目の要素であるオブジェクトのフィールド)の最初のオブジェクト。ただし、そのフィールドyearの値が2016よりも大きいか、2016よりも大きい数値に変換できるもの。"2017"などのyear値は、テストを満たす数値2017に変換されます。"recent"などのyear値は、一致せず、テストに失敗します。

  • $.friends[3].cars[0]?(@.year.number() > 2016) - 前と同じです。項目メソッドnumber()は数値に変換できる数値または文字列値のみを許可し、その動作は数値比較述語>によってすでに指定されています。

  • $.friends[3].cars[0]?(@.year.numberOnly() > 2016) - 前と同じですが、year値は数値である場合にかぎられます。項目メソッドnumberOnly()は、year値が文字列数字("2017"など)である車を除外します。

  • $.friends[3]?(@.addresses.city == "San Francisco") — 配列friendsの4番目の要素であるオブジェクト。ただし、そのaddressesフィールドの値が、フィールドcityの値が文字列"San Francisco"のオブジェクトであるもの。

  • $.friends[*].addresses?(@.city starts with "San ").zip - 住所cityの名前が"San "で始まる、friendsのすべてのaddressesの郵便番号。(この場合、フィルタは最後のパス・ステップではありません。)

  • $.friends[*].addresses?(@.city has substring "Fran").zip - 住所cityの名前に"Fran"が含まれる、friendsのすべてのaddressesの郵便番号。

  • $.friends[*].addresses?(@.city like "S_n%").zip - 住所cityの名前が"S"で、その後に任意の1文字、"n"、さらにゼロ個以上の任意の文字のシーケンスが続く、friendsのすべてのaddressesの郵便番号。アンダースコア(_)は1文字に一致し、パーセント(%)は複数の文字に一致します。

  • $.friends[*].addresses?(@.city like_regex "n +F").zip - 住所cityの名前に"n"とそれに続く少なくとも1つの空白文字が含まれる、friendsのすべてのaddressesの郵便番号。照合では大文字と小文字が区別され、city文字列の先頭に固定されることはありません。

  • $.friends[*].addresses?(@.city ci_regex "s.+o").zip - 住所cityの名前が"s"または"S"で始まり、"o"または"O"で終わる、friendsのすべてのaddressesの郵便番号。照合では大文字と小文字が区別されず(ci_)、city文字列全体が一致する必要があります(like_なし)。

  • $..zip - 任意のレベルのzipフィールドのすべての値。

  • $.friends[3]?(@.addresses.city == "San Francisco" && @.addresses.state == "Nevada") — 配列friendsの4番目の要素であるオブジェクト。ただし、city"San Francisco"住所に対する一致がありstate"Nevada"住所に対する一致もあるもの

    ノート: 論理積になったフィルタ条件は、必ずしも同じオブジェクトに適用する必要はありません。このフィルタでは、cityがSan Franciscoのオブジェクトがあるかどうかと、stateがNevadaのオブジェクトがあるかどうかがテストされます。cityがSan FranciscoでstateがNevadaの両方を備えたオブジェクトがあるかどうかはテストされませんJSON_EXISTSでのフィルタの使用を参照してください。

  • $.friends[3].addresses?(@.city == "San Francisco" && @.state == "Nevada") — 配列friendsの4番目の要素であるオブジェクト。ただし、そのオブジェクトに、city"San Francisco"の一致と、state"Nevada"の一致があるもの。

    前述の例とは異なり、この場合の論理積のフィルタ条件(フィールドcitystate)は、同じaddressesオブジェクトに適用されます。フィルタは、その外側にある特定のaddressesオブジェクトに適用されます。

  • $.friends[3].addresses?(@.city == $City && @.state == $State) - 前と同じですが、比較に使用される値はSQL/JSONの変数$Cityおよび$Stateです。この変数値は、PASSING句(PASSING ... AS "City", ... AS "State")のSQLバインド変数CityおよびStateによって指定されます。比較に変数を使用すると、問合せの再コンパイルが回避されることでパフォーマンスが向上します。

関連項目:

17.2.2 SQL/JSONパス式の構文の緩和

暗黙的な配列のラップおよびアンラップを可能にするため、SQL/JSONパス式の基本的な構文は緩和されています。これは、データが発展して特定のJSON値のかわりにこのような値の配列が使用される(またはこの逆)場合、コード内のパス式を変更する必要がないことを意味します。例を示します。

SQL/JSONパス式の基本的な構文では、SQL/JSONパス式の基本的な構文を定義しています。サポートされている実際のパス式の構文では、この定義を次のように緩和します。

  • パス式のステップが配列を対象としている(予測している)が、実際のデータは配列を示していない場合、データは暗黙的に配列にラップされます。

  • パス式のステップが非配列を対象としている(予測している)が、実際のデータは配列を示している場合、配列は暗黙的にアンラップされます。

この緩和により、次の省略が可能になります。すなわち、[*]は、オブジェクト・アクセッサ.の前にあり、オブジェクト・フィールド名が後ろに続く場合は常に省略でき、本質的に何の変更も行われません。この逆もまた真です。[*]は、オブジェクト・アクセッサ.の前に常に挿入でき、本質的に何の変更も行われません。

つまり、特定のオブジェクトの配列の各要素のフィールドpropの値を表すオブジェクト・ステップ[*].prop.propとして省略でき、単一オブジェクトのprop値を表すかのように見えるオブジェクト・ステップ.propは、オブジェクト・アクセッサが適用された配列の各要素のprop値も表します。

これは重要な機能です。なぜなら、データが発展して特定のJSON値のかわりにこのような値の配列が使用される(またはこの逆)場合、コード内のパス式を変更する必要がないからです。

たとえば、フィールドtypeおよびnumberを伴う単一オブジェクトが値であるフィールドPhoneを持つオブジェクトがデータにもともと含まれるときに、このデータが発展して電話の配列を表す場合、単一の電話番号と一致するパス式$.Phone.numberを引き続き使用できます。パス式$.Phone.numberは、番号を選択して単一の電話オブジェクトと一致することも、各番号を選択して電話オブジェクトの配列と一致することもできます。

同様に、データに両方の種類の表現が混在する(単一の電話オブジェクトを使用するデータ・エントリ、および電話オブジェクトの配列を使用するエントリ、または両方を使用するエントリさえも存在する)場合、同じパス式を使用して、これらの異なる種類のエントリの電話情報にアクセスできます。

次に、SQL/JSONパス式の基本的な構文の項のパス式の例を、相当する式の説明とともに示します。

  • $.friends – 次のいずれかのフィールドfriendsの値。

    • (単一の)コンテキスト項目オブジェクト。

    • ($[*].friendsに相当)コンテキスト項目配列内の各オブジェクト。

  • $.friends[0].name - 次のオブジェクトのいずれかのフィールドnameの値。

    • コンテキスト項目オブジェクトのフィールドfriendsの値である配列の最初の要素。

    • ($.friends.nameに相当)コンテキスト項目オブジェクトのフィールドfriendsの値。

    • ($[*].friends.nameに相当)コンテキスト項目配列の各オブジェクトのフィールドfriendsの値。

    • ($[*].friends[0].nameに相当)コンテキスト項目配列の各オブジェクトのフィールドfriendsの値である各配列の最初の要素。

    コンテキスト項目はオブジェクトでもオブジェクトの配列でもかまいません。後者の場合、配列内の各オブジェクトはフィールドfriendsについて照合されます。

    フィールドfriendsの値はオブジェクトでもオブジェクトの配列でもかまいません。後者の場合、配列内の最初のオブジェクトが使用されます。

  • $.*[*].name – 次のオブジェクトのいずれかのフィールドnameの値。

    • コンテキスト項目オブジェクトのフィールドの配列値の要素。

    • ($.*.nameに相当)コンテキスト項目オブジェクトのフィールドの値。

    • ($[*].*.nameに相当)コンテキスト項目配列のオブジェクトのフィールドの値。

    • ($[*].*[*].nameに相当)コンテキスト項目配列のオブジェクトのフィールドの配列値の各オブジェクト。

17.2.3 パス式での否定

パス式での否定は、パスで一致したデータが複数である場合、混乱を招く可能性があります。いくつかの簡単な例を説明します。

negationフィルタ条件には、述語! (read "not")の後にフィルタ条件(括弧内には!( condition))が続きます。そのセマンティクスは、conditionが失敗する(falseを返す)場合は必ず成功します(trueを返します)。

SQL/JSON条件json_existsは、特定のJSONデータの存在をチェックします。また、json_valueなどのSQL問合せファンクションは、既存のJSONデータを検索して返します。述語!は、引数条件によって提示される存在がfalseであることを確認します。これは、存在をチェックすることを意味します。

中置の等しくない比較述語は!=または<>と記述可能で、その2つの引数が異なる(trueを返す)か同じ(falseを返す)かをチェックします。

これはすべて簡単なことのように聞こえますが、パス式で一致したデータが複数ある場合は、複雑になることがあります...

次のドキュメントについて考えてみます:

{"customer" : "A",
 "locations" : [ {"country" : "France"} ]}

{"customer"  : "B",
 "locations" : [ {"country" : "Germany"} ]}

{"customer"  : "C",
 "locations" : [ {"country" : "France"}, {"country" : "Spain"} ]}

{"customer"  : "D",
 "locations" : [ {"country" : "Spain"} ]}

{"customer"  : "E",
 "locations" : []}

{"customer"  : "F"}

次のパス式について考えてみます。


-- Path 1: locations that include the country of France.
$.locations?( @.country == "France" )

-- Path 2: locations that include a country other than France.
$.locations?( @.country != "France" )

-- Path 3: locations that do NOT include the country of France.
$.locations?( !(@.country == "France") )

-- Path 4: locations with one or more countries, NONE of which is France.
$.locations?( exists@.country && !(@.country == "France") )

-- Path 5: locations with a country other than France or Germany.
$.locations?( (@.country != "France") || (@.country != "Germany") )
  • パス1は、顧客AおよびCのドキュメントを返します。それらのlocations配列には、値が"France"のフィールドcountryを持つ要素があるためです。

  • パス2は、顧客BCおよびDのドキュメントを返します。それらのlocations配列には、値が"France"ないフィールドcountryを持つ要素(CおよびDの場合は"Spain"Bの場合は"Germany")があるためです。顧客Eのドキュメントを返すパスはありません。そのlocations配列にそのような要素(countryがFranceかどうか)がないためです。そのlocations配列にはまったく要素がありませんまた、locationsフィールドがないため、どのパスも顧客Fのドキュメントを返しません。

  • パス3は、顧客BDおよびEのドキュメントを返します。それらのlocations配列には、値が"France"であるフィールドcountryを持つ要素がないためです。パス3は、顧客AおよびCのドキュメントを返しません。それらのlocations配列には、値が"France"のフィールドcountryを持つ要素があるためです。また、locationsフィールドがないため、顧客Fのドキュメントは返されません。

    特に、パス2とパス3の結果が異なることに注意してください。France以外の国を含めることは、Franceという国を含めないことと同じではありません。パス2にはFranceではないcountryが必要ですが、パス3には値がFranceであるcountryが存在しないことが必要です。パス2にはCが含まれ、Eは除外されます。GermanyはFranceではなく、Eには国が存在しないためです。パス3にはEが含まれ、Cは除外されます。Eにはcountryがなく、CのlocationsにはFranceが含まれているためです。

  • パス4は、顧客BおよびDのドキュメントを返します。これはパス3と同じですが、フィールドcountryが存在する必要があり、顧客Eのドキュメントは除外されています。

  • パス5は、locationsフィールドのないFを除くすべての顧客のドキュメントを返します。!=テストは、比較するcountryフィールドがないため、顧客Eに対しては成功します。また、すべての国がFranceでもGermanyでもないため、countryフィールドを含むすべてのドキュメントが成功します。顧客Fのドキュメントにのみcountryフィールドがありません。

述語inを使用する次のパスも考慮してください。

-- Path 6: locations that include France or Germany.
@.locations?( @.country in ("France", "Germany") )

-- Path 7: locations that do NOT include France or Germany.
@.locations?( !(@.country in ("France", "Germany")) )

-- Path 8: locations that have one or more countries, NONE of which is France or Germany.
@.locations?( exists(@.country)
              &&
              !(@.country in ("France", "Germany")) )
  • パス6は、顧客ABおよびCのドキュメントを返します。locations配列には、値がinで示されるcountryフィールドのセット("France""Germany")があるためです — AおよびCに対する"France"Bに対する"Germany"

  • パス7では、FranceとGermanyの顧客のドキュメントが除外されます。これは、Spainのみにある顧客Dおよび空のlocations配列を持つ顧客Eのドキュメントを返します。locationsフィールドがないため、顧客Fのドキュメントは返されません。

  • パス8は、顧客Dのドキュメントのみを返します。顧客ABおよびCのドキュメントは、FranceまたはGermanyに事業所があるため除外されます。顧客Eのドキュメントはcountryフィールドがないため除外され、顧客Fのドキュメントはlocationsフィールドがないため除外されます。



脚注の凡例

脚注1: 範囲指定内のtoは、非公式に配列スライス演算子と呼ばれることがあります。
脚注2: フィルタ条件またはフィルタ式は、非公式に述語と参照されることがあります。ただし、フィルタ条件は、実際には引数への述語の適用です。
脚注3: 単一の値リストのin条件は、単一の等価比較と同等です。たとえば、@.z in ("a")@.z == "a"と同等です。値なしin条件(@.z in ()など)は、一致しません。
脚注4: 空の値リスト(値または変数なし)ではエラーは発生しませんが、一致することもありません。
脚注5: !=は、SQL/JSON標準比較述語<>のOracle別名です。