7 MLE JavaScriptコードでのSODAコレクションの操作

Simple Oracle Document Access (SODA)はNoSQL形式のAPIセットであり、これを使用すると、Oracle Databaseのドキュメント(特にJSON)のコレクションを作成および格納でき、Structured Query Language (SQL)や、ドキュメントがどのようにデータベースに格納されているかを理解していなくても、そのコレクションの取得や問合せを行うことができます。

SODA APIは様々なプログラミング言語用に存在しており、MLE JavaScriptのサポートが含まれています。SODA APIはドキュメント中心です。任意のSODA実装を使用して、ほぼあらゆる種類のドキュメント(ビデオ、イメージ、サウンド、およびその他のバイナリ・コンテンツを含む)について、作成、読取り、更新、削除(CRUD)操作を実行できます。また、任意のSODA実装を使用し、パターン一致: 例による問合せ(QBE)を使用してJavaScript Object Notation (JSON)ドキュメントのコンテンツを問い合せることができます。CRUD操作はドキュメント・キーまたはQBEによって決定できます。

この章では、クライアント側のnode-oracledbドライバではなくマルチリンガル・エンジン(MLE)に基づいて、データベースでのJavaScriptについて説明します。この章でJavaScriptについて述べられている場合は常に、暗黙的にMLE JavaScriptを示します。

関連項目:

SODAに関する全体概要は、『Oracle Database Simple Oracle Document Access (SODA)の概要』を参照してください

トピック

SODA for In-Database JavaScriptの操作の概要

SODA APIは、MLE JavaScript SQLドライバの一部です。コレクションおよびドキュメントの対話においては、SODAデータベース・オブジェクトを取得する前に、まずデータベースとの接続を確立する必要があります。

SODAデータベースは、SODA APIを操作する場合の最上位の抽象オブジェクトです。

図7-1に、標準制御フローを示します。

図7-1 SODA for In-Database JavaScriptの基本ワークフロー



クライアント側のNode.jsまたはDenoから移植されていないアプリケーションでは、グローバル・スコープで使用可能な頻繁に使用される多数の変数など、MLE JavaScript SQLドライバで使用可能なコーディング支援のメリットが得られます。使用可能なグローバル変数と型の完全なリストは、「MLE JavaScriptドライバの操作」を参照してください。

SODAアプリケーションの場合、最も重要なグローバル変数は、SodaDatabaseオブジェクトを表すsodaオブジェクトです。sodaオブジェクトをグローバル・スコープで使用できるため、ボイラープレート・コードを記述する必要性が少なくなります。

SODAオブジェクト

SODA APIで使用されるオブジェクト。

SODA APIの中核となるオブジェクトは、次のとおりです:
  • SodaDatabase: SODA操作の最上位オブジェクト。これは、Oracle Database接続から取得されます。SODAデータベースは抽象化されたものであるため、そのSODAデータベース内のSODAコレクションにアクセスでき、そのコレクション内のドキュメントにアクセスできます。SODAデータベースは、Oracle Databaseのユーザーまたはスキーマに似ています。コレクションは表に似ています。ドキュメントは、1つの列に一意のドキュメント・キー、1つの列にドキュメント・コンテンツ、およびその他の列に様々なドキュメント属性を含む表の行に似ています。MLE JavaScript SQLドライバでは、sodaオブジェクトをグローバル変数として使用できます。これはSodaDatabaseオブジェクトを表し、ボイラープレート・コードを記述する必要性が少なくなります。

  • SodaCollection: SODAドキュメントのコレクションを表します。デフォルトでは、コレクションを使用するとJSONドキュメントを格納でき、各ドキュメントにデフォルトのメタデータ・セットが追加されます。ほとんどのユーザーにはこれをお薦めします。ただし、オプションのメタデータを使用すると、そのデータベース・ストレージ、バージョンおよびタイムスタンプ・ドキュメント・コンポーネントの追跡の有無、それらのコンポーネントの生成方法、サポートされるドキュメント・タイプなど、コレクションに関する様々な詳細を設定できます。

  • SodaDocument: ドキュメントを表します。通常、ドキュメント・コンテンツはJSONです。ドキュメントには、コンテンツ、キー、タイムスタンプ、メディア・タイプなどのプロパティがあります。デフォルトでは、ドキュメント・キーが自動的に生成されます。

ここに格納されたコレクションおよびドキュメントを操作する場合は、次のオブジェクトを使用します:
  • SodaDocumentCursor: find()操作からのgetCursor()メソッドの結果を表すカーソル・オブジェクト。反復すると、各SodaDocumentにアクセスできます。

  • SodaOperation: ドキュメントに対する読取りおよび書込み操作を実行するためにfind()で使用される内部オブジェクト。つなげられたメソッドにより、SodaOperationオブジェクトにプロパティが設定されます。これをターミナル・メソッドで使用して、ドキュメントを検索、カウント、置換または削除します。これは、直接アクセスできない内部オブジェクトです。

関連項目:

mle-js-oracledbでのSODAオブジェクトの使用の詳細は、サーバー側JavaScript APIのドキュメントを参照してください

SODA for In-Database JavaScriptの使用

SODA for In-Database JavaScriptにアクセスする方法と、これを使用してコレクションに対する作成、読取り(取得)、更新および削除(CRUD)操作を実行する方法について説明します。

この項では、SODA for MLE JavaScriptについて説明します。この項のコード・スニペットは、読みやすくするために簡略化されている場合があります。JavaScriptファンクションの全体がリストされるように注意されていますが、単独では実行できません。ファンクション定義をJavaScriptモジュールに埋め込み、MLE JavaScript SQLドライバをインポートすると、これらのコード例がOracle Database 23cの有効なJavaScriptコードに変換されます。

トピック

SODA for In-Database JavaScriptの開始

SODA for In-Database JavaScriptにアクセスする方法、およびこれを使用してデータベース・コレクションを作成し、ドキュメントをコレクションに挿入し、コレクションからドキュメントを取得する方法について説明します。

SODA for MLE JavaScriptの使用を開始する前に、コレクションの格納に使用されるアカウントにSODA_APPロールを付与する必要があります:

grant soda_app to soda_user
SODA機能にアクセスするには、MLE JavaScript SQLドライバを使用する必要があります。コードが起動される時点ではデータベース・セッションが存在するため、追加の接続処理は不要です。例7-1では、次の方法を示します:
  • SODAコレクションの作成、

  • JSONドキュメントの挿入、および

  • コレクション内のすべてのSODAドキュメントに対する反復と、画面へのその内容の出力

例7-1で示す各概念(コレクションの作成、ドキュメントの追加と変更、およびコレクションの削除)については、この章の後半で詳しく説明します。

