set a random values in a field using UpdateMany in mongoose query - node.js

I wanted to know whether this is possible that while updating many documents using UpdateMany in mongoose I need to set any of the status value from ["pending", "settled"] to a key called payment_settlement for different documents.
For better understanding I'm adding the route here:
router.post("/payment_settlement", (req, res, next) => {
var ar = ["pending", "settled"];
models.Payment.updateMany({ $set: { payment_settlement: ar[Math.floor(Math.random() * 2)] } })
.then(_ => {
return funcs.sendSuccess(res, _);
})
.catch(next);
});
The above approach gave me the same result for all documents either "pending" or "settled".
My doubt is that MongoDB(Mongoose) computing random value once at the beginning, and using that single random value for all documents.
However, we can achieve this using enum in mongoose schema. But I need to update existing documents with random values.
Any ideas or discussion is highly appreciated.
Thanks

Because you are using mongodb version 4.4.2 so you can use Updates with Aggregation Pipeline and $rand operator:
models.Payment.updateMany(
{},
[
{
$set: {
payment_settlement: {
$arrayElemAt: [
["pending", "settled"],
{ $round: { $rand: {} } }
]
}
}
}
]
)
MongoPlayground
Or if your array have more than 2 elements, you can use $multiply with $rand:
models.Payment.updateMany(
{},
[
{
$set: {
payment_settlement: {
$arrayElemAt: [
["pending", "settled", "cancelled"],
{ $round: { $multiply: [{ $rand: {} }, 2] } }
]
}
}
}
]
)
MongoPlayground

Related

Update nested object in array MongoDB

