Update document array field if the new element not exist - mongodb

var userSchema = mongoose.Schema({
username: { type: String},
email: String,
password: String,
tasks: [String]
});
I need to find the user document (by usename) and then to push a new task to the tasks array - if it does not already exist in the array.
I came to this solution:
user = await User.findOneAndUpdate(
{ username: username, 'tasks': { $ne: task} },
{ $push: { tasks: task } },{ 'upsert': false});
But how can I know to catch the reason the update didn't happened?
Is it because the username doesn't exist or is it because the task already exist?

$push will create duplicates in the task array. you need to use $addToSet operator:
db.collection.findOneAndUpdate(
{
username: 'test'
},
{
$addToSet: {
tasks: 'task'
}
},
{
returnNewDocument: true
})

Related

$push causing error Updating the path 'x' would create a conflict at 'x'

Given the example with Mongoose:
Schema:
const fields = {
...other fields
users: [
{ name: String, email: String, department: String, position: String, _id: false }
]
}
const Company = model('Company', fields);
Update action:
const companyId = 'company-id';
const user = { name: 'John', email: 'john#email.com' }
Company.findByIdAndUpdate(companyId, {
$push: {
users: user
}
}, { new: true })
.lean({ defaults: true })
.exec();
This causes an error:
Updating the path 'users' would create a conflict at 'users'.
I would think this would work correctly and I'm not doing anything else except pushing a new object to the array. Any help would be appreciated. Thanks!

iterate over large mongodb collection for purpose of updating schema

I have a 300k collection of test docs. I want to update all persons firstName and lastName to be lowercase.
const person = new Schema({
firstName: { type: String},
lastName: { type: String }
})
I've added lowecase:true to the schema but how do I update the existing documents?
I tried:
CaseFile
.find({ })
.cursor()
.eachAsync(async function (doc) {
await doc.save()
})
but i get the error
Error: Collection method find is synchronous
I also tried :
CaseFile
.find({ })
.then(docs => {
docs.forEach(doc => {
doc.save()
})
})
which gives the error:
JavaScript heap out of memory
db version v5.0.2
"mongoose": "^6.0.5",
thank you Wernfried Domscheit for the pipeline 🏄 solution:
CaseFile.updateMany({}, [
{
$set:
{
firstName: { $toLower: '$firstName' },
lastName: { $toLower: '$lastName' }
}
}]
)
.then(res => res)
Why on earth "iterate", i.e. line by line?
Use an aggregation pipeline:
db.CaseFile.updateMany({}, [
{ $set:
firstName: { $toLower: "$firstName" },
lastName: { $toLower: "$lastName" }
}
])

GraphQL Mutation Updating Users Followers with Mongoose/MongodDB - $set is empty error

I have this mutation set up:
followUser: {
type: UserType,
args: {
_id: { type: GraphQLString },
firebaseUid: { type: GraphQLString },
following: { type: new GraphQLList(GraphQLString)},
},
resolve(parentValue, { firebaseUid, _id, following}) {
const update = {
$set: { "following": [firebaseUid] },
$push: { "following": { firebaseUid } }
}
return UserSchema.findOneAndUpdate(
{ _id },
update,
{new: true, upsert: true}
)
}
},
I'm trying to add new followers into my graphql user's collection. My user model:
const UserSchema = new Schema(
{
firebaseUid: String,
following: [{ type: Schema.Types.ObjectId, ref: 'User' }],
followers: [{ type: Schema.Types.ObjectId, ref: 'User' }],
},
{ timestamps: true }
);
module.exports = mongoose.model("User", UserSchema);
So at first, the user doesn't have any followers, so it won't have that field yet. When user adds someone to their friends list, thats when the field will appear in mongodb. Right now I'm getting this error:
"message": "'$set' is empty. You must specify a field like so: {$set: {<field>: ...}}",
I'm not sure if I'm doing the $set correctly.
The UserType
const UserType = new GraphQLObjectType({
name: "User",
fields: () => ({
_id: { type: GraphQLString },
firebaseUid: { type: GraphQLString },
following: { type: new GraphQLList(GraphQLString) },
followers: { type: new GraphQLList(GraphQLString) },
...
})
});
edit:
current mongodb data collection:
_id: ObjectId("5e5c24111c9d4400006d0001")
name: "Mr. Smith"
username: "mrsmith"
after running the update
_id: ObjectId("5e5c24111c9d4400006d0001")
name: "Mr. Smith"
username: "mrsmith"
following: ["fdsaduybfeaf323dfa"] // <-- this gets added
Currently mongooses validator is rejecting the update. To fix this you need the following:
You only need to $push since it will automatically create an array if the property does not exist
You should remove the extra { } around the firebaseUid in the $push because otherwise the following array will contain objects with a firebaseUid property instead of directly containing the Uid (or would if the schema validator allowed it)
Mongo ObjectIds can only be converted from strings when they are 12-byte hexadecimal, and firebaseUid is not, so the schema should be typed to String instead of ObjectId as the validator will reject the field for update otherwise.

Complex query mongoose - embedded documents array

I want to execute a query for getting all "group" documents that have the userId in there array of users.
I've tried several different ways of query, but I always get an empty array.
What am I doing wrong?
group.js
let mongoose = require('mongoose');
const Group = mongoose.Schema({
name: {
type: String,
required: true
},
users: [{
userId: {
type: mongoose.SchemaTypes.ObjectId,
ref: 'users',
required: true
},
userType: {
type: String,
required: true
},
userStatus: {
type: String,
required: true
}
}]
})
module.exports = mongoose.model('group', Group);
groupController.js
exports.getUserGroups = function (req, res) {
Group.find({
"users.userid": "req.user._id"
}, function (err, groups) {
if (err)
res.send(err)
res.json(groups);
});
}
Field names are case-sensitive, so "users.userid" should be "users.userId" instead:
Group.find({
"users.userId": "req.user._id"
}, ...
You can try using $in operator
db.collection.find({
"users.userId": {
$in: [
req.user._id
]
}
})

Mongoose pull ObjectId from array

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} }};