2 Oracle Database API for MongoDBを使用したアプリケーションの開発

アプリケーションを開発または移行する際の考慮事項 — (1)ハウツー情報と(2)相違点および実行可能な調整の説明との組合せ。

2.1 索引付けおよびパフォーマンス・チューニング

Oracle Databaseでは、索引、マテリアライズド・ビュー、インメモリー列記憶域、Exadataストレージ・セルのプッシュダウンなど、JSONデータに対する問合せを加速する複数のテクノロジが用意されています。パフォーマンス・チューニングに対するアプローチは、アプリケーションのニーズによって異なります。

Oracle Databaseのcompatibleパラメータが23以上の場合は、MongoDBの索引操作createIndexおよびdropIndexを使用して、関連するOracle索引を自動的に作成および削除できます。compatibleパラメータが23より小さい場合、このようなMongoDBの索引操作はサポートされていないため、これらは無視されます。

データベース・リリースに関係なく、(1) Oracle Database Actionsの使用のJSONページ(JSONコレクションの索引の作成を参照)、(2) Simple Oracle Document Access (SODA)または(3) SQLを使用して、必要なすべてのOracle Database索引を直接作成できます。Oracle Database JSON開発者ガイドJSONデータの索引を参照してください。JSONデータを索引付けする場合は、通常、JSONページを使用する方法が最も簡単になります。

ノート:

MongoDBでは、同じデータベース内の複数の異なるコレクションに同じ名前の索引を使用できます。これはOracle Databaseでは許可されません。索引の名前は、特定のデータベース・スキーマ(データベース)のすべてのコレクションで一意である必要があります。

たとえば、次のような購買オーダー・ドキュメントのordersというコレクションに索引を付けるとします:

{ "PONumber" : 1600,
  "User" : "ABULL",
  "LineItems" : [{ "Part"     : { "Description" : "One Magic Christmas",
                                  "UnitPrice"   : 19.95,
                                  "UPCCode"     : 13131092899 },
                   "Quantity" : 9.0 },
                 { "Part"     : { "Description" : "Lethal Weapon",
                                  "UnitPrice"   : 19.95,
                                  "UPCCode"     : 85391628927
                                },
                   "Quantity" : 5.0 } ]}

2つの重要なユース・ケースは、(1)シングルトン・スカラー・フィールド、つまりドキュメントに1回のみ出現するフィールドを索引付けすることと、(2)配列の要素内のオブジェクトにあるスカラー・フィールドを索引付けすることです。フィールドPONumberの値を索引付けすることが最初のケースの例です。フィールドUPCCodeの値を索引付けすることが2番目のケースの例です。

例2-1例2-2および例2-3に、最初のケースを示します。例2-5に、2番目のケースを示します。

また、SDO_GEOMETRYデータを返すファンクションベースのSQL索引を使用して、GeoJSON (空間)データを索引付けすることもできます。さらに、すべてのJSONデータに対して、JSON検索索引を作成してから、SQL/JSON条件json_textcontainsを使用して全文問合せを実行できます。

例2-1 Database ActionsのJSONページを使用したシングルトン・スカラー・フィールドの索引付け

JSONページを使用してフィールドPONumberの索引を作成するには、次の手順を実行します。

  1. コレクション名(orders)を右クリックし、ポップアップ・メニューから「Indexes」を選択します。

    json_page_create_index_001.pngの説明が続きます
    図json_page_create_index_001.pngの説明
  2. 「New Index」ページで、次の手順を実行します。

    • 「Properties」検索ボックスに*を入力します。

      これにより、「Properties」リストに、コレクション内のすべてのスカラー・フィールドへのパスが移入されます。これらのパスは、JSONデータ・ガイドを使用してコレクション・データをサンプリングすることで提供されます。Oracle Database SQL言語リファレンスJSON_DATAGUIDEを参照してください。

      オプション「Advanced」をオンにした場合は、スライダを右に押すと、リストされているスカラー・フィールドのタイプも表示されます。表示されるタイプは、コレクションをサンプリングすることで選択されるタイプです。ただし、索引付けする目的でフィールドのタイプを変更できます。

    • 索引付けするフィールドのパスを選択します。このケースでは、1つのスカラー・フィールドPONumberのみを索引付けするため、それを選択します。

      ノート: このダイアログ・ボックスでは、複数のパスを選択できます。複数のパスを選択すると、それらのパスのデータに対してコンポジット索引が作成されます。脚注1ただし、2つの異なるフィールドを個別に索引付けする場合は、1つのコンポジット索引(両方のフィールドをまとめて索引付けする)ではなく2つの索引を作成します。

      索引データ型は選択したパス上にあるデータの型によって自動的に決定されますが、「Automatic」を有効にしてデータ型を変更することで、これを制御できます。たとえば、特定のフィールドのコレクション・データにJSON番号があると、number型がリストされますが、これをVARCHAR2に編集して、文字列値として強制的に索引付けできます。

    フィールドPONumberの値は一意です。コレクションで同じ数値がフィールドに複数回使用されないため、「Unique」の索引を選択します。

    「Index Nulls」も選択します。これは、ORDER BYを使用して結果をソートする問合せに必要です。これにより、すべてのドキュメントが索引内にエントリを持つようになります。

    フィールドPONumberの値はJSON数値です。つまり、数値の比較に索引を使用できます。

    json_page_create_index_002.pngの説明が続きます
    図json_page_create_index_002.pngの説明

例2-2 SODAを使用したシングルトン・スカラー・フィールドの索引付け

索引付けをサポートするSODA実装(プログラミング言語またはフレームワーク)ごとに、索引を作成する方法が用意されています。それらはすべて、SODA索引仕様を使用して、作成する索引を定義します。たとえば、SODA for RESTでは、HTTP POSTリクエストを使用してURI引数action=indexを渡し、POST本体に索引仕様を提供します。

