I'm creating simulation for goodreads by MERN stack
and when I'm using populate to retrieve books of specific user it returns empty array, I've done a lot of search but in vain
here's my model
const userSchema =new mongoose.Schema({
firstName:{
type:"string",required:true
},
books:[{
book:{type:mongoose.Schema.Types.ObjectId,ref:'Book'},rate:Number,shelve:''
}]});
And this is books model
const bookSchema =new mongoose.Schema({
title :{
type:"string",required:true
}});
And this is how I use populate
router.get("/one", (req, res, next) => {
User.find({firstName : "John"}).populate("books.book").exec(function (err, user) {
res.json(user)
});
})
and this is the resulted JSON
[{"_id":"5c70f299ef088c13a3ff3a2c","books":
[{"_id":"5c708c0077a0e703b15310b9"},{"_id":"5c708c0077a0e703b15310ba"},
{"_id":"5c708c0077a0e703b15310bb"},{"_id":"5c708c0077a0e703b15310bd"}]}]
I believe it's an issue with how your UserSchema is defined. My assumption is that including the rate and shelve in the definition of books is causing the problem.
Try removing those fields to start, and just populating books instead of books.book. If that works, then I would really reconsider putting those fields where you have them. In my own personal opinion, I think they seem better in the BookSchema since each book in the UserSchema has a rate and shelve anyways. Hope this helps!!
Related
I have been caught in a situation where I have to perform push and pull together.
I have Schema of categories and posts where Posts can have multiple categories and Categories will have array of multiple posts using Object Referencing method of mongoose like this-
PostSchema= new mongoose.Schema({
title:{type:String, required:true},
content:{type:String, required:true},
categories:[{type:mongoose.Schema.Types.ObjectId, ref:'Categories'}]
})
Same way I have set up Schema for categories-
CatSchema = new mongoose.Schema({
name:{type:String, required:true},
slug:{type:String, required:true},
posts:[{type:mongoose.Schema.Types.ObjectId, ref:'Posts'}]
})
Now When I create a post, I push postId in all categories of Categories collection.
But PROBLEM OCCURS when I try to UPDATE POST AND CHOOSE DIFFERENT CATEGORIES from earlier this time.
I can still push updated postId in newly selected categories but How do I pull postId from those categories which are not selected during update but selected during creation. They still store this postId as it was initially pushed.
In clearer, I want to update posts array in categories collection whenever update a category. All types of solutions are welcome including better approach of creating schema or solving this particular problem.
Thanks
I understand that for this update you require postID, arrayOldCaretoryiesID, and arrayNewCategoriesID.
Let's find and remove from the current post the category you need.
const deletedCatFromPost = await Post.updateOne({ _id: postID }, { $pull: { categories: arrayOldCaretoryiesID } })
Second, remove from old categories by an array of IDs the post you need.
const removedOldCategories = await Categories.updateMany({_id:{$or: arrayOldCaretoryiesID }},{ $pull:{ posts: postID }})
Finally, add to new categories by an array of IDs a new postID.
const addNewCategories = await Categories.updateMany({_id: { $or: arrayNewCategoriesID} },{$push:{ posts: postID }})
Hope this can help you.
Update:
Second, remove from old categories by an array of IDs the post you need.
const removedOldCategories = await Categories.updateMany({_id:{$in: arrayOldCaretoryiesID }},{ $pull:{ posts: postID }})
thanks in advance for your attention on this pretty basic question! I'm currently working on a FreeCodeCamp project involving Node, MongoDB and Mongoose (here's my Glitch project), and I'm very confused as to what is going on when I call .save() on a Mongoose model. You can see the full context on Glitch, but here is the specific part that's not behaving as I'd expect it to:
const { User } = require("./models/user");
...
app.post("/api/exercise/new-user", (req, res) => {
var newUser = new User({ name: req.body.username });
newUser.save((error) => console.error(error));
res.json({username: newUser.name, _id: newUser._id})
});
Here's models/user.js:
const mongoose = require("mongoose");
exports.User = mongoose.model(
"User",
new mongoose.Schema({
name: { type: String, required: true }
})
);
I'm seeing the JSON response in my browser with the information I'd expect, but I'm not seeing a new document in my database on MongoDB. What am I missing here? As far as I know when I call .save() on newUser the document should be showing up in MongoDB. In previous projects I did just this and it was working, and I can't figure out what's different in this situation.
And a broader question that I have that I didn't feel like was explained in the FCC curriculum is: at what point is a collection created in MongoDB by Mongoose? Is it when I create a new model? Like at this point in my code:
const mongoose = require("mongoose");
exports.User = mongoose.model(
"User",
new mongoose.Schema({
name: { type: String, required: true }
})
);
Or does it happen when I go to save an instance of the model? When is MongoDB told to create the collection? Thanks very much in advance!
Well, I've done it again, the answer was very simple. I saw that I was getting an error from the mongoose.connect() call, because it was having trouble with the URI starting with mongodb+srv:// instead of just mongodb://. I then saw that because I had forked the start project from FCC, it had some dated versions of Mongoose and MongoDB, just updating those two to the most recent versions (3.5.7 of MongoDB and 5.9.12 of Mongoose) solved the problem, and I'm now able to save the documents correctly!
This is maybe more a case of looking for advice. However, I will supply sample code as an example of what I want to achieve. I am trying to build my first back end system and I keep running into problems with the design.
My MongoDB database consists of 4 major parts - Profiles, Teams, Drafts and Users. The profile (being the main data which sources everything using IDs) schema has properties to hold arrays with the Teams and Drafts IDs. The idea is that when the profile is served it will populate all those properties with the relevant data by using the IDs.
Example of the Profile schema using Mongoose:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserProfileSchema = new Schema({
Name : String,
Email : String,
Admin : Boolean,
Status : Number,
UserID : String,
PrivateProfile: Boolean,
Drafts: [String], //This array holds the draft IDs
Teams:[String] //Holds the team profiles IDs
});
module.exports = mongoose.model('UserProfile', UserProfileSchema);
Example of Team Profile Schema using Mongoose:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TeamProfileSchema = new Schema({
Name : String,
CaptainID : String,
CaptainName : String,
Members : [String], //Array of user IDs
DateCreated : Boolean,
Reputation : Number
});
module.exports = mongoose.model('TeamProfile', TeamProfileSchema);
Example of a route to find all the Team Profiles the user is the Captain of and fetch all the members associated with that team:
router.route('/teams/captain/:user_id')
.get(function (req, res) {
TeamProfile.find({
CaptainID : req.params.user_id
}, function (err, teams) {
if (err)
res.send(err);
for (var x in teams) {
var membersArray = [];
for (var i in teams[x].Members) {
var ID = teams[x].Members[i];
UserProfile.find({
UserID : ID
}, function (err, profile) {
if (err)
res.send(err);
membersArray.push(profile);
});
}
teams[x].Members = membersArray;
console.log(teams[x].Members);
}
res.json(teams);
});
})
I understand that the route will not work, but how do I pull this off? I used a more vanilla approach only for the purpose to explain what I want to achieve. Any help would be highly appreciated.
I advise you to use combination of denormalization and normalization.
because MongoDB is not a relational database.
denormalization is much faster then normalization. You don't need to use relationship anymore.
You can read this article hope may helpfull to you.
Cheers
Every post has comments, and users can 'like' a comment. What Im trying to achieve is when a user is viewing a posts comments they will be able see the ones they've liked and not currently liked (allowing to like and unlike any comment)
Schema for Comments:
// ====================
// Comment Likes Schema
// ====================
var CommentLikes = new Schema({
userId:String,
commentId:String
})
// ==============
// Comment Schema
// ==============
var CommentSchema = new Schema({
postId:String,
authorId:String,
authorUsername:String,
authorComment:String,
likes:[CommentLikes],
created: {
type: Date,
default: Date.now
},
});
// Comment Model
var Comment = mongoose.model('Comment', CommentSchema, 'comment');
This seems like the best way to handle Comment Likes by using Embedded Documents.
Currently Im selecting all the post comments like so:
Comment.find({ postId:req.params.postId }).sort({ _id: -1 }).limit(20)
.exec(function (err, comments) {
if(err) throw err
// === Assuming Comment Likes Logic Here
// Return
return res.send({
comments:comments,
})
})
At the moment I'm returning all the likes back, irrespective of the current users Id. If the comment has 50k likes then this surely would be inefficient to send all the likes back, therefore I want to get all the comments but only the likes that have been created by the logged in user (using userId). What would be the most efficient way to do this? My first thought was to just run another find and get them that way? But surely can I not just run a find() on the parent then limit the Embedded Documents?
At the moment I am looking at mongoDB. I try to implement a simple one-to-many relation using nodejs and mongoose:
Model/Schema User:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var UserSchema = new Schema({
name: String
});
module.exports = mongoose.model('User', UserSchema);
Model/Schema Article:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ArticleSchema = new Schema({
name: {
type: String,
required: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
module.exports = mongoose.model('Article', ArticleSchema);
So my question, now:
How can I get all Users including its Articles?
Do I really have to add a ref to my UserScheme, too? What is the best-practice in an one-to-many relation like this? Is there something like a join in mongodb?
One User has many Articles - an Article belongs to one User.
Calling something like /user/:user_id , I want to receive the user with _id=user_id containing all of his articles.
That is the most horrible idea, for various reasons.
First, there is a 16MB BSON document size limit. You simply can not put more into a document. Embedding documents is rather suited for "One-to-(VERY-)Few" relationships than for a "One-to-Many".
As for the use case: What is your question here? It is
For a given user, what are the articles?
REST wise, you should only return the articles when /users/:id/articles is GETed and the articles (and only the articles) should be returned as a JSON array.
So, your model seems to be natural. As for the user:
{
_id: theObjectId,
username: someString
…
}
and an article should look like this:
{
_id: articleIdOrSlugOrWhatever,
authors: [theObjectId],
// or author: theObjectId
retention: someISODate,
published: someOtherISODate
}
So when your REST service is called for /users/:id you'd simply look up
var user = db.users.findOne({_id:id})
And when /users/:id/articles is called, you'd lookup
var articles = db.articles.find({author:id})
Problem solved in a scalable way, adhering to REST principles.