Mongoose: findByIdAndUpdate method is not updating nor inserting - node.js

The findByIdAndUpdate method should update or insert an object in the DB, but it does nothing. Nor it does throw an error message or something.
I also tried it with the original ObjectId as _id field, but doesn't work either.
Has anybody a clue what is missing to update or insert the object into the DB?
const schema = new Schema({
_id: {
type: Number,
required: true
},
name: {
type: String,
required: true
}
}, { toJSON: { virtuals: true } });
const myJson = {
"myobject": {
"_id": 781586495786495,
"name": "MyName"
}
}
const MyModel = mongoose.model('MyModel', schema);
MyModel.findByIdAndUpdate(myJson.myobject._id, myJson.myobject, { upsert: true });

I used your code and it actually works as intended. The reason why it doesn't return any errors nor does anything might be that you're not connected to the database at the time you're executing the operation on the model.
Please consider the following snippet, it worked for me.
mongoose.connect('mongodb://localhost:27017/testingcollection').then(() => {
const schema = new mongoose.Schema({
_id: {
type: Number,
required: true
},
name: {
type: String,
required: true
}
}, { toJSON: { virtuals: true } });
const myJson = {
"myobject": {
"_id": 781586495786495,
"name": "MyName"
}
};
const MyModel = mongoose.model('MyModel', schema);
MyModel.findByIdAndUpdate(myJson.myobject._id, myJson.myobject, { upsert: true }).then(obj => {
mongoose.connection.close();
});
});

Related

Mongoose - findByIdAndUpdate

All,
I can seem to figure out why the record in the database will not update. I am not 100% sure where my error is but this isn't really providing me a great error message. Can someone please take a look at this for me?
I believe that I am calling the mongoose request properly. Thank you in advance!
$ npm mongoose -v
8.15.0
const mongoose = require("mongoose");
const CartSchema = new mongoose.Schema(
{
owner: {
type: String,
unique: true,
required: true,
},
discount: {
type: Number,
},
total: {
type: Number,
},
items: [
{
itemId: {
type: Number,
},
sku: {
type: Number,
},
quantity: {
type: Number,
},
price: {
type: Number,
},
},
],
},
{ timestamps: true }
);
const Cart = mongoose.model("Cart", CartSchema);
module.exports = Cart;
Record in Database
{"_id":{"$oid":"630689708997a6589635986c"},
"owner":"611afa8b9069c9126cff3357",
"total":{"$numberInt":"0"},
"items":[],
"createdAt":{"$date":{"$numberLong":"1661372784844"}},
"updatedAt":{"$date":{"$numberLong":"1661372784844"}},
"__v":{"$numberInt":"0"}}
exports.add = async (req, res, next) => {
const { id, product } = req.body;
const addItem = { itemId: product._id, sku: product.sku, quantity: 1, price: product.price };
console.log(addItem);
try {
const updateCart = Cart.findByIdAndUpdate(id, { $addToSet: { items: addItem } }, { new: true, returnDocument: "after" });
if (!updateCart) return next(new ErrorResponse("Unable to update the cart record", 404));
console.log(updateCart);
if (updateCart) {
return sendRes(updateCart, 200, res);
} else {
return sendRes(updateCart, 201, res);
}
} catch (error) {
console.log(error);
next(error);
}
};
This issue was caused by me using an ASYNC Function without the AWAIT on the DB Call.
Please try once with this:
Cart.findByIdAndUpdate(id, { $addToSet: { items: addItem } }, { new: true, returnDocument: "after" });
The first obvious mistake is that you're searching for a document with the wrong field:"id", Kindly change that to "_id: id"
Also you might need to convert the _id string you have to MongoDB Object ID, like this:
const ObjectId = require('mongodb').ObjectId;
Cart.updateOne({_id: new ObjectId(id)}, { $addToSet: { items: addItem } }, { new: true, returnDocument: "after" });
For other update method, you need to specify the field, and also convert it to a MongoDB ID
OR
Cart.findByIdAndUpdate(id, { $addToSet: { items: addItem } }, { new: true, returnDocument: "after" })
You do not to specify the field in findByIdAndUpdate, just pass the id to it.