これは、フィールドPONumberpoNumIdxという一意の索引に対するSODA索引仕様です。

{ "name" : "poNumIdx",     
  "unique" : true,
  "fields" : [ { "path"     : "PONumber",
                 "dataType" : "NUMBER",
                 "order"    : "ASC" } ] }

例2-3 SQLを使用したシングルトン・スカラー・フィールドの索引付け

Database Actionsを使用すると、このSQLコードで表ordersの列dataにフィールドPONumberの索引を作成できます。これは、SQL/JSONファンクションjson_valueを使用して、フィールドPONumberの値を抽出します。

ドキュメントにPONumberフィールドがない場合や複数ある場合、コードはERROR ON ERROR処理を使用してエラーを生成します。

索引付けするフィールドを識別するパス式で項目メソッドnumberOnly()を使用して、フィールド値が数値であることを確認します。

number()では、数値以外のフィールドを数値に変換することもできるため、メソッドnumber()のかわりにメソッドnumberOnly()を使用します。たとえば、number()は、PONumberの文字列値"42"を数値の42に変換します。

同様に厳密に型チェックをする、このような"only"付きの項目メソッドとして、stringOnly()dateTimeOnly()およびbinaryOnly()などが、文字列、日付およびバイナリ値用にそれぞれ用意されています。

CREATE UNIQUE INDEX "poNumIdx" ON orders
  (json_value(data, '$.PONumber.numberOnly()' ERROR ON ERROR))

関連項目:

Oracle Database JSON開発者ガイドSQL/JSONパス式の項目メソッド

例2-4 配列の要素内のフィールドに対する複数値索引の作成

Oracle Database 21c以降では、配列内のオブジェクト(要素または要素内の下位レベルのオブジェクト)にフィールドが含まれるためにドキュメントに複数回発生する可能性があるフィールドの値に対して複数値索引を作成できます。

この例では、フィールドUPCCodeの値のコレクションordersに複数値索引を作成します。この例では、項目メソッドnumberOnly()を使用するため、数値のUPCCodeフィールドにのみ適用されます。

CREATE MULTIVALUE INDEX mvi_UPCCode ON orders o
      (o.data.LineItems.Part.UPCCode.numberOnly());

関連項目:

Oracle Database JSON開発者ガイドJSON_EXISTSの複数値ファンクションベースの索引の作成

例2-5 配列の要素内のフィールドに対するマテリアライズド・ビューおよび索引の作成

Oracle Database 21cより前は、配列内のオブジェクト(要素または要素内の下位レベルのオブジェクト)にフィールドが含まれているためにドキュメントに複数回出現する可能性がある、UPCCodeなどのフィールドに対して複数値索引を作成できません。

かわりに、この例のように、索引付けするデータを抽出するマテリアライズド・ビューを作成してから、そのビュー・データに対してファンクション索引を作成できます。

この例では、列upccodeを含むマテリアライズド・ビューmv_UPCCodeが作成されています。これは、表ordersの列dataの配列LineItemsにあるPartオブジェクト内のフィールドUPCCodeの投影です。次に、マテリアライズド・ビュー(mv_UPCCode)の列upccodeに対して索引mv_UPCCode_idxが作成されます。

CREATE MATERIALIZED VIEW mv_UPCCode
  BUILD IMMEDIATE
  REFRESH FAST ON STATEMENT WITH PRIMARY KEY
  AS SELECT o.id, jt.upccode
       FROM orders o,
            json_table(data, '$.LineItems[*]'
                       ERROR ON ERROR NULL ON EMPTY
              COLUMNS (upccode NUMBER PATH '$.Part.UPCCode')) jt;
 
CREATE INDEX mv_UPCCode_idx ON mv_UPCCode(upccode);

問合せオプティマイザは、リクエストされたデータにSQL文が最も効率的にアクセスする方法を見つける役割を担います。特に、問合せ対象のデータに適用される索引を使用するかどうかと、複数の索引が関連している場合にどの索引を使用するかを決定します。ほとんどの場合は、オプティマイザの判断に委ねることが最適なガイドラインとなります。

ただし、問合せによっては特定の索引を選択するように指定することもできます。これは、索引名を指定するMongoDB hintを使用して行うことができます。(オラクル社はMongoDB索引仕様の使用をサポートしていません。単に索引名を指定します。)

たとえば、この問合せでは、例2-1で作成したコレクションordersの索引poNumIdxを使用します。

db.orders.find({"PONumber":1600}).hint("poNumIdx")

あるいは、MongoDBヒント構文のOracle拡張機能である例による問合せ(QBE)演算子$nativeでOracle SQLヒントを渡して、使用する索引を指定することもできます。

$nativeの引数には、SQLヒント文字列(つまり、SQLコメント構文/*+...*/で囲んでいない実際のヒント・テキスト)と同じ構文があります。$nativeを使用して任意のSQLヒントを渡すことができます。特に、ヒントMONITORを使用して、現在のSQL文のモニタリングを有効にできます。次のコードは、それをfind()問合せに対して実行します。

db.orders.find().hint({"$native":"MONITOR"})

関連項目:

2.2 ユーザー、認証および認可

Oracle Databaseのセキュリティは、MongoDBのセキュリティとは大きく異なります。Oracle Database API for MongoDBのセキュリティ・モデルについて説明します(様々な操作を実行するためのユーザーの作成、ユーザーの認証およびユーザーの認可)。

MongoDBの場合、デフォルトではユーザー認証および認可チェックが有効になっていません。Oracle Databaseでは常に認証が必要であり、リクエストされた操作の実行が接続ユーザーに認可されていることを常に確認します。認証に有効なユーザー名およびパスワードを指定する必要があります。

