this is my second question today regarding Node.js. It's late, and I'd like some help to quickly integrate this function before I can go to bed.
I have a small Q&A app in which I can read and write data from/to MongoDB on my views pages.
However, I'd like to make a timestamp or date() register itself with each instance of items being written to MongoDB.
On the views, for now, only the author, title and body of text must be visible. But when I query Mongo, I'd like to have a seperate property that lists the date and time created. (Date alone suffices)
I have defined a property "date" : Date, in my Schema. I assumed it would automatically add this, but only title, author and body are added. I think it is because they are defined as vals in the routes for discussions but I'm note sure.
These are my code files:
discussions.js -- /routes
var mongoose = require('mongoose');
var express = require('express');
var router = express.Router();
var Discussion = require('../models/discussions');
router.get('/', function(req, res, next){
// alle db records uitlussen, op render alldiscussions
var db = req.db;
Discussion.find({},{},function(e,docs){
res.render('all_discussions', {
"all_discussions" : docs
});
console.log(docs);
});
});
router.get('/create', function(req, res, next){
res.render('add_discussion', {title: 'Diskussr'});
});
router.post('/submit', function(req, res) {
//set DB
var db = req.db;
//form vals
var author = req.body.name;
var title = req.body.title;
var body = req.body.body;
//set collection
var collection = db.get('discussions');
//insert
collection.insert({
"author" : author,
"title" : title,
"body" : body
}, function (err, doc) {
if (err) {
res.send("Database submit error");
}
else {
res.location("all_discussions");
res.redirect("all_discussions");
}
});
});
module.exports = router;
add_discussion.jade -- /views
extends layout
block content
h1 Start a discussion
p Start a discussion here on #{title} and help eachother out.
p Voeg hier uw vraag in:
form(action="/submit" method="post" name="submit_discussion")
input(id="name", placeholder="Your name" name="name")
br
br
input(id="title", placeholder="Brief summary of your problem." name="title")
br
br
input(id="body", placeholder="Explain your problem." name="body")
br
br
button(type="sumbit" id="submit" value="submit") Submit
br
discussions.js -- /models
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//schema discussions
var DiscussionSchema = new Schema({
author: String,
title: String,
body: String,
category: String,
created: Date
},
{ collection : 'discussions' });
// model
var Discussion = mongoose.model('Discussion', DiscussionSchema, 'discussions');
// test functie: aanmaken vraag on load
// var firstDiscussion = new Discussion({author: "Testuser 1", title: "Testvraag via models"});
// console.log(firstDiscussion);
// //vraag saven
// firstDiscussion.save(function (err, firstDiscussion){
// if (err) return console.error(err);
// });
module.exports = Discussion;
Just add a default value for the created field:
//schema discussions
var DiscussionSchema = new Schema({
author: String,
title: String,
body: String,
category: String,
created: { type: Date, default: Date.now }
},
{ collection : 'discussions' });
Or, since you are using ObjectId as primary key, you can extract the timestamp using getTimestamp() as suggested by #adeneo:
Discussion.find({}, function(e,docs){
docs.forEach(function(doc) {
console.log("created: " + doc._id.getTimestamp());
});
});
Related
I'm totally new to mongoDB, just coming from MySQL, so I'm trying to add a new document to a mongo database in Node.js, I have the code working except when I have to include a custom object.
Here's my code:
router.post('/', async (req, res) => {
const book= new Book({
title: req.body.book.title,
year_published: req.body.book.year_published,
author: req.body.author // ==> here is the problem without it works fine (comes the full author via body parameter)
});
try {
const savedBook = await book.save();
res.json({
insertedBook: savedBook
});
} catch (err) {
//console.log("Error:" + err);
res.json({error: err});
}
});
The book and author models (simplified):
// ======= AUTHORS ================ //
var mongoose = require('mongoose');
var mongoosePaginate = require('mongoose-paginate-v2');
const schema = new mongoose.Schema({
name: {
type: String,
required:true
},
place_birth: {
type: String,
required:true},
});
schema.plugin(mongoosePaginate);
const Authors = mongoose.model('Authors',schema);
module.exports = Authors;
// ======= BOOKS ================ //
var mongoose = require('mongoose');
var mongoosePaginate = require('mongoose-paginate-v2');
var ObjectId = mongoose.Schema.Types.ObjectId;
const schema = new mongoose.Schema({
title: {
type: String,
required:true
},
year_published: {
type: String,
required:true},
author: [{
type: ObjectId,
ref: 'Authors',
required:false
}],
});
schema.plugin(mongoosePaginate);
const Books = mongoose.model('Books',schema);
module.exports = Books;
Data posting:
{
"book": {
"title": "Entrada con cuernos",
"year_published": "2020",
},
"author": {
"name": "Marcus",
"place_birth": "Moscow",
}
}
What's the proper way to insert a book document?
Thanks
When creating a new Book, Book.author should be a mongoose document, meaning the Author should exist in the mongoDB already.
You need to first save the Author in the DB, then pass it in Boot.author with it's Author._id property set
P.S.: Use singular words when describing your collections:
const Authors = mongoose.model('Authors',schema);
const Authors = mongoose.model('Author',schema); // recommended
mongoose will take care of the plural naming
The first argument is the singular name of the collection your model is for. Mongoose automatically looks for the plural, lowercased version of your model name
I'm currently building a Node backend with MongoDB / Mongoose and I seem to be having some problem with tying my data together. Specifically, I wish for all users to be able to submit a form (question form) which will then be added to the "questions" collection. In addition to being added to the questions collection, I also need to store a reference to all of the questions a user has answer directly inside of the user object.
Below you can check out my code. Whenever I make a POST requestion to /questions, it spits out this error. I should note that it successfully adds documents into the questions collection, and each question contains the ID of the user who created it, but the main problem is the user's questions array is not getting updated to include an ID value of submitted questions.
Models/User.js
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt-nodejs');
const UserSchema = new Schema({
email: {
type: String,
lowercase: true,
unique: true,
required: true
},
password: {
type: String,
required: true
},
profile: {
firstName: { type: String },
lastName: { type: String }
},
questions: [
{
type: Schema.Types.ObjectId,
ref: 'Question'
}
],
role: {
type: String,
enum: ['Member', 'Client', 'Owner', 'Admin'],
default: 'Member'
},
resetPasswordToken: { type: String },
resetPasswordExpires: { type: Date }
},
{
timestamps: true
});
/** Pre-save of user to database,
hash password if password is modified or new
*/
module.exports = mongoose.model('User', UserSchema);
Models/Question.js
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
// Schema defines how questions will be stored in MongoDB
const QuestionSchema = new Schema({
questionString: String,
answer: Boolean,
_createdBy : [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
],
},{
//user timestamps to save date created as .createdAt
timestamps: true
});
module.exports = mongoose.model('Question', QuestionSchema);
Controller/QuestionController.js
const jwt = require('jsonwebtoken'),
crypto = require('crypto'),
Question = require('../models/question'),
User = require('../models/user'),
config = require('../config/main');
function setQuestionInfo(request) {
return {
_id: request._id,
questionString: request.questionString,
answer: request.answer,
user: request.user
}
}
exports.addQuestion = function(req, res, next) {
User.findById(req.user.id, (err, user) => {
if (err) throw new Error(err);
// We create an object containing the data from our post request
const newQuestion = {
questionString: req.body.questionString,
answer: req.body.answer,
// in the author field we add our current user id as a reference
_createdBy: req.user._id
};
// we create our new post in our database
Question.create(newQuestion, (err, question) => {
if (err) {
res.redirect('/');
throw new Error(err);
}
// we insert our newQuestion in our posts field corresponding to the user we found in our database call
user.questions.push(newQuestion);
// we save our user with our new data (our new post).
user.save((err) => {
return res.send('sucess!');
});
})
});
}
Router.js
module.exports = function(app) {
// Initializing route groups
const apiRoutes = express.Router(),
userRoutes = express.Router(),
authRoutes = express.Router(),
questionRoutes = express.Router();
//=========================
// Auth Routes
//=========================
/** ROUTES BELOW WORK FINE -- ONLY DEALS WITH POST TO /questions
*
app.use middle ware sets /auth as auth route (everything goes through /api/auth)
apiRoutes.use('/auth', authRoutes);
apiRoutes.get('/dashboard', requireAuth, function(req, res) {
res.send('It worked! User id is: ' + req.user._id + '.');
});
// Set user routes as a subgroup/middleware to apiRoutes
apiRoutes.use('/user', userRoutes);
// View user profile route
userRoutes.get('/:userId', requireAuth, UserController.viewProfile);
// Test protected route
apiRoutes.get('/protected', requireAuth, (req, res) => {
res.send({ content: 'The protected test route is functional!' });
});
// Registration route
authRoutes.post('/register', AuthenticationController.register);
// Login route
authRoutes.post('/login', requireLogin, AuthenticationController.login);
*/
// Problem Area --> Making POST req to /questions
apiRoutes.post('/questions', requireAuth, QuestionController.addQuestion);
// Set url for API group routes
app.use('/api', apiRoutes);
};
You've your schema defined to accept question ids for a user.
questions: [
{
type: Schema.Types.ObjectId,
ref: 'Question'
}
After you save with Question.create(newQuestion, (err, question)... the callback attribute question has the updated data, one with the ObjectId.
Now you add this ObjectId value to your existing questions array that you got from findById on User model.
user.questions.push(question._id);
Mongoose will use the questionId to fill your question object when you use populate on questions array, but thats part for retrieving information.
I want to delete a Mongodb document by id, passing it to Express route.
In the console, I receive a message that says it is deleted.
GET /api/videolinks 304 94.792 ms - -
Removed id= 562b905f633288ac0d8b4567
DELETE /api/videolinks/562b905f633288ac0d8b4567 200 68.550 ms - 19743
But it is not.
> db.hyperlinks.find({"_id": ObjectId("562b905f633288ac0d8b4567")})
{ "_id" : ObjectId("562b905f633288ac0d8b4567"), "file" : "http://storage.akamai.com/get/b113/p/coub/simple/cw_file/79632d71313/9aedca2cd4d3094e75834/iphone_hellosergii_iphone.mp4" }
My Angularjs factory:
/*global angular*/
angular.module('myService', [])
// each function returns a promise object
.factory('Videolinks', ['$http',function($http) {
return {
get : function() {
return $http.get('/api/videolinks');
},
delete : function(id) {
return $http.delete('/api/videolinks/' + id);
}
};
}]);
My route.js
var path = require('path');
var Videolink = require('./models/mydb');
var mongodb = require('mongodb');
// Get links
function getLinks(res){
Videolink.find(function(err, hyperlinks) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err) {
res.send(err);
}
res.json(hyperlinks); // return all videos in JSON format
});
}
module.exports = function(app) {
// api ---------------------------------------------------------------------
// use mongoose to get all videos in the database
app.get('/api/videolinks', function(req, res) {
getLinks(res);
});
// delete a video
app.delete('/api/videolinks/:video_id', function(req, res) {
Videolink.remove({
_id : mongodb.ObjectID(req.params.video_id)
}, function(err) {
if (err) {
res.send(err);
}
console.log("Removed id= " + req.params.video_id);
getLinks(res);
});
});
// application -------------------------------------------------------------
app.get('*', function(res) {
res.sendFile('index.html', {root: path.join(__dirname, './public')}); // load the single view file
});
};
The app.get functionality works pretty well here.
What could be wrong with app.delete?
Here is my DB schema in models/mydb
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var db_schema = new Schema({
//text: String
_id: String,
source: String,
orig_page: String,
likes: Number,
title: String,
file: String,
video_mobile_res: String,
video_high_res_mutes_muted: String,
audio_high_res: String,
video_med_res_muted: String,
audio_med_res: String
}, {collection: 'hyperlinks'});
module.exports = mongoose.model('Videolink', db_schema);
Your particular problem is that you defined the _id field as a String in your schema:
var db_schema = new Schema({
_id: String,
...
Take that out and your code should work fine. You may have even uncovered a mongoose bug, since you are supposed to be able to specify the _id field type. Maybe some mongoose expert can tell us more.
I am trying to join two collections and being able to get the combined data. To do so using Mongoose, i am supposed to use the populate syntax to achieve that. I am receiving error that the Schema Schema hasn't been registered for 'User_Fb'. From my code, I have exported the models and required in my server.js but the error is still appearing. What have I done wrong?
feed_post.model.js
var mongoose = require('mongoose');
var conn_new_app = mongoose.createConnection('mongodb://localhost/new_app');
var User_fb = require('../models/fb_db.model');
var Schema = mongoose.Schema;
var feed_postSchema = new Schema({
user_id: { type: Schema.Types.ObjectId, ref: 'User_Fb' },
content: String,
location: String,
image: [{ type : String }]
});
var Feed_Post = conn_new_app.model('Feed_Post', feed_postSchema);
module.exports = Feed_Post;
fb_db.model.js
var mongoose = require('mongoose');
var conn_new_app = mongoose.createConnection('mongodb://localhost/new_app');
var Feed_Post = require('../models/feed_post.model');
var user_fb = new mongoose.Schema({
name: String,
location: String,
fb_id: Number
});
var User_Fb = conn_new_app.model('User_Fb', user_fb);
module.exports = User_Fb;
server.js
var express = require('express'),
mongoose = require('mongoose'),
User = require('./app/models/user.model'),
Post = require('./app/models/post.model'),
Maptest = require('./app/models/maptest.model'),
Feed_Post = require('./app/models/feed_post.model'),
User_Fb = require('./app/models/fb_db.model'),
app = express();
app.get('/testget', function(req,res){
Feed_Post.findOne().populate('user_id').exec(function(err, c) {
if (err) { return console.log(err); }
console.log(c.fk_user.userName);
});
});
UPDATED from Pier-Luc Gendreau Answer's
fb_db.model.js
module.exports = function (connection) {
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var user_fb = new mongoose.Schema({
name: String,
location: String,
fb_id: Number
});
return connection.model('User_FB', user_fb);;
}
feed_post.model.js
module.exports = function (connection) {
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var feed_postSchema = new Schema({
user_id: { type: Schema.Types.ObjectId, ref: 'User_Fb' },
content: String,
location: String,
image: [{ type : String }]
});
return connection.model('Feed_Post', feed_postSchema);;
}
server.js
var express = require('express'),
app = express(),
mongoose = require('mongoose'),
conn_new_app = mongoose.createConnection('mongodb://localhost/new_app'),
User_Fb = require('./app/models/fb_db.model')(conn_new_app),
Feed_Post = require('./app/models/feed_post.model')(conn_new_app);
app.get('/testget', function(req,res){
Feed_Post.find().populate('user_id').exec(function(err, res) {
if (err) { return console.log(err); }
console.log(res);
});
});
This is all I had to do Customer.findOne({}).populate({ path: 'created_by', model: User }) instead of this Category.findOne({}).populate({'author'})
IF YOU (really) USE MULTIPLE mongoDB CONNECTIONS
I do realise that this is not the case for the OP, but if you genuinely use multiple connections you MUST provide the model when using .populate(), as mongoose will only "find" models on the same connection.
ie where:
var db1 = mongoose.createConnection('mongodb://localhost:27017/gh3639');
var db2 = mongoose.createConnection('mongodb://localhost:27017/gh3639_2');
var userSchema = mongoose.Schema({
"name": String,
"email": String
});
var customerSchema = mongoose.Schema({
"name" : { type: String },
"email" : [ String ],
"created_by" : { type: mongoose.Schema.Types.ObjectId, ref: 'users' },
});
var User = db1.model('users', userSchema);
var Customer = db2.model('customers', customerSchema);
Correct:
Customer.findOne({}).populate('created_by', 'name email', User)
or
Customer.findOne({}).populate({ path: 'created_by', model: User })
Incorrect (produces "schema hasn't been registered for model" error):
Customer.findOne({}).populate('created_by');
The problem is that you are creating a new connection in each and every model, so you end up with a bunch of different connection objects. Even though they are pointing to the same database, mongoose models don't know about other connections. You should instead create the connection object in your main app and then pass it around.
server.js
var express = require('express');
var mongoose = require('mongoose');
var app = express();
var conn_new_app = mongoose.createConnection('mongodb://localhost/new_app');
var Feed_Post = require('./app/models/feed_post.model')(conn_new_app);
app.get('/testget', function(req,res){
Feed_Post.findOne().populate('user_id').exec(function(err, c) {
if (err) { return console.log(err); }
console.log(c.fk_user.userName);
});
});
Then modify your models so they are actually a function:
feed_post.model.js
module.exports = function (connection) {
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var feed_postSchema = new Schema({
user_id: { type: Schema.Types.ObjectId, ref: 'User_Fb' },
content: String,
location: String,
image: [{ type : String }]
});
return connection.model('Feed_Post', feed_postSchema);;
}
Also, like Adrian mentions, you can use mongoose as a singleton. However, I do recommend using createConnection as you did because if you ever need a second connection, you won't have to refactor the connection code to your first database.
It looks like you are creating a different database connection for each model, which isolates the models from each other. Mongoose must assume this isolation, because they could exist on different databases or even database servers.
Try connecting once, and just calling mongoose.model() instead of connection.model() when defining your models. Mongoose is a singleton by default.
In my case, this issue because I haven't included the ref model into the application.
You can get the field you want by using select
Example to get the email of the customer:
Customer.findOne({}).populate({ path: 'created_by', model: User, select: 'email' })
More Details in mongoose documentation
Im running Express on my application with a delete route below:
router.route('/lists/:id')
.delete(function(req, res){
Entry.remove({
_id: req.params.id
}, function(err, list){
if(err)
res.send(err)
list.title = req.body.title;
res.json({message: 'successfully deleted'});
console.log('DELETE on /lists/'+ req.params.id);
});
});
Here is my Mongoose schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ListSchema = new Schema({
title: String
});
module.exports = mongoose.model('List', ListSchema);
When my application hits the delete route, my terminal logs the appropriate statement, but the model is not deleted from the database. When I call fetch on the collection, all of there records are still there.
I am using a very similar approach on a different collection of data on my website, and it works fine, so Im really at a loss for why this is happening.
Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema constructor. The type assiged is an ObjectId to coincide with MongoDBs default behavior
Try passing the _id as ObjectId:
var ObjectId = require('mongoose').Types.ObjectId;
var query = { _id: new ObjectId(req.params.id) };