Can't get Mongoose.js Subdocument Array to populate - node.js

I'm using mongoose.js on a node.js server connecting to mongodb and
I have a mongoose model like the following
SubSchema = new Schema({
_member: {type: ObjectId, ref: 'Member'},
members: [{type: ObjectId, ref: 'Member'}],
created: { type: Date, default: Date.now }
});
mongoose.model('SubModel', SubSchema);
MainSchema = new Schema({
_member: {type: ObjectId, ref: 'Member'},
subs: [SubSchema],
members: [{type: ObjectId, ref: 'Member'}],
created: { type: Date, default: Date.now }
});
var MainModel mongoose.model('MainModel', MainSchema);
which i pull with a command like this
var q = MainModel.find({})
.sort('created', -1)
.limit(25)
.populate("_member")
.populate("subs._member")
.populate("subs.members");
q.execFind(function(err, mains){
//mains[0].subs[0].members - will always be empty why?
});
my problem is that i can't get subs.members array to populate or even load, it just keeps showing as an empty array.
I've tried .populate("subs.members") to no avail even though subs._member loads just fine

try this
SubSchema = new Schema({
_member: {type: ObjectId, ref: 'Member'},
members: [{type: ObjectId, ref: 'Member'}],
created: { type: Date, default: Date.now }
});
var SubModel = mongoose.model('SubModel', SubSchema);//add
MainSchema = new Schema({
_member: {type: ObjectId, ref: 'Member'},
subs: [SubSchema],
members: [{type: ObjectId, ref: 'Member'}],
created: { type: Date, default: Date.now }
});
var MainModel = mongoose.model('MainModel', MainSchema);
MainModel.find({})
.sort('created', -1)
.limit(25)
.populate("_member")
.populate("subs._member")
.exec(function(err, mains){
//add
SubModel.populate(mains,{
path:'subs.members'
},function(err,mains){
//mains[0].subs[0].members - is not empty
});
});

#leesei: I can't comment on your post (too little rep), so I leave this as a separate answer.
In mongoose 3.6 subdoc population still doesn't work, the issue github.com/LearnBoost/mongoose/issues/1381 has been closed 7 months ago with the following solution as a workaround. I had to change it slightly to merge the populated subdocument back to the main document.
The subdocument's model Story has to be specified explicitly:
Person.findById(user1._id).populate("stories")
.exec(function(err, doc {
Story.populate(doc.stories, {path: 'creator'}, function (err, stories) {
doc.stories = stories;
return doc;
})
})
In the solution above this works:
Story.populate(doc.stories, {path: 'creator'}, callback)
but this still won't work:
Story.populate(doc, {path: 'stories.creator'}, callback)

Follow-up on #JohnnyHK's post, you can specify the Model to use in populate() for now:
https://github.com/LearnBoost/mongoose/issues/1377#issuecomment-15920370

I had several nest layers deep of sub docs, and none of the supplied options worked for me. I found this amazing Mongoose plugin that will do deep population seamlessly. You just use the same syntax you would expect to work with populate, but it actually works.
https://github.com/buunguyen/mongoose-deep-populate

I have something that looks a slightly different but populates the document with the array items. I'm wondering if it's the objectid's that are causing the issues.
var mongoose = require('mongoose');
var Schema = mongoose.Schema, ObjectID = Schema.ObjectId;
var SubSchema = new Schema({
testsub: String,
created: { type: Date, default: Date.now }
});
var MainSchema = new Schema({
test: String
subs: [SubSchema],
created: { type: Date, default: Date.now }
});
mongoose.model('MainSchema', MainSchema, mainschema);
var query = MainSchema.find({});

Related

How to create a dynamic nested mongoose document with the same schema on multiple levels

Before everyone tells me I can't call a const before initializing, I do know that.
But I think this is the simplest way to render the concept I have in mind, (where any subdocument within the replies array also has the same schema as the parent, and documents within the replies array of those subdocuments also having the same schema). I would really appreciate anyone's input.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
var commentSchema = new mongoose.Schema({
content: String,
createdAt: {
type: Date,
default: Date.now
},
score: {
type: Number,
default: 1
},
username: {
type: String,
lowercase: true
},
parent: {
type: Schema.Types.ObjectId,
ref: 'comment'
},
replyingTo: String,
replies: [commentSchema]
});
module.exports = mongoose.model("comment", commentSchema);
Since a const can't be called before initialization, to fix this issue the parent schema should be called on the children array after initialization the code below:
commentSchema.add({ replies: [commentSchema] })
The final result should look like this:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const commentSchema = new mongoose.Schema({
content: String,
createdAt: {
type: Date,
default: Date.now
},
score: {
type: Number,
default: 1
},
username: {
type: String,
lowercase: true
},
parent: {
type: Schema.Types.ObjectId,
ref: 'comment'
},
replyingTo: String,
});
commentSchema.add({ replies: [commentSchema] })

Sort by reverse order mongoose

Hi, I am trying to return my query in backwards order from which it was created.
The docs are a little unclear on how to use the sort method:
http://mongoosejs.com/docs/api.html#types_array_MongooseArray.sort
Here is my schema:
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.Types.ObjectId;
let PostSchema = new Schema({
title : String,
description: String,
image : String,
tags : [String],
original_poster: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
date: {
type: Date,
default: new Date()
}
})
module.exports = mongoose.model('Post',PostSchema);
I have run,
db.posts.find().sort({date:-1}).pretty()
For example, if my model was a 'Post' model and my first post was 'hello world' and my second post was 'this is a post'. I would like to see:
['this is a post', 'hello world']
However, what I am actually seeing is ['hello world','this is a post']
Figured out the answer
in posts schema add:
date: {
type: Date,
default: Date.now
}
then db.posts.find().sort({date:-1}).pretty() will yield the posts sorted from most recent to least recent
You have to add a creation timestamp in your schema and sort by its key.
let PostSchema = new Schema({
title : String,
description: String,
date : Date, // Here is your date
image : String,
tags : [String],
original_poster: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
}
})
and when you insert a document, use:
date: new Date()

