Adding an array inside my Mongoose schema - node.js

I have the following schema where I am trying to add an array of comments to my blog post schema, then inside the comments schema I need to add an array of pictures urls related to each specific comment. I've researched the web and found this link embedded documents to mongoose documentation, yet noticed that it is related to mongoose version 2.7 while we are currently at version 3.8. So was wondering if I am doing it right?, and if not can someone please help me by suggesting the best way for designing my blog post schema so that it includes the blog post array of comments as well as the array of pictures related to each comment. Thanks for your time and effort.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var pictures = new Schema({
picURL: String,
date: {
type: Date,
default: Date.now
}
});
var comments = new Schema({
subject: String,
body: String,
date: {
type: Date,
default: Date.now
},
pictures:[pictures]
});
var blogpost = new Schema({
title: String,
body: String,
date: {
type: Date,
default: Date.now
},
comments:[comments]
});
module.exports = mongoose.model('BlogPost', blogpost);

you have two common scenarios here how you would like to handle your information
Embedded document:
if you are likely to do more reads than writes it's recommended to follow this approach in this case your model could be like this:
var comments = new Schema({
subject: String,
body: String,
date: {
type: Date,
default: Date.now
},
pictures:[{
picURL: String,
date: {
type: Date,
default: Date.now
}
}]
});
and also your approach to me is ok and should run on 3.8 without potential issues.
Referenced document:
if you'll have more writes than reads you can different collections to split the information and make a reference to your objectId like:
var comments = new Schema({
subject: String,
body: String,
date: {
type: Date,
default: Date.now
},
pictures: [
{type: Schema.Types.ObjectId, ref: 'pictures'}
]
});
you'll need to separate each schema into it's own and delare a model for comments and images as well.
Either way both are valid if you ask me my personal preference is picking up embedded document approach.
EDIT:
this situation can be applied for N relationships between collections, I keep it simple for two relationships, but for you scenario could be like this:
var blogpost = new Schema({
title: String,
body: String,
date: {
type: Date,
default: Date.now
},
comments: [{
subject: String,
body: String,
date: {
type: Date,
default: Date.now
},
pictures:[{
picURL: String,
date: {
type: Date,
default: Date.now
}
}]
}]
});
referenced:
var blogpost = new Schema({
title: String,
body: String,
date: {
type: Date,
default: Date.now
},
comments:type: Schema.Types.ObjectId, ref: 'comments'}
});
hope that helps.

Related

How to query and get documents from two collections on mongoose

I need to query documents from two collections together on mongoose.
I am familiar with SQL query and but not familiar with mongoDB.
I have two schema for Users, Messages like following.
Users
const UserSchema = new mongoose.Schema({
name: String,
email: {type: String, unique: true},
password: String,
avatar: {type: String, default: ""},
created_at: { type: Date, default: Date.now() }
});
module.exports = mongoose.model('User', UserSchema);
Messages
const MessageSchema = new mongoose.Schema({
message: { type: String, default: "" },
from: { type: String, default: "" },
to: { type: String: default: "" },
is_read: { type: Boolean, default: false },
channel: { type: String, default: ''},
created_at: { type: Date, required: true, default: Date.now }
});
module.exports = mongoose.model('Message', MessageSchema);
I need to get messages with "is_read" is "false".
I want to get "user name" and "avatar" together.
The "from" value of message should be matched with "_id" of User.
I think this post sums it up well: Mongoose - query to get data from multiple collections
Specifically the second upvoted answer mentions similarities between sql and mongodb, and goes on to explain how to link collections in mongoose queries.

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 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');

Can't get Mongoose.js Subdocument Array to populate

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({});

Resources