Push values into array with mongoose - node.js

I have a problem pushing values into an array with mongoose (yes I have read many topics about it and tried many ways to do it).
So I have this schema
const Postit = new Schema({
text: {
type: String,
required: true
},
status: {
type: String,
default: 'TODO'
},
modified: {
type: Date,
default: Date.now
},
user: {
type: ObjectId,
ref: 'User',
required: true
},
collaborators: [String]
})
And I'm trying to push a string in the collaborators property where the queries match.
So this is the method I use to update it
addCollaborator(uid, pid) {
return Postit.updateOne({
_id: pid,
user: uid
},
{ $push: { collaborators: 'pepe' } },
(err, raw) => {
//TO DO
})
}
But nothing happens. The query match because if I change $push for $set and put a new value to status property for example it updates.
The funny thing is that if I run it in mongodb client terminal it works.
db.postits.updateOne({
_id: ObjectId("5beb1492cf484233f8e21ac1"),
user: ObjectId("5beb1492cf484233f8e21abf")
},
{ $push: {collaborators: 'pepe' }
})
What i'm doing wrong?

Pick promises or callbacks but do not mix them together. Either you do:
addCollaborator(uid, pid) {
Postit.updateOne({
_id: mongoose.Types.ObjectId(pid),
user: uid
},
{ $push: { collaborators: 'pepe' } },
(err, raw) => {
// TO DO
})
}
Or you do:
addCollaborator(uid, pid) {
return Postit.updateOne({
_id: mongoose.Types.ObjectId(pid),
user: uid
},
{ $push: { collaborators: 'pepe' } }).exec()
.then(result => {
// TO DO
})
}
Also make sure your objectId is an actual mongoose.Types.ObjectId

Ok, the problem is I was using an old schema.
The code works perfectly. Enough code for today...

Related

I have problem updating a subdocument in an array of subdocuments in MongoDB

I have problems updating a subdocument in an array of subdocuments.
Here is my data structure in the users collection:
{
favorites: [
{
id: new ObjectId("639707f36bf9468265d91810"),
expiresAt: 1671361200000,
reminder: false
},
{
id: new ObjectId("637cc4c986b4fbec43579e1f"),
expiresAt: 1672603200000,
reminder: false
}
],
_id: new ObjectId("637e8af40e43f40373686da2"),
email: 'something#something.com',
forename: 'something',
surname: 'something',
role: 'user',
password: 'something',
__v: 0
}
My Schema is:
const userSchema = new Schema({
email: String,
forename: String,
surname: String,
role: String,
password: String,
favorites: {
id: { type: Schema.Types.ObjectId, ref: "Event" },
expiresAt: Number,
reminder: Boolean,
},
});
I want to update the reminder field in a subdocument based on the subdocument’s id.
I’ve tried following approaches:
1.
User.findOneAndUpdate(
{ _id: req.body.user, "favorites.id": { $eq: BSON.ObjectId(req.body.id) } },
{ $set: { "favorites.$.reminder": true } },
).setOptions({ sanitizeFilter: true });
Here nothing happens. It finds the document but does not update it.
2.
User.findOneAndUpdate(
{ _id: req.body.user },
{ $set: { "favorites.$[elem].reminder": true } },
{
arrayFilters: [{ "elem.id": { $eq: BSON.ObjectId(req.body.id) } }],
returnNewDocument: true,
}
).setOptions({ sanitizeFilter: true });
Here it returns an error: “Error: Could not find path “favorites.0.id” in schema”
I cannot find where is my mistake? Any help is much appreciated!
P.S.
Mongo version is 5.0.14
Try to use updateMany instead.
User.updateMany(
{
_id: userId,
"favorites.id": eventId
},
{
$set: {
"favorites.$.reminder": true
}
},
function(err, res) {
if (err) {
// Handle error
} else {
// Handle success
}
}
);
I think you can adapt the query to your calling method findOneAndUpdate. But it's enough to you.

Mongoose query not returning updated information with { new: true } param

