1

I could not find any response to my issue. Probably also a search issue, but would appreciate some help. I have the following model:

var ChildSchema =  new Schema ({
    'name': string
});
var ClassSchema =  new Schema ({
    'name': string, 
    'children': [ChildSchema] 
});

I have only declared a model for class.

mongoose.model ('Class', ClassSchema); 

and then I have another model:

var TeacherSchema = new Schema ({
   'name': string, 
   'favorite': {type: ObjectId, ref: 'Class.children'} // a child from class children
   'least': {type: ObjectId, ref: 'Class.children'} // same as above
}); 

With relevant model:

mongoose.model ('Teacher', TeacherSchema); 

I am trying to retrieve a teacher by ID and get the children name populated:

Teacher.findbyId (teacherId).populate (favorite least)... 

This does not seem to work. Is this modelling correct? Is this possible?

(I am aware that my example might not be politically correct, so please forgive me...)

EDIT This is a reflection of something in project. I am aware that the children can be modelled as seperate collection, but in my project, I prefer to keep them as embedded documents (for various reasons). Each child does get an _id when pushed into the children array and class is saved. The teacher needs to point to a single child in a class in "favorite" and in "least", i.e. to keep the _id of that child.

Tally Barak
  • 282
  • 4
  • 11
  • I can see that the problem is that I need to pass classId as well, just not sure how. – Tally Barak May 16 '15 at 19:48
  • I've never seen a reference like that `ref:'Class.children'`, usually its `ref:'Class'` or `ref:'Children'`. Also, isn't `Class.children` an *Array* (of `[ChildSchema]`) and so it wouldn't have an `id` which is used to actually reference the document – laggingreflex May 16 '15 at 20:10
  • the array is subdocument, hence it is getting an _id. From the mongoose documentation: [link] (http://mongoosejs.com/docs/subdocs.html) Sub-documents enjoy all the same features as normal documents. The only difference is that they are not saved individually, they are saved whenever their top-level parent document is saved. – Tally Barak May 16 '15 at 20:12
  • http://stackoverflow.com/questions/24853383/mongoose-objectid-that-references-a-sub-document – Tally Barak May 16 '15 at 20:14
  • There are comments on that question that say Option 1 didn't work for them. I don't see how it would've either tbh. Basically you're referencing a simple plain Array. When you embed documents, sure they behave like regular docs, but they are still wrapped in an Array, and that Array doesn't have an ID, which is what's needed to reference a document. – laggingreflex May 16 '15 at 20:21
  • First of all, thanks for your help. I do not mean to be rude and argue, as I appreciate the help. The end result may be that you are right and this is not doable... As for _id. I did a class.children.push ({name: 'John'}) and saved class, and it in fact did get an _id on the child. since those are also _ids, I can search the class collection by class.find (children._id: childXXXXX) and get the child's parent class, so there should be no problem to locate the class and child by id. – Tally Barak May 16 '15 at 20:26
  • No worries at all. I think you have a misunderstanding. "the array is subdocument" < that is wrong, the array itself is not a subdocument, it's just an array of subdocuments. The array also doesn't have an `_id`, its subdocuments do. If you do `console.log(class.children._id)` it'll give you `undefined`. Now if that's undefined how can you use it to reference to a document. BTW even if it weren't, you're trying to link to teacher **a** favorite student (singular, right?) then how come you're referencing `Class.children` (plural)? The logic itself should make it impossible – laggingreflex May 16 '15 at 20:47
  • I am trying to link to a child, I.e. a sub document in the children array. I assumed that if I reference the array, class.children, and the model specifies it is an array of subdocuments, it will know to get a child. – Tally Barak May 16 '15 at 20:51
  • 1
    I think what you need is to simply create a model out of `ChildSchema` and then reference to that directly. – laggingreflex May 16 '15 at 20:57
  • That does not make sense, as I need to say where I expect to find the sub documents. I can potentially define another schema, let's say family , and reuse the children schema there. How will it know to look in the class model and not the family model? – Tally Barak May 16 '15 at 21:02
  • 1
    Ok first I think at this point first I should bring up the distinction between the **Mongoose**'s Schemas and Models, and **MonogoDB**'s Collections. Unless you already know it, Mongoose's Schemas and Models are just a higher level wrappers, otherwise all that MongoDB does is store Documents in Collections. So coming back to your case, `ChildSchema` or the related model in Mongoose will all just ultimately make a Collection "children" in MongoDB which will have various documents. If you want to differentiate them as Family children or Class children, you'll need to store a flag. – laggingreflex May 16 '15 at 21:09
  • Or maybe you'll need to get the Class or Family first, and then get their children to make that distinction. – laggingreflex May 16 '15 at 21:12
  • you are trying to reference something not present on the mongodb schema, just declare the "children" schema in mongo and use "ref: 'children' " – Sebastián Espinosa May 17 '15 at 07:49
  • I have a child schema, and childrent is the array of child schema. What do you mean by declare the children schema? Children are stored inside a class, not as a seperate collection – Tally Barak May 17 '15 at 07:58
  • It means create a model out of the schema `mongoose.model('Children', ChildSchema)`. You need to make it a separate **collection** (in MongoDB) before you can reference to it. Creating a Model does exactly that. It creates a **collection** in MongoDB with the same name as passed as first argument ('Children'). Just creating a Schema doesn't "connect" it to MongoDB. That connection (to a MongoDB **collection**) is what's needed to reference (`'ref: xxx'`) a document. – laggingreflex May 18 '15 at 05:36
  • Yes this is standard. The point of my question is that children should be sub document of class and not a seperate collection. – Tally Barak May 18 '15 at 05:38
  • I guess then Mongoose has you misguided then. The point of subdoment is only that it's a separate document in MongoDB but Mongoose will make it behave as if it's actually a subdocument. – laggingreflex May 18 '15 at 05:41
  • Also, when referring to subdocument, it could mean two things - [Embedded documents](http://mongoosejs.com/docs/2.7.x/docs/embedded-documents.html) and [Referenced (populated) documents](http://mongoosejs.com/docs/populate.html). Embedded documents don't actually exist as separate collection in MongoDB database. So that's why you can't actually reference to them (unless you make them as their own separate documents as well, as discussed above). – laggingreflex May 18 '15 at 05:44
  • The embedded documents from v2.7 http://mongoosejs.com/docs/2.7.x/docs/embedded-documents.html were replaced by sub docs in 4 http://mongoosejs.com/docs/subdocs.html. You can see that the same text was saved with a different term. It seems that the solution is to manually populate the sub / embedded document (or write my own plugin for it). – Tally Barak May 18 '15 at 08:06
  • 2
    I see. It changes nothing though. Embedded documents (syntax: `children: [childSchema]`) are inherently different from referenced (populate-able) documents (syntax: `{ref: 'Children'}`) in two regards: (1.) embedded docs don't actually need to be populated, they're already a *part* of the main document (down at the MongoDB level). (2.) referenced (populate-able) docs are stored as their own documents (collection) and thus they're the ones that actually *need* to be populated. In short: **you can't populate embedded docs**. PS: I don't get notification unless you mention me @laggingreflex – laggingreflex May 18 '15 at 08:32
  • 1
    @laggingreflex that seems to conclude it. https://github.com/Automattic/mongoose/issues/2772 – Tally Barak May 18 '15 at 09:01

2 Answers2

0

Answer seems to be: not doable. github.com/Automattic/mongoose/issues/2772 Also - could not find an appropriate plugin for it.

Tally Barak
  • 282
  • 4
  • 11
0

I have a similar problem. At least if you have a reference back to the Class from Child, this could work:

var TeacherSchema = new Schema ({
   'class': {type: ObjectId, ref: 'Class'}
   'name': string, 
   '_favorite': {type: ObjectId, ref: 'Class.children'} // a child from class children
   '_least': {type: ObjectId, ref: 'Class.children'} // same as above
}); 


TeacherSchema.virtual('favorite').get(function() {
  return this.class.children.id(this._favorite);
});
TeacherSchema.virtual('favorite').set(function(favorite) {
  this._favorite = favorite;
});

TeacherSchema.virtual('least').get(function() {
  return this.class.children.id(this._least);
});
TeacherSchema.virtual('least').set(function(least) {
  this._least = least;
});

TeacherSchema.set('toJSON', { getters: true, virtuals: true });

This works as long as a teacher has only 1 class.

sebbulon
  • 613
  • 7
  • 21