123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- 'use strict';
- const cloneRegExp = require('regexp-clone');
- const Decimal = require('../types/decimal128');
- const ObjectId = require('../types/objectid');
- const specialProperties = require('./specialProperties');
- const isMongooseObject = require('./isMongooseObject');
- const getFunctionName = require('./getFunctionName');
- const isBsonType = require('./isBsonType');
- const isObject = require('./isObject');
- const symbols = require('./symbols');
- const utils = require('../utils');
- /*!
- * Object clone with Mongoose natives support.
- *
- * If options.minimize is true, creates a minimal data object. Empty objects and undefined values will not be cloned. This makes the data payload sent to MongoDB as small as possible.
- *
- * Functions are never cloned.
- *
- * @param {Object} obj the object to clone
- * @param {Object} options
- * @param {Boolean} isArrayChild true if cloning immediately underneath an array. Special case for minimize.
- * @return {Object} the cloned object
- * @api private
- */
- function clone(obj, options, isArrayChild) {
- if (obj == null) {
- return obj;
- }
- if (Array.isArray(obj)) {
- return cloneArray(obj, options);
- }
- if (isMongooseObject(obj)) {
- // Single nested subdocs should apply getters later in `applyGetters()`
- // when calling `toObject()`. See gh-7442, gh-8295
- if (options && options._skipSingleNestedGetters && obj.$isSingleNested) {
- options = Object.assign({}, options, { getters: false });
- }
- if (utils.isPOJO(obj) && obj.$__ != null && obj._doc != null) {
- return obj._doc;
- }
- if (options && options.json && typeof obj.toJSON === 'function') {
- return obj.toJSON(options);
- }
- return obj.toObject(options);
- }
- if (obj.constructor) {
- switch (getFunctionName(obj.constructor)) {
- case 'Object':
- return cloneObject(obj, options, isArrayChild);
- case 'Date':
- return new obj.constructor(+obj);
- case 'RegExp':
- return cloneRegExp(obj);
- default:
- // ignore
- break;
- }
- }
- if (obj instanceof ObjectId) {
- return new ObjectId(obj.id);
- }
- if (isBsonType(obj, 'Decimal128')) {
- if (options && options.flattenDecimals) {
- return obj.toJSON();
- }
- return Decimal.fromString(obj.toString());
- }
- if (!obj.constructor && isObject(obj)) {
- // object created with Object.create(null)
- return cloneObject(obj, options, isArrayChild);
- }
- if (obj[symbols.schemaTypeSymbol]) {
- return obj.clone();
- }
- // If we're cloning this object to go into a MongoDB command,
- // and there's a `toBSON()` function, assume this object will be
- // stored as a primitive in MongoDB and doesn't need to be cloned.
- if (options && options.bson && typeof obj.toBSON === 'function') {
- return obj;
- }
- if (obj.valueOf != null) {
- return obj.valueOf();
- }
- return cloneObject(obj, options, isArrayChild);
- }
- module.exports = clone;
- /*!
- * ignore
- */
- function cloneObject(obj, options, isArrayChild) {
- const minimize = options && options.minimize;
- const ret = {};
- let hasKeys;
- for (const k of Object.keys(obj)) {
- if (specialProperties.has(k)) {
- continue;
- }
- // Don't pass `isArrayChild` down
- const val = clone(obj[k], options);
- if (!minimize || (typeof val !== 'undefined')) {
- if (minimize === false && typeof val === 'undefined') {
- delete ret[k];
- } else {
- hasKeys || (hasKeys = true);
- ret[k] = val;
- }
- }
- }
- return minimize && !isArrayChild ? hasKeys && ret : ret;
- }
- function cloneArray(arr, options) {
- const ret = [];
- for (const item of arr) {
- ret.push(clone(item, options, true));
- }
- return ret;
- }
|