findOneAndUpdate in mongoose (MERN) - mern

A small bottleneck arose while developing a short tutorial:
I would like to be able to push this model:
const studentFatherSchema = new Schema ({
yam:{
type: String,
trim: true
},
emailmanagement: {
type: String,
trim: true
},
students: [{
name: {
type: String,
trim: true
}
}]
});
Would I first have to find the parent object with findById (req.params.studentFatherId) and then do the update, or can I do it all at once? I'm trying to do it all at once, but I can't, what do you recommend?
Note: I can update the name and emailmanagement fields without problems, but doing an update with the push does not add it to me, I am testing it with Postman and
I have something like this but it doesn't work!
exports.updateAddChild = async (req, res, next) => {
try {
const student = req.body;
const studentFather = await StudentFather.findOneAndUpdate (
{_id: req.params.studentFatherId},
{$ push: {students: student}},
);
} catch (error) {
console.log (error);
next ();
}
}
Thank you very much for your help!

Related

Sequelize error "Unknown column in 'field list'" during CREATE for One-to-Many Association

I have been using the following tutorial to learn how to implement one-to-many relationship in Sequelize where a Tutorial has many Comment and Comment belongs to a Tutorial: https://www.bezkoder.com/sequelize-associate-one-to-many/
While I have the code modeling what is documented for setting up the relationship between the two models, I receive the following error during creating the Comment model:
Unknown column 'tutorialId' in 'field list'
Additionally, I receive the following SQL output:
Executing (default): INSERT INTO `comments` (`id`,`name`,`text`,`createdAt`,`updatedAt`,`tutorialId`) VALUES (DEFAULT,?,?,?,?,?);
app.js
const tutorialsRouter = require('./routes/api/tutorials');
const commentsRouter = require('./routes/api/comments');
app.use('/api/tutorials', tutorialsRouter);
tutorialsRouter.use('/:tutorialId/comments', commentsRouter);
/models/index.js
db.tutorials = require("./tutorial.model")(sequelize, Sequelize);
db.comments = require("./comment.model")(sequelize, Sequelize);
db.tutorials.hasMany(db.comments, { as: "comments" });
db.comments.belongsTo(db.tutorials, {
foreignKey: "tutorialId",
as: "tutorial",
});
/models/comment.model.js
module.exports = (sequelize, DataTypes) => {
const Comment = sequelize.define('comment', {
name: {
type: DataTypes.STRING,
},
text: {
type: DataTypes.STRING,
},
});
return Comment;
}
/routes/comments.js
const comments = require('../../controllers/comments.controller');
const router = require('express').Router({ mergeParams: true });
router.post('/', comments.create);
module.exports = router;
/controllers/comments.controller.js
const db = require('../models');
const Comment = db.comments;
exports.create = (req, res) => {
...
Comment.create({
name: req.body.name,
text: req.body.text,
tutorialId: req.params.tutorialId,
})
.then( ... )
.catch( ... );
}
Then in Postman I receive a 500 (of the error message above) when issuing the request:
POST localhost:3000/api/tutorials/1/comments
{
"name": "John Doe",
"text": "Lorem ipsum..."
}
I don't think I should have to define a tutorialId field on the Comment model. Grr...
This may be very obvious to some of you, but it's tripping me up trying to learn. Any help is very much appreciated. :)
The issue that you're having is a result of using aliases via the as property. See the docs for belongsTo and hasMany. Here's a code sample that performs the inserts without the error.
let {
Sequelize,
DataTypes,
} = require('sequelize')
async function run () {
let sequelize = new Sequelize('dbname', 'username', 'password', {
host: 'localhost',
port: 5555,
dialect: 'postgres',
logging: console.log
})
let Comment = sequelize.define('comment', {
name: {
type: DataTypes.STRING,
},
text: {
type: DataTypes.STRING,
},
})
let Tutorial = sequelize.define('tutorial', {
title: {
type: DataTypes.STRING,
},
content: {
type: DataTypes.STRING,
}
})
Tutorial.hasMany(Comment)
Comment.belongsTo(Tutorial)
// This just recreates the tables in the database.
// You would really only want to use a force sync
// in a development environment, since it will destroy
// all of the data....
await sequelize.sync({ force: true })
let tutorial = await Tutorial.create({
title: 'Tutorial',
content: 'Hmm....'
})
let comment = await Comment.create({
name: 'Comment',
text: 'Something, something....',
tutorialId: tutorial.id,
})
await sequelize.close()
}
run()
Edit
This is just an edit to my original answer above. The OP Tom Doe discovered that the issue was being caused by a mismatch between the definitions of the tables in the database and the models defined via sequelize (see comments below). As we discovered, one way to troubleshoot the mismatch is to force sync a new version of the database, and then compare the new version of the database with the original version. There may be differences in the definitions of the columns or the contraints. Force syncing the database can be done via the command
await sequelize.sync({ force: true})
Important Note: The above statement will overwrite the existing database and all of its data. See the docs for more information.