I need to find and update documents with category that corresponding to the query. Array could contain mo than one corresponding id.
Query:
{
"ids": ["61f1cda47018c60012b3dd01", "61f1cdb87018c60012b3dd07"],
"userId": "61eab3e57018c60012b3db3f"
}
I got collection with documents like:
`{
"_id":{"$oid":"61f1cdd07018c60012b3dd09"},
"expenses":[
{"category":"61eafc104b88e154caa58616","price":"1111.00"},
{"category":"61f1cdb87018c60012b3dd07","price":"2222.00"},
{"category":"61f1cda47018c60012b3dd01","price":"1241.00"},
{"category":"61f1cdb87018c60012b3dd07","price":"111.00"}
],
"userId":"61eab3e57018c60012b3db3f"
}`
my method:
async myMethod(ids: [string], userId: string) {
try {
const { ok } = await this.ExpensesModel.updateMany(
{"userId": userId, "expenses.category": { $in: ids }},
{$set: {"expenses.$.category": "newCategoryID"}}
);
return ok
} ........
I path array of ids ["61f1cda47018c60012b3dd01","61f1cdb87018c60012b3dd07","61f1cdb87018c60012b3dd07"] and userId, this code update only 1 category by document.
So can i made it with mongo build in methods? or i need to find matching document and update it it by my self and after that update or insert;
Update with arrayFilters
db.collection.update({
"expenses.category": {
$in: [
"61f1cda47018c60012b3dd01",
"61f1cdb87018c60012b3dd07"
]
}
},
{
$set: {
"expenses.$[elem].category": "61eab3e57018c60012b3db3f"
}
},
{
arrayFilters: [
{
"elem.category": {
$in: [
"61f1cda47018c60012b3dd01",
"61f1cdb87018c60012b3dd07"
]
}
}
]
})
mongoplayground

MongoDB updating array of objects does not work as expected

Got multiple documents in a db one of which looks like this:
{
"searchWord":[
"pizz",
"pizza"
],
"result":[
{
"idMeal":1,
"strMeal":"test1",
"strInstructions":"test1"
},
{
"idMeal":2,
"strMeal":"test2",
"strInstructions":"test2"
}
]
}
Tried to solve it like this:
eg:
db.meals.updateOne(
{
"searchWord": "pizz",
"result": { $elemMatch: { idMeal: "1" } }
},
{ $set: { 'result.$.strMeal' : "UPDATED" } }
)
This doesnt update the respective subdocument only the 2nd as if I wrote
{ $set: { 'result.1.strMeal' : "UPDATED" } }
(Which will result in the 2nd subdocument being updated)
This is the other idea (Same result)
db.meals.updateOne(
{ searchWord: "pizz", 'result.idMeal': 319012 },
{ $set: { "result.$.strMeal" : "fsdf" } }
)
What I dont seem to understand is its exactly the syntax that is provided by mongo yet it doesnt work
The "$" operator doesnt pick up which array of objects I wanna update
Try to use $[] in your $set for multiple positional element
db.collection.update({
"searchWord": "pizz"
},
{
$set: {
"result.$[r].strMeal": "UPDATED"
}
},
{
arrayFilters: [
{
"r.idMeal": 1
}
]
})
Here is the Mongo playground for your reference.

MongoDB: Regroup by two field with different value

I created a nodejs app in Typescript.
I want to group documents by two fields "one_id" and "two_id" with specified one_id value.
Here is my data in my collection:
{
"_id":"5a8b2953007a1922f00124fd",
"one_id":"307973260186877954",
"two_id":"415228402765660181"
}
{
"_id":"5a8b29a3007a1922f00124fe",
"one_id":"415228402765660181",
"two_id":"307973260186877954"
}
{
"_id":"5a8c119bf6ba49302c3ef67e",
"one_id":"394199132195127306",
"two_id":"270131587092316161"
}
{
"_id":"5a8c11a4f6ba49302c3ef67f",
"one_id":"270131587092316161",
"two_id":"394199132195127306"
}
{
"_id":"5a8c33132a182308a836bc1c",
"one_id":"307973260186877954",
"two_id":"397036401075552256"
}
{
"_id":"5a8c33242a182308a836bc1d",
"one_id":"397036401075552256",
"two_id":"307973260186877954"
}
And if I want to get pairs with one_id="307973260186877954", the excepted result would be: (must have another document with "inversed" fields content)
{
"_id":"5a8b2953007a1922f00124fd",
"one_id":"307973260186877954",
"two_id":"415228402765660181"
}
{
"_id":"5a8b29a3007a1922f00124fe",
"one_id":"415228402765660181",
"two_id":"307973260186877954"
}
{
"_id":"5a8c33132a182308a836bc1c",
"one_id":"307973260186877954",
"two_id":"397036401075552256"
}
{
"_id":"5a8c33242a182308a836bc1d",
"one_id":"397036401075552256",
"two_id":"307973260186877954"
}
I don't know if you understand me.
Thank you, I hope someone will understand me!
You can use $lookup to self join the rows and output the document when there is a match and $project with exclusion to drop the joined field.
db.col.aggregate([
{"$lookup":{
"from":col,
"localField":"one_id",
"foreignField":"two_id",
"as":"onetwo"
}},
{"$lookup":{
"from":col,
"localField":"two_id",
"foreignField":"one_id",
"as":"twoone"
}},
{"$match":{"onetwo.0":{"$exists":true}, "twoone.0":{"$exists":true}}},
{"$project":{"onetwo":0,"twoone":0}}
])
I think this should be your solution. Not sure. Just gave it a try
db.TempCollections.aggregate([
{
$lookup:{
from:"TempCollections",
let:{
leftone_id:"$one_id",
lefttwo_id:"$two_id"
},
pipeline:[
{
$match: {
$expr: {
$and: [
{
$eq: [
"$one_id",
"$$lefttwo_id"
]
},
{
$eq: [
"$two_id",
"$$leftone_id"
]
}
]
}
}
}
],
as:"Final"
}
}
],
{
allowDiskUse:true
});

MongoDB request: Filter only specific field from embedded documents in embedded array

I have got some troubles with a MongoDB request that I would like to execute. I am using MongoDB 3.2 with Mongoose in a node.js context. Here is the document:
{
_id: ObjectId('12345'),
name: "The name",
components: [
{
name: "Component 1",
description: "The description",
container: {
parameters: [
{
name: "Parameter 1",
value: "1"
},
// other parameters
],
// other information...
}
},
// other components
]
}
And I would like to list all the parameters name for a specific component (using component name) in the specific document (using _id) with this output:
['Parameter 1', 'Parameter 2', ...]
I have got a mongoose Schema to handle the find or distinct methods in my application. I tried many operations using $ positioning operator. Here is my attempt but return all parameters form all components:
listParameters(req, res) {
DocumentModel.distinct(
"components.container.parameters.name",
{_id: req.params.modelId, "components.name": req.params.compId},
(err, doc) => {
if (err) {
return res.status(500).send(err);
}
res.status(200).json(doc);
}
);
}
but the output is the list of parameter name but without the filter of the specific component. Can you help me to find the right request? (if possible in mongoose JS but if it is a Mongo command line, it will be very good :))
You would need to run an aggregation pipeline that uses the $arrayElemAt and $filter operators to get the desired result.
The $filter operator will filter the components array to return the element satisfying the given condition whilst the $arrayElemAt returns the document from the array at a given index position. With that document you can then project the nested parameters array elements to another array.
Combining the above you ideally want to have the following aggregate operation:
DocumentModel.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(req.params.modelId) } },
{ "$project": {
"component": {
"$arrayElemAt": [
{
"$filter": {
"input": "$components",
"as": "el",
"cond": { "$eq": ["$$el.name", req.params.compId] }
}
}, 0
]
}
} },
{ "$project": {
"parameters": "$component.container.parameters.name"
} }
], (err, doc) => {
if (err) {
return res.status(500).send(err);
}
const result = doc.length >= 1 ? doc[0].parameters : [];
res.status(200).json(result);
})
i don`t know how to do it with mongoose but this will work for you with mongo
db.getCollection('your collaction').aggregate([ // change to youe collaction
{$match: {_id: ObjectId("5a97ff4cf832104b76d29af7")}}, //change to you id
{$unwind: '$components'},
{$match: {'components.name': 'Component 1'}}, // change to the name you want
{$unwind: '$components.container.parameters'},
{
$group: {
_id: '$_id',
params: {$push: '$components.container.parameters.name'}
}
}
]);

Use mongoose / mongodb to $pull nested data from an array

say I have this array property ('articles') on a Mongoose schema:
articles: [
{
kind: 'bear',
hashtag: 'foo'
},
{
kind: 'llama',
hashtag: 'baz',
},
{
kind: 'sheep',
hashtag: 'bar',
}
]
how can I use
$pull https://docs.mongodb.org/manual/reference/operator/update/pull/
to remote objects from this array by checking the value of hashtag to see if it matches a pattern?
For example, if I want to remove an object in the articles array where hashtag='foo'.
My best guess is the following, but it doesn't seem to work:
var data = {
"$pull": {
"articles": {
"$elemMatch": {
"hashtag": "foo"
}
}
}
};
Model.update({},data); //doesn't quite work
this one seems to work:
var data = {
"$pull": {
"articles": {
"hashtag": 'foo'
}
}
};
Model.update({},data); //seems to work
if you have a better solution or if you can show an alternate solution please provide an answer thank you
The $pull operator is basically a "mini query" in itself, and as such operators like $elemMatch become irrelevant as the "query" is directly performed on each array member anyway.
As such:
Model.update(
{},
{ "$pull": { "articles": { "hashtag": "foo" }},
{ "multi": true },
function(err,numAffected) {
// handle result here
}
)
So it is looking for the matching properties within the ( correct ) specified array in all array documents, and then removing them where there is a match.
The .update() also just returns the number of affected documents, and is usually used with { "multi": true } when you expect more than one document to be updated.
If you are just looking for "one" document, or expect the modified document in response, then use .findOneAndUpdate() instead:
Model.findOneAndUpdate(
{},
{ "$pull": { "articles": { "hashtag": "foo" }},
{ "new": true },
function(err,numAffected) {
// handle result here
}
)
Though not really practical without any selection criteria, since this just updates the first document found in the collection.

Resources