How to increment a field inside the array of documents in mongo Db? - mongodb

I want to increment the quantity field inside the products array of the cart when the productId matches to the itemid. Also I want to get the updated document in return.
Here is my Cart Model
import mongoose from 'mongoose';
const cartSchema = new mongoose.Schema({
userId: {
type: String,
required: true,
},
products: [
{
productId: {
type: String,
},
quantity: {
type: Number,
default: 1
}
}
]
}, {timestamps: true});
const Cart = new mongoose.model('Cart', cartSchema);
export default Cart;
and here is what I m doing:
const updatedCart = await Cart.findOneAndUpdate({"products.productId": itemid}, {$inc: {quantity: 1}}, {new: true})
//console.log(updatedCart)
But an empty array return and also quantity didn't increment.

As from your schema, there is no quantity field. Hence your mongoose operation is not updating any document.
Hence it will not return any document as new: true only returns the updated document.
[options.returnOriginal=null] «Boolean» An alias for the new option. returnOriginal: false is equivalent to new: true.
You can use positional $ operator to update the quantity of filtered product in products array.
The positional $ operator identifies an element in an array to update without explicitly specifying the position of the element in the array.
const updatedCart = await Cart.findOneAndUpdate({ "products.productId": itemid },
{ $inc: { "products.$.quantity": 1 } },
{ new: true });
Sample Mongo Playground

Related

How to filter documents using find method in mongoose based on the data from reference in documents?

I am working on e-commerce like app. I have orderItem Schema
const orderItemsSchema = mongoose.Schema(
{
order: {
type: mongoose.Schema.Types.ObjectId,
ref: 'OrderItems',
required: true,
},
product: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Products',
required: true,
},
quantity: {
type: Number,
default: 1,
},
subCost: {
type: Number,
required: true,
},
},
{
timestamps: true,
}
);
Where product schema has a field "owner" which is also a reference.
I am expecting to get orderItems based on owners of the products.
For Example: A owner want to check which products of him has been sold. So he will query orderItems to get his sold items.
I'm not an expert in mongoose, so maybe the syntax is not entirely correct:
// You get all products _id that owner currently sells
const yourOwnerObjectId = mongoose.Types.ObjectId(yourOwnerId); // Create the objectId from a string
const productsOwner = Products.find({owner: yourOwnerObjectId}).select({_id: 1})
// You get all orders that contains any of previous product _id
const orderWithProductsSold = OrderItems.find({_id: {$in: productsOwner}})
I'm not sure about what returns the first query regarding _id. Maybe you have to do some type of casting to ObjectId or whatever to perform the second query, but I think the idea is right.

Update many documents in MongoDB (new Schema)

I need some help with mongo client commands, i have to update every document in collection, this is my new ProductSchema:
const ProductSchema = Schema({
name: { type: String, default: ''},
price: { type: Number, default: 0 },
image: { type: [String], default: [] },
category: { type: String, default: '' },
});
module.exports = mongoose.model('Product', ProductSchema);
The old ProductSchema has as default Product.image = null. So i have a lot of products this way stored in DB. How can i update every document in my DB Collection if Product.image == null ? and then assign it an empty array (Product.image = []). Thanks in advance.
You only need to execute update query. As docs explain method allows three fields:
query: The selection criteria for the update.
update: The modifications to apply.
options: Like multi, upsert or whatever...
So, if you want to update only values where image = null you need to query only elements where: image: null and then do the update: image: [].
Using mongo you have to add multi: true in options to update all values and not only the first who match.
So the query is like this:
db.collection.update({
"image": null
},
{
"$set": {
image: []
}
},
{
multi: true
})
Example here
Also, if you are using mongoose you can call directly updateMany() and then you don't need multi: true option.

Validate array of strings in mongoose

I wanted to validate each value from the request which is array of strings. Something like
emails: [ 'johndoe#gmail.com', 'jandoe#gmail.com' ]
Here is my schema
const UserSchema = mongoose.Schema({
name: {
type: String,
index: true,
required: true,
},
emails: [String],
});
In my validation I wanted to make sure that each email is not already exists in the database. I've tried the following
body("emails").custom((value, { req }) => {
return User.findOne({
emails: { $all: value },
_id: { $ne: req.params.id },
}).then((exists) => {
if (exists) {
return Promise.reject("Email already exists!");
}
});
});
But the problem is if I tried to post multiple emails in array the validation fails and the data will be inserted to db. How can I check if one of the emails already exists and reject the request?
In the docs of $in, it mentioned that:
If the field holds an array, then the $in operator selects the documents whose field holds an array that contains at least one element that matches a value in the specified array...
So you can solve it by:
User.findOne({
emails: { $in: value },
_id: { $ne: req.params.id },
})...

