Update last element of a nested array in mongodb - node.js

I have tried the below query of mongoose which does not seem to work:
Model.findOneAndUpdate(
{
name: origin
},
{
$set: {
'field1.$[id1].field2.-1': "value"
}
},
{
arrayFilters: [
{ 'id1.userId': "customerId" }
],
new: true
}
);
Note: field1 and field2 are arrays
The negative indexes are not accepted by MongoDB which is causing problems.

You may consider using the $set (aggregation) operator and use double $map operator:
db.collection.aggregate([
{ $match: { name: "myname" } }
{
$set: {
field1: {
$map: {
input: "$field1",
in: {
$cond: {
if: { $ne: [ "$$this.userId", "customerId" ] },
then: "$$this",
else: {
$mergeObjects: [
"$$this",
{
field2: {
$concatArrays: [
{ $slice: [ "$$this.field2", { $add: [ { $size: "$$this.field2" }, -1 ] } ] },
["value"]
]
}
}
]
}
}
}
}
}
}
}
])
Mongo Playground

Apply the $set operator together with the $ positional operator in your update to change the name field.
The $ positional operator will identify the correct element in the array to update without explicitly specifying the position of the element in the array, thus your final update statement should look like:
db.collection.update(
{ "friends.u.username": "michael" },
{ "$set": { "friends.$.u.name": "hello" } }
)
Answer taken from - https://stackoverflow.com/a/34431571

Related

Removing Dynamic Fields by Association in MongoDB Aggregation

I'm trying to display a MongoDB aggregation result via react chartjs. in aggregation, I can remove one field whose value is static via the set operator. is there a way to remove a second field by an association whose value is dynamic? in the example below, {"A": "N"} denotes the field that is readily removed by the set operator, whereas {"A_count":1} denotes the corresponding dynamic field that I am trying to remove.
starting aggregation output
[{
"_id":"Fubar",
"A_set":[{"A":"Y"},{"A":"N"}],
"A_count_set":[{"A_count":0},{"A_count":1}]
}]
set operation for static field removal
{$set: {
A_set: {
$filter: {
input: "$A_set",
as: "x",
cond: { "$ne": [ "$$x", {"A":"N"}] }
}
}
}}
current aggregation output
[{
"_id":"Fubar",
"A_set":[{"A":"Y"}],
"A_count_set":[{"A_count":0},{"A_count":1}]
}]
target aggregation output
[{
"_id":"Fubar",
"A_set":[{"A":"Y"}],
"A_count_set":[{"A_count":0}]
}]
$project merge two array with the same position
$set filter array
$addFields recover the original array
$project remove the merge array
aggregate
db.collection.aggregate([
{
$project: {
anotherValue: {
$map: {
input: {
$range: [
0,
{
$size: "$A_set"
}
]
},
as: "idx",
in: {
$mergeObjects: [
{
$arrayElemAt: [
"$A_set",
"$$idx"
]
},
{
$arrayElemAt: [
"$A_count_set",
"$$idx"
]
}
]
}
}
}
}
},
{
$set: {
anotherValue: {
$filter: {
input: "$anotherValue",
as: "x",
cond: {
"$ne": [
"$$x.A",
"N"
]
}
}
}
}
},
{
$addFields: {
"A_set": {
$map: {
input: "$anotherValue",
as: "a",
in: {
"A": "$$a.A"
}
}
},
"A_count_set": {
$map: {
input: "$anotherValue",
as: "a",
in: {
"A_count": "$$a.A_count"
}
}
}
}
},
{
"$project": {
"anotherValue": 0
}
}
])
mongoplayground

How to select either a field, if it exists or 0 inside $add operation?

I am using the $add operator in mongoose aggregate to sum a bunch of fields.
{ $set: { newField: { $add: ["$fieldOne", "$fieldTwo", "$fieldThree"] } } },
One of those fields fieldThree only exists in some documents. So in the documents in which it doesn't exists, I would like to replace it with 0. How may I do so?
db.collection.update({},
[
{
$addFields: {
fieldThree: {
$ifNull: [
"$fieldThree",
0
]
}
}
},
{
$set: {
newField: {
$add: [
"$fieldOne",
"$fieldTwo",
"$fieldThree"
]
}
}
},
],
{
multi: true
})
https://mongoplayground.net/p/L422i6DPE8k