例7-1 MLE JavaScriptでのSODAの一般的なワークフロー

この例では、MLE JavaScriptでSODAコレクションを使用する一般的なワークフローを示します。

CREATE OR REPLACE MLE MODULE intro_soda_mod 
LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function testSODA(dropCollection) {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // Create a collection with the name "MyJSONCollection".
    // This creates a database table, also named "MyJSONCollection",
    // to store the collection. If a collection with the same name
    // exists it will be opened
    const col = db.createCollection("MyJSONCollection");

    // Create a JSON document (based on the HR.EMPLOYEES table for employee 100)
    const doc = {
        "employee_id": 100,
        "job_id": "AD_PRES",
        "last_name": "King",
        "first_name": "Steven",
        "email": "SKING",
        "manager_id": null,
        "department_id": 90
    };

    // Insert the document into a collection.
    col.insertOne(doc);

    // Find all documents in the collection and print them on screen
    try {

        // use a cursor to iterate over all documents in the collection
        const c = col.find().getCursor();
    
        let resultDoc;
        while (resultDoc = c.getNext()) {
            const content = resultDoc.getContent();

            console.log(`
                ------------------------------------------
                key:           ${resultDoc.key}
                content (select fields):
                - employee_id  ${content.employee_id}
                - job_id       ${content.job_id}
                - name         ${content.first_name} ${content.last_name}
                version:       ${resultDoc.version}
                last modified: ${resultDoc.lastModified}
                created on:    ${resultDoc.createdOn}
                media type:    ${resultDoc.mediaType}`
            );
        }

        // it is very important to close the SODADocumentCursor to free resources
        c.close();
    } catch (err) {
        throw err;
    }

    // commit open transactions
    connection.commit();

    // optionally drop the collection
    if (dropCollection) {
        col.drop();
    }
}
/

SODA for In-Database JavaScriptによるドキュメント・コレクションの作成

SODA for In-Database JavaScriptを使用して新しいドキュメント・コレクションを作成する方法について説明します。

コレクションを使用すると、ドキュメントを論理的にグループ化できます。コレクションを作成またはコレクションにアクセスする前に、いくつかのステップを完了する必要があります。まず接続オブジェクトを作成します。接続オブジェクトは、MLE JavaScriptモジュールでのすべてのSODA対話の開始点です:

// get a connection handle to the database session
const connection = oracledb.defaultConnection();

接続を取得したら、それを使用してConnection.getSodaDatabase()をコールでき、これは、コレクションを作成するための前提条件となります:

// get a SODA database
const db = connection.getSodaDatabase();

SODAデータベースが使用できる状態で最後に行うステップは、コレクションの作成です。コレクション名では大/小文字が区別されます:

// Create a collection with the name "MyJSONCollection".
// This creates a database table, also named "MyJSONCollection",
// to store the collection. If a collection with the same name
// exists, it will be opened
const col = db.createCollection("MyJSONCollection");

前述の文は、デフォルトでJSONドキュメントを格納できるコレクションを作成します。SodaDatabase.createCollection()に渡されているコレクション名が既存のコレクション名である場合、単にそのコレクションがオープンします。また、SodaDatabase.openCollection()を使用して既存する既知のコレクションをオープンすることもできます。

カスタム・メタデータ(例7-2を参照)がSodaDatabase.createCollection()に指定されていないかぎり、デフォルトのコレクション・メタデータが提供されます。デフォルトのメタデータには、次の特性があります:

  • コレクション内の各ドキュメントには、次のコンポーネントがあります:
    • キー
    • コンテンツ
    • 作成タイムスタンプ
    • 最終変更タイムスタンプ
    • バージョン
  • コレクションはJSONドキュメントのみを格納できます。
  • ドキュメント・キーおよびバージョン情報は、汎用一意識別子(UUID)機能を使用して自動的に生成されます。

オプションのコレクション・メタデータをcreateCollection()のコールに提供できますが、ほとんどの場合、デフォルトのコレクション構成をお薦めします。

すでに同じ名前のコレクションが存在する場合は、単にそのコレクションがオープンし、そのオブジェクトが戻されます。カスタム・メタデータがメソッドに渡され、そのメタデータが既存のコレクションのものと一致しない場合、コレクションはオープンせず、エラーが発生します。一致するには、すべてのメタデータ・フィールドが同じ値である必要があります。

関連項目:

コレクション・メタデータの詳細は、『Oracle Database Simple Oracle Document Access (SODA)の概要』を参照してください。

例7-2 カスタム・メタデータを含むコレクションの作成

この例では、カスタム・メタデータを含むコレクションの作成方法を示します。キーを、VARCHAR2列に格納される自動作成されたUUID値として格納するのではなく、数値フィールドとして格納します。

export function createCustomCollection() {
    
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // create a collection with custom metadata. Unless a custom
    // metadata field (keyColumn, versionColumn) is specified it 
    // will not be used/present in the collection. The metadata 
    // argument is mostly based on the defaults except for the 
    // client-assigned key column
    const col = db.createCollection(
        "myCustomCollection", 
        {
            "metaData": {
                "keyColumn": {
                    "name": "ID",
                    "sqlType": "NUMBER",
                    "assignmentMethod": "CLIENT"
                },
                "versionColumn": {
                    "name": "VERSION",
                    "method": "UUID"
                },
                "lastModifiedColumn": {
                    "name": "LAST_MODIFIED"
                },
                "creationTimeColumn": {
                    "name": "CREATED_ON"
                }
            }
        }
    );
    console.log(`
        Custom collection created.
        Assignment method: ${col.metaData.keyColumn.assignmentMethod}`
    );
}

値がデフォルトから外れているフィールドはkeyColumnのみですが、versionColumnlastModifiedColumnおよびcreationTimeColumnを追加する必要があります。そうしないと、コレクション内で使用されません。

SODA for In-Database JavaScriptでの既存のドキュメント・コレクションのオープン

メソッドSodaDatabase.openCollection()を使用して、既存のドキュメント・コレクションをオープンしたり、指定された名前が既存のコレクションを示しているかどうかをテストできます。

例7-3 既存のドキュメント・コレクションのオープン

この例では、collectionNameという名前のコレクションをオープンします。SodaDatabase.openCollection()によって戻されるコレクション・オブジェクトがnullでないことを確認することが非常に重要です。リクエストされたコレクションが存在しない場合、メソッドはエラーをスローするのではなく、null値を戻します。

export function openCollection(collectionName) {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // perform a lookup. If a connection cannot be found by that
    // name no exception nor error are thrown, but the resulting
    // collection object will be null
    const col = db.openCollection(collectionName);
    if (col === null) {
        throw `No such collection ${collectionName}`;
    }

    // do something with the collection
} 

