Hey guys i am pretty new to Nodejs so let me first describe my problem
I created a mongooseschema of comments like
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const commentsschema = new Schema({
firstname: {
type: String,
required: true
},
middlename:{
type:String
},
lastname:{
type:String,
required:true
},
comments:{
type:String,
required:true
},
upvote:{
type:Number
},
downvote:{
type:Number
}
});
module.exports = mongoose.model("comments", commentsschema);
then in my controllers file i created it and added it to db whenever user submits a comment
exports.postcomment = (req, res, next) => {
//All firstname, lastname etc are taken from req.body just to make my code short i havent included those lines
const commentinpage = new Comments({
firstname: fname,
middlename:mname,
lastname:lname,
comments: comment,
upvote: 0,
downvote: 0
});
return commentinpage.save().then(() => {
res.redirect("/");
});
};
now in later point of time when another user clicks on upvote button i want to increase the upvote entry in my db so i want to call a method in mongoose schema ..So i tried like this
const Comments = require("../modals/Comments");
Comments.upvoteco().then(result=>{
console.log(this.upvote)
}
and then in my schema
commentsschema.methods.upvoteco=function(){
console.log(this.upvote)
return ++this.upvote
}
but i get the error TypeError: Comments.upvoteco is not a function
You can not call the method defined in schema with the model, You can call it with the object instance i.e using and mongoose object instance(document) in that specific collection.
And to call one with the model you should define a static method:
try changing:
commentsschema.methods.upvoteco = function() {
console.log(this.upvote);
return ++this.upvote;
}
to this:
commentsschema.statics.upvoteco = function() {
console.log(this.upvote);
return ++this.upvote;
}
and try calling your method like:
Comments.upvoteco(function(err, result) {
if (err) {
console.log('error: ', err);
} else {
console.log(this.upvote);
}
});
Check the official docs for more clearity: https://mongoosejs.com/docs/2.7.x/docs/methods-statics.html
hope this helps :)
Related
Given a user model:
import { model, Schema } from 'mongoose'
export interface User {
email: string
}
const userSchema = new Schema<User>(
{
email: {
type: String,
required: true,
},
},
)
export const UserModel = model<User>('User', userSchema)
I'm trying to save it as so:
// inside an async function
const newUser: HydratedDocument<User> = new UserModel({
email: 'aaa#aaa.com',
})
console.log(newUser)
await newUser.save()
Which results in newUser.save is not a function. What am I missing? Also, here is the output of the `console.log(newUser)
My stupid mistake: I was calling everything on the fronted. Solution is as easy as moving relevant db calls to the endpoint.
You will need to also define a model, change your schema definition to this:
first create model from Schema :
var UserModel = mongoose.model('User', User);
then create object out of User model
var user = new UserModel(req.body)
then call
user.save(function(){});
check documentation http://mongoosejs.com/docs/api.html#model_Model-save
I have a simple controller that creates a post for a user. Another schema is linked to it. When I try to create a new post, I need to get the id of the post so that I can link other schema to it.
Here is the schema:
const mongoose = require("mongoose");
const User = require("./User");
const View = require("./View");
const ArticleSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true,
},
body: {
type: String,
required: true,
},
status: {
type: String,
default: "public",
enum: ["public", "private"],
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
views: {
type: mongoose.Schema.Types.ObjectId,
ref: "View",
},
createdAt: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model("Article", ArticleSchema);
It is fine when I want to link the user field because I have that stored in memory.
But the view field requires postId of that particular document. And I can't get it without first creating the document.
My create post controller:
module.exports.createArticleController = async function (req, res) {
try {
req.body.user = req.User._id;
const article = await Article.create(req.body).exec()
res.redirect(`/${article.title}/${article._id}`);
} catch (e) {
console.error(e);
}
};
So my question is,
How can i get the id in the process of executing the model.create() so that i can link the view to that id. Maybe something using the this operator
I don't want to use update after create.
You can generate your own id and save it
ObjectId id = new ObjectId()
You can get object Id's right after creating an instance of Model or create your own object id's and save them.
Here's how i achieved it:
module.exports.createArticleController = async function (req, res) {
try {
const instance = new Article();
instance.title = req.body.title;
instance.body = req.body.body;
instance.status = req.body.status;
instance.user = req.User._id;
instance.views = instance._id;
const article = await instance.save();
if (article) {
res.redirect(`/${article.title}/${article._id}`);
}
} catch (e) {
console.error(e);
}
};
Or you can create them and save it to the db.
var mongoose = require('mongoose');
var myId = mongoose.Types.ObjectId();
const instance = new YourModel({_id: myId})
//use it
Continue reading
How do I get the object Id in mongoose after saving it.
Object Id's format and usage
You can just simply create a schema ovject like this:
const task: TaskDocument = new this.taskSchema({ ...createTaskDto })
This is from one of my projects, since the ObjectId from MongoDB is based on the operating machine and the time it is created, it doesn't need the database to generate the id.
You can now access task._id to get your id without saving it.
I am practicing my express.js skills by building a relational API and am struggling to populate keys in a schema.
I am building it so I have a list of properties, and those properties have units. The units have a propertyId key.
This is currently returning an empty array, whereas if i remove the populate({}) it returns an array of ObjectIds.
I've read a number of posts and some people solved this by using .populate({path: 'path', model: Model}); but this doesn't seem to be doing the trick. I think it might be the way I am adding a propertyId to the unit but I'm not sure. Can anyone see where I am going wrong? Any help will be massively appreciated.
Here are the schemas.
Property:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PropertySchema = new Schema({
title: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
units: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'unit'
}
]
});
module.exports = Property = mongoose.model('property', PropertySchema);
Unit:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const UnitSchema = new Schema({
title: {
type: String,
required: true
},
propertyId: {
type: Schema.Types.ObjectId,
ref: 'property'
}
});
module.exports = Unit = mongoose.model('unit', UnitSchema);
I am then creating the unit like this:
-- api/properties/:id/units --
router.post('/:id/units', async (req, res) => {
// Get fields from req.body
const { title } = req.body;
// Get current property
const property = await Property.findById(req.params.id);
try {
// Throw error if no property
if (!property) {
return res.status(400).json({ msg: 'Property not found' });
}
// Create new unit
const newUnit = new Unit({
title,
propertyId: req.params.id
});
// Add new unit to property's units array
property.units.unshift(newUnit);
// Save property
await property.save();
// Return successful response
return res.status(200).json(property);
} catch (error) {
console.error(error.message);
return res.status(500).send('Server error');
}
});
And trying to populate in the GET request
-- /api/properties/:id/units --
const Unit = require('../../models/Unit');
router.get('/:id/units', async (req, res) => {
const property = await Property.findOne({ _id: req.params.id }).populate({path: 'units', model: Unit});
const propertyUnits = property.units;
return res.status(200).json(propertyUnits);
});
If i remove the .populate({path: 'units', model: Unit});, I get a list of unit id's like this:
[
"5ff7256cda2f5bfc1d2b9108",
"5ff72507acf9b6fb89f0fa4e",
"5ff724e41393c7fb5a667dc8",
"5ff721f35c73daf6d0cb5eff",
"5ff721eb5c73daf6d0cb5efe",
"5ff7215332d302f5ffa67413"
]
I don't know, why you don't try it like this:
await Property.findOne({ _id: req.params.id }).populate('units')
I've been try that code above and it's working.
Note: Make sure to check your req.params.id is not null or undefined and make sure the data you find is not empty in your mongodb.
Updated: I've been try your code and it's working fine.
The issue was caused by inconsistent naming and not saving the new created unit as well as the updated property.
I double checked all my schema exports and references and noticed I was using UpperCase in some instances and LowerCase in others, and saved the newUnit as well as the updated property in the POST request and it worked.
I'm building an app where a user logs in and can create a grocery list on their account (there are more things they can do like create recipes, but this is the example I want to use). Right now I have it so everybody who logs in sees the same list. But I want each user to be able to log in and view their own grocery list that they made. I'm assuming the logic is literally like logging into a social media site and viewing YOUR profile, not somebody else's.
I'm using mongoDB/mongoose and I just read about the populate method as well as referencing other schemas in your current schema. Here is my schema for the list:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create item schema
const GroceryListItemSchema = new Schema({
item: {
type: String,
required: [true, 'Item field is required']
},
userId: {
type: mongoose.Schema.Types.ObjectId, ref: "user",
}
});
// Create an Item model
const GroceryListItem = mongoose.model('groceryListItem', GroceryListItemSchema);
module.exports = GroceryListItem;
And here is the post request to add a list item:
//POST request for shopping list
router.post("/list", checkToken, (req, res, next) => {
// Add an item to the database
const groceryListItem = new GroceryListItem({
item: req.body.item,
userId: ???
})
groceryListItem.save()
.then((groceryListItem) => {
res.send(groceryListItem);
})
.catch(next);
});
Here is my userModel - not sure if this is necessary to show:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
password2: {
type: String,
required: true,
},
});
const User = mongoose.model("users", UserSchema);
module.exports = User;
(in case anyone is wondering why the model is called "users"-- that's what I initially called it on accident and when I changed the name to "user" it errored out...so I changed it back.)
I am not sure how to add the userId when making an instance of the groceryListItem. In the mongoose docs (https://mongoosejs.com/docs/populate.html#saving-refs), they use the example of a Story and Person Schema. They reference each other, and then they create an instance of Person, calling it author. Then they grab the _id from author and reference it in their instance of Story, called story1. So that makes sense to me. But the only way they can do that is because author and story1 are located in the same file.
So it seems like what I should do is grab the user _id by saying userId: users._id. But my new User instance is in my user routes. And I'd rather not combine the two. Because then I'd have another list to combine as well so that would be my user routes, recipe routes, and shopping list routes all in one file and that would be extremely messy.
Anyone have any idea how I can make this work? It seems so simple but for some reason I cannot figure this out.
Thank you!!
EDIT - frontend API call:
handleSubmitItem = (e) => {
e.preventDefault();
const newItem = {
item: this.state.userInput,
};
authAxios
.post(`http://localhost:4000/list/${userId}`, newItem)
.then((res) => {
this.setState({ items: [...this.state.items, newItem] });
newItem._id = res.data._id;
})
.catch((err) => console.log(err));
this.setState({ userInput: "" });
};
Here you can simply pass in the user ID in the POST request params. The POST URL in the frontend should look like this; {localhost:9000/like/${userID}}
You can get the user ID at the express backend like this;
router.post("/list/:id", checkToken, (req, res, next) => {
// Add an item to the database
const groceryListItem = new GroceryListItem({
item: req.body.item,
userId: req.params.id
})
groceryListItem.save()
.then((groceryListItem) => {
res.send(groceryListItem);
}).catch(next);
});
I have a problem. I am new to node.js and mongoDB (using mongoose). In MySQL when I have defined a table with required fields the database will refuse to accept input that don't conform to the model's rules. I have noticed that in mongoDB, at least, the way I have set it up, this is not the case.
I have defined the following model in blog-schema.js:
const mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = mongoose.Schema({
title: {
type:String,
required: true,
},
author: {
type: String,
required: true,
},
category: {
type: String,
required: true,
},
text: {
type: String,
required: true,
},
date: {
type: Date,
default: Date.now,
},
})
module.exports = mongoose.model('BlogPost', userSchema, 'blog');
In this, I set required:true for all fields apart from date. I then implemented this in conn.js:
const mongoose = require('mongoose')
, BlogPost = require('./schemata/blog-schema')
, db_config = require('./config')
, uri = 'mongodb://' + db_config.user + ":" + db_config.password + "#" + db_config.host + db_config.database;
DataFunctions = function (){
mongoose.connect(uri, db_config.opts);
mongoose.Promise = global.Promise;
this.connections = {};
this.schemas = {};
this.schemas.BlogPost = BlogPost;
this.connections.db = mongoose.connection;
};
DataFunctions.prototype.insert = function(data = {}, callback = null) {
var schema = this.schemas.BlogPost;
this.connections.db.on('error', console.error.bind(console, 'connection error'));
this.connections.db.once('open', function(dataStructure = schema) {
this.items = data;
if (callback != null) {
dataStructure.collection.insertOne(this.items, callback);
mongoose.connection.close();
}
else {
dataStructure.collection.insertOne(this.items, function(err, docs) {
if (err) throw err;
});
mongoose.connection.close();
}
});
mongoose.connection.close();
}
DataFunctions.prototype.retrieve = function(params = {}, columns = '', callback = null) {
var schema = this.schemas.BlogPost;
this.connections.db.on('error', console.error.bind(console, 'connection error'));
this.connections.db.once('open', function(dataStructure = schema) {
if (callback != null) {
dataStructure.find(params, columns, callback);
}
else {
dataStructure.find(params, columns, function(err, data) {
if (err) throw err;
});
}
});
}
module.exports = DataFunctions;
However, when I execute the insert function, it accepts it without error even when fields marked required are left blank. I would really appreciate any assistance in working out how to validate the data inserted into the mongoDB collection.
I am using mongoos version 5.3.6, and mongoDB version 4.0.3
Thank you.
Edit
Thanks to everyone who replied, based on some of the comments below I changed dataStructure.collection.insertOne() to dataStructure.create(), which appears to include validation.
You need to add verification on submit as well or before submit. When the form has errors it is actually invalid so check if it is invalid before submitting..
Tbh you code seems a little verbose, complicated and confusin.. is there are reason you are doing it like this? For example you are using a mongoose schema but not actually submitting with the mongoose methods which is why none of your validations are occuring.. insertOne is not a mongoose method, and you aren't using your model to save the entry. it would be model.save(data)
also you can directly save without having to call the schema again, just declare a new variable.
const post = new BlogPost(data); post.save().then(console.log).catch(console.log);
//also mongoose.connect already returns a promise
mongoose
.connect(
dbUrl,
{ useNewUrlParser: true }
)
.then(() => console.log("Connected"))
.catch(error => console.log("Failed " + error));
I believe you are passing empty string and that's why the validators are not flagging the entries as erroneous. Try passing null for the fields and check the behavaior.