How to update one field from a passed object in mongoose - mongodb

Incase I have an object that looks like the one below
const auth = {
geohash: args.input.geohash,
offenses: args.input.offenses,
online: args.input.online,
paid: args.input.paid,
profilePic: args.input.profilePic,
username: args.input.username,
}
and I pass it inorder to update a document
const update = { _id: mongoose.Types.ObjectId(args._id) }
const value = await DiscoverUsers.findOneAndUpdate(update, auth, { useFindAndModify: false, new: true })
so incase I only want to update the username and I don't want to keep creating a mutation for updating each field in the document.
lets say my mutation looks like this
mutation{
updateDiscoverUsers(_id:"5dab7c198a83f235c89a964a",input:{username:"peter"}){
username
}
}
but this only updates the username but it makes the rest of the fields null but I only want to find a way to only update the fields I have passed in the mutation and the rest remain the same. so I can update the username and profilePic only and the rest remain unchanged.
I would be grateful for the help and thanks in advance

You should use the atomic operator $set to update only where you want, and you should pass only the fields you want to update, not all of them otherwise all the fields are going to be updated by the new value.
like:
const value = await DiscoverUsers.findOneAndUpdate(update, {$set:{username:"pedro"}}, { useFindAndModify: false, new: true })

Related

Format response from mongoose into a model

How do you go about formatting the data response from mongoose? For a simple Post Schema
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
}
},{
timestamps: true
});
Whenever I do a GET request to find all the post, It returns me all its fields including _id and __v in which I wouldn't want to return those fields in an API.
Is there a way I would select only certain fields that I would want to return?
As far as I've found was that I could set a second parameter of title onto my query and it would return only the _id and title.
const post = await Post.find({},'title');
I find the method above isn't the proper way to filter fields in cases in the future where the values are deeply nested object and we would like to pick out certain values.
Is there perhaps a way to create a Model/Class and pick the fields based on the Model/Class and return the respond?
You can use select from mongoose.
You can either select only the fields you want.
var find = await model.find({}).select("my_field")
Or not show the fields you don't want
var find = await model.find({}).select("-my_field")
Check the documentation

Mongoose set default on field update if field is not present or null

