Building MongoDB query with conditions - mongodb

I need to build a MongoDB query by pushing a new language if it does not exist in the array already. But if it exists I get an error this '$push' is empty. It is correct.
My question is how to build the query adding $push only when it is necessary?
let pushNewLanguage = {};
if (!profile.languages || (profile.languages && !profile.languages.find(l => l === languageId))) {
pushNewLanguage = { languages: languageId };
}
const profileUpdate = await
Profiles.rawCollection().update(
{ userId: this.userId },
{
$inc: { countPublishedPoems: 1 },
$push: pushNewLanguage
}
);

Remove the conditional logic and use $addtoSet instead of $push.
$addToSet will only add the item if it doesn’t exist already.
const profileUpdate = await
Profiles.rawCollection().update(
{ userId: this.userId },
{
$inc: { countPublishedPoems: 1 },
$addToSet: { languages: languageId }
}
);

Since you are writing Javascript, you can create a "base" update object, and then add the $push property if you need:
const update = {
$inc: { countPublishedPoems: 1 }
}
if (!profile.languages || (profile.languages && !profile.languages.find(l => l === languageId))) {
update["$push"] = { languages: languageId };
}
const profileUpdate = await
Profiles.rawCollection().update(
{ userId: this.userId },
update
);

Related

mongoose update inside array object

How to update mongodb based
image
need to update in_state Object based on testid and user_id
code
const test = await usertest.update(
{ "testid" :"oOEbG3ycsl5ZIPrFNk172SVma0zTwD6xHvpUM4JWKAj9fg18nRCtq1B0XLdYeuiQh","user_id":"63297394d10aa70d52708a4c"},
{
$set: {
'testdata.1.in_state': in_state
}
}
)
how can achieve this
Try referencing the testdata elements with $:
const test = await usertest.update(
{
testid: 'oOEbG3ycsl5ZIPrFNk172SVma0zTwD6xHvpUM4JWKAj9fg18nRCtq1B0XLdYeuiQh',
user_id: '63297394d10aa70d52708a4c',
},
{
$set: {
'testdata.$.in_state': in_state,
},
}
);

How to pass element index in array to mongoDB query?

Building cart on website and when product is added i want to first check if it is already in cart, if yes increment quantity by 1, if not add it. Cart is an array of objects and i want to pass index of object that contains added product to increment function but can't figure out how to do so.
async function add(product, userId) {
const user = await User.findById(userId);
const product = isProductInCart(product, user.cart); // returns true and index in cart if found
if (product.found === true) {
await User.findOneAndUpdate(
{ _id: userId },
{ $inc: { cart[product.index].quantity : 1 }} // not working
);
} else {
await User.findOneAndUpdate({ _id: userId }, { $push: { cart: product } });
}
}
function isProductInCart(product, cart) {
let productFound = { found: false, index: -1 };
for (let i = 0; i < cart.length; i++)
if (cart[i].name === product.name) {
productFound.found = true;
productFound.index = i;
break;
}
return productFound;
}
It looks like your code can be simplified if you consider using the $ positional operator:
let userWithCart = User.findOneAndUpdate(
{ _id: user, 'cart.name': product.name },
{ $inc: { 'cart.$.quantity' : 1 }}
)
if(!userWithCart ){
await User.findOneAndUpdate({ _id: userId }, { $push: { cart: product } });
}
First findOneAndUpdate will return no value when there's no corresponding cart.name (and you need to $push it). Otherwise MongoDB will automatically match the cart you want to update based on cart.name condition and increment the quantity subfield.
EDIT:
If you still need to proceed the way you've started you just need to evaluate the path in JavaScript:
{ $inc: { [`cart.${product.index}.quantity`] : 1 }}

mongoose $inc is not working would like to insert value increment by one

I am trying to increment view field by one always when it hit this api but does not work
error : Posts validation failed: view: Cast to number failed for value
view: {
type: Number,
default: 0
},
async request(req, res) => {
const post = await PostsModel.findOne({ _id: post_id });
post.view = { $inc: { view: 1 } };
await post.save();
}
In your way, you need to do post.view = post.view + 1 instead of post.view = { $inc: { view: 1 } }; because it will set the view field to be the object { $inc: { view: 1 } }.
Or if you want to use $inc, you need to make an update operation. Something like:
await PostsModel.findOneAndUpdate({ _id: post_id }, { $inc: { view: 1 } });

Using mongoose lean after saving