SODA for In-Database JavaScriptによる特定のコレクションの有無の確認

SodaDatabase.openCollection()を使用すると、特定のコレクションの存在を確認できます。コレクションの引数が既存のコレクションを示さない場合、nullを返し、それ以外の場合、その名前のコレクションをオープンします。

例7-3では、collectionNameが既存のコレクションの名前でない場合、colには値nullが代入されます。

SODA for In-Database JavaScriptによる既存のコレクションの検出

SodaDatabase.getCollectionNames()を使用すると、特定のSodaDatabaseオブジェクトのすべての既存コレクションの名前をフェッチできます。

コレクションの数が非常に多い場合は、戻される名前の数を制限できます。また、例7-5に示すように、ユーザー定義文字列で始まるコレクションに検索を制限できます。

例7-4 既存のすべてのコレクション名のフェッチ

この例では、メソッドgetCollectionNames()を使用して既存のすべてのコレクションの名前を出力します。

export function printCollectionNames() {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // loop over all collection names
    const allCollections = db.getCollectionNames();
    for (const col of allCollections) {
        console.log(`- ${col}`);
    }
}
/

例7-5 戻されるコレクションのリストのフィルタリング

この例では、ユーザー定義文字列startWithで始まるコレクションの名前のみを出力することによって、getCollectionNames()の結果を制限します。

export function printSomeCollectionNames(numHits, startWith) {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // loop over all collection names
    const allCollections = db.getCollectionNames(
        {
            limit: numHits,
            startsWith: startWith
        }
    );
    for (const col of allCollections) {
        console.log(`- ${col}`);
    }
}

SODA for In-Database JavaScriptによるドキュメント・コレクションの削除

SodaCollection.drop()を使用して、既存のコレクションを削除します。

注意:

SQLを使用して、コレクションの基礎になっているデータベースを削除しないでくださいコレクションを削除することは、そのデータベース表を削除することで終わりにはなりません。表に格納されるドキュメント以外にも、コレクションにはOracle Databaseに永続化されるメタデータも含まれています。コレクションの基礎になっている表を削除しても、コレクション・メタデータは削除されません

ノート:

SODAを使用する通常のアプリケーションを日常的に使用する場合、コレクションをドロップして再作成する必要はありません。しかし、なんらかの理由でこれを行う必要がある場合は、このガイドラインが適用されます。

多少なりともコレクションを使用しているアプリケーションがある場合は、コレクションを削除した後に異なるメタデータで再作成しないでください。すべてのライブSODAオブジェクトが解放されるように、コレクションを再作成する前にこのようなアプリケーションをすべて停止します。

コレクションのドロップだけでは、問題は発生しません。ドロップされたコレクションの読取りまたは書込み操作でエラーが発生します。コレクションをドロップして同じメタデータを持つコレクションを再作成しても、問題はありません。ただし、異なるメタデータでコレクションを再作成して、SODAオブジェクトを使用するライブ・アプリケーションが存在する場合、古いコレクションがアクセスされるというリスクがあり、この場合、エラーは発生しません

SODA for Javaなど、コレクション・メタデータ・キャッシングを許可するSODA実装では、このようなキャッシュが有効になっている場合に、このリスクが高くなります。その場合、コレクションがドロップされても(共有またはローカルの)キャッシュが古いコレクション・オブジェクトのエントリを返す可能性があります。

ノート:

SodaCollection.drop()を使用する前に、コレクションへのすべての書込みをコミットしてください。メソッドが正常に終了するには、コレクションに対してコミットされていないすべての書込みを最初にコミットする必要があります。そうでない場合は、例外が発生します。

例7-6 コレクションの削除

この例は、コレクションcolを削除します。

CREATE OR REPLACE MLE MODULE drop_col_mod 
LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function openAndDropCollection(collectionName) {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // look the collection up
    const col = db.openCollection(collectionName);
    if (col === null) {
        throw `No such collection ${collectionName}`;
    }

    // drop the collection - POTENTIALLY DANGEROUS
    col.drop();
}
/

SODA for In-Database JavaScriptによるドキュメントの作成

SODA for In-Database JavaScriptによるドキュメントの作成について説明します。

SodaDocumentクラスはSODAドキュメントを表します。JSONドキュメントが中心ですが、他のコンテンツ・タイプもサポートされています。SodaDocumentには、実際のドキュメントのコンテンツとメタデータの両方が格納されます。

設計上、JavaScriptは特にJSONとの連携に適しているため、これが他のプログラミング言語よりも優位になっています。

次に、単純なJSONドキュメントの例を示します。

// Create a JSON document (based on the HR.EMPLOYEES table for employee 100)
const doc = {
    "employee_id": 100,
    "job_id": "AD_PRES",
    "last_name": "King",
    "first_name": "Steven",
    "email": "SKING",
    "manager_id": null,
    "department_id": 90
};

ノート:

SODAでは、JSONコンテンツがRFC 4627に準拠する必要があります。

SodaDocumentオブジェクトは、次の3つの方法で作成できます:

  • sodaDatabase.createDocument()の結果として。これは、SODAの挿入および置換メソッドに使用できる元のSodaDocumentオブジェクトです。SodaDocumentには、コンテンツおよびメディア・タイプのコンポーネントが設定されます。createdOnなどの属性は定義されません。SodaDocument.createDocument()のコール時に指定されていないオプション属性も定義されません。
  • sodaOperation.getOne()のコールなどのデータベースからの読取り操作、またはsodaOperation.getCursor()コール後のsodaDocumentCursor.getNext()からの読取り操作の結果として。これらは、ドキュメントのコンテンツおよび属性(タイムスタンプなど)を含む完全なSodaDocumentオブジェクトを戻します。
  • sodaCollection.insertOneAndGet()sodaOperation.replaceOneAndGet()またはsodaCollection.insertManyAndGet()メソッドの結果として。これらは、ドキュメント・コンテンツ自体を除くすべての属性を含むSodaDocumentsを戻します。システム生成キーなどのドキュメント属性や、新規および更新されたバージョンのドキュメントを検索する場合に便利です。

ドキュメントには次のコンポーネントがあります。

  • キー

  • コンテンツ

  • 作成タイムスタンプ

  • 最終変更タイムスタンプ

  • バージョン

  • メディア・タイプ(JSONドキュメントの場合、"application/json")

例7-7 SODAドキュメントの作成

