node mongoose updating an Object in Array doesn't work - node.js

I am trying to update an Array of objects wo any success..
the update statement does not work ..
I may have to update the table instance array and update it with the new valueand then save the table...
const picked = table.meta.permissions.find(obj => obj._id == req.params.permissionId);
console.log('picked: %j', picked); // need to update it !
// how can I update this permission object with the new value from req.body ?
table.save()
.then(savedTable => res.json(savedTable))
.catch(e => next(e););
I have an Array of permissions in a 'meta' field:
MODEL
const Schema = mongoose.Schema;
const Permission = new Schema({
permission: {
role_id: { type: String },
canWrite: { type: Boolean }
}
});
const TableSchema = new mongoose.Schema({
meta: {
name: { type: String, required: true },
permissions: [Permission],
...
},
...
);
In the controller , I firstly load the requested table and append it to the req object, then I execute the updatePermission function, and try to update the table instance permission with new values using $set
CONTROLLER
import Table from '../../models/table.model';
/**
* Load table and append to req.
*/
function load(req, res, next, id) {
Table.get(id)
.then((table) => {
req.table = table;
return next();
})
.catch(e => next(e));
}
function updatePermission(req, res, next) {
const table = req.table;
console.log('Current table.meta.permissions: %j', table.meta.permissions, '\n');
console.log('update permission: ', req.params.permissionId, ' with: ', req.body, '\n');
const query = { 'meta.permissions._id': req.params.permissionId }
const update = { $set: { 'meta.permissions.$.permission': req.body } };
const options = { new: true};
table.update(query, update, options)
.then(savedTable => res.json(savedTable))
.catch((e) => { next(e); });
}
The console log displays the current table permissions and the req.params and req.body
Why the update statement doesn't run correctly
thanks for feedback

I found a way to solve this issue but I don't know if it's the best one ?
I update the table object using some() then I save the table..
function updatePermission(req, res, next) {
const table = req.table;
table.meta.permissions.some((obj, idx) => {
if (obj._id == req.params.permissionId) {
table.meta.permissions[idx].permission = req.body;
return true;
}
return false;
});
table.save()
.then(savedTable => res.json(savedTable))
.catch(e => next(e); );
}

Related

ReferenceError: Cannot access 'savedPost' before initialization

My POST method which throws the ReferenceError:
postsRouter.post('/:postNumber', async (req, res, next) => {
const postDate = new Date().toUTCString()
const post = new Post({
...req.body,
poster: req.body.poster
? (req.body.poster.includes(config.AP) ? req.body.poster.replace(config.AP, '') : req.body.poster)
: 'nonamer',
date: postDate,
IP: req.socket.remoteAddress,
admin: req.body.poster.includes(config.AP) ? true : false
})
try {
const [savedPost, updatedBubble] = await Promise.all([
/* save() doesn't save the post with the new postnumber which it should (see the pre('save')
function in the next code block), it's only in the post that is being returned while saving. */
post.save().catch(e => { console.log('Task 1 failed!'); next(e) }),
// req.params.postNumber is the postnumber of the bubble a.k.a thread being replied to.
Post.findOneAndUpdate(
{ postNumber: req.params.postNumber },
{ $push: { replies: savedPost.postNumber } })
.catch(e => { console.log('Task 2 failed!'); next(e) })
])
res.status(201).json(savedPost)
console.log('!!!!! ' + req.socket.remoteAddress + ' saved a post: ' + JSON.stringify(savedPost))
} catch (e) {
next(e)
}
})
Also this mess of a code is in my Post model (which utilizes Counter model):
// Increase postnumber in counter collection's sole document, and insert it into new post.
postSchema.pre('save', function(next) {
let post = this
const ccc = Counter.findByIdAndUpdate(
{ '_id': 'postNumCounter' },
{ '$inc': { 'currentPostNum': 1 }}, // Works
{ new: true },
// inserting postnumber not working, fix
function(error, counter) {
post.postNumber = counter.currentPostNum
}
)
// Not working either
post.postNumber = ccc.currentPostNum
next()
})
My goal is I simply want to grab the currentPostNum after incrementing it, and insert that into the post before saving it.
Solved myself. I made the pre('save') function asynchronous as well, and ditched Promise.all in the controller POST method. Now everything works in tandem.
postSchema.pre('save', async function (next) {
let post = this
const counter = await Counter.findByIdAndUpdate(
{ '_id': 'postNumCounter' },
{ '$inc': { 'currentPostNum': 1 } }, // Works
{ new: true }
)
post.postNumber = counter.currentPostNum
next()
})

How to update a MongoDB document field only once?

I'm trying to make a Mongoose query to run only once if the field inside the document hasn't been updated already, but I'm getting a bit lost with the $exist, $update, $ne - I don't know what to use really.
I have the following query:
const assignPhotoCodeToModel = (modelID, photoCode) => {
const filter = { id: modelID }
const update = { photoCode: photoCode }
const options = { new: true, useFindAndModify: false }
return new Promise((resolve, reject) => {
ModelModel.findOneAndUpdate(filter, update, options)
.then((response) => {
resolve(response)
})
.catch((error) => {
reject(error)
})
})
}
But I want this to run only if the field Model.photoCode is empty. If it already has a value, I just want it to return that value.
The problem I'm having is that every time the backend hits the route, a new photoCode gets assign to the model even if it has been already assigned. So it gets reassigned again again with a new one.
router.post(
'/payment/success',
(req, res, next) => {
...omitted_code...
photoCodeModel
.assignPhotoCodeToModel(req.body.modelId, photoCode)
... omitted_code
})
Edit 1
I'm writing this after trying the answers provided here. At first glance they seem to work, but now I'm getting into the error that when the Model.photoCode already has a value in it the return of the following function:
const assignPhotoCodeToModel = (modelID, photoCode) => {
//the difference is here
const filter = { id: modelID, photoCode : null }
const update = { $set: { photoCode: photoCode } }
const options = { new: true, useFindAndModify: false }
return new Promise((resolve, reject) => {
ModelModel.findOneAndUpdate(filter, update, options)
.then((response) => {
resolve(response)
})
.catch((error) => {
reject(error)
})
})
}
returns null, while I'm actually expecting the same model document to be returned. I'm guessing is because of the filtering? When I remove the photoCode : null it goes back to "working", but then I'm back to the problem that photoCode field gets populated with new values.
Basically I would like that function populates the filed photoCode once, but every subsequent call should just bring me back the same model document.
I think what you need is $set .
And, following your logic, I think you have to query "document that doesn't have photoCode yet".
It should be:
const assignPhotoCodeToModel = (modelID, photoCode) => {
//the difference is here
const filter = { id: modelID, photoCode : null }
const update = { $set: { photoCode: photoCode } }
const options = { new: true, useFindAndModify: false }
return new Promise((resolve, reject) => {
ModelModel.findOneAndUpdate(filter, update, options)
.then((response) => {
resolve(response)
})
.catch((error) => {
reject(error)
})
})
}
Write it in a neat asynchronous form. It will work.
const assignPhotoCodeToModel = async (modelID, photoCode) => {
const filter = { id: modelID, photoCode : null };
const update = { photoCode: photoCode };
const options = { new: true, useFindAndModify: false };
return await ModelModel.findOneAndUpdate(filter, update, options);
}

I want to delete all the item but except of the current user

I am creating a call that deletes all the users except the current user logged in.
Here is my code;
exports.deletealluser = async (req, res) => {
try {
const { sub } = req.user;
const usersExceptCurrent = await User.find({ _id: !sub });
const deletedUsers = await User.deleteMany(usersExceptCurrent);
res.status(201).json({
message: 'A all user is successfully deleted!',
deletedUsers,
});
} catch (err) {
return res.status(400).json({
message: 'Something went wrong.',
});
}
};
sub is the id of the current user. As you can see, I call find query first to filter the data which is not equal to sub. Then I use the usersExceptCurrent as filter to my deleteMany query.
But it returns status 400
And here is my axios call;
const onDelete = async () => {
try {
const { data } = await fetchContext.authAxios.delete(
'admin/delete-all-users'
);
fetchContext.setUserList(
fetchContext.userList.filter((row) => row === data.deletedUsers)
);
setSignupSuccess(data.message);
setSignupError('');
setOpen(false);
setOpenAlert(false);
} catch (err) {
setIsLoaded(true);
setError(err);
const { data } = err.response;
setSignupError(data.message);
setSignupSuccess('');
}
};
Use $ne
$ne selects the documents where the value of the field is not equal to the specified value. This includes documents that do not contain the field.
db.collection.find({ _id: { $ne: sub } })
Demo - https://mongoplayground.net/p/ecMNn4ueZrn
If you still face for _id should be ObjectId you can do
const ObjectId = require("mongodb").ObjectId;
db.collection.find({ _id: { $ne: ObjectId(sub) } })
See what ! does, converts to bool value in JS
console.log(!"a");
console.log(!2);

Updating a nested objects in Mongoose

I have the following express route:
const updateSnapshot = async (req, res) => {
const accountId = req.body.account_id;
if (!accountId) {
return fail(res, 'account id is missing', 400);
}
try {
const account = await Account
.findOne({ _id: accountId})
.populate({
path: 'snapshot',
model: 'Snapshot'
});
// I want to update these fields in snapshot
const snapshot = {
friends_count: data.friends_count,
updated_date: new Date()
};
account.snapshot.friends_count = snapshot.friends_count;
account.snapshot.updated_date = snapshot.updated_date;
await account.save();
return success(res, snapshot);
} catch(error) {
fail(res, error.message, 500);
}
};
I want to update the nested object snapshot (just the fields friends_count and update_date) however when I check the database it seems to have not work. What am I doing wrong here?
const updateSnapshot = (req, res) => {
const accountId = req.body.account_id;
if (!accountId) {
return fail(res, 'account id is missing', 400);
};
Account
.findOneAndUpdate({ _id: accountId }, {
$set: {
snapshot: {
friends_count: data.friends_count,
updated_date: new Date()
}
}
}, {
new: true
})
.then(account => {
if(!account) {
return fail(res, "Account not found", 404);
} else {
return success(res, account);
};
})
.catch(err => {
return fail(res, error.message, 500);
});
};
Here we're using the findOneAndUpdate method and promises to find and update the document in one operation.
findOneAndUpdate takes the query criteria as the first parameter, the second parameter updates values specified in the object (we're using the $set operator to update specific values of the snapshot object), and the three parameter is an object that defines the operation's options (new is set to true to return the newly updated object).
Note: $set will replace the entire snapshot object so if there are other properties inside the snapshot object, they will need to be included inside the $set object.

Sequelize how to get all data if there is no query string passed

I'm pretty new to Sequelize.
I'm trying to create a handler to get all playlists in my database.
This is what I want to do:
If there is a query string then it should return the result based on that query.
If there is no query string passed then it should return all my playlists.
This is my playlist model:
const Playlist = db.define("playlist", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: Sequelize.STRING,
unique: true,
},
});
Here is my handler:
exports.getPlaylists = async (req, res) => {
try {
const { name } = req.query;
console.log(name); // this prints the name
const result = await Playlist.findAll({
where: {
name:
name === undefined ? {} : { $like: `%${name}%` },
},
});
if (result) {
res.status(200).send(result);
} else {
response.status(404).send("No Playlists found");
}
} catch (error) {
res.status(500).send(`Internal server error: ${error}`);
}
};
This works well if I passed a name in the query. but If I didn't pass any query string. It returns an empty array.
$like is an alias for Sequelize.Op.like
What should I put instead of the empty object?
I checked this question How get all data if field in query string is empty Node and Sequelize with Postgres but the proposed solutions didn't work with me
Create a filter object based on the condition. If you pass empty object to where, it won't apply that in the query.
exports.getPlaylists = async (req, res) => {
try {
const { name } = req.query;
const filters = {};
if (name)
filters.name = {
[Op.like]: `${name}%`, // you can also use $like if you are using older version of sequelize
}
const result = await Playlist.findAll({
where: filters,
});
if (result) {
res.status(200).send(result);
} else {
response.status(404).send("No Playlists found");
}
} catch (error) {
res.status(500).send(`Internal server error: ${error}`);
}
};
This way you can prepare complex filters based on other query strings.

Resources