Oracle Database API for MongoDBでは、認証用に次の接続オプション値のみがサポートされています。

  • オプションauthMechanismPLAIN値(プレーン・テキスト認証)。特に、SCRAM-SHA-*認証方式はサポートされていません。

  • オプションauthSource$external値。(認証方式がPLAINの場合、MongoDBには常にこれが必要です。)

Oracle Database API for MongoDBは、Oracle Databaseのユーザー、権限およびロールを利用します。MongoDBクライアントまたはドライバを使用して、これらのユーザーおよびロールを追加または変更することはできません。かわりに、SQLまたはOracle Database Actionsを使用してこれを実行できます。APIを使用するために必要なOracle Databaseの最小ロールは、CONNECTRESOURCEおよびSODA_APPです。

Oracle REST Data Services (ORDS)で使用する場合は、ユーザー(データベース・スキーマ)も有効にする必要があります。これは、PL/SQLプロシージャORDS.enable_schemaを呼び出すか、Oracle Database Actionsを使用して実行できます。

MongoDBの場合、データベースとはコレクションのセットのことです。Oracle Database API for MongoDBの場合、これはOracle Databaseのスキーマに対応します。

ノート:

Oracle API for MongoDBを使用してデータベースを削除しても、基礎となるデータベース・スキーマは削除されません。かわりに、スキーマ内のすべてのコレクションを削除します。

管理ユーザーは、SQLを使用してスキーマを削除できます(たとえば、Autonomous Oracle DatabaseでDatabase Actionsを使用します)。

APIの場合、ユーザー名はデータベース・スキーマ名である必要があります。この名前は大文字と小文字が区別されず、英字以外の文字(数字を含む)で開始できず、セキュアなパスワードを指定する必要があります。

APIのユーザーは、通常、そのスキーマ(ユーザー名はスキーマ名です)内でのみ操作を実行できます。このような操作の例として、新しいコレクションの作成、ドキュメントの読取りと書込み、索引の作成などがあります。

管理ユーザーが存在しないデータベース・スキーマ(ユーザー)にデータを挿入しようとすると、そのスキーマはスキーマ専用アカウントとして自動的に作成されます。つまり、パスワードがなく、ログインできません。新しいスキーマには、SODA_APPCREATE SESSIONCREATE TABLECREATE VIEWCREATE SEQUENCECREATE PROCEDUREおよびCREATE JOBの権限が付与されます。また、スキーマには無制限の表領域割当て容量も与えられ、Oracle REST Data Services (ORDS)を使用できるようになります。

APIの通常ユーザーの場合、現在のMongoDBデータベースを別のデータベースに切り替えるMongoDBシェル・コマンド(use <database>など)は通常サポートされていません。別のデータベース・スキーマに切り替えると、エラーが発生します。

ただし、管理ユーザー(CREATE USERALTER USERDROP USERのいずれの権限も持つユーザー)は、新規ユーザー(データベース・スキーマ)を作成し、任意のユーザーとして任意のスキーマにアクセスできます。

管理ユーザーは、次の操作を実行できます。

  • 他のユーザーのスキーマを使用します。

    現在のユーザー以外のスキーマにアクセスする場合にプロキシ接続を使用できます。たとえば、管理ユーザーとして接続しているユーザーは、other_userとして直接接続する場合と同じロールと権限を使用して、スキーマother_userで操作を実行できます。

  • 新規ユーザー(スキーマ)を作成します。

    たとえば、まだ存在しないスキーマtotoに管理ユーザーがコレクションを作成しようとすると、そのスキーマ(ユーザー)が自動的に作成されます。

管理ユーザーの使用を本番アプリケーションでは許可しないことをお薦めします。かわりに、アプリケーションには通常のユーザーとして、最小限の権限で接続してください。特に、特定のスキーマ(ユーザー)に固有のMongoClientを使用して、データベースにアプリケーションを接続します。

関連項目:

  • Oracle Autonomous Database Serverlessの使用Autonomous Databaseでのユーザーの作成

  • Oracle Autonomous Database Serverlessの使用Autonomous Databaseでのユーザー・ロールおよび権限の管理

  • SQLを使用してデータベース・スキーマ(データベース・ユーザーとも呼ばれる)を作成する方法の詳細は、Oracle Database SQL言語リファレンスCREATE USER

  • SQLを使用してロールをデータベース・スキーマに付与する方法の詳細は、Oracle Database SQL言語リファレンスGRANT

  • Oracle Database API for MongoDBでのAutonomous Database (Autonomous JSON Databaseを含む)の使用の詳細は、Oracle Autonomous Database Serverlessの使用Oracle Database API for MongoDBの使用。ここでは、セキュリティや接続など、APIで使用するためのデータベースの構成について説明します。

  • ORDSでのデータベース・スキーマの有効化については、『Oracle REST Data Services開発者ガイド』ORDS.ENABLE_SCHEMAを参照してください

2.3 MongoDBからOracle Databaseへのアプリケーション・データの移行

JSONデータをMongoDBからエクスポートしてOracle Databaseにインポートする方法をいくつか説明します。移行に関する考慮事項について説明します。