export function createJSONDoc() {
    
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // define a payload
    const payload = {
        "employee_id": 100,
        "job_id": "AD_PRES",
        "last_name": "King",
        "first_name": "Steven",
        "email": "SKING",
        "manager_id": null,
        "department_id": 90
    };

    // create a SODA document using the default key
    const docDefaultKey = db.createDocument(payload);
    console.log(`
        ------------------------------------------
        SODA Document using default key
        content (select fields):
        - employee_id  ${docDefaultKey.getContent().employee_id}
        - job_id       ${docDefaultKey.getContent().job_id}
        - first name   ${docDefaultKey.getContent().first_name}
        media type:    ${docDefaultKey.mediaType}`
    );

    // create a SODA document using a custom key (requires that the
    // collection this document is inserted to uses a user-assigned
    // key)
    const docCustomKey = db.createDocument(
        payload, 
        {
            key: "12345"
        }
    );

    // print details about the new SODA Document
    console.log(`
        ------------------------------------------
        SODA Document using custom key
        content (select fields):
        - employee_id  ${docCustomKey.getContent().employee_id}
        - job_id       ${docCustomKey.getContent().job_id}
        - first name   ${docCustomKey.getContent().first_name}
        key:           ${docCustomKey.key}
        media type:    ${docCustomKey.mediaType}`
    );
}

この例にあるようなSodaDocumentインスタンスの作成は、標準ではなく例外です。ほとんどの場合、開発者はSodaCollection.insertOne()またはSodeCollection.insertOneAndGet()を使用します。SodaCollection.insertOne()の使用は、例7-8に示されています。

SODA for In-Database JavaScriptによるコレクションへのドキュメントの挿入

SodaCollection.insertOne()または関連コール(sodaCollection.insertOneAndGet()など)を使用すると、ドキュメントをコレクションに追加できます。コレクションがクライアント割当てキーで構成されておらず、入力ドキュメントがキーを指定していない場合、これらのメソッドでは、ドキュメント・キーが自動的に作成されます。

SodaCollection.insertOne()は単にドキュメントをコレクションに挿入しますが、SodaCollection.insertOneAndGet()はさらに結果ドキュメントを戻します。結果ドキュメントには、ドキュメント・キーおよびその他の生成されたドキュメント・コンポーネントが含まれます。ただし、実際のドキュメントのコンテンツは除きます(これはパフォーマンス向上のために行われます)。

どちらのメソッドでも、カスタム・メタデータを使用してコレクションが作成されていないかぎり、作成タイムスタンプ、最終変更タイムスタンプおよびバージョンの値が自動的に設定されます。カスタム・メタデータには、一部のデフォルト・メタデータが含まれていない場合があります。コレクションで定義されていない属性を問い合せると、NULL値が戻されます。

ノート:

コレクションがクライアント割当てドキュメント・キーで構成されていて(デフォルトの場合と異なるケース)、入力ドキュメントがコレクション内の既存のドキュメントを識別するキーを提供している場合、これらのメソッドは例外をスローします。例外を発生させるのではなく、入力ドキュメントで既存のドキュメントを置き換える場合は、「SODA for In-Database JavaScriptによるコレクションへのドキュメントの保存」を参照してください。

例7-8 コレクションへのSODAドキュメントの挿入

この例では、SodaCollection.insertOne()を使用してコレクションにドキュメントを挿入する方法を示します。

export function insertOneExample() {
    
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // create/open a SODA collection
    const col = db.createCollection("MyJSONCollection");

    // define a payload
    const payload = {
        "employee_id": 100,
        "first_name": "Steven",
        "last_name": "King"
    };

    // insert the document
    col.insertOne(payload);
}

例7-9 コレクションへのドキュメントの配列の挿入

この例では、SodaCollection.insertMany()を使用して、1つのコマンドで複数のドキュメントを挿入する方法を示します。

export function insertManyExample() {
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // create/open a SODA collection
    const col = db.createCollection("MyJSONCollection");

    // define a payload (an array of objects)
    const payload = [
        {
            "employee_id": 100,
            "first_name": "Steven",
            "last_name": "King"
        },
        {
            "employee_id": 101,
            "first_name": "Neena",
            "last_name": "Kochhar"
        },
        {
            "employee_id": 102,
            "first_name": "Lex",
            "last_name": "De Haan"
        },
        {
            "employee_id": 103,
            "first_name": "Alexander",
            "last_name": "Hunold"
        },
        {
            "employee_id": 104,
            "first_name": "Bruce",
            "last_name": "Ernst"
        },
        {
            "employee_id": 105,
            "first_name": "David",
            "last_name": "Austin"
        }
    ]

    // insert the document
    col.insertMany(payload);
}

SODA for In-Database JavaScriptによるコレクションへのドキュメントの保存

SodaCollection.save()およびsaveAndGet()を使用して、ドキュメントをコレクションに保存します。

これらのメソッドは、メソッドinsertOne()およびinsertOneAndGet()と似ていますが、コレクションがクライアント割当てドキュメント・キーで構成されていて、入力ドキュメントがコレクション内のドキュメントをすでに識別するキーを提供している場合、入力ドキュメントが既存のドキュメントを置き換える点が異なります。これに対し、メソッドinsertOne()およびinsertOneAndGet()は、そのような場合に例外をスローします。

ノート:

デフォルトでは、コレクションは自動的に生成されたドキュメント・キーで構成されます。したがって、デフォルトのコレクションの場合、メソッドsave()およびsaveAndGet()は、それぞれメソッドinsertOne()およびinsertOneAndGet()と同等です。

例7-10 コレクションへのドキュメントの保存

この例では、SodaCollection.saveAndGet()の使用方法を示します。JavaScriptコードで、カスタム・メタデータを使用して定義したコレクションにSODAドキュメントを格納し、save()およびsaveAndGet()が説明どおりに動作することを確認します。特にキー列はユーザー管理であるため、SodaDocumentコンストラクタでキーが示されています。