Implementing post('updateOne') hook middleware with Mongoose(MongoDB)

I am trying to calculate the average value of the ratings for a Product per Document that is being rated. Instead of calculating the average rating of the Product every time when we need the value. I calculate it every time someone rates it. To accomplish this task I am implementing a post(‘updateOne’) hook middleware. Though not sure I can accomplish this by implementing this hook. Let me know if I am going into the wrong direction. Here is the error I am getting avgRating error from post updateOne hookTypeError: Cannot read property 'aggregate' of undefined.
file - Product.js
import mongoose from 'mongoose';
const { ObjectId } = mongoose.Schema;
const ParentSchema = new mongoose.Schema(
{
title: {
type: String,
trim: true,
required: true,
maxlength: 32,
text: true,
},
slug: {
type: String,
unique: true,
lowercase: true,
index: true,
},
price: {
type: Number,
required: true,
trim: true,
maxlength: 32,
},
quantity: Number,
ratings: [
{
star: Number,
postedBy: { type: ObjectId, ref: 'User' },
},
],
},
{
timestamps: true,
}
);
const avgRating = async function () {
try {
const stats = await this.aggregate([
{
$addFields: {
avgRating: { $avg: '$ratings.star' },
nRatings: { $size: '$ratings' },
},
},
]);
console.log('stats: ', stats);
} catch (err) {
console.log(`avgRating error from post updateOne hook${err}`);
}
};
// Call avgRating after updateOne
ParentSchema.post(
'updateOne',
{ document: true, query: false },
async function () {
await avgRating();
}
);
export default mongoose.models.Product ||
mongoose.model('Product', ParentSchema);
file - productServices.js
import Product from './Product.js';
const updateRating = async (existingRatingObject, star) => {
const query = {
ratings: { $elemMatch: existingRatingObject },
};
const update = { $set: { 'ratings.$.star': star } };
const option = { new: true };
try {
const doc = new Product();
const ratingUpdated = await doc.updateOne(query, update, option);
return ratingUpdated;
} catch (error) {
console.log('product model updateRating error: ', error);
}
};
Your avgRating function does not have access to the mongoose document under this. That is why you are getting the error, Cannot read property 'aggregate' of undefined, because this (the document) which you want to modify does not exists in the function.
You need to use an instance method available under mongoose. With instance method, you can call this to reference the document.
Check out:
https://www.freecodecamp.org/news/introduction-to-mongoose-for-mongodb-d2a7aa593c57/#instace-methods
https://mongoosejs.com/docs/guide.html#methods

How to make parent value optional and child value required in mongoose

I'm working on a project and stuck at a point where the docOne is optional. But if docOne is there, the children of the docOne should be required. Is there any way of achieving this behavior through the schema model? Thank you.
const MySchema = new Schema(
{
name: { type: String },
docOne: {
required: false,
type: {
docTwo: {
required: true,
type: {
isActive: { type: Boolean, required: true },
},
},
},
},
},
{ timestamps: true },
);
try defining the child schema first, and use the child schema in your parent schema.
this way you can make use of sub-documents / embedded-documents where, you can make only required to true.
you can refer https://mongoosejs.com/docs/subdocs.html and https://mongoosejs.com/docs/2.7.x/docs/embedded-documents.html#:~:text=Embedded%20documents%20are%20documents%20with,error%20handling%20is%20a%20snap! for this.
If you are using mongoose Mongoose 5.8.0 or above, just add typePojoToMixed: false to the scheme options and the validations should work fine.
const mySchema = new Schema(
{
// ...Schema definition remains the same
},
{ timestamps: true, typePojoToMixed: false },
);
const myModel = mongoose.model('myModel', mySchema);
const sampleDocument = {
name: 'John Doe',
};
myModel.validate(sampleDocument); // No Error
sampleDocument.docOne = {};
myModel.validate(sampleDocument); // Throws ValidationError for docOne.docTwo
If you are using a lesser version of Mongoose, the other thing that can work is declaring the schema type of the nested objects as a separate schemas:
const docTwoSchema = new Schema({
isActive: { type: Boolean, required: true },
},
// Use the "_id: false" option to avoid an autogenerated _id property
{ _id: false }
);
const docOneSchema = new Schema({
docTwo: {
type: docTwoSchema,
required: true,
}
},
// Use the "_id: false" option to avoid an autogenerated _id property
{ _id: false }
);
const mySchema = new Schema(
{
name: { type: String },
docOne: {
type: docOneSchema,
required: false,
},
},
{ timestamps: true },
);
const myModel = mongoose.model('myModel', mySchema);
const sampleDocument = {
name: 'John Doe',
};
myModel.validate(sampleDocument); // No Error
sampleDocument.docOne = {};
myModel.validate(sampleDocument); // Throws ValidationError for docOne.docTwo
Useful Links
Mongoose SubDocuments

