How do I properly save a Mongoose document? - node.js

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!

Related

Populate in mongoose returns array of _ids

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!!

One-To-Many relation in MongoDB

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.

Mongoose schemas and collections

Hi everyone this is the first time I ask a question in here,
I'm very new into the MEAN stack and by now I'm trying to develop an application using it. As I understand in mongodb an Schema (Database) can have one or more collections (Tables(?)), when I'm using mongoose I define a Song model and an Artist model:
For Song:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var songSchema = new Schema({
songName: { type: String },
songArtist: [{type : Schema.Types.ObjectId, ref : 'Artist'}]
});
module.exports = mongoose.model('Song', songSchema);
For artist:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var artistSchema = new Schema({
artistName: { type: String }
});
module.exports = mongoose.model('Artist', artistSchema);
My app.js looks like this:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/song', function(err, res) {
if(err) throw err;
console.log('Connected to Database');
});
var models = require('./models/song')(app, mongoose);
The issue with this is, as I understand and saw, that I'm creating 2 databases/schemas while I want to Create one database/schema and have this two collections in there:
SongDatabase:
--- Song
--- Artist
How should I do it in this case with my mongoose models/controllers and my app.js? Thanks in advance :)
I solved all my doubts taking a look into this tutorial, as you can see in here they implement two collections within one database (My inicial concern). Once you are connected to the database and you perform a post to the collection you want, in this case thread or post, it will create the collection into mongodb.
The issue there is that your connection string is mongodb://localhost/song
Song becomes your DB, and within Song you should be able to see two collections
- SongS
- ArtistS
Posible solutions: Flush the database, drop all. Start clean and check what your application is doing. Use another name for the DB

MongoDB find() returns nothing

I am trying to query my database from the mongodb shell to retrieve all the entries. Still, my find() method returns nothing.
This is the mongoose model that I am using:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ArticleSchema = new Schema({
title: String,
content: String,
author: { type: String, default: 'Vlad'},
postDate: { type: Date, default: Date.now }
});
ArticleSchema.methods.formatTitle = function() {
var link = this.title.replace(/\s/g, '-');
return '/article/' + link;
};
ArticleSchema.methods.snapshot = function() {
var snapshot = this.content.substring(0, 500);
return snapshot;
};
module.exports = mongoose.model('Article', ArticleSchema);
When loading my Express.js application in the browser, I have no issues displaying all the articles that I have added using my API. Still, I just want to go inside the mongo shell and query them. I have used the following query:
// blog is the database name (mongodb://localhost:27017/blog)
db.blog.find()
I get an empty string in return, basically a new line in the console.
Your answer is right there, in the question:
// blog is the database name (mongodb://localhost:27017/blog)
db.blog.find()
So db already refers to correct database. Instead of blog, put collection name, not db name.
db.articles.find()
Mongo shell will connect when started to the test database, you will need to change the database and then execute the find on your collection :
use blog
db.articles.find()
First you choose the db, then you find on the collection.
use blog
db.articles.find()

Mongoose ODM use

I read an article in this link http://theholmesoffice.com/mongoose-and-node-js-tutorial/
here there is a code:
exports.teamlist = function(gname,callback){
db.once('open', function(){
var teamSchema = new mongoose.Schema({
country: String,
GroupName: String
});
var Team = db.model('Team', teamSchema);
Team.find({'GroupName':gname}, function (err, teams) {
if(err){
onErr(err,callback);
}else{
mongoose.connection.close();
console.log(teams);
callback("",teams);
}
})// end Team.find
});// end db.once open
};
Here it calls db.once method whereas in other places its used like this
var mongoose = require('mongoose')
,Schema = mongoose.Schema
,ObjectId = Schema.ObjectId;
var postSchema = new Schema({
thread: ObjectId,
date: {type: Date, default: Date.now},
author: {type: String, default: 'Anon'},
post: String
});
module.exports = mongoose.model('Post', postSchema);
In the router part its used like this
exports.show = (function(req, res) {
Thread.findOne({title: req.params.title}, function(error, thread) {
var posts = Post.find({thread: thread._id}, function(error, posts) {
res.send([{thread: thread, posts: posts}]);
});
})
});
And in the app.js there is
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/norum');
I dont understand why these two have different approach and which one is the better one and why? Can Anyone please help me. One thing that I have observed is that the second approach is the one most used. SO please help me as to which one is a better approach.
I know this is mainly concerned regarding creation of the schemes in Mongodb, and so the once method looks little better. But still I am not at all sure. Please help.
Basically, in the first approach - it mearly shows you what you can do with the mongoose. It presumes that somewhere you were opening a connection to the database (presumably a few string of code back). Then it listens to the event and does all the job later. It's only there to show you what you can do.
For intance, this creates a mongoose model (it doesnt rely on the database connection being open) - hence it could be included from a separate file with ease and generally be written as a module as it is in the second approach
var teamSchema = new mongoose.Schema({
country: String,
GroupName: String
});
var Team = db.model('Team', teamSchema);
Later on in the first approach you close the connection to the database, but thats not the thing you would want to do in the real application. You have a connection pool, and use it to frequently query the database, closing the connection is only needed when you plan to stop the application from running.
Team.find({'GroupName':gname}, function (err, teams) {
if(err){
onErr(err,callback);
}else{
mongoose.connection.close();
console.log(teams);
callback("",teams);
}
First line - you perform find on the database, if no err was returned - you close the connection, log the results, etc..
Anyway, to sum up - first approach is generally a show-off, second - is what its like in a real world - where you separate logic, make models reusable and includable, where you have router logic

Resources