How to display details in one to many relationship - node.js

In my case, one maincategory has many subcategories.
maincategory model defined:
const MainCategorySchema = mongoose.Schema({
mainCategoryName: {
type: String,
unique: true,
required: true,
},
});
subcategory model defined
const SubCategorySchema = mongoose.Schema({
subCategoryName: {
type: String,
unique: true,
required: true,
},
main_category: {
type: mongoose.Schema.Types.ObjectId,
ref: "MainCategory",
},
});
module.exports.getAllSubCategories = function (callback) {
SubCategory.find(callback);
};
route
router.get( "/subCategories",
passport.authenticate("jwt", { session: false }),
(req, res) => {
SubCategory.getAllSubCategories((err, subCategory) => {
if (err) {
let message = "No category";
return res.json({
success: false,
message,
});
} else {
return res.send(subCategory);
}
});
}
);
How can I display mainCategoryName along with subCategoryName?

You need to populate main_category like this:
router.get("/subCategories",
passport.authenticate("jwt", { session: false }), (req, res) => {
SubCategory.find()
.populate("main_category")
.exec((err, subCategory) => {
if (err) {
let message = "No category";
return res.json({
success: false,
message
});
} else {
return res.send(subCategory);
}
});
}
);

Related

Mongoose update subarray item

I'm trying to update sub-array item in my collection , i'm trying to use set but can't get it work with _id , it only work when I say array[0] ...
Here is my method :
exports.updateSubCategory = (req, res) => {
const category = req.category;
Category.findById(category._id, function (err, doc) {
if (err) {
return res.status(400).json({
error: "Can't Find parent category",
});
} else {
doc.subcategory.set(0, { name: req.body.name }); works
doc.subcategory.set(req.body.id, { name: req.body.name });//doesn't work
doc.subcategory.set({_id:req.body.id}, { name: req.body.name });//doesn't work
doc.save((err, updatedCategory) => {
if (err) {
return res.status(400).json({
error: "Can't update subcategory",
});
}
res.json(updatedCategory);
});
}
});
};
My schema :
const mongoose = require("mongoose");
const categorySchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
required: true,
maxlength: 32,
unique: true,
},
subcategory: [
{
name: {
type: String,
trim: true,
required: true,
maxlength: 32,
unique: true,
},
},
],
},
{ timestamps: true }
);
module.exports = mongoose.model("Category", categorySchema);
solution :
exports.updateSubCategory = (req, res) => {
const category = req.category;
Category.findById(category._id, function (err, doc) {
if (err) {
return res.status(400).json({
error: "Can't Find parent category",
});
} else {
let subdoc = doc.subcategory.id(req.body.id);
subdoc.name = req.body.name;
doc.save((err, updatedCategory) => {
if (err) {
return res.status(400).json({
error: "Can't update subcategory",
});
}
res.json(updatedCategory);
});
}
});
};

Error when trying to insert row to table because of UUID foreign key with sequelize.js