CREATE OR REPLACE MLE MODULE save_doc_mod 
LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function saveJSONDoc() {
    
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // create/open a collection using custom metadata
    const col = db.openCollection("myCustomCollection");
    if ( col === null ) {
        throw `'myCustomCollection' does not exist`;
    }

    // create the SODA document, use the employee_id as its key
    let docCustomKey = db.createDocument(
        {
        "employee_id": 100,
        "job_id": "AD_PRES",
        "last_name": "King",
        "first_name": "Steven",
        "email": "SKING",
        "manager_id": null,
        "department_id": 90
        },
        {
            "key": "100"
        }
    );

    // insert the document into the custom collection
    let savedDoc = col.saveAndGet(docCustomKey);

    // get some meta information about the new document
    console.log(`Document saved for the 1st time: 
    - department id: ${docCustomKey.getContent().department_id}
    - key:           ${savedDoc.key}
    - version:       ${savedDoc.version}
    - last modified: ${savedDoc.lastModified}
    - created on:    ${savedDoc.createdOn}`);

    // correct the previous mistake - the document should have
    // been inserted with a department_id of 30
    // - create the new document (important to use the same key)
    docCustomKey = db.createDocument(
        {
        "employee_id": 100,
        "job_id": "AD_PRES",
        "last_name": "King",
        "first_name": "Steven",
        "email": "SKING",
        "manager_id": null,
        "department_id": 30
        },
        {
            "key": "100"
        }
    );

    // - update the existing document
    savedDoc = col.saveAndGet(docCustomKey);

    // - show the changed metadata. Cannot refer to the content
    //   as it is not returned by saveAndGet() for performance
    //   reasons
    console.log(`Updated document saved successfully:
    - department id: ${docCustomKey.getContent().department_id}
    - key:           ${savedDoc.key}
    - version:       ${savedDoc.version}
    - last modified: ${savedDoc.lastModified}
    - created on:    ${savedDoc.createdOn}`);
}
/

最初のドキュメントがコレクションに保存された後、ドキュメントのdepartment_idの値が誤っており、修正が必要なことが判明します。誤りが修正され、ドキュメントが再度保存されます。これにより、前のコンテンツが上書きされ、department_idが正しい値30に効率的に設定されます。

SODA for In-Database JavaScriptの読取りおよび書込み操作

読取りおよび書込み操作(挿入および保存以外)を指定する主な方法は、SodaOperationクラスが提供するメソッドを使用することです。SodaOperationメソッドをつなげて、コレクションに対する読取りおよび書込み操作を指定できます。

非ターミナルSodaOperationメソッドは、呼び出された同じオブジェクトを戻し、メソッドをつなげることができるようにします。

ターミナルSodaOperationメソッドは常に、操作を実行するメソッド・チェーンの最後に出現します。

ノート:

SodaOperationオブジェクトは内部オブジェクトです。そのプロパティは直接変更しないでください。

メソッドのnode-oracledbドキュメントに異なる記述がなければ、すべての非ターミナル・メソッドをつなぎ、ターミナル・メソッドでチェーンを終了できます。ただし、すべての組合せに意味があるわけではありません。たとえば、keys()などのドキュメントを一意に識別しないメソッドとversion()メソッドをつなげても意味はありません。

表7-1 読取り操作用の非ターミナル・メソッドの概要

メソッド 説明
key() 指定したドキュメント・キーを持つドキュメントを検索します。
keys() 指定した複数のドキュメント・キーを持つ複数のドキュメントを検索します。
filter() フィルタ仕様(JSONで表される例による問合せ)と一致するドキュメントを検索します。
version() 指定したバージョンのドキュメントを検索します。通常、これはkey()とともに使用されます。
headerOnly() 結果からドキュメント・コンテンツを除外します。
skip() 結果内で指定した数のドキュメントをスキップします。
limit() 結果ドキュメントの数を指定した数に制限します。

表7-2 読取り操作用のターミナル・メソッドの概要

メソッド 説明
getOne() 最大1つのドキュメントを戻す操作を作成して実行します。たとえば、非ターミナル・メソッドkey()の呼出しを含む操作などです。
getCursor() 読取り操作の結果によってカーソルを取得します。
count() 操作で見つかったドキュメントの数をカウントします。
getDocuments() 問合せ基準に一致するドキュメントの配列を取得します。

表7-3 書込み操作用のターミナル・メソッドの概要

メソッド 説明
replaceOne() 1つのドキュメントを置き換えます。
replaceOneAndGet() 1つのドキュメントを置き換えて、結果ドキュメントを返します。
remove() ドキュメントをコレクションから削除します。

関連項目:

SODA for In-Database JavaScriptによるコレクション内のドキュメントの検索

コレクション内のドキュメントを検索するには、SodaCollection.find()を呼び出します。SodaOperationオブジェクトが作成されて戻されます。これは、非ターミナル・メソッドおよびターミナル・メソッドを含むメソッド・チェーンを介して使用されます。

問合せを実行するには、SodaOperation.getCursor()を呼び出して、その結果のカーソルを取得します。次に、カーソルを使用して結果リスト内の各ドキュメントにアクセスします。これは、例7-1およびその他の例で示されています。リソースを節約するために、カーソルを必ずクローズすることが重要です。

ただし、これはコレクション内のドキュメントを検索する際の一般的なワークフローではありません。SodaOperationクラスで提供される複数のメソッドをつなげる方が一般的です。

例7-11 キーによるドキュメントの検索

この例は、メソッドfind()key()およびgetOne()を使用してドキュメントをキーで検索する方法を示しています。

例7-2で示したように、customCollectionが作成されていると想定しています。

CREATE OR REPLACE MLE MODULE find_doc_mod 
LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function findDocByKey() {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open a collection in preparation of a document
    // lookup
    const col = db.openCollection("myCustomCollection");
    if (col === null) {
        throw `myCustomCollection does not exist`;
    }

    try {
        // perform a lookup of a document with key "100"
        const doc = col.find().key("100").getOne();
        console.log(`
            document found for key ${doc.key}
            contents: ${doc.getContentAsString()}`
        );
    } catch (err) {
        throw `No document found in 'myCustomCollection' with key 100`;
    }
}
/

ノート:

キーは、数値形式の場合でも二重引用符で囲む必要があります。

指定されたキーの検索に失敗した場合、データベースはORA-01403 (「データが見つかりません。」)例外をスローします。例外は適切に処理することをお薦めします。この例では、エラーが確実に検出され、業界で最もよく知られている方法に従って処理される責任をファンクションのコール元が担っています。

例7-12 複数のキーを使用したドキュメントの検索

この例では、メソッドfind()keys()getCursor()およびgetNext()を使用して、配列で指定された複数のキーを検索します。

export function findDocByKeys() {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open a collection in preparation of a document
    // lookup
    const col = db.openCollection("myCustomCollection");
    if (col === null) {
        throw `myCustomCollection does not exist`;
    }

    try {
        // perform a lookup of a document with keys ["100","101"]
        const docCursor = col.find().keys(["100", "101"]).getCursor();

        let doc
        while (( doc = docCursor.getNext())) {
            console.log(`
                document found for key ${doc.key}
                contents: ${doc.getContentAsString()}`
            );
        }
        docCursor.close();
    } catch (err) {
        throw `No document found in 'myCustomCollection' with either key`;
    }
}

ORA-1403 (「データが見つかりません。」という例外)で失敗するのではなく、find()操作では、単に、コレクションで見つからないキーのデータが戻されません。キーが見つからない場合は、何も戻されません。