次のいずれかの方法でアプリケーション・データを移行できます。

  • MongoDBコマンドライン・ツールmongoexportおよびmongoimportを使用します。

    mongoexportはMongoDBインスタンスからファイル・システムにデータをエクスポートし、mongoimportはエクスポートされたデータをファイル・システムからOracle Databaseにインポートします。mongoimportを使用する場合は、データベース接続情報を指定します。例2-6にこれを示します。

  • CompassなどのMongoDBツールをOracle Databaseに接続した後、そのツールを使用してOracle Databaseにデータをインポートします。JSONコレクションの名前を選択し、「ADD DATA」を選択します。

    これにより、コレクション・データが含まれているJSONファイルを参照してインポートするためのポップアップ・ダイアログ・ボックスが表示されます。MongoDB Compassを参照してください。

    mongodb_compass.pngの説明が続きます
    図mongodb_compass.pngの説明
  • JSONデータをファイル・システムにエクスポートした後、それをOracle Cloud Object Storeにインポートし、PL/SQLプロシージャDBMS_CLOUD.copy_collectionを使用してそこからコレクションにロードします。例2-7にこれを示します。

    これによりデータがパラレルで処理されるため、通常はmongoimportより高速です。

  • JSONドキュメントをMongoDBへの接続から読み取ってOracle Databaseへの接続に書き込むプログラムを記述します。

例2-6 mongoexportおよびmongoimportを使用したJSONデータのOracle Databaseへの移行

この例では、コレクションsalesをMongoDBからファイル・システム・ファイルsales.jsonにエクスポートします。次に、そのファイルからOracle Databaseにコレクションsalesとしてデータをインポートします。ユーザーは、データベース・スキーマ<user>としてパスワード<password>を使用してホスト<host>に接続されています。

mongoexport --collection=sales --out sales.json

mongoimport 'mongodb://<user>:<password>@<host>:27017/<user>?authMechanism=PLAIN&authSource=$external&ssl=true' --collection=sales --file=sales.json

ノート:

URIパーセント・エンコーディングを使用して、接続文字列URIの任意の予約文字(特にユーザー名とパスワードの文字)を置換します。予約文字とそのパーセント・エンコーディングは次のとおりです。

! # $ % & ' ( ) * +
%21 %23 %24 %25 %26 %27 %28 %29 %2A %2B
, / : ; = ? @ [ ]
%2C %2F %3A %3B %3D %3F %40 %5B %5D

たとえば、ユーザー名がRUTHで、パスワードが@least1/2#?の場合、サーバー<server>へのMongoDB接続文字列は次のようになります。

'mongodb://RUTH:%40least1%2F2%23%3F@<server>:27017/ruth/ ...'

使用するツールまたはドライバによっては、URI接続文字列の一部としてではなく、別のパラメータとしてユーザー名とパスワードを指定できる場合があります。その場合、含まれている予約文字をエンコードする必要はありません。

関連項目:

関連項目:

Oracle Database API for MongoDBでのAutonomous Database (Autonomous JSON Databaseを含む)の使用の詳細は、Oracle Autonomous Database Serverlessの使用Oracle Database API for MongoDBの使用。ここでは、セキュリティや接続など、APIで使用するためのデータベースの構成について説明します。

例2-7 DBMS_CLOUD.COPY_COLLECTIONを使用したコレクションへのJSONデータのロード

この例では、PL/SQLプロシージャDBMS_CLOUD.copy_collectionを使用して、Oracle Cloud Object Storeから新しいコレクションnewCollectionにデータをロードします。データはMongoDBからファイル・システムにエクスポートされ、そこからパラメータfile_uri_listの値として渡されるオブジェクト・ストアの場所にインポートされたと想定しています。

copy_collectionパラメータFORMATとして渡される値は、フィールドrecorddelimiterおよびtypeを持つJSONオブジェクトです。

  • フィールドrecorddelimiterでは、入力データのレコードを改行文字で区切ることを指定しています。レコードごと、つまり改行区切りの入力データ内の行ごとに1つのJSONドキュメントが作成されます。

  • フィールドtypeでは、入力JSONデータにEJSON拡張オブジェクトを含めることができ、それらを解釈する必要があることを指定しています。

パラメータFORMATの詳細は、Oracle Autonomous Database Serverlessの使用DBMS_CLOUDパッケージ・フォーマット・オプションを参照してください。

BEGIN
  DBMS_CLOUD.copy_collection(
    collection_name => 'newCollection',
    file_uri_list   => 'https://objectstorage.../data.json',
    format          => json_object(
                         'recorddelimiter' : '''\n''',
                         'type'            : 'ejson'));
END;
/

関連項目:

2.4 MongoDB集計パイプラインのサポート

Oracle Database API for MongoDBでは、MongoDB集計パイプライン、つまりMongoDBコマンドaggregateがサポートされています。パイプライン・コードを使用して、一連の操作として問合せを実行できます。このプロシージャ型アプローチの宣言型の代替としてSQLを使用することもできます。

MongoDBの集計パイプラインは、基本的にSQL機能の部分的なエミュレーションです。MongoDBでは、ソート、グループ化、順序付けなどの操作をパイプラインの個別のステップとして表します。このアプローチはプロシージャ型です。問合せを一連の操作として実行する方法を指定します。

一方、SQLは宣言型です。必要な問合せ結果を指定すると、オプティマイザは、使用可能な索引、データ統計、コスト見積りなどに基づいて最適な実行プランを選択します。つまり、実行する処理を指定し、ユーザーではなくオプティマイザが実行すべき方法を決定します。

Oracle DatabaseによるJSONデータのSQLサポートには、ドキュメントやコレクションの操作、およびJSONデータとJSON以外のデータ(リレーショナル、空間、グラフなど)の結合が含まれます。Oracle Database API for MongoDBのユーザーとして、特定の操作を手動で指定および順序付けすることなく、SQLをJSONデータに直接適用できます。

ただし、MongoDB集計パイプライン・コードを使用する場合、MongoDB APIはパイプラインのステージおよび操作を同等のSQLコードに自動的に変換し、オプティマイザは可能なかぎり最適な実行プランを選択します。APIでは、MongoDB集計パイプラインのステージおよび操作のサブセットがサポートされています。詳細は、集計パイプライン演算子を参照してください。