How to add or remove a element in double nested array?

Example of the document:
{
postId:'232323',
post:'This is my first post',
commentsOnPost:[
{
commentId:'232323_8888',
comment:'Congrats',
repliesOnPost:[
{
replyId:'232323_8888_66666',
reply:'Thanks',
likesOnReply:['user1','user5','user3'],
}
]
}
]
}
I want to add userid in likesOnReply if users do not exist in likesOnReply, similarly remove userid from likesOnReply if exist.
I have tried like this but not working properly
await collection('post').findOneAndUpdate(
{
postId: postId,
'commentsOnPost.commentId': commentId,
'commentsOnPost.repliesOnPost.replyId': replyId
},
{
$push: { 'commentsOnPost.$[].repliesOnPost.$.likes': userid },
},
);
There is no straight way to do both the operation to pull or push in a single query,
There are 2 approaches,
1) Find and update using 2 queries:
use arrayFilters to updated nested array elements
$push to insert element
$pull to remove element
var post = await collection('post').findOne({
posted: postId,
ommentsOnPost: {
$elemMatch: {
commentId: commentId,
repliesOnPost: {
$elemMatch: {
replyId: replyId
likesOnReply: userid
}
}
}
}
});
var updateOperator = "$push";
// FOUND USER ID THEN DO REMOVE OPERATION
if (post) updateOperator = "$pull";
// QUERY
await collection('post').updateOne(
{ postId: postId },
{
[updateOperator]: {
"commentsOnPost.$[c].repliesOnPost.$[r].likesOnReply": userid
}
},
{
arrayFilters: [
{ "c.commentId": commentId },
{ "r.replyId": replyId }
]
}
)
Playground
2) Update with aggregation pipeline starting from MongoDB 4.2:
$map to iterate loop of commentsOnPost array check condition if commentId match then go to next process otherwise return existing object
$mergeObjects to merge current object with updated fields
$map to iterate loop of repliesOnPost array and check condition if replyId match then go to next process otherwise return an existing object
check condition for likesOnReply has userid then do remove using $filter otherwise insert using $concatArrays
await collection('post').findOneAndUpdate(
{ postId: "232323" },
[{
$set: {
commentsOnPost: {
$map: {
input: "$commentsOnPost",
in: {
$cond: [
{ $eq: ["$$this.commentId", commentId] },
{
$mergeObjects: [
"$$this",
{
repliesOnPost: {
$map: {
input: "$$this.repliesOnPost",
in: {
$cond: [
{ $eq: ["$$this.replyId", replyId] },
{
$mergeObjects: [
"$$this",
{
likesOnReply: {
$cond: [
{ $in: [userid, "$$this.likesOnReply"] },
{
$filter: {
input: "$$this.likesOnReply",
cond: { $ne: ["$$this", userid] }
}
},
{
$concatArrays: ["$$this.likesOnReply", [userid]]
}
]
}
}
]
},
"$$this"
]
}
}
}
}
]
},
"$$this"
]
}
}
}
}
}]
)
Playgorund

How do I get an object that is inside another with nodejs and mongodb?

