Reputation: 5758
I'm trying to define a folder schema in Mongoose. For each folder, I'd like to store a reference to the parent folder, as well as an array of child subfolders (to make traversal in either direction easy):
var folderSchema = new mongoose.Schema({
name: String,
parent: { type: Schema.Types.ObjectId, ref: 'Folder' },
subfolders: [{ type: Schema.Types.ObjectId, ref: 'Folder' }],
});
When creating a folder, I want to be able to only specify the parent folder, and then have a pre-save hook take care of adding the subfolder to the parent folder's subfolders
array. This part works fine:
folderSchema.pre('save', function (next) {
var self = this;
// If creating a subfolder, then add the subfolder to
// the parent folder's "subfolders" array
if (this.parent) {
Folder.findById(this.parent, function (err, parent) {
if (err) return next(err);
if (parent) {
parent.subfolders.push(self);
return parent.save(next);
}
return next();
});
} else {
next();
}
});
Also, when deleting a subfolder, I would like to use a pre-remove hook to automatically remove the subfolder from the parent folder's subfolders
array. Unfortunately, I can't get this part to work:
folderSchema.pre('remove', function (next) {
var self = this;
// If deleting a subfolder, then remove it from the parent
// folder's "subfolders" array
//
// (Note: it would also be a good idea to recursively remove
// all subfolders here, but I'm not attempting this yet.)
if (this.parent) {
Folder.findById(this.parent, function (err, parent) {
if (err) return next(err);
if (parent) {
parent.subfolders.id(self._id).remove(); // TypeError: Object 53a64349741d1ae82274c9a2 has no method 'id'
return parent.save(next);
}
return next();
});
} else {
next();
}
});
The line that's erroring out is this one:
parent.subfolders.id(self._id).remove();
It results in this error:
TypeError: Object 53a64349741d1ae82274c9a2 has no method 'id'
at Promise.<anonymous> (C:\Users\serg\temp\mongoose\folders\index.js:53:35)
at Promise.<anonymous> (C:\Users\serg\temp\mongoose\folders\node_modules\mongoose\node_modules\mpromise\lib\promise.js:177:8)
at Promise.EventEmitter.emit (events.js:95:17)
at Promise.emit (C:\Users\serg\temp\mongoose\folders\node_modules\mongoose\node_modules\mpromise\lib\promise.js:84:38)
at Promise.fulfill (C:\Users\serg\temp\mongoose\folders\node_modules\mongoose\node_modules\mpromise\lib\promise.js:97:20)
at C:\Users\serg\temp\mongoose\folders\node_modules\mongoose\lib\query.js:1393:13
at model.Document.init (C:\Users\serg\temp\mongoose\folders\node_modules\mongoose\lib\document.js:250:11)
at completeOne (C:\Users\serg\temp\mongoose\folders\node_modules\mongoose\lib\query.js:1391:10)
at Object.cb (C:\Users\serg\temp\mongoose\folders\node_modules\mongoose\lib\query.js:1150:11)
at Object._onImmediate (C:\Users\serg\temp\mongoose\folders\node_modules\mongoose\node_modules\mquery\lib\utils.js:137:16)
What I was trying to do is use the MongooseDocumentArray#id
method to remove the sub document, as shown in the example on the Sub Docs page of Mongoose's documentation. However, it looks like that method is not available, and I'm not sure why that is. Did I define the schema incorrectly? Is the id
method not available from within middleware? Or am I simply not using it correctly?
Upvotes: 2
Views: 6986
Reputation: 23390
The difference here is that you're using an embedded reference to the object in your array, rather than a direct sub-document. In the mongoose example of sub-document, it shows:
var childSchema = new Schema({ name: 'string' });
var parentSchema = new Schema({
children: [childSchema]
});
So in this example, each "child" is actually stored directly within the "parent" (so the child lives within the parent collection). In your scenario, you have a reference to your subfolders
ID rather than the direct storing of the object:
subfolders: [{ type: Schema.Types.ObjectId, ref: 'Folder' }],
So when you perform your query, the result in subfolders
is actually an array of IDs, one of which is 53a64349741d1ae82274c9a2
. This has no method id
which is causing your problem. However, you could use populate
to populate the objects for those IDs. But even doing that wouldn't give you the DocumentArray
function you need, since mongoose treats these as simply an Array
, rather than a DocumentArray
(which would have the id
function you are looking for). You can see the logic which it uses to determine how to cast these objects here. In the end, you'll see that when you load these objects mongoose casts them as an Array
type:
return new Types.Array(path, cast || Types.Mixed, obj);
So if you want this id
function you can change your schema, or simply do what it does in place. The logic isn't that much, so you might consider just searching the array for the one that matches that ID manually.
Upvotes: 8