MongoDBとは異なり、Oracle Databaseではソート、結合またはグループ化されるデータのサイズに制限がありません。任意の数のコレクションにわたる何百万ものドキュメントを対象としたレポート作業や分析作業に使用できます。

JSONデータのOracle Database簡易ドット表記法、または標準のSQL/JSONファンクションjson_valuejson_queryおよびjson_tableを使用して、JSONデータからレポートや分析の目的で値を抽出できます。SQL/JSON生成ファンクションを使用して、リレーショナル・データおよびその他の種類のデータ(空間データやグラフ・データなど)をJSONデータに変換できます。1つのSQL FROM句を使用して、複数の表およびコレクションにあるJSONデータを結合できます。

MongoDB集計パイプラインは、1つ以上のコレクションからJSONドキュメントに対して操作を実行します。これは、連続するステージで構成され、それぞれがドキュメント操作を実行し、結果のドキュメントを次のステージに渡してさらに処理します。どのステージの操作でも、前のステージから渡されたドキュメントをフィルタリングしたり、次のステージのために変換(更新)したり、新しいドキュメントを作成したりできます。変換には、複数のドキュメントのフィールド値を組み合せることができる$avg (平均)などの集計演算子(アキュムレータとも呼ばれる)を使用できます。

パイプライン内の各ステージは、JSON値である集計式で表されます。詳細は、MongoDB集計パイプラインのドキュメントを参照してください。

宣言SQLコードを使用して、集計パイプラインを使用して行うことを実現できます。これは、Oracle Databaseパラメータcompatible23より小さい場合に特に関連します。この場合、ほとんどのMongoDB集計パイプラインはサポートされていません。例2-8にこれを示します。

例2-8 MongoDB集計パイプライン・コードのかわりにSQLコードを使用

この例では、郵便番号別に平均売上を計算します。まず、これを実行するMongoDB集計パイプライン式を示し、次に、同等のSQLコードを示します。

MongoDB集計パイプライン:

次のコードは、結果の計算方法をMongoDBに指示します。これには実行順序が指定されています。

db.sales.aggregate(
  [{"$group" : {"_id"    : "$address.zip",
                "avgRev" : {"$avg" : "$revenue"}}},
   {"$sort"  : {"avgRev" : -1}}])

SQL:

次のコードは、出力表現のグループ化と順序を宣言して指定します。実行順序など計算の実行方法は指定しません。簡単に言えば、結果は郵便番号別にグループ化され、平均収益値の降順で表示されます。問合せは、郵便番号(文字列)および平均収益(数値)のスカラー値が含まれる2列の行を返します。


SELECT s.data.address.zip.string(),
       avg(s.data.revenue.number())
  FROM sales s
  GROUP BY s.data.address.zip.string()
  ORDER BY 2 DESC;

次の問合せは似ていますが、結果がJSONオブジェクトの行となります。各行には、郵便番号を表す文字列フィールドzipと、平均収益を表す数値フィールドavgRevがあります。SQL/JSON生成ファンクションjson_objectは、引数であるSQL式の評価結果からJSONオブジェクトを構成します。

SELECT json_object('zip'    : s.data.address.zip.string(),
                   'avgRev' : avg(s.data.revenue.number()))
  FROM sales s
  GROUP BY s.data.address.zip.string()
  ORDER BY avg(s.data.revenue.number()) DESC;

2.5 MongoDBドキュメントおよびOracle Database

ここでは、MongoDBで使用されるJSONドキュメントとOracle Databaseで格納され使用されるJSONドキュメントとの関係を示します。

ノート:

このトピックは、MongoDBから移行してOracle Databaseに格納するJSONドキュメントに当てはまります。JSONリレーショナル二面性ビューによって生成/サポートされるJSONドキュメントには当てはまりません。MongoDB互換の二面性ビューについては、Mongo DB APIとJSONリレーショナル二面性ビューの使用を参照してください。

既存のアプリケーションとそのデータをMongoDBからOracle Databaseに移行することも、MongoDB上のアプリケーションと同じまたは類似したデータを使用する新しいアプリケーションをOracle Database上で開発することもできます。どちらの場合も、JSONデータはドキュメントに格納されます。

MongoDBで使用されるドキュメントとOracle Databaseで使用されるドキュメントの相違点を全般的に理解しておくと便利です。特に、MongoDBドキュメントをOracle Databaseで使用できるようにするために、インポートするMongoDBドキュメントがどのように処理されるかを理解するために役立ちます。

ここに示す情報に含まれる詳細な内容は、概要を把握するためのみこのトピックを参照する場合には無視できます。ただし、記載内容を認識しておくと、将来必要に応じて参照するときに役立ちます。

MongoDBドキュメントのコレクションをインポートすると、各ドキュメントのキーおよびコンテンツがOracle Databaseに適したフォームに変換されます。

MongoDBドキュメントには、BSONというネイティブのバイナリJSON形式があります。Oracle Databaseドキュメントには、OSONというネイティブのバイナリJSON形式があります。したがって、MongoDBドキュメントに加えられる変更の1つに、そのバイナリ形式をBSONからOSONに変換するというものがあります。この変換は、ドキュメントのキーとコンテンツの両方に適用されます

ノート:

Oracle Database API for MongoDBの場合、MongoDB自体に関しては、ステージで入力が受信され、BSONデータ(つまり、MongoDB形式のバイナリJSONデータ)の形式で出力が生成されます。

ドキュメント・キー: 相違点と変換(23aiより前のOracle Database)

この項は、23aiより前のOracle Databaseリリースにのみ当てはまります。