How to push data into array using mongoose, with schema having single only single object

I am learning Mongoose, and got struct on pushing data into array blogs.
my schema is
module.exports = function(mongoose) {
var UserSchema = new Schema({
count:String,
_id : {id:false},
blogs: [{ type: Schema.Types.ObjectId, ref: 'Blog' }]
},
{
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' }
});
var BlogSchema = new Schema({
blogs:[{
post : String,
title: String,
author : String,
userId: {
type: String,
default: function() {
return crypto.randomBytes(12).toString('hex');
}
},
_id: {type: String, required: true}
}],
});
var models = {
User : mongoose.model('User', UserSchema),
Blog : mongoose.model('Blog', BlogSchema)
};
return models;
}
Problem is here userSchema will always have/be a single object, whcih will keep track of count of total blogs.
I know it can be done using findOneAndUpdate but I don't have id/object created for UserSchema.
Any help is appreciated. Thanks

How to compare _id value of Models made using Mongoose?

I have the following two schemas of User and Critique and I've got the data persisted in MongoDB database:
var userSchema = mongoose.Schema({
critiques: [{ type: Schema.ObjectId, ref: 'Critique' }],
});
var User = mongoose.model("User", userSchema);
var critiqueSchema = mongoose.Schema({
author: {type: String, default: ''},
date: { type: Date, default: Date.now },
comment: { type: String, default: '' },
stars: { type: Number, default: 0 },
_user: { type: Schema.ObjectId, ref: 'User' }
});
var Critique = mongoose.model("Critique", critiqueSchema);
user.critiques[0]._id.equals(critique._id) is giving me undefined is not a function.
How to compare _id value of a User instance with the Critique instance?
The critiques field of your user object directly contains an array of ObjectIds, so it would just be:
user.critiques[0].equals(critique._id)
user.critiques would only contain full Critique objects if you chained a .populate('critiques') call in the find where you obtained user.

MongooseJS: Subdoc Schemas in Separate Files

Let's say that I have a schema called LeagueSchema, which needs to contain some general information about the league (e.g. the name, time created, etc.), as well as some more complicated objects (e.g. memberships). Because these memberships are not needed outside of the league, I don't think it's necessary for them to be their own collections. However, I think for the sake of modularity it would be best for these schemas to live in their own separate files.
It would look something like this:
league.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var LeagueSchema = new Schema({
created: {
type: Date,
default: Date.now
},
updated: {
type: Date,
default: Date.now
},
name: {
type: String,
default: '',
trim: true
},
memberships: [MembershipSchema]
});
membership.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var MembershipSchema = new Schema({
startDate: {
type: Date,
default: Date.now
},
endDate: {
type: Date,
default: null
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
Unfortunately, this doesn't work. I get the following error:
ReferenceError: MembershipSchema is not defined
This is obviously happening because LeagueSchema is dependent on MembershipSchema, but I'm not sure what the best way to include it is. Can I define it as a dependency somehow? Or should I just include the file?
Also, is it bad practice to use subdocuments this way? Is there any reason it would be better to let all of these objects live in their own collections?
In your membership.js, export the membership sub-doc schema as a module:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var MembershipSchema = new Schema({
startDate: {
type: Date,
default: Date.now
},
endDate: {
type: Date,
default: null
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
module.exports = MembershipSchema;
You can then require the exported module schema in your LeagueSchema document:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var MembershipSchema = require('./membership');
var LeagueSchema = new Schema({
created: {
type: Date,
default: Date.now
},
updated: {
type: Date,
default: Date.now
},
name: {
type: String,
default: '',
trim: true
},
memberships: [MembershipSchema]
});
To answer your second question, as a general rule, if you have schemas that are re-used in various parts of your model, then it might be useful to define individual schemas for the child docs in separate files as so you don't have to duplicate yourself. A good example is when you use subdocuments in more that one model, or have two fields in a model that need to be distinguished, but still have the same subdocument structure.
If your memberships are not used elsewhere then rather treat the schema as an embedded document (document with schema of its own that is part of another document, such as items within an array):
Example definition and initialization:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var MembershipSchema = new Schema({
startDate: {
type: Date,
default: Date.now
},
endDate: {
type: Date,
default: null
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
var LeagueSchema = new Schema({
created: {
type: Date,
default: Date.now
},
updated: {
type: Date,
default: Date.now
},
name: {
type: String,
default: '',
trim: true
},
memberships: [MembershipSchema]
});
mongoose.model('League', LeagueSchema);
Your membership.js file should export the schema and the league.js file should import it. Then your code should should work.
In membership.js towards the bottom add:
module.exports = MembershipSchema;
In league.js, add
var MembershipSchema = require('membership.js');

Resources