I have a mongoose schema like this suppose:-
var mSchema = new Schema({
name: { type: String, required: true}
});
and have been using this schema for a year and now i want to add gender to it like this :-
var mSchema = new Schema({
name: { type: String, required: true},
gender: { type: String, default: 'Male' }
});
whenever there will be an update request i want this gender to automatically set Male as default but i found that default don't set on update request.
(Note: It's just an example not a real life scenario. i just want mongoose default work if field is not present or null)
Is there any way in which i can set default on the updation of document ?
If you are using a function like update(), then this is not directly possible as stated by this answer. Still, you can simply switch to a function like findOne() and use save(), which should do the same.
When upserting documents, you can also check out the setDefaultsOnInsert option: https://mongoosejs.com/docs/defaults.html#the-setdefaultsoninsert-option
const options = {
// Create a document if one isn't found. Required
// for `setDefaultsOnInsert`
upsert: true,
setDefaultsOnInsert: true
};
await XY.findOneAndUpdate(query, update, options);

Mongoose: atomic FindOne-Or-Insert(), do not update existing instance if found

In Mongoose, I am seeking to perform atomically a way to Model.FindOne-Or-Insert(), similar functionality and signature as currently available Model.FindOneAndUpdate() except if an instance exists (i.e. matches filter) then do not update using provided object but return instance found as is, and if not exists (i.e. no match with filter) then insert object and return new instance.
I could not find a way using Model.FindOneAndUpdate() not to perform an update to an existing instance by trying out variances to its options and not providing fields to object that preferred not to update if instance exists.
So, my current non-atomic workaround is Model.FindOne() and if not found then perform Document.save().
const Foo = DB.model('foo', FooSchema)
async function findOneAndUpdateFoo(jsonFoo, next) {
const filter = {
deletedAt: null
}
if (jsonFoo.dsAccountId) {
filter.dsAccountId = jsonFoo.dsAccountId
}
if (jsonIntegration.dsUserId) {
filter.dsUserId = jsonIntegration.dsUserId
}
if (jsonFoo.providerId) {
filter.providerId = jsonFoo.providerId
}
const fooDoc = {
name: jsonFoo.name,
dsAccountId: jsonFoo.dsAccountId,
dsUserId: jsonFoo.dsUserId,
providerId: jsonFoo.providerId,
providerName: jsonFoo.providerName,
// Most of these fields could be empty
accessToken: jsonFoo.accessToken,
refreshToken: jsonFoo.refreshToken,
scope: jsonFoo.scope,
tokenType: jsonFoo.tokenType,
expiresAt: jsonFoo.expiresAt
}
return await Foo.findOneAndUpdate(
filter, // find a document with that filter
fooDoc, // document to insert when nothing was found
{ upsert: true, new: true, runValidators: true } // options
)
.catch(next)
}
Suggestions? Thank you
You can use $setOnInsert in your update parameter so that it will only apply in the insert case; with the update becoming a no-op in the case where the document already exists:
return await Foo.findOneAndUpdate(
filter, // find a document with that filter
{$setOnInsert: fooDoc}, // document to insert when nothing was found
{ upsert: true, new: true, runValidators: true }
)
Note that you should also create a unique index over the fields included in your filter and then handle the possibility of a duplicate error. See this post for the details why.

How can I check that a mongo update command succeeded in node

I'm trying to check that an update command accomplished but when I check for the nModified I'm getting 0 although I do see that the field value changes from one value to another (not kept the same value).
static async updateProfile(username, profileData) {
const usersCollection = db.dbConnection.collection(dbConfig.collectionNames.users);
const updateRes = await usersCollection.update({email: username},
{"$set": {
firstName: profileData.firstName,
lastName: profileData.lastName,
payment: profileData.payment,
}
});
return updateRes.result.nModified > 0;
}
Is there another way to verify the update?
One of the way by findAndModify method:
You can easily compare whole new object and verify each key.
db.getCollection('usertests').findAndModify({
query: {"email":"xxx#xxx.com"},
update: {name: "HHH", "email":"xxx#xxx.com"},
new: true
})
new: true is responsible to return whole updated document. If fail to update it will return null.
Take care here to pass the whole document while update.
update() only return a number of the documents that were successfully updated. So, your logic to check if updated successfully or not is also valid.

Clean up dead references with Mongoose populate()

If a user has an array called "tags":
var User = new Schema({
email: {
type: String,
unique: true,
required: true
},
tags: [{
type: mongoose.Schema.Types.ObjectId,
ref:'Tag',
required: true
}],
created: {
type: Date,
default: Date.now
}
});
and I do a populate('tags') on a query:
User.findById(req.params.id)
.populate("tags")
.exec(function(err, user) { ... });
If one of the tags in the list has actually been deleted, is there a way to remove this dead reference in "tags"?
Currently, the returned user object IS returning the desired result -- ie. only tags that actually exist are in the tags array... however, if I look at the underlying document in mongodb, it still contains the dead tag id in the array.
Ideally, I would like to clean these references up lazily. Does anyone know of a good strategy to do this?
I've tried to find some built-in way to do that but seems that mongoose doesn't provide such functionality.
So I did something like this
User.findById(userId)
.populate('tags')
.exec((err, user) => {
user.tags = user.tags.filter(tag => tag != null);
res.send(user); // Return result as soon as you can
user.save(); // Save user without dead refs to database
})
This way every time you fetch user you also delete dead refs from the document. Also, you can create isUpdated boolean variable to not call user.save if there was no deleted refs.
const lengthBeforeFilter = user.tags.length;
let isUpdated = user.tags.length;
user.tags = user.tags.filter(tag => tag != null);
isUpdated = lengthBeforeFilter > user.tags.length;
res.send(user);
if (isUpdated) {
user.save();
}
Assuming you delete these tags via mongoose, you can use the post middleware.
This will be executed after you've deleted a tag.
tagSchema.post('remove', function(doc) {
//find all users with referenced tag
//remove doc._id from array
});
its sample retainNullValues: true
Example:
User.findById(req.params.id)
.populate({
path: "tag",
options: {
retainNullValues: true
}
})