Why is my query not returning updated information?
UserSchema.findByIdAndUpdate(
{ _id: userId },
{ $set: { couponList: couponList } }, { new: true }).populate('couponList').exec().then(user => {
// user returning with the old information
}).catch(err => console.log(err));
I have 3 params:
first one is the id of the user i want to update (objectId)
second one is the information I want to update (objectId Array)
third is the flag that says I want to receive the updated information (Boolean)
My coupon schema goes like this:
import mongoose from 'mongoose';
const CouponSchema = new mongoose.Schema({
name: {
type: String,
default: 'Unknown'
},
description: {
type: String,
default: undefined
},
validity: {
type: Date,
default: null
},
code: {
type: String,
default: undefined
},
blackList: {
type: Array,
ref: 'user',
default: []
},
blackListFlag: {
type: Boolean,
default: false,
},
whiteList: {
type: Array,
ref: 'user',
default: []
},
limit: {
type: Number,
default: 0,
},
counter: {
type: Number,
default: 0,
},
amount: {
type: Number,
default: 0,
},
discountType: {
type: String,
default: undefined,
}
}, { collection: 'coupon' });
export default mongoose.model('coupon', CouponSchema);
And in my user schema I have a ref to the coupon schema:
couponList : {
type: Array,
ref: 'coupon',
default: []
},
I think you need to define the field couponList in your schema.
Edit: Just noticed the UserSchema, theoretically, you should be fine, if you are pushing correct objectIds.
findByIdAndUpdate with {new: true} must work as intended.
But I'm not aware of Your code totally and what kind of data You're sending as couponList.
So try to separate update and select operations and see what happens. In fact mongoose does the same when You call findByIdAndUpdate.
For example using express framework:
const User = mongoose.model('user');
router.put('/user/:userId/coupons', async (req, res) => {
try {
const {userId} = req.params;
const {couponList} = req.body;
await User.updateOne(
{_id: userId},
{$set: {couponList: couponList}},
{upsert: false}
);
const user = await User
.findById(userId)
.populate('couponList').lean();
res.status(200).send(user);
}
catch (error) {
console.log(error);
res.status(500).send({})
}
});
P.S. Only reason for that unexpected behavior may be that somehow (but it's not possible) it uses native driver for which {new: true} must be written as: {returnNewDocument: true}
Check this link
I found out that the problem was not with returning updated information but it was on populating the collection.
The correct reference to the coupon collection in user schema:
couponList: [ { type: mongoose.Schema.ObjectId, ref: 'coupon' } ],

Why $pull does not work when using mongodb on node js [duplicate]

i'm trying to do a pretty simple operation, pull an item from an array with Mongoose on a Mongo database like so:
User.update({ _id: fromUserId }, { $pull: { linkedUsers: [idToDelete] } });
fromUserId & idToDelete are both Objects Ids.
The schema for Users goes like this:
var UserSchema = new Schema({
groups: [],
linkedUsers: [],
name: { type: String, required: true, index: { unique: true } }
});
linkedUsers is an array that only receives Ids of other users.
I've tried this as well:
User.findOne({ _id: fromUserId }, function(err, user) {
user.linkedUsers.pull(idToDelete);
user.save();
});
But with no luck.
The second option seem to almost work when i console the lenghts of the array at different positions but after calling save and checking, the length is still at 36:
User.findOne({ _id: fromUserId }, function(err, user) {
console.log(user.linkedUsers.length); // returns 36
user.linkedUsers.pull(idToDelete);
console.log(user.linkedUsers.length); // returns 35
user.save();
});
So it looks like i'm close but still, no luck. Both Ids are sent via the frontend side of the app.
I'm running those versions:
"mongodb": "^2.2.29",
"mongoose": "^5.0.7",
Thanks in advance.
You need to explicitly define the types in your schema definition i.e.
groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }],
linkedUsers: [{ type: Schema.Types.ObjectId, ref: 'User' }]
and then use either
User.findOneAndUpdate(
{ _id: fromUserId },
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
or
User.findByIdAndUpdate(fromUserId,
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
I had a similar issue. I wanted to delete an object from an array, using the default _id from mongo, but my query was wrong:
const update = { $pull: { cities: cityId }};
It should be:
const update = { $pull: { cities: {_id: cityId} }};

mongoose findByIdAndUpdate returns null on unmodified model data in graphql [duplicate]

This question already has answers here:
Why does a GraphQL query return null?
(6 answers)
Closed 3 years ago.
I have an issue with trying to update a mongoose schema using graphql and .findByIdAndUpdate.
whenever I try to update a document, the the new data is updated correctly, however the unmodified one becomes null.
So if I run this in graphiql:
mutation {
updateProfile(id: "5b05b4cfd53486377061eab3", name: "dave") {
id
name
age
interests
}
}
It returns with this object:
{
"data": {
"updateProfile": {
"id": "5b05b4cfd53486377061eab3",
"name": "dave",
"age": null,
"interests": null
}
}
}
The new data has been updated to the document, but the old data has been been deleted.
The idea is to be able to update just a single part of the document without the rest turning into null.
The GraphQL code responsible for updating:
updateProfile: {
type: profileType,
args: {
id: { type: new GraphQLNonNull(GraphQLID) },
name: { type: GraphQLString },
age: { type: GraphQLString },
interests: { type: GraphQLString },
},
resolve(parent, args) {
return Profile.findByIdAndUpdate(args.id, {
name: args.name,
age: args.age,
interests: args.interests
}, {new: true}, (err, Profile) => {
if (err) {
return res.status(500).send(err);
}
return;
});
}
}
And here's the schema:
const profileSchema = mongoose.Schema({
name: String,
age: String,
interests: String,
});
Any and all help is appreciated, thanks in advance!
This can be solved like below example.
Previously you are setting or updating the fields by the values of args params individually.
You need to pass the args params directly in "findByIdAndUpdate(args.id,args)" like the below code
It will update only the fields from the request.
In mutations:
updateProfile: {
type: profileType,
args: {
id: { type: new GraphQLNonNull(GraphQLID) },
name: { type: GraphQLString },
age: { type: GraphQLString },
interests: { type: GraphQLString },
},
resolve(parent, args) {
return Profile.findByIdAndUpdate(args.id,args, (err, Profile)=> {
if(err) {
return res.status(500).send(err);
}
return
})
}
}
I hope this will be useful for others, Thank you.

How to do query in mongoose

let UserSchema = new Schema({
created: {
type: Date,
default: Date.now,
required: true
},
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
role: {
default: 'user',
type: String, //we can consider using array of strings as in case user has several roles at the same time
requierd: true
},
devices: [
{
id: '1',
permissions:['start','stop']
},
{
id: '2',
permissions:['start','restart']
}
]
});
How can i push new {id:'2', permissions:['stop']} without duplications, when i receive it in req.params.headers. I need to check is id:'2' already exist or not, then I'm shuld check is permission['stop'] is exist, and then if it isn't exist i should push it in collection.
If you want to find any object that matchs either the id or the permission, you can do it with $or operand, something like this:
return User.findOne({ $or:[ {'devices.id': idValue}, {'devices.permissions': permission} ]}).exec()
.then(user => {
if(user) {
console.log("We don't have to push it");
} else {
console.log("We have to push it");
}
});
You can use update with elemMatch in condition to find the inner object. To avoid adding duplicate keys in array, you can use $addToSet
<>.update({"devices": { "$elemMatch": { "id": "2" }}},{ "$addToSet": { "devices.$.permissions": "stop" }})

Resources