I'm working on a project and need to retrieve specific subdocuments from a model by their subdocument _id
's. I then plan on making updates to those subdocuments and saving the main document. The mongoose subdocument documentation lists a number of methods you can call on the parent.children
array, but methods that don't already exist in Javascript for arrays give an error saying they do not exist and it doesn't compile. I'm referencing this documentation: https://mongoosejs.com/docs/subdocs.html
I understand that should be able to use .findOneAndUpdate
to make my updates, and using the runValidators
option everything should still be validated, but I would also like to just retrieve the subdocument itself as well.
I looked at this post: MongoDB, Mongoose: How to find subdocument in found document? , and I will comment that the answer is incorrect that if a subdocument schema is registered it automatically creates a collection for that schema, the collection is only made if that schema is saved separately. You cannot use ChildModel.findOne()
and retrieve a subdocument, as the collection does not exist, there is nothing in it.
Having IChildModel
extend mongoose.Types.Subdocument
and having the IParent
interface reference that instead of IChild
and not registering the ChildModel
does not change anything other than no longer allowing calls to .push()
to not accept simple objects (missing 30 or so properties). Also trying mongoose.Types.Array<IChild>
in the IParent
interface with this method does not change anything.
Changing the IParent
interface to use mongoose.Types.Array<IChild>
for the children
property allows addToSet()
to work, but not id()
or create()
I'm using Mongoose version 5.5.10
, MongoDb version 4.2.0
and Typescript version 3.4.5
import mongoose, { Document, Schema } from "mongoose";
// Connect to mongoDB with mongoose
mongoose.connect(process.env.MONGO_HOST + "/" + process.env.DB_NAME, {useNewUrlParser: true, useFindAndModify: false});
// Interfaces to be used throughout application
interface IParent {
name: string;
children: IChild[];
}
interface IChild {
name: string;
age: number;
}
// Model interfaces which extend Document
interface IParentModel extends IParent, Document { }
interface IChildModel extends IChild, Document { }
// Define Schema
const Child: Schema = new Schema({
name: {
type: String,
required: true
},
age: {
type: Number,
required: true
}
});
const ChildSchema: Schema = Child;
const Parent: Schema = new Schema({
name: {
type: String,
required: true
},
children: [ChildSchema]
});
const ParentSchema: Schema = Parent;
// Create the mongoose models
const ParentModel = mongoose.model<IParentModel>("Parent", Parent);
const ChildModel = mongoose.model<IChildModel>("Child", Child);
// Instantiate instances of both models
const child = new ChildModel({name: "Lisa", age: 7});
const parent = new ParentModel({name: "Steve", children: [child]});
const childId = child._id;
// Try to use mongoose subdocument methods
const idRetrievedChild = parent.children.id(childId); // Property 'id' does not exist on type 'IChild[]'.ts(2339)
parent.children.addToSet({ name: "Liesl", age: 10 }); // Property 'addToSet' does not exist on type 'IChild[]'.ts(2339)
parent.children.create({ name: "Steve Jr", age: 2 }); // Property 'create' does not exist on type 'IChild[]'.ts(2339)
// If I always know the exact position in the array of what I'm looking for
const arrayRetrievedChild = parent.children[0]; // no editor errors
parent.children.unshift(); // no editor errors
parent.children.push({ name: "Emily", age: 18 }); // no editor errors