oracle-nosqldb
Version:
Node.js driver for Oracle NoSQL Database
273 lines (267 loc) • 11.7 kB
JavaScript
/*-
* Copyright (c) 2018, 2025 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl/
*/
'use strict';
/**
* Desribes row and key format and datatypes used by the driver.
*/
/**
* This is a marker type for records such as table rows or query results.
* <p>
* The driver uses plain JavaScript objects to represent table rows for
* put operations and as results of get operations and query results. A row
* object consists of properties each designating a table field (on input) or
* field generated by a query (on output). The property keys must
* match their corresponding field names and property values are used as
* corresponding field values. The match between the keys and field names
* is case-insensitive. For put operations such as {@link NoSQLClient#put},
* the property values must be of a type that maps to the corresponding data
* type of the table field as described in {@link FieldValue}. When a row
* object is received on output, such as from {@link NoSQLClient#get}, its
* value will always conform to the schema of the table from which the it was
* received or the implied schema of a query projection.
* <p>
* Note the following:
* <ul>
* <li>To represent rows as objects, you may only use plain JavaScript
* objects, that is object literals. The driver will reject any class
* instances</li>
* <li>You may also specify a valid JSON string as a row value, in which case
* it will be parsed via <em>JSON.parse</em> and the result subjected to the
* same validation as above</li>
* </ul>
*
* @example //Put
* //Let's assume the following table schema:
* //Emp(id INTEGER, lastName STRING, firstName STRING, address RECORD(
* //city STRING, streetName STRING, streetNo INTEGER)) PRIMARY KEY(id)
*
* const client = new NoSQLClient(...);
* .....
* let res = await client.put('Emp', {
* id: 1000,
* lastName: 'Doe',
* firstName: 'Jonh',
* address: {
* city: 'Redwood City',
* streetName: 'Broadway',
* streetNo: 1000
* }
* });
*
* @example //Query, continued from previous example
* const opt = {};
* do {
* res = await client.query('SELECT id, address.city from Emp', opt);
* if (res.rows.length) {
* console.log(res.rows[0]);
* //{ id: 1000, city: 'Redwood City' }
* }
* opt.continuationKey = res.continuationKey;
* } while(res.continuationKey);
*
* @example //Delete, continued from previous example
* //Primary key is represented same as row, but only with primary key fields
* //included.
* res = await client.delete('Emp', { id: 1000 });
*
* @see {@link NoSQLClient#put}
* @see {@link NoSQLClient#get}
* @see {@link NoSQLClient#query}
*
* @global
* @typedef {object} Row
*/
/**
* This is a marker type for primary keys, index keys, full or partial. The
* key is represented in the same way as {@link Row}, as plain JavaScript
* object, but only key fields are included.
* @see {@link Row}
*
* @global
* @typedef {object} Key
*/
/**
* This is a marker type for field values which are property values of the
* {@link Row} and {@link Key} objects.
* <p>
* FieldValue represents a data item in Oracle NoSQL Database.
* FieldValue objects can be one of several JavaScript and Node.js types,
* the type being dependent on how this value maps onto a given database
* type. For put and other operations where field values are used as input
* their type must be one of the allowable types that maps onto given
* database type (usually being the type of a table field).
* <p>
* FieldValue objects used for put operations are not validated against the
* target table schema in the driver. Validation happens where the table
* schema is available. If an instance does not match the target table an
* error results.
* <p>
* You may specify field value as a (synchronous) function. In this case the
* function will be called with no arguments and its return value will be used
* as the field value. This could be useful if this function is returned from
* a closure context or bound to a class that automatically generates field
* values in some manner.
* <p>
* FieldValue objects returned by the driver (such as from get or query
* operations) always conform to a table schema, or to the shape implied
* by a query projection.
* <p>
* Here we will discuss how JavaScript types map onto the database types and
* vice versa. When field values are used as an input (such as by
* {@link NoSQLClient#put}), some conversions are allowed so there could be
* more than one JavaScript type used for given database type. In this
* instance, we will list preferred JavaScript type first followed by others
* if any. For output field values returned by the driver (such as by
* {@link NoSQLClient#get} and {@link NoSQLClient#query})
* there is a definite JavaScript type used for a given database type. For
* completeness, we will list mappings on input and on output separately.
* Note that for composite database types such as Array, Map, Record and
* JSON, their constituent elements also follow these mappings.
* <p>
* For datatype <em>Number</em>, the driver supports integration with 3rd
* party number libraries such as decimal.js, bignumber.js and others. See
* {@link DBNumberConfig} for details. If this feature is enabled, the
* field value will be an object representing number in the 3rd party
* library, indicated in the table below as "3rd party number". Thus 3rd
* party number will be the output type and preferred input type. If this
* feature is not enabled, the output type and preferred input type will be
* Javascript number. In both cases, you may also use string (representing
* number) as an input type.
* <p>
* Oracle NoSQL Database has a special type JSON NULL, which represents a
* [JSON]{@link https://www.json.org} type NULL. JSON NULL may occur as a
* value of a field of Oracle NoSQL Database type JSON or one of its
* subfields that has a value 'null'. JSON NULL value is different and
* separate from SQL NULL. For example, if table <em>MyTable</em> has a field
* <em>info</em> of type JSON, a query such as
* <em>SELECT info.name from MyTable</em> will yield different results for
* records where the value of <em>info.name</em> is null (e.g. if the value of
* <em>info</em> is { "name": null }) with result being JSON NULL and for
* records where the value of the <em>info</em> field itself is NULL
* (SQL NULL), with result being a SQL NULL. In addition, the <em>info</em>
* field itself may take values of SQL NULL or JSON NULL which are distinct
* values, the latter being a value of a JSON type NULL (i.e. the value of
* <em>info</em> is JSON value <em>null</em>). For more details, please see
* [SQL Reference Guide]{@link https://docs.oracle.com/en/database/other-databases/nosql-database/20.2/sqlreferencefornosql/}
* and SQL for Oracle NoSQL Database Specification.
* <p>
* The driver represents JSON NULL as JavaScript type <b>null</b> and SQL NULL
* as JavaScript type <b>undefined</b>. When such distinction is not
* important, you may use non-strict comparison (e.g. <em>value == null</em>)
* to determine if the field value is NULL (either SQL or JSON). To
* distinquish between JSON and SQL NULL, use strict comparison (e.g.
* <em>value === undefined</em>). Note that for non-JSON fields, on input you
* may pass either undefined or null, or omit a field alltogether for
* {@link NoSQLClient#put} operations, all of the above being interpreted as
* SQL NULL. For JSON fields, on input you may use either null or undefined
* as a value of the subfield (e.g. <em>info.name</em>) which will be
* interpreted as JSON NULL, but for the field itself (e.g. <em>info</em>)
* null and undefined will be treated as distinct values (JSON NULL and SQL
* NULL correspondingly) as mentioned above.
*
* <table>
* <tr><th>Database Type</th><th>JavaScript Input Type(s)</th><th>JavaScript
* Output Type</th></tr>
* <tr>
* <td>Array</td><td>Array</td>
* <td>Array</td>
* </tr>
* <tr>
* <td>Binary</td><td>Buffer</td>
* <td>Buffer</td>
* </tr>
* <tr>
* <td>Boolean</td><td>boolean</td><td>boolean</td>
* </tr>
* <tr>
* <td>Double</td><td>number</td><td>number</td>
* </tr>
* <tr>
* <td>Enum</td><td>string</td><td>string</td>
* </tr>
* <tr>
* <td>Fixed Binary</td><td>Buffer</td>
* <td>Buffer</td>
* </tr>
* <tr>
* <td>Float</td><td>number</td><td>number</td>
* </tr>
* <tr>
* <td>Integer</td><td>number</td><td>number</td>
* </tr>
* <tr>
* <td>Json</td><td>object, string, Map</td>
* <td>object</td>
* </tr>
* <tr>
* <td>Json Null</td><td>null, undefined (only as sub-field of JSON field)</td>
* <td>null</td>
* </tr>
* <tr>
* <td>Long</td><td>number, bigint</td>
* <td>number, bigint</td>
* </tr>
* <tr>
* <td>Map</td><td>object, Map</td><td>object</td>
* </tr>
* <tr>
* <td>SQL Null</td><td>undefined (or omit field), null (only for non-JSON fields)</td>
* <td>undefined</td>
* </tr>
* <tr>
* <td>Number</td><td>3rd party number, Javascript number (with limitation), string</td><td>3rd party number or Javascript number (with limitation)</td>
* </tr>
* <tr>
* <td>Record</td><td>object, Map</td><td>object</td>
* </tr>
* <tr>
* <td>String</td><td>string</td><td>string</td>
* </tr>
* <tr>
* <td>Timestamp</td><td>Date, string</td>
* <td>Date</td>
* </tr>
* </table>
* <p>
* Note the following:
* <ul>
* <li>For database types Binary and FixedBinary the driver uses Node.js
* <em>Buffer</em> class</li>
* <li> For database type <em>Long</em>, you have an option to use either
* JavaScript type <em>number</em> or <em>bigint</em>. By default,
* <em>number</em> is used. Note that JavaScript type <em>number</em> cannot
* have precise representation of integers outside of safe integer range,
* that is outside of range from <em>Number.MIN_SAFE_INTEGER</em> to
* <em>Number.MAX_SAFE_INTEGER</em>. For values outside that range (that is
* when their magnitude exceeds 53 bits in size), loss of precision may
* occur. Alternatively, you can use JavaScript type
* [bigint]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt}
* by setting option {@link Config}#longAsBigInt. In this case, values of
* datatype <em>Long</em> returned by get and query operations will be
* represented as <em>bigint</em>, which avoids the loss of precision for the
* entire range of datatype <em>Long</em>. For values passed to operations
* such as put and delete, you may use either <em>bigint</em> or
* <em>number</em>. Note that in either case <em>Long</em> values passed to
* the driver may not be outside the range of datatype <em>Long</em> (from
* -2^63 to 2^63-1)</li>
* <li>For datatype <em>Number</em>, unless you are using 3rd party number
* library feature (see {@link DBNumberConfig}), the driver will return
* Javascript number values in the results, which means loss of precision and
* rounding errors may occur in some cases. On input (for put operations),
* you may pass NoSQL Number values either as 3rd party numbers (if enabled),
* Javascript numbers or strings</li>
* <li>To represent database type Timestamp, you may use either
* JavaScript <em>Date</em> class or a string. The string
* should represent a date in valid ISO 8601 format. Timestamps are always
* stored and managed in UTC</li>
* </ul>
*
* @tutorial tables
*
* @global
* @typedef {(string|number|boolean|object|Array|Map|Buffer|Date)} FieldValue
*/