123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- 'use strict';
- 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 trustedSymbol = require('./query/trusted').trustedSymbol;
- 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(utils.isMongooseArray(obj) ? obj.__array : 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 });
- }
- const isSingleNested = obj.$isSingleNested;
- if (utils.isPOJO(obj) && obj.$__ != null && obj._doc != null) {
- return obj._doc;
- }
- let ret;
- if (options && options.json && typeof obj.toJSON === 'function') {
- ret = obj.toJSON(options);
- } else {
- ret = obj.toObject(options);
- }
- if (options && options.minimize && isSingleNested && Object.keys(ret).length === 0) {
- return undefined;
- }
- return ret;
- }
- const objConstructor = obj.constructor;
- if (objConstructor) {
- switch (getFunctionName(objConstructor)) {
- case 'Object':
- return cloneObject(obj, options, isArrayChild);
- case 'Date':
- return new objConstructor(+obj);
- case 'RegExp':
- return cloneRegExp(obj);
- default:
- // ignore
- break;
- }
- }
- if (isBsonType(obj, 'ObjectID')) {
- return new ObjectId(obj.id);
- }
- if (isBsonType(obj, 'Decimal128')) {
- if (options && options.flattenDecimals) {
- return obj.toJSON();
- }
- return Decimal.fromString(obj.toString());
- }
- // object created with Object.create(null)
- if (!objConstructor && isObject(obj)) {
- return cloneObject(obj, options, isArrayChild);
- }
- if (typeof obj === 'object' && 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 (typeof obj.valueOf === 'function') {
- return obj.valueOf();
- }
- return cloneObject(obj, options, isArrayChild);
- }
- module.exports = clone;
- /*!
- * ignore
- */
- function cloneObject(obj, options, isArrayChild) {
- const minimize = options && options.minimize;
- const omitUndefined = options && options.omitUndefined;
- const seen = options && options._seen;
- const ret = {};
- let hasKeys;
- if (seen && seen.has(obj)) {
- return seen.get(obj);
- } else if (seen) {
- seen.set(obj, ret);
- }
- if (trustedSymbol in obj) {
- ret[trustedSymbol] = obj[trustedSymbol];
- }
- let i = 0;
- let key = '';
- const keys = Object.keys(obj);
- const len = keys.length;
- for (i = 0; i < len; ++i) {
- if (specialProperties.has(key = keys[i])) {
- continue;
- }
- // Don't pass `isArrayChild` down
- const val = clone(obj[key], options, false);
- if ((minimize === false || omitUndefined) && typeof val === 'undefined') {
- delete ret[key];
- } else if (minimize !== true || (typeof val !== 'undefined')) {
- hasKeys || (hasKeys = true);
- ret[key] = val;
- }
- }
- return minimize && !isArrayChild ? hasKeys && ret : ret;
- }
- function cloneArray(arr, options) {
- let i = 0;
- const len = arr.length;
- const ret = new Array(len);
- for (i = 0; i < len; ++i) {
- ret[i] = clone(arr[i], options, true);
- }
- return ret;
- }
- function cloneRegExp(regexp) {
- const ret = new RegExp(regexp.source, regexp.flags);
- if (ret.lastIndex !== regexp.lastIndex) {
- ret.lastIndex = regexp.lastIndex;
- }
- return ret;
- }
|