So I am trying to add a key to a returned post. But I can't seem to get lean() to work. How can I manipulate the returned post after save?
I was thinking maybe I need to add lean to my findById like this Post.findById(req.params.id).lean().then(). But that didn't work, plus that only makes the first initial post mutable. It will say
post.save is not a function
if I do it like Post.findById(req.params.id).lean().then() as well
I want to only return the object about to be sent back to the client, I do not want they key saved in the actual document.
Post.findById(req.params.id)
.then(post => {
if (
post.likes.filter(like => like.user.toString() === req.user.id)
.length === 0
) {
return res
.status(400)
.json({ notliked: "You have not yet liked this post" });
}
// Get remove index
const removeIndex = post.likes
.map(item => item.user.toString())
.indexOf(req.user.id);
// Splice out of array
post.likes.splice(removeIndex, 1);
// Save
post.save().then(post => {
post["liked"] = false; <-------
res.json(post);
});
})
edit
Post.findById(req.params.id)
.lean()
.then(post => {
if (
post.likes.filter(like => like.user.toString() === req.user.id)
.length === 0
) {
return res
.status(400)
.json({ notliked: "You have not yet liked this post" });
}
// Get remove index
const removeIndex = post.likes
.map(item => item.user.toString())
.indexOf(req.user.id);
// Splice out of array
post.likes.splice(removeIndex, 1);
post["liked"] = false;
res.json(post);
// Save
post.save();
})
gives error
post.save is not a function
You can simply do this by searching for the req.user.id inside the indexOf likes array
Post.findOne({ _id: req.params.id }).lean().then((post) => {
if (post.likes.indexOf(req.user.id) !== -1) {
post.isLiked = true
}
post.isLiked = false
res.json(post)
})
Far better with the aggregation
Post.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(req.user.id) }},
{ "$addFields": {
"isLiked": { "$in": [mongoose.Types.ObjectId(req.user.id), "$likes"] }
}}
])
EDIT :- If you want to update document then use update query
Post.findOneAndUpdate(
{ _id: req.params.id },
{ $pull: { likes: { user: req.user.id } }},
{ new: true }
).then((post) => {
res.json(post)
})
Post Schema for likes
...
likes: [
{
user: {
type: Schema.Types.ObjectId,
ref: "users"
}
}
]
...

Mongoose query using if else possible?

I have this Schema:
const guestSchema = new Schema({
id: String,
cart: [
{
product: {
type: mongoose.Schema.ObjectId,
ref: "products"
},
quantity: Number
}
]
});
I have this query:
Guest.findOneAndUpdate(
{ id: req.sessionID },
{
$cond: [
{ "cart.product": { $ne: req.body.itemID } },
{ $push: { "cart": { product: req.body.itemID, quantity: 1 } } },
{ $inc: { "cart.quantity": 1 } }
]
},
{ upsert: true, new: true }
).exec(function(err, docs) {
err ? console.log(err) : res.send(docs);
});
Basically, what I'm trying to do is update based on a condition. I tried using $cond, but found out that operator isn't used for querys like I'm doing.
Based on this:
{ $cond: [ <boolean-expression>, <true-case>, <false-case> ] }
I want something similar to the functionality of this operator for my query.
Let's break down my condition:
For my boolean expression: I want to check if req.body.itemID is $ne to any of the values in my cart
If true then: $push the itemID and quantity into the cart
Else (then item already exists): $inc the quantity by 1
Question: How would achieve this result? Do I need to make two seperate querys? I'm trying to avoid doing that if possible
I went through all their Update Field Operators, and there's probably no way to do this in the way I want.
I wonder why there is no $cond for update operators. Nonetheless, I have the solution to what I wanted the functionality accomplish. Just not in the elegant fashion that I would like it.
Guest.findOneAndUpdate(
{ id: req.sessionID },
{ id: req.sessionID }, //This is here in case need to upsert new guest
{ upsert: true, new: true }
).exec(function(err, docs) {
if (err) {
console.log(err);
} else {
//Find the index of the item in my cart
//Returns (-1) if not found
const item = doc.cart.findIndex(
item => item.product == req.body.itemID
);
if (item !== -1) {
//Item found, so increment quantity by 1
doc.cart[item].quantity += 1;
} else {
//Item not found, so push into cart array
doc.cart.push({ product: req.body.itemID, quantity: 1 });
}
doc.save();
}
});
This type of logic does not belong within the database query. It should happen in the application layer. MongoDB is also very fast at retrieving and updating single records with an index so that should not be a concern.
Please try doing something like this:
try {
const guest = await Guest.findOne().where({
id: req.sessionID
}).exec();
// your cond logic, and update the object
await guest.save();
res.status(200).json(guest);
} catch (error) {
handleError(res, error.message);
}