例7-13 QBEを使用したコレクション内のドキュメントのフィルタリング

この例では、filter()を使用してコレクション内のドキュメントを検索します。非ターミナルのSodaOperation.filter()メソッドは、コレクション内のJSONドキュメントをフィルタする強力な方法を提供し、複雑なドキュメント問合せおよびJSONドキュメントの順序付けを可能にします。フィルタ仕様には、比較、正規表現、論理演算子および空間演算子などを含めることができます。

filterConditionで定義されている検索式は、部門30で働く110より大きい従業員IDを持つすべての従業員に一致します。

export function findDocByFiltering() {
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open a collection in preparation of a document
    // lookup. This particular collection contains all the
    // rows from the HR.employees table converted to SODA
    // documents
    const col = db.openCollection("employees_collection");
    if (col === null) {
        throw `employees_collection does not exist`;
    }

    // find all employees with an employee_id > 110 working
    // in department 30
    const filterCondition = {
        "employee_id": { "$gt": 110 },
        "department_id": "30"
    }

    try {

        // perform the lookup operation using the QBE
        const docCursor = col.find().filter(filterCondition).getCursor();
        let doc;
        while (( doc = docCursor.getNext())) {
            console.log(`
                ------------------------------------
                document found matching the search criteria
                - key:           ${doc.key}
                - employee id:   ${doc.getContent().employee_id}
                - department ID: ${doc.getContent().department_id}
                - name:          ${doc.getContent().last_name}`
            );
        }

        docCursor.close();
    } catch (err) {
        throw `No document found in 'myCustomCollection' with either key`;
    }
}

関連項目:

例7-14 ページ区切り問合せでのskip()およびlimit()の使用

行数が過度に大きくなる場合は、ページ分けするか、戻されるドキュメントの数を制限できます。この例では、このような状況でskip()およびlimit()を使用する方法を示します。

export function paginationExample() {
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open a collection in preparation of a document
    // lookup. This particular collection contains all the
    // rows from the HR.employees table converted to SODA
    // documents
    const col = db.openCollection("employees_collection");
    if (col === null) {
        throw `employees_collection does not exist`;
    }

    // find all employees with an employee_id > 110 working
    // in department 80
    const filterCondition = {
        "employee_id": { "$gt": 110 },
        "department_id": "80"
    }

    try {

        // perform the lookup operation using the QBE, skipping the first
        // 10 documents and limiting the result set to 10 documents
        const docCursor = 
            col.find().filter(filterCondition).skip(10).limit(10).getCursor();
        let doc
        while (( doc = docCursor.getNext())) {
            console.log(`
                ------------------------------------
                document found matching the search criteria
                - key:           ${doc.key}
                - employee id:   ${doc.getContent().employee_id}`
            );
        }

        docCursor.close();
    } catch (err) {
        throw `No document found in 'myCustomCollection' with either key`;
    }
}

例7-15 ドキュメント・バージョンの指定

この例では、非ターミナル・メソッドversion()を使用して、特定のドキュメント・バージョンを指定します。これは、書込み操作用のターミナル・メソッドとともに使用する場合、オプティミスティック・ロックの実装に便利です。

export function versioningExample() {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open a collection in preparation of a document
    // lookup
    const col = db.openCollection("myCustomCollection");
    if (col === null) {
        throw `myCustomCollection does not exist`;
    }

    try {
        // perform a lookup of a document with key "100" and version "v1"
        const doc = col.find().key("100").version("v1").getOne();
        console.log(`
            document found for key ${doc.key}
            contents: ${doc.getContentAsString()}`
        );
    } catch (err) {
        throw `No document found for key 100 and version "v1"`;
    }
} 

例7-16 見つかったドキュメントの数のカウント

この例では、find()filter()およびcount()メソッドを使用して、コレクションで見つかったドキュメントの数をカウントする方法を示します。filter()式は、部門30で働くすべての従業員に結果を制限します。

export function countingExample() {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open a collection in preparation of a document lookup
    const col = db.openCollection("employees_collection");
    if (col === null) {
        throw `employees_collection does not exist`;
    }

    try {
        // perform a lookup operation identifying all employees working
        // in department 30 limiting the result to headers only
        const filterCondition = {"department_id": "30" };
        const numDocs = col.find().filter(filterCondition).count();
        console.log(`there are ${numDocs} documents matching the filter`);
    } catch (err) {
        throw `No document found in 'employees_collection' matching the filter`;
    }
} 

SODA for In-Database JavaScriptによるコレクション内のドキュメントの置換

コレクション内の1つのドキュメントのコンテンツを別のドキュメントのコンテンツで置き換えるには、まずそのキーを使用して、変更するドキュメントを検索します。SodaOperation.key()は非ターミナル操作であるため、コンテンツを置換する最も簡単な方法は、SodaOperation.key()SodaOperation.replaceOne()またはSodaOperation.replaceOneAndGet()につなげることです。

SodaOperation.replaceOne()はドキュメントを置換するのみですが、SodaOperation.replaceOneAndGet()はドキュメントを置換し、コール元にその結果の新しいドキュメントを提供します。

どちらのメソッドでも、最終変更タイムスタンプとバージョンの値が更新されます。置換では、ドキュメント・キーと作成タイムスタンプは変更されません。

SodaOperation.replace()SodaOperation.save()の違いは、キーがコレクションにまだ存在しない場合、後者では挿入を実行することです。置換操作では、SodaOperation.key()メソッドによる検索で既存のドキュメントを見つける必要があります。

ノート:

デフォルト・メソッドを含め、バージョンを生成する一部のメソッドでは、ドキュメント・コンテンツのハッシュ値を生成します。このような場合、ドキュメント・コンテンツが変更されないと、バージョンも変更されません。

例7-17 コレクション内のドキュメントの置換および結果ドキュメントの戻し

この例は、コレクション内のドキュメントを置換し、変更されたドキュメントへの参照を戻す方法を示しています。

CREATE OR REPLACE MLE MODULE rep_ret_doc_mod 
LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function replaceExample() {
    
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open myCustomCollection
    const col = db.openCollection("myCustomCollection");
    if ( col === null ) {
        throw "'myCustomCollection' does not exist";
    }

    // create the changed SODA document, change department_id to 80
    const newDoc = db.createDocument(
        {
            "employee_id": 100,
            "job_id": "AD_PRES",
            "last_name": "King",
            "first_name": "Steven",
            "email": "SKING",
            "manager_id": null,
            "department_id": 80
        }
    );;  

    // perform a lookup of the document about to be changed
    try {
        const resultDoc = col.find().key("100").replaceOneAndGet(newDoc);

        // print some metadata (note that content is not returned for 
        // performance reasons)
        console.log(`Document updated successfully:
        - key:           ${resultDoc.key}
        - version:       ${resultDoc.version}
        - last modified: ${resultDoc.lastModified}
        - created on:    ${resultDoc.createdOn}`);
    } catch (err) {
        throw err;
    }
}
/