MongoDBの場合、ドキュメントを識別する一意キーはドキュメント自体の必須フィールド_idの値です。23aiより前のOrace Databaseリリースの場合、ドキュメントを識別する一意キーはドキュメントから分離され、キーはドキュメントを格納する列とは別のデータベース列に格納されます。キー列はidという名前で、コレクション・データを格納する表の主キー列です。

23aiより前のOracle Databaseにコレクションをインポートすると、Oracle Database API for MongoDBによって、MongoDBドキュメント内のフィールド_idの値からid列の値が作成されます。MongoDBフィールド_idには、複数の異なるデータ型の値を指定できます。そのフィールドに対応するOracle Databaseのid列は、常にSQLデータ型VARCHAR2 (文字データ、つまり文字列)になります。

インポートしたドキュメントの_idフィールドは、インポート中およびそれ以降変更されません。Oracle Databaseではこれは使用されず、かわりに列idが使用されます。ただし、これも変更されないため、アプリケーションで任意にそのフィールドを使用することは引き続き可能です。ドキュメントのフィールド_idが変更されることはありません。アプリケーションでも変更(削除または更新)できません。

SQLまたはSimplified Oracle Document Access (SODA)を使用してドキュメントを操作する必要がある場合は、列idを直接使用できます。たとえば、その主キー列を使用すると、JSONデータを他のデータベース・データと簡単に結合できます。MongoDBからのインポートによって生成されるドキュメントはSODAドキュメントです(ネイティブ・バイナリOSONデータが含まれています)。

ドキュメントとドキュメント・キーの分離によってもたらされる次の考慮事項に注意してください。

  • MongoDBからインポートされたすべてのドキュメントには引き続き_idフィールドがありますが、23aiより前のOracle Databaseの場合、JSONコレクション内のドキュメントには_idフィールドが必要ありません。また、23aiより前のOracle Databaseの場合、ドキュメントとそのキーは分離されているため、MongoDBからインポートされたドキュメント以外のドキュメントに、ドキュメント・キーとの関連がない_idフィールドが含まれる可能性があります。

  • MongoDBでは様々なタイプの_id値が許可され、これらはすべて文字列値(VARCHAR2)に変換されるため、なんらかの理由でコレクションに_id値が"123" (JSON文字列)および123 (JSON数値)のドキュメントが含まれている場合、そのコレクションをインポートすると、重複キー・エラーが発生します。これは、これらの列id値がそれぞれ同じ文字列値に変換されるためです。

フィールド_idのBSON値は、表2-1に従ってVARCHAR2列のid値に変換されます。_idフィールド値が表にリストされていないタイプである場合は、生成されたObjectId値に置き換えられた後、id列値に変換されます。

表2-1 BSONフィールド_id値の列ID VARCHAR2値への変換

_idフィールド・タイプ ID列VARCHAR2値
Double 標準的な数値形式文字列
32-bit integer 標準的な数値形式文字列
64-bit integer 標準的な数値形式文字列
Decimal128 標準的な数値形式文字列
String 変換なし(文字のエスケープなしなど)
ObjectId 小文字の16進数文字列
Binary data (UUID) 小文字の16進数文字列
Binary data (UUID以外) 大文字の16進数文字列

VARCHAR2値の標準的な数値形式は次のとおりです。

  • 入力数値に小数部分がなく(整数)、40桁以下で表示できる場合は、整数として表示されます。必要に応じて、指数で表記されないように後続のゼロが使用されます。たとえば、1E+9ではなく1000000000が使用されます。

  • 入力数値に小数部がある場合、数値は小数点記号を使用して40桁以下で表示されます。必要に応じて、指数で表記されないようにするために、ゼロが使用されます。たとえば、1E-5ではなく0.00001が使用されます。

  • 入力数値を変換したときに、40桁の形式では桁精度が失われる場合は、かわりに数値が指数で表示されます。これは、数値が整数であっても、絶対値が非常に小さいか非常に大きい数値の場合に発生する可能性があります。たとえば、1の後にゼロが100個続くことを避けて1E100が使用されます。

実際、この標準的な数値形式では、ほとんどの場合、列idに対して数値の_idフィールド値は見やすく整形されたVARCHAR2値になります。指数を使用する形式は、必要な場合にのみ使用され、通常頻繁には使用されません。

ドキュメント・コンテンツ変換

2つの一般的な考慮事項:

  • BSON形式では、同じオブジェクト内でフィールド値の重複が許可されます。OSON形式では許可されません。OSONに変換する場合、BSONデータで重複フィールドが検出されるとエラーが発生します。

  • OSON形式では、オブジェクト内のフィールドの順序が認識されません。アプリケーションが(JSON標準に準拠して)特定の順序に依存したり、特定の順序を想定することはできません。BSON形式は、オブジェクト・フィールドの順序を維持します。アプリケーションでは順序が変更されないことに依存できます。

表2-2に、スカラーBSONデータをスカラーOSONデータに変換する際に適用される型マッピングを示します。使用されるOSONスカラー型は、特に記述されていないかぎり、SQLデータ型です。リストされていないBSON型は変換されません。かわりに、それが検出されるとエラーになります。これには、BSON型regexJavaScriptなどがあります。

表2-2 JSONスカラー型変換: BSONからOSON形式

