Can't create correct query to reach mongodb document - node.js

I need to translate a mongo shell command to the correct mongoose update in my express route.
The :id in the url is the ObjectId # in my document. The req.body will have an object with the key/values for title, season_number, episode_number, and watched. I thought I'd just replace this part of the mongo shell query
{ 'season_number': 1, 'episode_number': { $gt: 4, $lt: 6 },
with
{
'season_number': req.body.season_number,
'episode_number': {
$gt: req.body.episode_number - 1,
$lt: req.body.episode_number + 1
}
}
in the query but that did not find the document.
Route
router.put('/api/shows/:id/episodes/add', function(req, res){
var query = {
/*
I've tried many things but my query never returns the document
to update so I am pretty sure the problem is here
*/
}
var setObject = {
$push:{
'episodes':req.body
}
}
TV.update(query, setObject, function(err, results){
if(err){console.log(err)}
else{res.json(results)};
})
})
Mongo Shell Document
{
"_id" : ObjectId("581972b7b04acfc99b4dae0f"),
"title" : "Designated Survivor",
"poster" : "https://images-na.ssl-images-amazon.com/images/M/MV5BMTY5NzYzODU4N15BMl5BanBnXkFtZTgwNzA1MjUwMDI#._V1_.jpg",
"rated" : "TV-14",
"program_time" : 60,
"network" : "ABC",
"airs_on" : [
"Wednesday"
],
"streams_on" : [
"123Movies",
"Hulu Plus"
],
"genre" : [
"Drama"
],
"episodes" : [
{
"season_number" : 1,
"episode_number" : 1,
"title" : "Pilot",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 2,
"title" : "The First Day",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 3,
"title" : "The Confession",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 4,
"title" : "The Enemy",
"watched" : true
},
{
"season_number" : 1,
"episode_number" : 5,
"title" : "The Mission",
"watched" : true
},
{
"title" : "The Interrogation",
"season_number" : 1,
"episode_number" : 6,
"watched" : false
}
],
"test" : "gt four less than 6"
}
Mongo Shell Command that added the 6th Episode
db.tvShows.findOneAndUpdate(
{
$and: [
{ '_id': ObjectId('581972b7b04acfc99b4dae0f') },
{ 'episodes': {
$elemMatch: {
'season_number': 1,
'episode_number': { $gt: 4, $lt: 6 }
}
} }
]
},
{
$push: {
'episodes': {
'title': 'The Interrogation',
'season_number': 1,
'episode_number': 6,
watched: false
}
}
}
)

Related

Mongoose : Find and Update Multiple Nested Documents

My Documents are as follows.
{
"order_id" : "1",
"payment_status" : false,
"items" : [
{
"item_id" : 1,
"payment_status" : false,
},
{
"item_id" : 2,
"payment_status" : false,
},
{
"item_id" : 3,
"payment_status" : false,
},
]
}
I need to update the fields payment_status for {"order_id":1} and {"item_id" : 1} and {"item_id" : 3}. Also, I need to update the same in bulk for matching conditions. Is this possible in mongoose?
You want to be using $arrayFilters like so:
db.collection.updateMany({
"order_id": "1"
},
{
"$set": {
"items.$[item].payment_status": true
}
},
{
arrayFilters: [
{
"item.item_id": {
$in: [
1,
3
]
}
}
]
})
Mongo Playground

Mongoose update object inside a nested array

I know there are hundreds of same questions, but I've read them and can't get this to work.
What I want to do is to update the given object inside tasks array with given req.body params
I tried most of the solutions but probably wrongly implemented them. This is closest to what I can imagine working.
Example document
{
"_id" : ObjectId("5f421bf98bc1d33d7c646535"),
"todoSections" : [
{
"_id" : ObjectId("5f42202b9c6c3040ea0d5326"),
"name" : "first section",
"tasks" : [
{
"status" : 4,
"priority" : 2,
"_id" : ObjectId("5f42274952e6864b19252e37"),
"name" : "task 1",
"assignedUsers" : [],
"comments" : [],
"subTasks" : []
},
{
"status" : 4,
"priority" : 2,
"_id" : ObjectId("5f422a6377eaa74f2e403940"),
"name" : "task 2",
"creationDate" : ISODate("2020-08-23T08:35:47.497Z"),
"assignedUsers" : [],
"comments" : [],
"subTasks" : []
}
]
},
{
"_id" : ObjectId("5f42202f9c6c3040ea0d5327"),
"name" : "another section..",
"tasks" : []
},
],
"__v" : 0
}
My code
const mongodb = require("mongodb");
const { ObjectId } = require("mongodb");
Todo.updateOne(
{
_id: req.body.todoId,
},
{
$set: {
"todoSections.$[outer].tasks$[inner]": {
name: req.body.name,
status: req.body.status
},
},
},
{
arrayFilters: [
{ "outer._id": ObjectId(req.body.sectionId) },
{ "inner._id": ObjectId(req.body.taskId) },
],
},
(err, result) => {
if (!err) {
if (result.nModified === 0) {
res.status(400).send(result);
console.log(result);
return;
} else {
res.status(200).send("ok");
}
} else {
res.status(400).send(err);
console.log(err);
return;
}
}
);
Result returns:
{
"ok": 0,
"n": 0,
"nModified": 0
}

How to do two different arrayFilters in the same mongodb update?

In the application im building I have two updates that I want to do in the same query. I want to find the subdocument with the matching task_id and update its priority. In the same call I want to increment all the subdocuments with a priority higher than 3. Is it possible to combine these two in the same query?
const project = await Project.updateOne(
// first search
{ _id: req.params.project_id },
{ $set: {'tasks.$[element].priority': req.body.priority, 'tasks.$[element].state': req.body.state }},
{ arrayFilters: [{ 'element._id': req.params.task_id }] }
// second search
{ _id: req.params.project_id },
{ $inc: {'tasks.$[element].priority': 1 }},
{ arrayFilters: [{ 'element.priority': { $gt: 3 } }] }
);
you must be used different identifiers for your arrayFilters. your identifier is element. your code must be like that:
const project = await Project.updateOne(
// first search
{_id: req.params.project_id},
{
$set: {'tasks.$[elementA].priority': req.body.priority, 'tasks.$[elementA].state': req.body.state},
$inc: {'tasks.$[elementB].priority': 1}
},
{
arrayFilters: [
{'elementA._id': req.params.task_id},
{'elementB.priority': {$gt: 3}}
]
},
)
NOTE: The identifier must begin with a lowercase letter and contain only alphanumeric characters (from MongoDB official website, link)
You can do it simultaneously by using two arrayFilters. Consider the below:
Current collection:
{
"_id" : 1,
"array1" : [
{
"k1" : 1,
"v1" : 100
},
{
"k1" : 2,
"v1" : 15
},
{
"k1" : 1,
"v1" : 100
}
],
"array2" : [
{
"k2" : 1,
"v2" : 10
},
{
"k2" : 2,
"v2" : 1000
},
{
"k2" : 1,
"v2" : 20
}
]
}
Query:
db.collection.update(
{ _id: 1 },
{ $set:
{
'array1.$[elem1].v1': 100,
'array2.$[elem2].v2': 1000
}
},
{ arrayFilters:
[
{'elem1.k1':1},
{'elem2.k2': 2}
],
multi: true
}
)
As you can see that, I have created two filtered positional operator (elem1 and elem2), with the help of arrayFilters option. I can used this to perform my updates.
Result:
{
"_id" : 1,
"array1" : [
{
"k1" : 1,
"v1" : 100
},
{
"k1" : 2,
"v1" : 15
},
{
"k1" : 1,
"v1" : 100
}
],
"array2" : [
{
"k2" : 1,
"v2" : 10
},
{
"k2" : 2,
"v2" : 1000
},
{
"k2" : 1,
"v2" : 20
}
]
}
You can see in the above updated collection that the k1 field in array1 with value 1, it's v1 field have been updated to 100 and the k2 field in array2 with value 2, it's v2 field have been updated to 100.
So in your case you need to do something like below:
updateOne(
{ _id: req.params.project_id},
{
$set: {
'tasks.$[elem1].priority': req.body.priority,
'tasks.$[elem1].state': req.body.state
},
$inc: {
'tasks.$[elem2].priority': 1
}
},
{
arrayFilters: [
{ 'elem1._id': req.params.task_id },
{ 'elem2.priority':
{ $gt: 3 }
}
]
}
)
I hope it's helpful.

Mongo Node driver how to get all fields of $max aggregate from an array of objects

I have a collection called "products" which has an array of "bids" objects.
I want to find out the Maximum bid for each product, for this I am aggregating Products on $max with $bids.bidamount field. However this is only giving me the largest bid amount. How do I project all the bid fields for the max aggregation.
Here is a sample document
{
"_id" : ObjectId("58109a5138fe12215cfdc064"),
"product_id" : 2,
"item_name" : "Auction Item1",
"item_description" : "Test",
"seller_name" : "ak#gmail.com",
"item_price" : "20",
"item_quantity" : 7,
"sale_type" : "Auction",
"posted_at" : "2016:10:26 04:58:09",
"expires_at" : "2016:10:30 04:58:09",
"bids" : [
{
"bid_id" : 1,
"bidder" : "ak#gmail.com",
"bid_amount" : 300,
"bit_time" : "2016:10:26 22:36:29"
},
{
"bid_id" : 2,
"bidder" : "ak#gmail.com",
"bid_amount" : 100,
"bit_time" : "2016:10:26 22:37:29"
}
],
"orders" : [
{
"buyer" : "ak#gmail.com",
"quantity" : "2"
},
{
"buyer" : "ak#gmail.com",
"quantity" : "3"
}
]
}
Here is my mongo query:
db.products.aggregate([
{
$project: {
bidMax: { $max: "$bids.bid_amount"}
}
}
])
which gives the following result:
{
"_id" : ObjectId("58109a5138fe12215cfdc064"),
"bidMax" : 300
}
db.products.aggregate([{$unwind:"$bids"},{$group:{_id:"$_id", sum:{$sum:"$bids.bid_amount"}}},{$project:{doc:"$$ROOT", _id:1, sum:1}, {$sort:{"sum":-1}},{$limit:1}]),
which return something like { "_id" : ObjectId("5811b667c50fb1ec88227860"), "sum" : 600, doc:{your document....} }
This should do it:
db.products.aggregate([{
$unwind: '$bids'
}, {
$group: {
_id: '$products_id',
maxBid: {
$max: '$bids.bid_amount'
}
}
}])
db.collectionName.aggregate(
[
{
$group:
{
_id: "$product_id",
maxBidAmount: { $max: "$bids.bid_amount" }
}
}
]
)
Hey use this query, you will get the result.

Mongodb lookup + group by with mongoose

I have two different mongoose collection as follow :
{ "_id" : 1, "countryId" : 1, "price" : 12, "quantity" : 24 }
{ "_id" : 2, "countryId" : 2, "price" : 20, "quantity" : 1 }
{ "_id" : 3 }
{ "_id" : 4, "countryId" : 1, "price" : 12, "quantity" : 24 }
{ "_id" : 1, "id" : 1, description: "Colombia"}
{ "_id" : 3, "id" : 2, description: "Mexic" }
I'm trying to aggregate them so that i can have a result as follow :
{"country":"Colombia","total":48}
{"country":"Mexic","total":1}
I've tried many things but it's always failing here is the last version of what i'm working on ( i've changed the data but you get the idea ) :
Model.aggregate([
{
$lookup:
{
from: "countryList",
localField: "countryId",
foreignField: "id",
as: "country"
},
{
$project: {
quantity:1, country:{$country:"$countryList.description"}
}
},{
$group:{
{ _id : null, qtyCountry: { $sum: "$quantity" } }
}
}
}],function (err, result) {
if (err) {
console.log(err);
} else {
console.log(result)
}
}
);
Is it even possible ?
Yes, it is possible. You can try the following aggregation pipeline.
var pipeline = [
{"$match":{"countryId":{"$exists":true}}},
{"$group" : {"_id":"$countryId", "quantity":{"$sum":"$quantity"}}},
{"$lookup":{"from":"countryList","localField":"_id", "foreignField":"id","as":"country"}},
{"$unwind":"$country"},
{"$project": {"country":"$country.description", "total":"$quantity", _id:0}}
]
Sample output:
{ "country" : "Mexic", "total" : 1 }
{ "country" : "Colombia", "total" : 48 }

Resources