ノート:

パフォーマンス上の理由から、実際のドキュメントのコンテンツは戻されないため、変更されたコンテンツを読み取ろうとするとエラーが発生します。

SODA for In-Database JavaScriptによるコレクションからのドキュメントの削除

コレクションからのドキュメントの削除は置換と似ています。最初のステップは、検索操作(通常はドキュメントのキーに基づいて、またはSodaOperation.filter()の検索式を使用して)を実行することです。SodaOperation.remove()のコールはターミナル操作です。つまり、チェーンの最後の操作です。

例7-18 ドキュメント・キーによるコレクションからのドキュメントの削除

この例では、ドキュメント・キーが"100"のドキュメントを削除します。

CREATE OR REPLACE MLE MODULE rm_doc_mod 
LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function removeByKey() {
    
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open myCustomCollection
    const col = db.openCollection("myCustomCollection");
    if ( col === null ) {
        throw "'myCustomCollection' does not exist";
    }

    // perform a lookup of the document about to be removed and 
    // ultimately remove it. 
    const result = col.find().key("100").remove();
    if ( result.count === 0) {
        throw "Document was not deleted - is this an invalid key?"
    }
}
/

例7-19 フィルタによるコレクションからのJSONドキュメントの削除

この例では、フィルタを使用して、department_id70のJSONドキュメントを削除します。その後、削除されたドキュメントの数を出力します。

export function removeByFilter() {
    
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open employees_collection
    const col = db.openCollection("employees_collection");
    if ( col === null ) {
        throw "'employees_collection' does not exist";
    }

    // perform a lookup based on a filter expression and remove the 
    // documents matching the filter
    const result = col.find().filter({"department_id": "70"}).remove();
    
    return result.count;
}

SODA for In-Database JavaScriptによるコレクション内のドキュメントの索引付け

索引は、NoSQLスタイルのSODA APIとリレーショナル・アプローチのどちらを使用するかに関係なく、データ・アクセスを高速化できます。SODAコレクション内のドキュメントの索引付けには、SodaCollection.createIndex()を使用します。そのIndexSpecパラメータはテキストのJSON索引指定です。

既存の索引は、SodaCollection.dropIndex()を使用して削除できます。

JSON検索索引は、全文問合せおよび非定型構造問合せに使用され、永続的な記録およびJSONデータ・ガイド情報の自動更新に使用されます。

関連項目:

例7-20 SODA for In-Database JavaScriptを使用したJSONフィールドに対するBツリー索引の作成

この例では、コレクションemployees_collection内のJSONドキュメントの数値フィールドdepartment_idに対して一意ではないBツリー索引を作成します。

CREATE OR REPLACE MLE MODULE b_tree_mod 
LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function createBTreeIndex() {
    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open the collection
    const col = db.openCollection("employees_collection");
    if ( col === null ) {
        throw "'employees_collection' does not exist";
    }

    const indexSpec = {
        "name": "departments_idx",
        "fields": [
            {
                "path": "department_id",
                "datatype": "number",
                "order": "asc"
            }
        ]
    };

    col.createIndex(indexSpec);

    console.log(`B-Tree index successfully created`);
}
/

例7-21 SODA for In-Database JavaScriptを使用したJSON検索索引の作成

この例では、コレクションemployees_collectionのドキュメントを索引付けするためのJSON検索索引の作成方法を示します。これは、非定型問合せおよび全文検索(QBE演算子$containsを使用する問合せ)に使用できます。これによって、JSONドキュメントに関するデータ・ガイド情報(集計構造情報および型情報)が自動的に蓄積および更新されます。索引仕様には、フィールドnameのみが含まれます(例7-20のBツリー索引とは異なり、フィールドfieldsは含まれません)。

export function createSearchIndex() {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open the collection
    const col = db.openCollection("employees_collection");
    if ( col === null ) {
        throw "'employees_collection' does not exist";
    }

    const indexSpec = {
        "name": "SEARCH_AND_DATA_GUIDE_IDX",
        "dataguide": "on",
        "search_on": "text_value"
    }

    col.createIndex(indexSpec);

    console.log(`Search & Data Guide index successfully created`);
}

非定型(検索)の索引付けのみを高速化する場合は、フィールドdataguideに値"off"を指定する必要があります。dataguide索引付け機能が不要な場合は、同じ方法でこれをオフにできます。

例7-22 SODA for In-Database JavaScriptを使用した索引の削除

この例は、SodaCollection.dropIndex()およびforceオプションを使用してコレクションの既存の索引を削除する方法を示しています。

export function dropIndex(indexName) {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open the collection
    const col = db.openCollection("employees_collection");
    if ( col === null ) {
        throw "'employees_collection' does not exist";
    }

    // drop the index
    const result = col.dropIndex(indexName, {"force": true});
    if (! result.dropped ) {
        throw `Could not drop SODA index ${indexName}`;
    }
}

SodaCollection.dropIndex()は、単一のフィールドdroppedを含む結果オブジェクトを戻します。索引が削除された場合、その値はtrueで、それ以外の場合はfalseです。どちらの場合でもメソッドは正常に終了します。

オプションのパラメータ・オブジェクトをメソッドに指定できます。forcetrueに設定すると、基礎となるOracle Databaseドメイン索引で通常の削除が許可されない場合に、JSON索引の削除が強制されます。

SODA for In-Database JavaScriptを使用したコレクションのデータ・ガイドの取得

データ・ガイドは、一連のJSON文書に含まれる構造および型情報の概要を示します。これらの文書内で使用されているフィールドに関するメタデータを記録します。JSONドキュメントに対する優れたインサイトを提供し、データ・セットの概要把握に役立ちます。

SodaCollection.getDataGuide()を使用してデータ・ガイドを作成できます。SODAでデータ・ガイドを取得するには、コレクションがJSONのみであり、"dataguide"オプションが"on"であるJSON検索索引を持っている必要があります。データ・ガイドは、sodaCollection.getDataGuide()からSodaDocumentのJSONコンテンツとして戻されます。データ・ガイドは現在のコレクションの状態から推測されます。コレクションが大きくなったり、ドキュメントが変更されると、その後にgetDataGuide()がコールされるたびに新しいデータ・ガイドが戻されます。

例7-23 コレクションのデータ・ガイドの生成