I need to obtain the information contained in the array named detalleInsumos through the _id of that object.
I have tried many ways, but I still have not found the solution to my problem, since it always shows me all the objects of that document, which does not work for me.
This is the document from which I need to get that information:
{
"_id":{"$oid":"5f9041196462be3c5ca1e53d"},
"codigoFinca":"000",
"nombreFinca":"PROVINCIANA",
"fechaRegistro":"2020-10-21",
"semanaRegistro":"43",
"usuarioRegistro":"cotorreo",
"trabajadoresFinca":[
{
"porcentajeRecargo":0,
"_id":{"$oid":"5f9041196462be3c5ca1e53e"},
"udpTrabajador":[
{
"unidadesAPagar":null,
"valorUnidad":"",
"areaLaborada":"2",
"semanaNormal":null,
"semanaAtrazos":null,
"_id":{"$oid":"5f9041196462be3c5ca1e53f"},
"detalleInsumos":[
{"_id":{"$oid":"5f9041196462be3c5ca1e540"},
"codigoInsumo":"20000001",
"descripcionInsumo":"NYLON X 5 KILOS",
"cantidadAplicada":"153",
"idRDI":"426715",
"idDetalleSaldo":"24070"
}
],
"codigoLabor":"101",
"nombreLabor":"AMARRE",
"loteLaboro":"1"
}
],
"codigoTrabajador":"0000",
"nombresTrabajador":"HUMBERTO MENA MOSQUERA",
"horasJornada":"10",
"horasLaboradas":"10"
}
],
"createdAt":{"$date":"2020-10-21T14:09:29.876Z"},
"updatedAt":{"$date":"2020-10-21T15:09:51.657Z"},
"__v":0
}
And this is what I have tried from nodejs:
const consultauno = await Modelo.findOne({
'trabajadoresFinca.udpTrabajador.detalleInsumos._id': new ObjectId(idInsumo)
},
{
"trabajadoresFinca.udpTrabajador.detalleInsumos": 1
});
console.log(consultauno);
You can try,
$match your condition
$reduce to iterate loop of trabajadoresFinca array, second $reduce to iterate loop of udpTrabajador array, $filter to get matching object from detalleInsumos array,
$arrayElemAt will get first object from array when condition match
$mergeObjects will merge initialValue or reduce and matching object
const consultauno = await Modelo.aggregate([
{
$match: {
"trabajadoresFinca.udpTrabajador.detalleInsumos._id": ObjectId(idInsumo)
}
},
{
$project: {
detalleInsumos: {
$reduce: {
input: "$trabajadoresFinca",
initialValue: {},
in: {
$mergeObjects: [
"$$value",
{
$reduce: {
input: "$$this.udpTrabajador",
initialValue: {},
in: {
$mergeObjects: [
"$$value",
{
$arrayElemAt: [
{
$filter: {
input: "$$this.detalleInsumos",
cond: {
$eq: ["$$this._id", ObjectId(idInsumo)]
}
}
},
0
]
}
]
}
}
}
]
}
}
}
}
}
])
Playground
Second approach, you can use $unwind,
$match your conditions
$unwind deconstruct trabajadoresFinca array
$unwind deconstruct udpTrabajador array
$unwind deconstruct detalleInsumos array
$match your conditions
$project to show required fields
const consultauno = await Modelo.aggregate([
{
$match: {
"trabajadoresFinca.udpTrabajador.detalleInsumos._id": ObjectId(idInsumo)
}
},
{ $unwind: "$trabajadoresFinca" },
{ $unwind: "$trabajadoresFinca.udpTrabajador" },
{ $unwind: "$trabajadoresFinca.udpTrabajador.detalleInsumos" },
{
$match: {
"trabajadoresFinca.udpTrabajador.detalleInsumos._id": ObjectId(idInsumo)
}
},
{
$project: {
trabajadoresFinca: "$trabajadoresFinca._id",
udpTrabajador: "$trabajadoresFinca.udpTrabajador._id",
detalleInsumos: "$trabajadoresFinca.udpTrabajador.detalleInsumos"
}
}
])
Playground

Searching in nested arrays Mongodb

I have a mongodb with some JSON data which includes and nested arrays. I am
trying to make a query to count how many documents have a specific
value. For example here is how my json data looks:
{
"_id" : ObjectId("5ecb815bf4b8512918224e71"),
"array1" : [
{
"_id" : ObjectId("5ecb815bf4b8512918224e85"),
"xxxx" : "1450",
"yyyy" : 83,
"array2" : [
{
"_id" : ObjectId("5ecb815bf4b8512918224e88"),
"aaaa" : "1470420945276",
},
{...},
{...}]
}
The query that i am trying is the following:
db.example.aggregate([
{
$project: {
value1: {
$filter: {
input: "$array1",
as: "array",
cond: { $eq: [ "$$array.array2.aaaa" , "1470420945276" ] }
}
}
}
},
{
$project: {
value1Count: { $size: "$value1" }
}
}
])
But doesnt work and returns that value1Count=0. It looks like it doesnt nnavigate into the array2 to
read the value of the 'aaaa'. Any help?
You were almost close to getting the desired value. The problem is $$array.array2.aaaa returns an array value, so we can't use $eq here. Instead, we should use $in operator.
db.example.aggregate([
{
$project: {
value1: {
$filter: {
input: "$array1",
as: "array",
cond: {
$in: [
"1470420945276",
"$$array.array2.aaaa"
]
}
}
}
}
},
{
$project: {
value1Count: {
$size: "$value1"
}
}
}
])
MongoPlayground | Alternative solution

Resources