Insert default values not working mongodb

I am using mongoose version 5.2.5 and here is the sample model of my order
....
let PlaceOrderSchema = new mongoose.Schema({
any: {}
}, { strict: false },
{ timestamps: { updatedAt: 'last_updated', createdAt: 'created' });
I am using the above model in main script with mongoose save and findOneAndUpdate.
In our production system , we are seeing many document that does not have last_updated key missing in the save document.
Here are sample code of the mongoose save method and findOneAndUpdate in our main script.We are seeing some of the documents have updated_at keys while very few of them does not have it while saving the document
let orderModel = require('./orderModel');
let newOrder = {
order_no: 1234
};
//save usage code
(new Order(newOrder).save({lean: true}, ()=> {
//do...something
}));
//findOneAndUpdate usage Code
let orderNo = 123
OrderModel.findOneAndUpdate({ order_no: orderNo },
{
$set: { items: [{product_id: 'abc', quantity: 1}] },
},
{ new: true, upsert: true },
(err, res) => {
//do_something
});
Can any one share why we have few documents are getting saved without updated_at?
You need to use option setDefaultsOnInsert: true during the update operation.
Docs
By default, mongoose only applies defaults when you create a new
document. It will not set defaults if you use update() and
findOneAndUpdate(). However, mongoose 4.x lets you opt-in to this
behavior using the setDefaultsOnInsert option.
OrderModel.findOneAndUpdate(
{ order_no: orderNo },
{ $set: { items: [{ product_id: "abc", quantity: 1 }] }},
{ new: true, upsert: true, setDefaultsOnInsert: true }
)

Is an ObjectId automatically generated for a nested object?

My schema is as follows:
const MessageType = {
// ...
oAuth: { provider: String, id: String },
attachments: [ {name: String, contentType: String} ],
// ...
}
MessageSchema = new mongoose.Schema(MessageType, { timestamps: true});
Messages = mongoose.model("Message", MessageSchema);
When I insert a new Message document using Messages.create, an ObjectId (_id) is also generated for attachments, in addition to my name and contentType fields, ie:
[ { name: "xxx", contentType: "yyy", _id: zzzzzz }]
Why is this happening, for attachments but not oAuth?
For avoiding that the _id was generated you must set the option _id: false, Also if you don't want to save the empty attachments object, you need to set default: undefined.
const MessageTypeSchema = new mongoose.Schema({
oAuth: {
type: String
},
attachments: {
type: [
{
type: String
}
],
_id: false,
default: undefined
}
});
Here the code that I used to test:
console.log('-------- Document with attachments --------');
new MessageTypeModel({
oAuth:'xxxxxxxxxxxxx',
attachments: ['teste.png','teste2.jpg']
}).save().then(result => {
console.log(result);
});
console.log('-------- Document without attachments --------');
new MessageTypeModel({
oAuth:'xxxxxxxxxxxxx'
}).save().then(result => {
console.log(result);
});
And here the result of execution:
Mongoose creates _id for single nested subdocuments or arrays, and your object field oAuth is not one of this cases:
Subdocuments are documents embedded in other documents. In Mongoose,
this means you can nest schemas in other schemas. Mongoose has two
distinct notions of subdocuments: arrays of subdocuments and single
nested subdocuments.
Each subdocument has an _id by default. Mongoose
document arrays have a special id method for searching a document
array to find a document with a given _id.
var childSchema = new Schema({ name: 'string' });
var parentSchema = new Schema({
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments. Caveat: single nested subdocs only work
// in mongoose >= 4.2.0
child: childSchema
});
Link of Mongoose documentation: Mongoose SubDocs
You can define _id : false in attachments array.
const MessageType = {
// ...
attachments: [ {name: String, contentType: String, _id: false} ],
// ...
}