I am using sequelize.js with node.js and postgres.
I got 2 simple tables from an example as a 'POC' of sorts.
I changed the ID to be UUID and I am having an issue with the insert into the second table ( with the UUID FK ).
I am using postman to test it.
I am creating todo rows with UUID with no issues,
Then I am trying to create a todo item which has a todo id as foreign key
and it seems that it is failing to recognize that ID!
I tried a manual script in postgres and it worked.
I am probably missing something code wise but I cant figure out what.
here is the error which is being returned to me in postman -
{
"name": "SequelizeDatabaseError",
"parent": {
"name": "error",
"length": 96,
"severity": "ERROR",
"code": "22P02",
"file": "uuid.c",
"line": "137",
"routine": "string_to_uuid",
"sql": "INSERT INTO \"TodoItems\" (\"id\",\"content\",\"complete\",\"createdAt\",\"updatedAt\",\"todoId\") VALUES ($1,$2,$3,$4,$5,$6) RETURNING *;"
},
"original": {
"name": "error",
"length": 96,
"severity": "ERROR",
"code": "22P02",
"file": "uuid.c",
"line": "137",
"routine": "string_to_uuid",
"sql": "INSERT INTO \"TodoItems\" (\"id\",\"content\",\"complete\",\"createdAt\",\"updatedAt\",\"todoId\") VALUES ($1,$2,$3,$4,$5,$6) RETURNING *;"
},
"sql": "INSERT INTO \"TodoItems\" (\"id\",\"content\",\"complete\",\"createdAt\",\"updatedAt\",\"todoId\") VALUES ($1,$2,$3,$4,$5,$6) RETURNING *;"
}
Here are the relevant js files -
todoItems.js controller -
const TodoItem = require('../dal/models').TodoItem;
const uuid = require('uuid/v4');
module.exports = {
create(req, res) {
return TodoItem
.create({
content: req.body.content,
todoId: req.params.todoId,
})
.then(todoItem => res.status(201).send(todoItem))
.catch(error => res.status(400).send(error));
},
update(req, res) {
return TodoItem
.find({
where: {
id: req.params.todoItemId,
todoId: req.params.todoId,
},
})
.then(todoItem => {
if (!todoItem) {
return res.status(404).send({
message: 'TodoItem Not Found',
});
}
return todoItem
.update({
content: req.body.content || todoItem.content,
complete: req.body.complete || todoItem.complete,
})
.then(updatedTodoItem => res.status(200).send(updatedTodoItem))
.catch(error => res.status(400).send(error));
})
.catch(error => res.status(400).send(error));
},
destroy(req, res) {
return TodoItem
.find({
where: {
id: req.params.todoItemId,
todoId: req.params.todoId,
},
})
.then(todoItem => {
if (!todoItem) {
return res.status(404).send({
message: 'TodoItem Not Found',
});
}
return todoItem
.destroy()
.then(() => res.status(204).send())
.catch(error => res.status(400).send(error));
})
.catch(error => res.status(400).send(error));
},
};
todos.js controller-
const Todo = require('../dal/models').Todo;
const TodoItem = require('../dal/models').TodoItem;
module.exports = {
create(req, res) {
return Todo
.create({
title: req.body.title,
})
.then((todo) => res.status(201).send(todo))
.catch((error) => res.status(400).send(error));
},
list(req, res) {
return Todo
.findAll({
include: [{
model: TodoItem,
as: 'todoItems',
}],
order: [
['createdAt', 'DESC'],
[{ model: TodoItem, as: 'todoItems' }, 'createdAt', 'ASC'],
],
})
.then((todos) => res.status(200).send(todos))
.catch((error) => res.status(400).send(error));
},
retrieve(req, res) {
return Todo
.findByPk(req.params.todoId, {
include: [{
model: TodoItem,
as: 'todoItems',
}],
})
.then((todo) => {
if (!todo) {
return res.status(404).send({
message: 'Todo Not Found',
});
}
return res.status(200).send(todo);
})
.catch((error) => res.status(400).send(error));
},
update(req, res) {
return Todo
.findByPk(req.params.todoId, {
include: [{
model: TodoItem,
as: 'todoItems',
}],
})
.then(todo => {
if (!todo) {
return res.status(404).send({
message: 'Todo Not Found',
});
}
return todo
.update({
title: req.body.title || todo.title,
})
.then(() => res.status(200).send(todo))
.catch((error) => res.status(400).send(error));
})
.catch((error) => res.status(400).send(error));
},
destroy(req, res) {
return Todo
.findByPk(req.params.todoId)
.then(todo => {
if (!todo) {
return res.status(400).send({
message: 'Todo Not Found',
});
}
return todo
.destroy()
.then(() => res.status(204).send())
.catch((error) => res.status(400).send(error));
})
.catch((error) => res.status(400).send(error));
},
};
todo table create migration -
module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.createTable('Todos', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID,
},
title: {
type: Sequelize.STRING,
allowNull: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
}),
down: (queryInterface /* , Sequelize */) => queryInterface.dropTable('Todos'),
};
todo-item table create migration -
module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.createTable('TodoItems', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID,
},
content: {
type: Sequelize.STRING,
allowNull: false,
},
complete: {
type: Sequelize.BOOLEAN,
defaultValue: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
todoId: {
type: Sequelize.UUID,
onDelete: 'CASCADE',
references: {
model: 'Todos',
key: 'id',
as: 'todoId',
},
},
}),
down: (queryInterface /* , Sequelize */) =>
queryInterface.dropTable('TodoItems'),
};
todo model -
const uuid = require('uuid/v4');
'use strict';
module.exports = (sequelize, DataTypes) => {
const Todo = sequelize.define('Todo', {
title: {
type: DataTypes.STRING,
allowNull: false,
}
});
Todo.associate = (models) => {
Todo.hasMany(models.TodoItem, {
foreignKey: 'todoId',
as: 'todoItems',
});
};
Todo.beforeCreate((item, _ ) => {
return item.id = uuid();
});
return Todo;
};
todo-item model -
const uuid = require('uuid/v4');
'use strict';
module.exports = (sequelize, DataTypes) => {
const TodoItem = sequelize.define('TodoItem', {
content: {
type: DataTypes.STRING,
allowNull: false,
},
complete: {
type: DataTypes.BOOLEAN,
defaultValue: false,
}
});
TodoItem.associate = (models) => {
TodoItem.belongsTo(models.Todo, {
foreignKey: 'todoId',
onDelete: 'CASCADE',
});
};
TodoItem.beforeCreate((item, _ ) => {
return item.id = uuid();
});
return TodoItem;
};
What does your router code look like? Are you using correct path parameter for todoId? If you're using express for example. it should look like app.post("/todos/:todoId/todo_items", todoItemController.create) . Note the camelcase todoId . That will ensure that the req.params.todoId you're referencing in todoItems controller would have the right value.
Also, make sure you have a correct body parser to handle req.body.content correctly. In express, this would be done via body body-parser library and app.use(bodyParser.json()) . Add a breakpoint or log statement in the todoItem controller create code and verify that you actually have the correct parameter values.
If you happen to have the error above, it might be because you are nesting other entities in your request body and therefore the UUID is not getting converted from string to a UUID.
For instance if you have a request body like
{
"Transaction": {
"id" : "f2ec9ecf-31e5-458d-847e-5fcca0a90c3e",
"currency" : "USD",
"type_id" : "bfa944ea-4ce1-4dad-a74e-aaa449212ebf",
"total": 8000.00,
"fees": 43.23,
"description":"Description here"
},
}
and therefore in your controller you are creating your entity like
try {
await Transaction.create(
{
id: req.body.Transaction.id,
currency: req.body.Transaction.currency,
type_id: req.body.Transaction.type_id,
total: req.body.Transaction.total,
fees: req.body.Transaction.fees,
description: req.body.Transaction.description,
}......
Your id and type_id are mostly likely not being converted from string to a UUID.
There are multiple ways of tackling this. The most straightforward approach is to do an explicit conversion from string to UUID.
To do this, import parse from the uuid npm module and do the explicit conversion as you can see in the code sample below.
const { parse: uuidParse } = require("uuid");
try {
await Transaction.create(
{
id: uuidParse(req.body.Transaction.id),
currency: req.body.Transaction.currency,
type_id: uuidParse(req.body.Transaction.type_id),
total: req.body.Transaction.total,
fees: req.body.Transaction.fees,
description: req.body.Transaction.description,
}.....
This explicit conversion from string to a UUID will mostly solve the issue.

Axios.delete() not triggering as expected

So I've got a classes Model which contains an array of people who will attend the class, I am trying to remove people from the classes.
So this is the Model:
const mongoose = require('mongoose');
const classMembersSchema = mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user',
}
})
const classSchema = mongoose.Schema({
location: {
type: String,
required: true
},
type: {
type: String,
required: true
},
name: {
type: String,
required: true
},
time: {
type: String,
required: true
},
classMembers: [classMembersSchema]
});
module.exports = mongoose.model('createClass', classSchema);
The classMembers Array is the one I mentioned that I am trying to remove members from. classMembers: [classMembersSchema].
This is the axios.delete:
deleteClassHandler = () => {
axios.delete('/api/classes/' + this.props.id + '/user/' + this.props.userId)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
}
This is the route:
router.delete('/:id/user/:userId', ClassesController.deleteUser);
This is the controller:
exports.deleteUser = (req, res) => {
GymClass.findById({
_id: req.params.id
}, 'classMembers', (err) => {
if (err) {
res.status(401).json({
message: "Error Occured!"
})
} else {
GymClass.findByIdAndDelete({
"classMembers.userId" : mongoose.Types.ObjectId(req.params.userId)
}, (err) => {
if(err) {
console.log('Keeps hitting here!');
res.status(401).json({
message: "Error Occured!"
})
} else {
res.status(200).json({
message: "Success!"
})
}
});
}
})
}
Everything works fine until it hits the console.log('Keeps hitting here!');
At the start of the function the req.params.id which is the class Id of which class we want to modify and the req.params.userId which is the user we want to remove from the Array inside the Model do have the right values but when it gets to that step it gives me the Error.
I'm thinking it could be that it is not finding this:
GymClass.findByIdAndDelete({
"classMembers.userId" : mongoose.Types.ObjectId(req.params.userId)
Since it's in an Array within the classMembers. Any idea or advice to get this to work? Many thanks.

Instance Methods is not working in Sequelize

I can't seem to set the User model properly. I've implemented some instance methods that don't seem to be working
var UserDetails = mysequelize.sequelize.define('user_tb', {
id: {
autoIncrement: true,
type: mysequelize.sequelize.Sequelize.INTEGER,
allowNull: false,
},
username: {
type: mysequelize.sequelize.Sequelize.STRING,
primaryKey: true,
unique: true,
},
hierarchyid: {
type: mysequelize.sequelize.Sequelize.STRING
},
password: {
type: mysequelize.sequelize.Sequelize.STRING,
validate: {
len: {
args: [6, 15],
msg: "Please enter a password with at least 6 chars but no more than 15"
}
},
allowNull: false
},
customerid: {
type: mysequelize.sequelize.Sequelize.INTEGER
},
statususer: {
type: mysequelize.sequelize.Sequelize.STRING,
allowNull: false
},
userid: {
unique: true,
type: mysequelize.sequelize.Sequelize.STRING,
allowNull: false
},
authtoken: {
unique: true,
type: mysequelize.sequelize.Sequelize.STRING,
allowNull: false
}
},
{
tableName: 'user_tb',
timestamps: false,
freezeTableName: true
});
UserDetails.prototype.toJSON = function(){
var user = this;
var userObject = user.toObject();
return _.pick(userObject,['userid','password']);
};
UserDetails.prototype.findByCredentials = function(userid, password)
{
console.log('Sunddep');
var User = this;
// return User.fin
User.findOne({userid}).then((user)=> {
if(!user)
{
return Promise.reject();
}
return new Promise((resolve,reject) => {
bcrypt.compare(password,user.password,(err,res) => {
if(res)
{
resolve(user);
}
else{
reject();
}
})
})
});
}
UserDetails.prototype.generateAuthToken = function()
{
var user = this;
var access = 'authtoken';
var token = jwt.sign({userid:
user.userid.toHexString(),access},process.env.JWT_SECRET).toString();
user.build({
access: token
});
user.save().then(() =>{
return token;
});
}
module.exports = {UserDetails}
server.js
app.post('/user/login', (req, res) => {
console.log(req.body);
var body = _.pick(req.body, ['userId', 'password']);
user.findByCredentials(body.userId, body.password).then(() => {
res.send('Sundeep');
},
(e) => {
sendData: ({
wsState: '0',
messageCode: 'WS000001',
message: 'No user find with this Id',
userData: []
});
res.status(400).send(sendData)
});
});
Hi, I am getting error while calling instance method from other class. Can any one tell me how can i achieve it
UserDetails.prototype.findByCredentials = function(userid, password) {.....}
But while run the server.js file i getting error like UserDetails.findByCredentials is not a function while calling from other class
Thanks for help in advance.

Mongoose findOneAndUpdate: Is there a way to update object in subdocument?

I have a model like this:
// Document
var programSchema = new Schema({
name: {
type: String
},
session: [sessionSchema]
}, {
timestamps: true
});
// Subdocument
var sessionSchema = new Schema({
name: {
type: String
},
info: {
type: String
},
order: {
type: Number
}
}, {
timestamps: true
});
Is there a way to access the subdocuments object and edit if exists, else create new?
I figured something like this:
router.post('/createsession', function (req, res) {
var options = { upsert: true, new: true, setDefaultsOnInsert: true };
var SessionData = req.body.session;
if (!SessionData.id) {
SessionData.id = mongoose.Types.ObjectId();
}
Program.findOneAndUpdate({ _id: req.body.session.id }, { $push: { session: SessionData } }, options, function (err, session) {
if (err) {
return res.status(409).json({
success: false,
message: 'Error creating/updating session'
});
} else {
return res.status(200).json({
success: true,
session: session
});
}
});
});
This only creates a new document. Would I be able to edit existing with this same query?
Try like this
var options = { upsert: true};
Program.findOneAndUpdate({ _id: req.body.session.id }, { $set: {
//Set individually values
name: req.nnae,
} },
options, function (err, session) {
if (err) {
return res.status(409).json({
success: false,
message: 'Error creating/updating session'
});
} else {
return res.status(200).json({
success: true,
session: session
});
}
});
});

Resources