UNPKG

mongo-dot-notation

Version:
113 lines (112 loc) 3.86 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.flatten = void 0; const operator_1 = require("./operator"); const mergeOptions = (options) => (Object.assign({ array: false, skipEmptyObjects: false }, options)); /** * Transforms a given object into the MongoDB's update instructions. * @param value the input object to transform * * @example * ```ts * flatten({ * first_name: $rename('firstName'), * lastName: 'Doe', * updatedOn: $timestamp(), * address: { * primary: true * }, * access: $push(new Date()), * }); * * // equivalent to: * { * "$rename": { * "first_name": "firstName" * }, * "$set": { * "lastName": "Doe", * "address.primary": true * }, * "$currentDate": { * "updatedOn": { * "$type": "timestamp" * } * }, * "$push": { * "access": new Date() * } * } * ``` * * @example * ```ts * // update arrays * flatten({ scores: [{ value: 4 }, {value: 2}] }, { array: true} ); * * // equivalent to: * { * "$set": { * "scores.0.value": 4, * "scores.1.value": 2, * } * } * ``` */ const flatten = (value, options) => { if (isAtomic(value) || Array.isArray(value)) { return value; } const keyValues = Object.entries(value); if (keyValues.length === 0) { return value; } const d = dot(mergeOptions(options)); return keyValues.reduce((acc, [key, value]) => d(acc, key, value), {}); }; exports.flatten = flatten; const mergeInner = (obj, field, inner, value) => (Object.assign(Object.assign({}, obj), { [field]: Object.assign(Object.assign({}, obj[field]), { [inner]: value }) })); const dot = (options) => { const merge = (instructions, operator, field, value) => { if (!(0, operator_1.isOperator)(value)) { return mergeInner(instructions, operator, field, value); } if ((0, operator_1.getType)(value) === 'merge') { const mergeValue = (0, operator_1.getValue)(value); return isNullOrUndefined(mergeValue) ? instructions : dotMerge(instructions, `${field}.${operator}`, mergeValue); } return mergeInner(instructions, (0, operator_1.getType)(value), `${field}.${operator}`, (0, operator_1.getValue)(value)); }; const visit = (instructions, field, value) => { const tail = isAtomic(value) || (Array.isArray(value) && !options.array); if (tail) { return merge(instructions, '$set', field, value); } if ((0, operator_1.isOperator)(value)) { return merge(instructions, (0, operator_1.getType)(value), field, (0, operator_1.getValue)(value)); } const keyValues = Object.entries(value); if (keyValues.length === 0) { const ignoreValue = (Array.isArray(value) && options.array) || options.skipEmptyObjects; return ignoreValue ? instructions : merge(instructions, '$set', field, value); } return keyValues.reduce((acc, [key, value]) => visit(acc, `${field}.${key}`, value), instructions); }; return visit; }; const dotMerge = dot({ array: true, skipEmptyObjects: true }); const isAtomic = (value) => isPrimitive(value) || isBsonType(value); const TYPEOF_PRIMITIVES = ['number', 'string', 'boolean', 'symbol', 'bigint']; /* istanbul ignore next */ const INSTANCEOF_PRIMITIVES = [Date, RegExp, typeof Buffer !== undefined ? Buffer : []] .flat() .filter(Boolean); const isPrimitive = (value) => { return (isNullOrUndefined(value) || TYPEOF_PRIMITIVES.some((type) => typeof value === type) || INSTANCEOF_PRIMITIVES.some((type) => value instanceof type)); }; const isNullOrUndefined = (value) => value === null || typeof value === 'undefined'; const isBsonType = (value) => '_bsontype' in value;