Skip to content

Commit

Permalink
feat(map): support maps of nested docs
Browse files Browse the repository at this point in the history
Re: #681
  • Loading branch information
vkarpov15 committed Apr 1, 2018
1 parent 7444dde commit 9054bc8
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 10 deletions.
1 change: 1 addition & 0 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ Schema.prototype.path = function(path, obj) {
const mapPath = path + '.$*';
this.paths[path + '.$*'] = Schema.interpretAsType(mapPath,
obj.of || { type: {} }, {});
this.paths[path].$__schemaType = this.paths[path + '.$*'];
}

if (this.paths[path].$isSingleNested) {
Expand Down
2 changes: 1 addition & 1 deletion lib/schema/embedded.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ Embedded.prototype.doValidate = function(value, fn, scope) {
}

if (!(value instanceof Constructor)) {
value = new Constructor(value);
value = new Constructor(value, null, scope);
}
value.validate({__noPromise: true}, fn);
}, scope);
Expand Down
3 changes: 2 additions & 1 deletion lib/schema/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class SchemaMap extends SchemaType {
if (val instanceof MongooseMap) {
return val;
}
return new MongooseMap(val, this.path, doc);

return new MongooseMap(val, this.path, doc, this.$__schemaType);
}
}

Expand Down
61 changes: 58 additions & 3 deletions lib/types/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
*/

class MongooseMap extends Map {
constructor(v, path, doc) {
constructor(v, path, doc, schemaType) {
if (v != null && v.constructor.name === 'Object') {
v = Object.keys(v).reduce((arr, key) => arr.concat([[key, v[key]]]), []);
}
super(v);

this.$__parent = doc;
this.$__path = path;
this.$isMongooseMap = true;
this.$__schemaType = schemaType;

this.$__runDeferred();
}

set(key, value) {
Expand All @@ -25,9 +27,32 @@ class MongooseMap extends Map {
throw new Error('Mongoose maps do not support keys that contain `.`, ' +
'got "' + key + '"');
}

// 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;
}

try {
value = this.$__schemaType.
applySetters(value, this.$__parent, false, this.get(key));
} catch (error) {
this.$__parent.invalidate(this.$__path + '.' + key, error);
return;
}

super.set(key, value);

if (this.$__parent != null) {
if (value != null && value.$isSingleNested) {
value.$basePath = this.$__path + '.' + key;
}

if (this.$__parent != null && this.$__parent.$__) {
this.$__parent.markModified(this.$__path + '.' + key);
}
}
Expand All @@ -43,6 +68,16 @@ class MongooseMap extends Map {
inspect() {
return new Map(this);
}

$__runDeferred() {
if (!this.$__deferred) {
return;
}
for (let i = 0; i < this.$__deferred.length; ++i) {
this.set(this.$__deferred[i].key, this.$__deferred[i].value);
}
this.$__deferred = null;
}
}

Object.defineProperty(MongooseMap.prototype, '$__parent', {
Expand All @@ -57,4 +92,24 @@ Object.defineProperty(MongooseMap.prototype, '$__path', {
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
});

module.exports = MongooseMap;
1 change: 1 addition & 0 deletions lib/types/subdocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Subdocument.prototype.$isValid = function(path) {

Subdocument.prototype.markModified = function(path) {
Document.prototype.markModified.call(this, path);

if (this.$parent && this.$basePath) {
if (this.$parent.isDirectModified(this.$basePath)) {
return;
Expand Down
14 changes: 9 additions & 5 deletions test/types.map.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,23 +124,27 @@ describe('Map', function() {
const TestSchema = new mongoose.Schema({
m: {
type: Map,
of: new mongoose.Schema({ n: Number })
of: new mongoose.Schema({ n: Number }, { _id: false, id: false })
}
});

const Test = db.model('MapEmbeddedTest', TestSchema);

return co(function*() {
let doc = yield Test.create({ m: { bacon: { n: 2 } } });
assert.deepEqual(doc.toObject().m.get('bacon'), { n: 2 });
let doc = new Test({ m: { bacon: { n: 2 } } });

yield doc.save();

assert.ok(doc.m instanceof Map);
assert.deepEqual(doc.toObject().m.get('bacon').toObject(), { n: 2 });

doc.m.get('bacon').n = 4;
yield doc.save();
assert.deepEqual(doc.toObject().m.get('bacon'), { n: 4 });
assert.deepEqual(doc.toObject().m.get('bacon').toObject(), { n: 4 });

doc = yield Test.findById(doc._id);

assert.deepEqual(doc.toObject().m.get('bacon'), { n: 4 });
assert.deepEqual(doc.toObject().m.get('bacon').toObject(), { n: 4 });
});
});
});

0 comments on commit 9054bc8

Please sign in to comment.