Is there a way i could keep track of the Time and the entity that was changed in a model

Basically I'm trying to get the time and the entity changed in a particular model when ever the update method is called.
This is my model I want to keep track of:
const mongoose = require("mongoose");
const modelSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
name: {
type: String,
required: true,
},
note1: String,
note2: String,
note3: String,
images: {
type: Array,
required: true
},
status: {
enum: ['draft', 'pending_quote', 'pendong_payment', 'in_production', 'in_repair', 'pemding_my_review', 'fulfilled'],
type: String,
default: "draft"
},
price: {
type: mongoose.Schema.Types.ObjectId,
ref: "Price",
}
}, {
timestamps: true,
})
module.exports = mongoose.model("Model", modelSchema)
And this is the method I call to update the status:
exports.updateModel = async (req, res) => {
try {
let id = req.params.id;
let response = await Model.findByIdAndUpdate(id, req.body, {
new: true
})
res.status(200).json({
status: "Success",
data: response
})
} catch (err) {
res.status(500).json({
error: err,
msg: "Something Went Wrong"
})
}
}
you can add a new field in your schema like:
logs:[{
entity: String,
timeStamp: Date
}]
Then updating it basing on your current code:
let id = req.params.id;
// I don't know whats in the req.body but assuming that it
// has the correct structure when passed from the front end
let response = await Model.findByIdAndUpdate(id,
{
$set:req.body,
$push:{logs:{entity:'your entity name here',timeStamp:new Date()}}
}, {
new: true
})

How do I access `findAndModify` method from Mongo in Node (with Mongoose)?

This is /models/joke.js:
var mongoose = require ('mongoose')
, database = mongoose.connect('localhost', 'joke', { server: { poolSize: 3 } });
var jokeSchema = mongoose.Schema({
content: String,
upvotes: {
type: Number,
default: 0
},
downvotes: {
type: Number,
default: 0
},
views: {
type: Number,
default: 0
},
published: {
type: Boolean,
default: true
},
author_id: Number,
created: {
type: Date,
default: Date.now
}
});
var Joke = mongoose.model('Joke', jokeSchema);
module.exports = Joke;
And I'm doing this to check if something exist — if doesn't, then create:
var Joke = require ('./models/joke');
// ...
Joke.findAndModify({
query: {
content: content
},
update: {
$setOnInsert: {
content: "test",
}
},
new: true,
upsert: true
});
But my console shout me the following:
TypeError: Object function model(doc, fields, skipId) {
if (!(this instanceof model))
return new model(doc, fields, skipId);
Model.call(this, doc, fields, skipId);
} has no method 'findAndModify'
I can understand the reason for the error – I'm calling it through a model instead of a collection, but how do I access my Jokes' collection methods?
I mean, all of the examples were using db.collection.findAndModify, but what is this db.collection? How do I call it?
To access findAndModify update functionality from Mongoose, use findOneAndUpdate:
Joke.findOneAndUpdate(
{ content: content },
{ $setOnInsert: { content: "test" } },
{ new: true, upsert: true },
callback
);

Resources