/*- * Copyright (c) 2018, 2026 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/ */ import type { Config } from "./config"; /** * NoSQL database allows you to store arbitrary precision decimal numbers * in tables by using database type Number, which supports all * numbers represented by Java BigDecimal class. By default, the driver * represents them as Javascript numbers. However this has some limitations: *
* Because Javascript and Node.js do not currently have a standard way to * represent arbitrary precision decimal numbers, the driver allows you to use * a 3rd party number library of your choice. Typically the number libraries * represent numbers as objects of certain prototype or class and have methods * to perform arithmetic and other operations. If you enable this feature, * the driver will represent the column values of datatype Number as * objects of this type from the number library. You can pass these objects * as record fields for put operations, as key fields for get operations and * they will also be returned as part of get and query results wherever the * column value or expression result is of datatype Number. Then * your application can use methods from the number library to perform further * numeric operations on number objects or convert them to suitable display * representation. *
* In most cases you only need the following: *
* The above is sufficient for most cases when using default settings and * for number libraries that provide standard method names for common * operations. The driver has been tested with the following number * libraries: *
* These libraries should work out of the box with simple steps as described * above. However you may use any number library of your choice as long as * it uses objects to represent arbitrary precision decimal numbers, provides * constructor function to create these objects and supports some common * operations. Below we describe what the driver needs to know from the * number library and whether it will work out of the box as mentioned * above or if it may require additional configuration. *
* The driver needs to know the following: *
* Note that configuration for this feature is always specified as * dbNumber property of {@link Config} and thus can be provided * either as part of {@link Config} Javascript object or in JSON file * containing {@link Config}. *
* dbNumber property can be one of the following: *
* The only required property is {@link Constructor}. As menitoned, * static and instance properties are only needed when * the number library is not using common method names, or if you need to * customize implementation, see {@link NumberLibMethods}. *
* An important note about precision and rounding: *
* Different number libraries have different options to round the results * of arithmetic and other operations. For example, some libraries like * decimal.js and decimal.js-light round to precision, which * is total number of significant digits in the number. These libraries * typically have a precision value specified as configuration setting in the * constructor. Other libraries, like bignumber.js and * big.js round to scale, or decimal places, which is number of * digits after decimal point. These libraries typically have decimal * places value as configuration setting in the constructor. In addition, * some libraries, like decimal.js and bignumber.js do * automatic rounding of some arithmetic operations and other libraries like * big.js and decimal.js-light allow only manual rounding * via methods such as round. *
* The above may have implication on some query results. On the server side, * NoSQL database uses Java BigDecimal and rounds each arithmetic operation * according to precision and rounding mode settings of MathContext, as * mentioned in {@link DBNumberConfig}. The driver, on the other hand, does * not perform rounding during query processing, other than the rounding * performed automatically by some number libraries as mentioned above. This * means that for queries that use arithmetic expressions and some aggregate * functions, the query results may slightly differ when using different * number libraries. From the libraries tested, decimal.js has the * closest matching behavior to query processing on the server side because * it automatically rounds arithmetic operations to precision using rounding * mode setting. *
* You may also use the rounding methods from the number library to perform * rounding after receiving query results. This may be useful when using * number libraries that do not perform automatic rounding. In addition, * number libraries may have other settings related to rounding. See * documentation for the number library of your choice. If required, you can * clone and customize the constructor and pass it as * {@link Constructor} property. You may also customize * arithmetic operations further using static and instance * properties, see {@link NumberLibMethods}, in case non-standard behavior is * needed. *
* The driver need to know precision and rounding mode in order to create * MathContext for server-side query processing. It will try to infer these * settings from the constructor as described in {@link NumberLibPrecision} * and {@link NumberLibRoundingMode}. For libraries that round to scale, * such as bignumber.js and big.js, it is not possible * to infer precision from constructor, so you may need to set * {@link precision} property, otherwise the driver will use the default * value. For {@link precision} and {@link roundingMode} properties, if * set, the driver will use their values instead of inferring them from * the constructor. *
* Note that setting properties {@link precision} and {@link roundingMode} * only affects how rounding is done on the server side. The driver will not * set any properties of the constructor. If the number library does * automatic rounding, it will do so based on the constructor as passed. If * the number library supports precision and rounding mode settings and does * automatic rounding/trucation, you can ensure that the same settings are * used by the server and the client by letting the driver infer them from * the constructor (by using {@link getPrecision} and * {@link getRoundingMode} properties if necessary). E.g. for libraries * decimal.js and decimal.js-light the driver will * automatically infer and use precision and rounding mode settings. */ export interface NumberLibConfig { /** * String representing number library * module name, if the sole export is the constructor, or constructor * function. Constructor function must be able to create instances from * number's string representation or from Javascript number (although * other options may also be provided by the number library). Note the * upper case to disambiguate from Object's constructor property. */ Constructor: string|DBNumberConstructor; /** * In rare cases when the number library export * is not the constructor, you may specify the module name. If this * property is set and {@link Constructor} property is specified as a * string, then the {@link Constructor} property specifies property name * of the module.exports object instead of the module name. */ module?: string; /** * Static method mappings, that is * methods that are properties of the constructor itself. If not set, or * for any required method not present in the mapping, the driver will try * to infer it from constructor. See {@link NumberLibMethods}. */ static?: NumberLibMethods; /** * Instance method mappings, that is * methods that are properties of the constructor's prototype. If not * set, or for any required method not present in the mapping, the driver * will try to infer it from constructor's prototype. See * {@link NumberLibMethods}. */ instance?: NumberLibMethods; /** * Precision to use for rounding of * server-side query calculations on datatype Number. If not * set, the driver will try to infer the value from constructor (see * {@link NumberLibPrecision}). If cannot be inferred, default precision * of 20 is used. */ precision?: number; /** * Rounding mode to use for rounding of * server-side query calculations on datatype Number. See * {@link RoundingModesMap} for details on supported roundings modes. * This property can be specified either as rounding mode name string, * such as 'DOWN', 'UP', 'HALF_DOWN', etc. (with or without * ROUND_ prefix) or as number library-specific constant value, * as long as the driver can find a mapping between rounding mode names * and their values in the number library (see {@link RoundingModesMap}). * If not set, the driver will try to infer the value from constructor * (see {@link NumberLibRoundingMode}). If cannot be inferred, default * value of ROUND_HALF_UP is used. */ roundingMode?: unknown; /** * Specifies how to get * precision value from constructor. See {@link NumberLibPrecision}. If * not set, the driver will use {@link NumberLibConfig#precision}. */ getPrecision?: NumberLibPrecision; /** * Specifies how to get * rounding mode from constructor. See {@link NumberLibRoundingMode}. If * not set, the driver will use {@link NumberLibConfig#roundingMode}. */ getRoundingMode?: NumberLibRoundingMode; /** * Specifies mapping between * rounding mode names and their constant values in the number library. * If not set, the driver will try to infer the mapping from constuctor or * module (see {@link RoundingModesMap}). */ RoundingModes?: RoundingModesMap | string; } /** * Object that specifies method mappings for methods from the number library * that are required by the driver. Set this as * {@link NumberLibConfig#static} property for mappings to static methods * (properties of the constructor) and to {@link NumberLibConfig#instance} * property for mappings to instance methods (properties of the constructor's * prototype). You may also set both of the above to provide static mappings * for some methods and instance mappings for others. For any required * method, one mapping is sufficient (otherwise the driver will prefer * instance over static). *
* Unless otherwise specified, the required operations are binary (i.e. * they operate on two number objects). This means that static methods take 2 * parameters and instance methods have this context as the 1st * number object and pass 2nd number as one parameter. *
* You can specify each method mapping as either string or a function. If * string, the driver will use constructor's or prototype's property by that * name for static or instance method respectively. You may also provide a * function for either static or instance method (as long as it follows the * rules for arguments and this context described in previous * paragraph). This allows you to customize implementation if needed. *
*
* The following assumptions can be made about the argument types: *
* The above means that you only need to set mappings for methods with * non-typical names that would not be found on corresponding name list or if * you with to customize thier implementation. In particular, for 4 tested * number libraries mentioned in {@link DBNumberConfig}, no mappings are * required. */ export interface NumberLibMethods { /** * Compare two numbers n1 and n2. * Return value of this function should be > 0 if n1 > n2, = 0 if n1 = n2 * and < 0 if n1 < n2. If not set, the driver will look for methods named * comparedTo, compareTo, cmp and * compare in that order */ compare?: string|((n1: any, n2: any) => number); /** * Determine if two numbers are * equal. Should return true/false. This property may be useful for * equality checks during query processing which is usually faster than * numeric comparison via {@link compare}. If not set, * the driver will look for instance methods equals, * isEqualTo and eq in that order. If not found, the * driver will use {@link compare} for equality checks. */ valuesEqual?: string|((n1: any, n2: any) => boolean); /** * Add two numbers n1 and n2, return number * object with value of n1 + n2. If not set, the driver will look for * methods named plus and add in that order. */ add?: string|((n1: any, n2: any) => any); /** * Subtract number n2 from n1, return * number object with value of n1 - n2. If not set, the driver will look * for methods named minus, sub and subtract in * that order. */ subtract?: string|((n1: any, n2: any) => any); /** * Multiply numbers n1 and n2, return * number object with value n1 * n2. If not set, the driver will look for * methods named times, multipliedBy, multiply * and mul in that order. */ multiply?: string|((n1: any, n2: any) => any); /** * Divide number n1 by n2, return number * object with value of n1 / n2. If not set, the driver will look for * methods named dividedBy, divide and div in * that order. */ divide?: string|((n1: any, n2: any) => any); } /** * Value of {@link NumberLibConfig#getPrecision} property. * This property may be: *
* This property may be: *
* Also see {@link RoundingModesMap} for explanation on rounding modes. */ export type NumberLibRoundingMode = string | ((cons: DBNumberConstructor) => unknown); /** * Specifies constant values used for rounding modes in the number library. *
* Rounding mode specifies rounding behavior of numerical and/or rounding * operations in the number library, specifically indicating how the least * significant digit of the result should be calculated. There are several * well known rounding modes. NoSQL database supports rounding modes * used by Java BigDecimal and specified as * {@link https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/math/RoundingMode.html | RoundingMode} * enumeration. Also see documentation for the number library of your choice * on supported rounding modes. *
* This object may be specified as {@link NumberLibConfig#RoundingModes} * property and it helps the driver determine the rounding mode constant * values (which are usually Javascript numbers) for different rounding modes * used by the number library. The driver uses it to infer which rounding * mode is used by the library or set as {@link NumberLibConfig#roundingMode} * property, see {@link NumberLibRoundingMode}. *
* In most cases, it is not necessary to set * {@link NumberLibConfig#RoundingModes} and thus use this object. * Specifically, this property is not necessary if: *
* Note that you may also set {@link NumberLibConfig#RoundingModes} to string * in which case it will be used as a property name or path ('.'s allowed) of * constructor or module to the object containing rounding mode constants * instead of checking candidate names as described above. *
* The properties listed correspond to each rounding mode supported * by the driver. Note that some number libraries, such as big.js, * support only a subset of these. In this case the driver will infer all it * can. If neither {@link NumberLibConfig#RoundingModes} nor * {@link NumberLibConfig#roundingMode} properties are set and rounding mode * constants cannot be inferred, the driver will assume default rounding mode * ROUND_HALF_UP. *
* Note that some number libraries may support rounding modes not listed here * and not supported by Java BigDecimal, such as ROUND_HALF_CEIL and * ROUND_HALF_FLOOR. Using of these rounding modes will result in * {@link NoSQLArgumentError}. *
* Although numeric rounding mode values are usually used, if the library * of your choice using other type for rounding mode constants you may specify * them as well. Any value is allowed except undefined and * null. */ export interface RoundingModesMap { /** * Constant value for ROUND_UP rounding mode. */ UP?: unknown; /** * Constant value for ROUND_DOWN rounding mode. */ DOWN?: unknown; /** * Constant value for ROUND_CEILING rounding mode. */ CEILING?: unknown; /** * Constant value for ROUND_FLOOR rounding mode. */ FLOOR?: unknown; /** * Constant value for ROUND_HALF_UP rounding mode. */ HALF_UP?: unknown; /** * Constant value for ROUND_HALF_DOWN rounding mode. */ HALF_DOWN?: unknown; /** * Constant value for ROUND_HALF_EVEN rounding mode. */ HALF_EVEN?: unknown; }