BSON型 OSON型脚注2 ノート
Double BINARY_DOUBLE NA
32-bit integer NUMBER (Oracleの数値) intのフラグが付けられます。
64-bit integer NUMBER (Oracleの数値) longのフラグが付けられます。
Decimal128 NUMBER (Oracleの数値) decimalのフラグが付けられます。ノート: この変換は失われる可能性があります。
Date TIMESTAMP WITH TIME ZONE 常にUTCタイムゾーン。
String VARCHAR2 常に文字セットAL32UTF8 (Unicode UTF-8)になります。
Boolean BOOLEAN 初期化パラメータcompatibleの値が23以上の場合のみサポートされます。(23aiより前のリリースでは、Oracle SQLのBOOLEAN型はありません。)
ObjectId ID (RAW(12)) NA
Binary data (UUID) ID (RAW(16)) NA
Binary data (UUID以外) RAW NA
Null NULL JSON nullに使用されます。

脚注2 これらは、特に記述されていないかぎり、SQLデータ型です。

関連項目:

2.6 MongoDBとOracle Databaseとのその他の相違点

MongoDBとOracle Databaseとの様々な相違点について説明します。これらの相違点には、通常、他のトピックでは触れていません。アプリケーションをOracle Databaseに移行する場合や、MongoDBコマンドを使用するOracle Database向けの新しいアプリケーションを開発する場合に、これらの相違点を考慮してください。

  • MongoDBでは、JSONオブジェクトのフィールドが順序付けられます。Oracle Databaseでは順序付けられません。たとえば、フィールド_idは必ずしもオブジェクトの最初のフィールドではありません。アプリケーションでは、特定のフィールド順序を想定したり、その順序に依存しないようにする必要があります。JSON言語標準では、オブジェクト・フィールドは順序付けされず、配列要素のみが順序付けされます。Oracle Database JSON開発者ガイドJSONの構文とそれで表されるデータを参照してください。

  • MongoDBでは、フィールド_idの値をJSONオブジェクトにすることができます。Oracle Database API for MongoDBでは、フィールド_idObjectIdStringDouble32-bit integer64-bit integerDecimal128Binary data (UUIDのサブタイプ)の各BSON型のみがサポートされています。他の型にするとエラーが発生します。BSON型を参照してください。

    _idのオブジェクト値を必要とする既存のアプリケーションを移行する場合は、データ内のフィールド_idの値を新しいフィールドにコピーし、_idの文字列値を使用することを検討してください。

  • MongoDBトランザクションに関する読取りおよび書込みの問題は、Oracle Databaseには該当しません。Oracle DatabaseトランザクションはACIDに完全に準拠しているため、信頼性(原子性、一貫性、分離および永続性)があります。ACID準拠により、トランザクションの処理中に障害が発生しても、データの正確性と一貫性が保たれます。

  • Oracle API for MongoDBは、次のMongoDBトランザクション機能をサポートしていません。

    • トランザクション内にcreateCollectionなどのDDL操作を含めること。トランザクション内にコレクションまたは索引を作成しようとすると、エラーが発生します。

    • 複数のデータベースにまたがる操作を含めること。トランザクション内のすべての操作は、単一のデータベース(スキーマ)に限定する必要があります。そうしないと、エラーが発生します。

  • エラー発生時に再試行可能な書込みまたはコミット。

    MongoDBでretryWrite操作を行うとエラーが発生します。デフォルトでretryWriteがオンになっているドライバを使用する場合は、接続文字列にretryWrites=falseを設定してオフにしてください。

  • Oracle DatabaseとMongoDBでは、読取り分離レベルと一貫性レベルが異なります。Oracle Database API for MongoDBでは、Oracle Databaseの概要データの同時実行性と整合性で説明されているように、読取りコミットの一貫性を使用します。

  • Oracle Database API for MongoDBは、PLAIN (LDAP SASL)認証メカニズムのみをサポートし、Oracle Databaseの認証および認可に依存します。

  • Oracle Databaseでは、コマンド(findなど)のMongoDB collationフィールドはサポートされません。フィールドcollationを使用すると、エラーが発生します。Unicodeバイナリ照合順序を使用して値が照合されます。

  • MongoDBでは、同じデータベース内の複数の異なるコレクションに同じ名前の索引を使用できます。これはOracle Databaseでは許可されません。索引の名前は、特定のデータベース・スキーマ(データベース)のすべてのコレクションで一意である必要があります。

  • MongoDBのドキュメントの最大サイズは16 MBです。Oracle Databaseの場合(したがってMongoDB APIの場合)、最大サイズは32 MBです。

2.7 他のユーザー(データベース・スキーマ)が所有するコレクションへのアクセス

そのスキーマにログインすると、別のユーザー(データベース・スキーマ)が所有するMongoDB APIコレクションに直接アクセスできます。そのコレクションが自分のスキーマ内のコレクションにマップされている場合は、そのスキーマにログインせずに、別のユーザーが所有するコレクションに間接的にアクセスできます。

JSONドキュメントのMongoDB APIコレクションは、(1)コレクション内のJSONドキュメントを含むコレクション・バッキング表と、(2)データ・ディクショナリに格納され、様々なコレクション構成プロパティを指定する、JSON形式のコレクション・メタデータで構成されます。バッキング表は、指定されたデータベース・ユーザー/スキーマに属しています。メタデータはデータベース・データ・ディクショナリに格納されます。

マップされたコレクションは、既存の表に加えて定義(マップ)されるコレクションで、任意のデータベース・スキーマに属し、他の1つ以上のコレクションを補足することもできます。

バッキング表に対する異なる権限またはロールを様々なユーザーに付与することで、コレクション(マップされたコレクションを含む)に対するどの操作をそれらのユーザー(スキーマ)に対して許可するかを制御できます。

例2-9にこれを示します。

例2-9 あるスキーマでコレクションを作成し、別のスキーマでそのコレクションに1つのコレクションをマッピング

この例では、ユーザーjohnはコレクションjohn_collを(データベース・スキーマjohnに)作成し、そのコレクションにドキュメントを追加します。次に、ユーザーjohnは、コレクションjohn_collのバッキング表に対するいくつかのアクセス権限をユーザーjanetに付与します。

