123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- 'use strict';
- const Mixed = require('../schema/mixed');
- const clone = require('../helpers/clone');
- const deepEqual = require('../utils').deepEqual;
- const getConstructorName = require('../helpers/getConstructorName');
- const handleSpreadDoc = require('../helpers/document/handleSpreadDoc');
- const util = require('util');
- const specialProperties = require('../helpers/specialProperties');
- const isBsonType = require('../helpers/isBsonType');
- const populateModelSymbol = require('../helpers/symbols').populateModelSymbol;
- /*!
- * ignore
- */
- class MongooseMap extends Map {
- constructor(v, path, doc, schemaType) {
- if (getConstructorName(v) === 'Object') {
- v = Object.keys(v).reduce((arr, key) => arr.concat([[key, v[key]]]), []);
- }
- super(v);
- this.$__parent = doc != null && doc.$__ != null ? doc : null;
- this.$__path = path;
- this.$__schemaType = schemaType == null ? new Mixed(path) : schemaType;
- this.$__runDeferred();
- }
- $init(key, value) {
- checkValidKey(key);
- super.set(key, value);
- if (value != null && value.$isSingleNested) {
- value.$basePath = this.$__path + '.' + key;
- }
- }
- $__set(key, value) {
- super.set(key, value);
- }
- get(key, options) {
- if (isBsonType(key, 'ObjectID')) {
- key = key.toString();
- }
- options = options || {};
- if (options.getters === false) {
- return super.get(key);
- }
- return this.$__schemaType.applyGetters(super.get(key), this.$__parent);
- }
- set(key, value) {
- if (isBsonType(key, 'ObjectID')) {
- key = key.toString();
- }
- checkValidKey(key);
- value = handleSpreadDoc(value);
- // Weird, but because you can't assign to `this` before calling `super()`
- // you can't get access to `$__schemaType` to cast in the initial call to
- // `set()` from the `super()` constructor.
- if (this.$__schemaType == null) {
- this.$__deferred = this.$__deferred || [];
- this.$__deferred.push({ key: key, value: value });
- return;
- }
- const fullPath = this.$__path + '.' + key;
- const populated = this.$__parent != null && this.$__parent.$__ ?
- this.$__parent.$populated(fullPath) || this.$__parent.$populated(this.$__path) :
- null;
- const priorVal = this.get(key);
- if (populated != null) {
- if (value.$__ == null) {
- value = new populated.options[populateModelSymbol](value);
- }
- value.$__.wasPopulated = { value: populated.value };
- } else {
- try {
- value = this.$__schemaType.
- applySetters(value, this.$__parent, false, this.get(key), { path: fullPath });
- } catch (error) {
- if (this.$__parent != null && this.$__parent.$__ != null) {
- this.$__parent.invalidate(fullPath, error);
- return;
- }
- throw error;
- }
- }
- super.set(key, value);
- if (value != null && value.$isSingleNested) {
- value.$basePath = this.$__path + '.' + key;
- }
- const parent = this.$__parent;
- if (parent != null && parent.$__ != null && !deepEqual(value, priorVal)) {
- parent.markModified(this.$__path + '.' + key);
- }
- }
- clear() {
- super.clear();
- const parent = this.$__parent;
- if (parent != null) {
- parent.markModified(this.$__path);
- }
- }
- delete(key) {
- if (isBsonType(key, 'ObjectID')) {
- key = key.toString();
- }
- this.set(key, undefined);
- super.delete(key);
- }
- toBSON() {
- return new Map(this);
- }
- toObject(options) {
- if (options && options.flattenMaps) {
- const ret = {};
- const keys = this.keys();
- for (const key of keys) {
- ret[key] = clone(this.get(key), options);
- }
- return ret;
- }
- return new Map(this);
- }
- $toObject() {
- return this.constructor.prototype.toObject.apply(this, arguments);
- }
- toJSON(options) {
- if (typeof (options && options.flattenMaps) === 'boolean' ? options.flattenMaps : true) {
- const ret = {};
- const keys = this.keys();
- for (const key of keys) {
- ret[key] = clone(this.get(key), options);
- }
- return ret;
- }
- return new Map(this);
- }
- inspect() {
- return new Map(this);
- }
- $__runDeferred() {
- if (!this.$__deferred) {
- return;
- }
- for (const keyValueObject of this.$__deferred) {
- this.set(keyValueObject.key, keyValueObject.value);
- }
- this.$__deferred = null;
- }
- }
- if (util.inspect.custom) {
- Object.defineProperty(MongooseMap.prototype, util.inspect.custom, {
- enumerable: false,
- writable: false,
- configurable: false,
- value: MongooseMap.prototype.inspect
- });
- }
- Object.defineProperty(MongooseMap.prototype, '$__set', {
- enumerable: false,
- writable: true,
- configurable: false
- });
- Object.defineProperty(MongooseMap.prototype, '$__parent', {
- enumerable: false,
- writable: true,
- configurable: false
- });
- Object.defineProperty(MongooseMap.prototype, '$__path', {
- enumerable: false,
- writable: true,
- configurable: false
- });
- Object.defineProperty(MongooseMap.prototype, '$__schemaType', {
- enumerable: false,
- writable: true,
- configurable: false
- });
- Object.defineProperty(MongooseMap.prototype, '$isMongooseMap', {
- enumerable: false,
- writable: false,
- configurable: false,
- value: true
- });
- Object.defineProperty(MongooseMap.prototype, '$__deferredCalls', {
- enumerable: false,
- writable: false,
- configurable: false,
- value: true
- });
- /*!
- * Since maps are stored as objects under the hood, keys must be strings
- * and can't contain any invalid characters
- */
- function checkValidKey(key) {
- const keyType = typeof key;
- if (keyType !== 'string') {
- throw new TypeError(`Mongoose maps only support string keys, got ${keyType}`);
- }
- if (key.startsWith('$')) {
- throw new Error(`Mongoose maps do not support keys that start with "$", got "${key}"`);
- }
- if (key.includes('.')) {
- throw new Error(`Mongoose maps do not support keys that contain ".", got "${key}"`);
- }
- if (specialProperties.has(key)) {
- throw new Error(`Mongoose maps do not support reserved key name "${key}"`);
- }
- }
- module.exports = MongooseMap;
|