Sequelize Error: Include unexpected. Element has to be either a Model, an Association or an object

I am receiving the error when I make a call to my API with a get request:
Include unexpected. Element has to be either a Model, an Association or an object.
My Models look like this:
module.exports = (sequelize, Sequelize) => {
const Productions = sequelize.define("productions", {
id: {
type: Sequelize.SMALLINT,
autoIncrement: true,
primaryKey: true
},
setupTime: {
type: Sequelize.DECIMAL(6, 3)
},
notes: {
type: Sequelize.TEXT
}
}, { timestamps: false });
return Productions;
};
module.exports = (sequelize, Sequelize) => {
const ProductionPrints = sequelize.define("productionPrints", {
id: {
type: Sequelize.SMALLINT,
autoIncrement: true,
primaryKey: true
},
compDate: {
type: Sequelize.DATE
}
}, { timestamps: false });
return ProductionPrints;
};
The relationship between the models is defined here:
db.productions = require("./productions.model.js")(sequelize, Sequelize);
db.productionprints = require("./production-prints.model.js")(sequelize, Sequelize);
db.productions.hasOne(db.productionprints, {
foreignKey: {
name: 'productionId',
allowNull: false
}
});
db.productionprints.belongsTo(db.productions, { foreignKey: 'productionId' });
And the sequelize query looks as so:
const db = require("../models");
const Productions = db.productions;
const ProductionPrints = db.productionPrints;
exports.findAll = (req, res) => {
Productions.findAll({
include: [ { model: ProductionPrints, as: 'prints' } ]
})
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "An error occurred while finding the productions."
});
});
};
I have checked around for others with the issue but have had no avail with any solutions posted on those problems. Generally it was caused by typos, or error in the require paths. I have checked those and all my other includes work, just not on any of the models I include on the productions model.
Any feedback is greatly appreciated.
Error was being caused by a typo:
db.productions = require("./productions.model.js")(sequelize, Sequelize);
db.productionprints = require("./production-prints.model.js")(sequelize, Sequelize);
when this was being referenced in the assigned to a constant:
const Productions = db.productions;
const ProductionPrints = db.productionPrints;
shame on me for changing my case use:
db.productionprints != db.productionPrints
I had the same issue , this is usually caused by naming issue , to track the issue you can check one of the following places to resolve it
check if you are calling the correct model class name
when importing models becarefull not to call the file name instead of model name => the one exported
3.check if you got your association correctly by calling the exported model not the file name
check if your cases e.g users vs Users.
a bonus tip is to use same name for model and file name to avoid these issues because the moment you make them different you likely to make these mistakes
Following the answer of Kelvin Nyadzayo, i have the model.findOne(options) method with a
options.include like this:include: [ { } ] in the options parameter
The include has to have the proper syntax: [{model: Model, as: 'assciationName'}]
And the mine was empty
So this, was triggering the same error

set model values from json in nodejs

Hi Guys I want to know how to save json string into mongoose model object?
let me explain a simplified version of my problem:
I have a schema model:
const mongo = require('mongoose');
const clientSchema = mongo.Schema({
name: {type: String},
age: {type: Number},
updated_at: {type: Date},
}
and I have a put method which is shown below:
var Client = mongo.model('client', clientSchema);
//Update User
server.put(`/api/clients/:_id`, (req, res) =>
{
Client.model.findById(req.params._id, (err, foundedclient) =>
{
if(err) res.send(err);
//***********************************************************//
/*I want to update foundedclient from req.body here! */
/*some function like : foundedclient.JsonTovalues(req.body); */
//***********************************************************//
foundedclient.updated_at = new Date().toISOString();
foundedclient.save((err) =>
{
res.send('saved successfully!');
});
});
});
the req.body is a json:
{
"name":"bardia",
"age":27,
}
I want to update foundedclient value from req.body at the position I highlighted in the code by //*******// signs. I want a hypothetical function such as foundedclient.JsonTovalues(req.body). what is the best way to achieve that? In other words what is the best way to save json as mode values?
Thank a lot
you can define an instance method to something similar to updateByJson as explained below
const clientSchema = mongo.Schema({
name: {type: String},
age: {type: Number},
updated_at: {type: Date},
}
// here simply calling update method internally but exposed as an instance method
clientSchema.methods.updateByJson = function(jsonToUpdate, cb){
// will work if you are using mongoose old version 3.x
this.constructor.update({_id: this._id}, {$set:jsonToUpdate}, cb);
// should work with latest versions
this.model('client').update({_id: this._id}, {$set:jsonToUpdate}, cb);
}
Your client code will look like this
var Client = mongo.model('client', clientSchema);
//Update User
server.put(`/api/clients/:_id`, (req, res) =>
{
Client.model.findById(req.params._id, (err, foundedclient) =>
{
if(err) res.send(err);
jsonToUpdate = req.body
jsonToUpdate.updated_at = new Date().toISOString();
foundedclient.updateByJson(jsonToUpdate, (err) => {
res.send('saved successfully!');
});
});
});
I hope this helps you.

Updating sub array in JSON with a REST API in Mean Stack

I'm developing a MEAN stack application and I'm hung up on how to actually update a document that has been saved into the MongoDB already. I've seen that I have to use patch instead of post in my REST API paths, but it's still a little clouded to me. I want to insert a new Package into the Package JSON Array in the User JSON.
Possible Duplicate, but he's overriding a value in the array and not adding a new object into it.
My JSON Schema:
//User schema
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
require: true
},
username:{
type:String,
required: true
},
password:{
type:String,
required: true
},
packages: [{
from: String,
to: String,
tracking: String
}]
});
My REST API Paths
//Update
router.patch('/update', (req, res) => {
const username = req.body.username;
const packages = req.body.packages;
User.getUserByUsername(username, (err, user) => {
if(!user){
return res.json({success: false, msg: 'User not found'});
} else {
User.addPackages(user, req.body.packages, (err, user) => {
if(err){
res.json({success: false, msg:'Failed to update packages'});
} else {
res.json({success: true, msg:'update packages'});
}
})
}
});
});
My Module's:
module.exports.addPackages = function(user, packages, callback){
User.findOneAndUpdate(
{username:user.username},
{$push: {"packages" : {
"to" : packages.to,
"from" : packages.from,
"tracking" : packages.tracking
}}},
{new:true},
function(err, newPackage){
if (err) throw err;
});
}
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
module.exports.getUserByUsername = function(username, callback){
const query = {username: username}
User.findOne(query, callback);
}
They're updating into my MongoDB, but just the object ID and not the values...
db.your_collection.update({},
{$set : {"new_field":1}},
{upsert:false,
multi:true})

