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);
});
Related
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 facing an issue in the association of sequelize
My data was saved in the database but roles are not saved
I making registration using postgres but facing an issue of setRole is not defined
My code is
Auth_User.js is a model for signIn and signUp user we create the association in this file
import Sequelize from "sequelize";
import { sequelize } from "../Database/database";
const Auth_User = sequelize.define("authUsers", {
userName: {
type: Sequelize.STRING,
},
email: {
type: Sequelize.STRING,
},
password: {
type: Sequelize.STRING,
},
});
Auth_User.associate = function(models) {
Auth_User.belongsToMany(models.Role, { through: 'user_roles', foreignKey: 'userId', otherKey: 'roleId'});
};
export default Auth_User;
Role.js in this file we create a role model where we have admin and user
import Sequelize from "sequelize";
import { sequelize } from "../Database/database";
const Role = sequelize.define('roles', {
id: {
type: Sequelize.INTEGER,
primaryKey: true
},
name: {
type: Sequelize.STRING
}
});
Role.associate = function(models) {
Role.belongsToMany(models.Auth_User, { through: 'user_roles', foreignKey: 'roleId', otherKey: 'userId'})
};
export default Role;
Know signup.js
here we create signup we are facing the issue in this file
making issue here user.setRole is not a function
import Auth_User from '../Model/auth.model';
import Role from '../Model/role.model';
var bcrypt = require('bcryptjs');
var jwt = require('jsonwebtoken');
var db= require('../Database/database')
const Op = db.Sequelize.Op;
export const signup = (req, res) => {
// Save Auth_User to Database
console.log("Processing func -> SignUp");
Auth_User.create({
userName: req.body.userName,
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 8)
}).then(user => {
Role.findAll({
where: {
name: {
[Op.or]: req.body.roles.map(role => role.toUpperCase())
}
}
}).then(roles => {
console.log(user)
user.setRole(roles).then(() => {
res.send("User registered successfully!");
});
}).catch(err => {
res.status(500).send("Error -> " + err);
});
}).catch(err => {
res.status(500).send("Fail! Error -> " + err);
})
}
First of all, I recommend you to be really careful with the model name you choose in the model definition: sequelize.define(modelName, attributes, options). Sequelize automatically makes the plural for you, so a good way to define tables is to always use the singular, but of course this is completely optional. This means that if you use as model name role instead of roles, the table name will be automatically set as roles by Sequelize.
The second recommendation is to not use the uppercase in this same model name, because when you have to deal with automatically created foreign keys and you want to access them through object.property, you will have to use upper or lowercase depending on how you defined the name. So, for example if you have a companyId in a Users table, in order to obtain it you will have to call user.CompanyId if your model name for the company table was written as Company and user.companyId if it was written as company.
Finally, and answering your main question, I recommend you to look the official docs for Sequelize regarding the Special methods/mixins added to instances (https://sequelize.org/master/manual/assocs.html#special-methods-mixins-added-to-instances), where you will find all the different combinations depending on the association. In your specific case, for a belongsToMany association, its special method has to be used using the plural form. In your case, it should be user.setRoles(roles). This doesn't apply, however, if you prefer to use the add method, which can be used either as user.addRole(roles) or user.addRoles(roles)
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.
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 :)
I am using mongoose with Mongodb v3.4.3
Below is my image model code
const mongoose = require("mongoose");
const CoordinateSchema = require("./coordinate");
const ImageSchema = new mongoose.Schema({
image_filename: {
type: String,
required: true
},
image_url: {
type: String,
required: true
},
coordinates: [CoordinateSchema],
});
Below is my CoordinateSchema code
const mongoose = require("mongoose");
const CoordinateSchema = new mongoose.Schema({
coordinates : {
type: Array,
default: [],
}
});
module.exports = CoordinateSchema;
Below is my api js code running on express,
router.post('/receiveCoordinates.json', (req, res, next) => {
Image.findOneAndUpdate({image_filename:req.body.file_name}).then((image) => {
})
});
How to finish this code so I can store coordinates data in Image model.
Thanks.
UPDATE
To update the coordinates inside of findOneAndUpdate, you simply check that the returned document isn't undefined (which would mean your image wasn't found). Modify your api.js code like so:
router.post('/receiveCoordinates.json', (req, res, next) => {
Image.findOneAndUpdate({image_filename:req.body.file_name}).then((image) => {
if (!image) return Promise.reject(); //Image not found, reject the promise
image.where({_id: parent.children.id(_id)}).update({coordinates: req.body.coordinates}) //Needs to be an array
.then((coords) => {
if (!coords) return Promise.reject();
//If you reach this point, everything went as expected
});
}).catch(() => {
console.log('Error occurred');
);
});
Here's my guess why it isn't working.
In ImageSchema, you are sub-nesting an array of CoordinateSchema. But CoordinateSchema is a document which already contains an array.
This is probably not what you're looking for. If you're using mongoose version 4.2.0 or higher, you can nest CoordinateSchema inside of ImageSchema as a single document. Re-write your ImageSchema like this:
// ...
const ImageSchema = new mongoose.Schema({
// ...
coordinates: CoordinateSchema,
});
If this didn't work or doesn't resolve your issue, please let me know so we can work together to find a solution.