次に、ユーザーjanetは、新しいコレクションjanet_collを(スキーマjanetで)スキーマjohnのコレクションjohn_collにマップします。(元のコレクションとマップされたコレクションは、異なる名前(john_colljanet_collなど)を持つ必要はありません。両方とも同じ名前を持つことができます。)

次に、ユーザーjanetは、スキーマjanetで使用可能なコレクションをリストし、マップされたコレクションjanet_collのコンテンツを読み取ります。このコンテンツは、コレクションjohn_collのコンテンツと同じです。

(mongoshに送信されるコマンドはそれぞれ1行(文字列)ですが、わかりやすくするために、ここでは複数行にわたって続けて表示しています。)

ノート:

Oracle Database API for MongoDBへの入力およびそこからの出力の例では、シェルmongoshの構文を使用します。

1.ユーザーjohnとしてデータベースに接続している場合は、PL/SQLコードを実行して、表john_collに基づくコレクションjohn_collを作成します。create_collectionの2番目の引数は、MongoDB互換コレクションに必要なメタデータです。(バッキング表名はコレクション名から導出されます。コレクション表のデフォルトのネーミングを参照してください。)

DECLARE
  col SODA_COLLECTION_T;
BEGIN
  col := DBMS_SODA.create_collection(
           'john_coll', 
           '{"contentColumn"      : {"name"       : "DATA",
                                     "sqlType"    : "BLOB",
                                     "jsonFormat" : "OSON"}, 
             "keyColumn"          : {"name"             : "ID", 
                                     "assignmentMethod" : "EMBEDDED_OID",
                                     "sqlType"          : "VARCHAR2"}, 
             "versionColumn"      : {"name" : "VERSION", "method" : "UUID"}, 
             "lastModifiedColumn" : {"name" : "LAST_MODIFIED"}, 
             "creationTimeColumn" : {"name" : "CREATED_ON"}}');
END;

2.ユーザーjohnとしてシェルmongoshを使用してデータベースに接続し、そのスキーマ内のコレクション(Johnのコレクション)をリストし、コレクションjohn_collにドキュメントを挿入して、挿入の結果を表示します。

mongosh 'mongodb://john:...
@MQSSYOWMQVGAC1Y-CTEST.adb.us-ashburn-1.oraclecloudapps.com:27017/john
?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true'
john> show collections;

出力:

john_coll
john> db.john_coll.insert({"hello" : "world"});
john> db.john_coll.find()

出力:

[ { _id: ObjectId("6318b0060a51240e4bf3b001"), hello: 'world' } ]

3.スキーマjohnで、コレクションjohn_collおよび同じ名前のバッキング表john_collに対するアクセス権限をユーザーjanetに付与します。

GRANT SELECT, INSERT, UPDATE, DELETE ON john.john_coll TO janet;

4.ユーザー(スキーマ)janetとしてデータベースに接続している場合は、スキーマjanet内に新しいコレクションjanet_collを作成します。このコレクションは、スキーマjohn内のコレクションjohn_collマップされます。

メソッドcreate_collection()の2番目の引数はコレクション・メタデータです。ここでは、マップ先のコレクションのスキーマ名とバッキング表名を指定します。最後の引数CREATE_MODE_MAPは、元のコレクションを補足する表に加えて、新しいコレクションがマップされることを指定します。

DECLARE
  col SODA_COLLECTION_T;
BEGIN
  col := DBMS_SODA.create_collection(
           'janet_coll', 
           '{"schemaName"         : "JOHN",
             "tableName"          : "JOHN_COLL", 
             "contentColumn"      : {"name"       : "DATA",
                                     "sqlType"    : "BLOB",
                                     "jsonFormat" : "OSON"}, 
             "keyColumn"          : {"name"             : "ID",
                                     "assignmentMethod" : "EMBEDDED_OID",
                                     "sqlType"          : "VARCHAR2"}, 
             "versionColumn"      : {"name" : "VERSION", "method" : "UUID"}, 
             "lastModifiedColumn" : {"name" : "LAST_MODIFIED"}, 
             "creationTimeColumn" : {"name" : "CREATED_ON"}}', 
           DBMS_SODA.CREATE_MODE_MAP);
END;

ノート:

コレクション・メタデータ引数で使用されるスキーマ名および表名は、データ・ディクショナリに表示されるとおりである必要があります。この場合、大文字である必要があります。次の問合せを使用して、コレクション<collection>の正しいスキーマ名および表名を取得できます(<collection>の所有者として接続されている場合)。

SELECT c.json_descriptor.schemaName FROM USER_SODA_COLLECTIONS c
  WHERE uri_name = '<collection>';

SELECT c.json_descriptor.tableName FROM USER_SODA_COLLECTIONS c
  WHERE uri_name = '<collection>';

5.ユーザーjanetとしてシェルmongoshを使用してデータベースに接続し、使用可能なコレクションをリストし、コレクションjanet_collのコンテンツ(Johnのコレクションjohn_collのコンテンツと同じ)を表示します。

mongosh 'mongodb://janet:...
@MQSSYOWMQVGAC1Y-CTEST.adb.us-ashburn-1.oraclecloudapps.com:27017/janet
?authMechanism=PLAIN&authSource=$external&ssl=true&retryWrites=false&loadBalanced=true' 

janet> show collections;
janet_coll
janet> db.janet_coll.find()

[ { _id: ObjectId("6318b0060a51240e4bf3b001"), hello: 'world' } ]


脚注の説明

脚注1: MongoDBではコンポジット索引を複合索引と呼びます。コンポジット索引は、連結索引とも呼ばれます。