Mongoose / NodeJS - String from db is retrieved but considered as not defined

Here's my problem now:
I query a findOne and populate on my DB in order to retrieve an array of string to use in my .EJS but the log says that the value is not defined but its give the value name : "stringName is not defined"
I must have missed something..
This is the User schema :
var UserSchema = new mongoose.Schema({
username: { type: String, required: true, index: {
unique: true } },
email: { type: String, required: true, index: {unique: true } },
password: { type: String, required: true },
tables: [{ type: Schema.Types.ObjectId, ref: 'Table' }],
resetPasswordToken: String,
resetPasswordExpires: Date,
uuid: String,
});
This is the Table schema :
var TableSchema = Schema({
name: { type: String, required: true, index: { unique: true }},
logos: [{ type: Schema.Types.ObjectId, ref: 'Logo'}],
});
This is where I do the query and send the document to the .ejs page:
app.get('/dashboard/:uuid', function(req, res){
if (req.user && userId != "")
{
var query = User.findOne({username: req.user.username}).populate('tables').select('tables');
query.exec(function (err, tables){
if (err) return console.error(err);
console.log (tables.tables[0].name); // Return the right string name
res.render('./pages/dashboard.ejs', {username: req.user.username, tables: tables.tables});
});
}
else
res.redirect('/');
});
And this is the script in ejs that is supposed to render the table names in my page:
<script>
$(document).ready(function (){
<% for(var i = 0; i < tables.length; i++) {%>
var newTab = "<div class=\"square\" style=\"display: inline-block\"><span style=\"margin-top: 50%; text-align: center\">" + <%=tables[i].name%> + "</span></div>";
$(newTab).appendTo('.jumbotron');
<%}%>
});
</script>
If you guys could enlighten up a bit my way that would be so great !
Take a look at this implementation, this is how i would query schema, in firs example we reuse req.user (good), in second we make 2 database calls (bad). In your example you make 1 database call but not populating Logo field of table schema (bad).
app.get('/dashboard/:uuid', function(req, res){
// first example
// no need to query users, you already have tables field
if (!req.user) // what is userId, why you check it
// add `err` checks
return res.redirect('/');
TableSchema
.find({ _id: { $in: req.user.tables } })
.populate('logos', 'url'); // Logo schema fields
.exec(function(err, result_tables){
res.render('./pages/dashboard.ejs', {username: req.user.username, tables: result_tables});
});
// or second example
// if you still for some reason cannot use req.user.tables field
// but strongly recommend to use first one
User.findById(req.user._id, 'tables')
.exec(function (err, user_tables){
// add `err` checks
TableSchema.populate(user_tables, { path: 'logos', model: 'Logo' }, function (err, result_tables){
// add `err` checks
res.render('./pages/dashboard.ejs', {username: req.user.username, tables: result_tables});
});
});
});
As per your comment
in chrome browser : " Uncaught ReferenceError: stringName is not defined " (stringName = what's in tables[0].name)
Try to use forEach operator
<script>
$(document).ready(function (){
<% tables.forEach(function(table){ %>
var newTab = "<a ommited><%= table.name %></a>"; //notice: no `"`
$(newTab).appendTo('.jumbotron');
<% }) %>
});
</script>