この例では、メソッドgetDataGuide()を使用してコレクションemployees_collectionのデータ・ガイドを取得し、メソッドgetContentAsString()を使用してコンテンツを文字列として出力します。

CREATE OR REPLACE MLE MODULE data_guide_mod
LANGUAGE JAVASCRIPT AS

import oracledb from "mle-js-oracledb";

export function createDataGuide() {

    // get a connection handle to the database session
    const connection = oracledb.defaultConnection();

    // get a SODA database
    const db = connection.getSodaDatabase();

    // open the collection
    const col = db.openCollection("employees_collection");
    if ( col === null ) {
        throw "'employees_collection' does not exist";
    }

    // generate a Data Guide
    const doc = col.getDataGuide();
    console.log(doc.getContentAsString());
}
/

SODA for In-Database JavaScriptによるトランザクションの処理

クライアント側のJavaScript SQLドライバとは異なり、MLE JavaScript SQLドライバはautoCommit機能を提供していません。モジュール・コールの場合はPL/SQLレイヤーで、またはconnection.commit()connection.rollback()をコールすることでJavaScriptコードで直接的に、トランザクションをコミットまたはロールバックする必要があります。

注意:

コミットされていない操作でエラーが発生し、トランザクションを明示的にロールバックしない場合、不完全なトランザクションは、関連データを一貫性のない状態(コミットされていない部分的な結果)のままにする場合があります。

SODA for In-Database JavaScriptの使用例の概要

SODA for In-Database JavaScriptの使用を最初から最後まで示すエンドツーエンドの例が含まれています。

Example 7-24 SODA for In-Database JavaScriptの使用

この例には、SODA for in-database JavaScriptをエンドツーエンドで使用する次のステップが含まれています:
  1. employeesColコレクションと対話するJavaScriptモジュールを定義します。
  2. コレクションにJSONドキュメントを保持するJavaScriptファンクションを作成します。
  3. コレクションで見つかったすべての従業員をリストする別のJavaScriptファンクションを定義します。
  4. PL/SQLパッケージ内の各ファンクションへのコール仕様を指定します。

JavaScript モジュールの定義

このモジュールでは、グローバル・スコープで使用可能な変数を使用して、必要なコーディングの量を制限します。

CREATE OR REPLACE MLE MODULE employees_module
LANGUAGE JAVASCRIPT AS
 
/// <reference types="mle-js" />
 
const colName = "employeesCol";
 
/**
* add an employee document to the employeesCol collection
*
* @param {json} employeeDoc - the new employee document to be added
*/
export function addEmployee(employeeDoc) {
 
    // this code example uses the SODA database object (soda)
    // available in the global scope
    const col = soda.createCollection(colName);
    col.insertOne(employeeDoc);
}
 
/**
* return a JSON document containing all the employees from the collection
*
* @returns {json} a document listing employee_id, last name, and department_name
*/
export function findAllEmployees() {
 
    // this array will contain all the employee documents
    // shortened to include employee ID, last name and 
    // department name. It is safer to assume the array is
    // empty at first
    let employeeList = [];
 
    const col = soda.openCollection(colName);
    if ( col === null ) {
        return employeeList;
    }
 
    // iterate over all documents in the collection
    const allDocs = col.find().getDocuments();
    for ( let doc of allDocs ) {
        const payload = doc.getContent();
        // add the relevant fields to a JSON document before
        // pushing it to the employeeList array
        const employeeDetail = {
            "employee_id": payload.employee_id,
            "last_name": payload.last_name,
            "department_name": payload.department_name
        };
        employeeList.push(employeeDetail);
    };
 
    // return the list of employees found in the collection
    return employeeList;
}
/

コール仕様の指定

JavaScriptモジュールemployees_moduleで定義された両方のファンクションのコール仕様は、次のパッケージで提供されます。パッケージは、定義者権限ではなく実行者権限で定義されます。パッケージの実行者にはSODA_APPロールが付与されている必要があります。付与されていない場合、エラーがスローされます。

CREATE OR REPLACE PACKAGE employees_pkg 
AUTHID current_user
AS

    PROCEDURE add_employee(employee JSON)
    AS MLE MODULE employees_module
    SIGNATURE 'addEmployee';
 
    FUNCTION find_all_employees
    RETURN JSON
    AS MLE MODULE employees_module
    SIGNATURE 'findAllEmployees';
 
END employees_pkg;
/

コレクションへのサンプル・データの追加

次の匿名PL/SQLブロックは、HR.EMPLOYEESからの2つの行をJSONドキュメントに変換し、前に作成したコール仕様によって外部化されたJavaScriptコードを起動してコレクションに挿入します。

DECLARE
    l_employee  JSON;
BEGIN
    SELECT
        JSON{
            'employee_id' IS e.employee_id,
            'first_name' IS e.first_name,
            'last_name' IS e.last_name,
            'email' IS e.email,
            'phone_number' IS e.phone_number,
            'hire_date' IS e.hire_date,
            'job_id' IS e.job_id,
            'salary' IS e.salary,
            'commission_pct' IS e.commission_pct,
            'manager_id' IS e.manager_id,
            'department_name' IS d.department_name
        }
    INTO
        l_employee
    FROM
        hr.employees e 
        JOIN hr.departments d ON (e.department_id = d.department_id)
    WHERE
        e.employee_id = 100;
    
    employees_pkg.add_employee(l_employee);
 
    SELECT
        JSON{
            'employee_id' IS e.employee_id,
            'first_name' IS e.first_name,
            'last_name' IS e.last_name,
            'email' IS e.email,
            'phone_number' IS e.phone_number,
            'hire_date' IS e.hire_date,
            'job_id' IS e.job_id,
            'salary' IS e.salary,
            'commission_pct' IS e.commission_pct,
            'manager_id' IS e.manager_id,
            'department_name' IS d.department_name
        }
    INTO
        l_employee
    FROM
        hr.employees e 
        JOIN hr.departments d ON (e.department_id = d.department_id)
    WHERE
        e.employee_id = 101;
    
    employees_pkg.add_employee(l_employee);
END;
/

コレクション内のすべての従業員の検索

最後に、EMPLOYEES_PKG.find_all_employees()を使用して、コレクションに格納されている従業員の短縮リストを提供できます。

SELECT 
  JSON_SERIALIZE(
    employees_pkg.find_all_employees
    PRETTY
  ) AS all_employees;
/

結果:

ALL_EMPLOYEES
--------------------------------------------------
[
  {
    "employee_id" : 100,
    "last_name" : "King",
    "department_name" : "Executive"
  },
  {
    "employee_id" : 101,
    "last_name" : "Yang",
    "department_name" : "Executive"
  }
]