Mongo DB aggregation on array - arrays

im trying to add element to array in document MongoDB collection with Aggregate (not regular update) match to specific name
but i dont find the right syntax
for example:
{"name":"TEST1","department":"T1","skills":["JS","HTML"],"expY":5}
{"name":"TEST2","department":"T2","skills":["Java","CSS"],"expY":3}
i want to add to skills array "CSS" i try with sort, match, group, project, ($add, $push etc) and none of those syntax not working
db.collection.aggregate([
{ $match : { name : "TEST1" } },
HERE I DONT KNOW WHAT IS THE RIGHT SYNTAX TO ADD ELEMENT TO skills array
])

If I am understading your question correctly, you want to match all the documents with name "TEST1" and then add "CSS" to the skills array. If that's correct, here is one way to do that:
db.collection.aggregate([
{
$match: {
name: "TEST1"
}
},
{
$project: {
name: "$name",
department: "$department",
expY: "$expY",
skills: {
$concatArrays: [
"$skills",
[
"CSS"
]
]
}
}
}
])
Playground
Here is how to update your document to add "CSS" to the array:
db.your_collection.update({ name : "TEST1" }, { $push: { skills: 'CSS' } })

Related

Mongo DB find value in array of multiple nested arrays

I need to check if an ObjectId exists in a non nested array and in multiple nested arrays, I've managed to get very close using the aggregation framework, but got stuck in the very last step.
My documents have this structure:
{
"_id" : ObjectId("605ce5f063b1c2eb384c2b7f"),
"name" : "Test",
"attrs" : [
ObjectId("6058e94c3994d04d28639616"),
ObjectId("6058e94c3994d04d28639627"),
ObjectId("6058e94c3994d04d28639622"),
ObjectId("6058e94c3994d04d2863962e")
],
"variations" : [
{
"varName" : "Var1",
"attrs" : [
ObjectId("6058e94c3994d04d28639616"),
ObjectId("6058e94c3994d04d28639627"),
ObjectId("6058e94c3994d04d28639622"),
ObjectId("60591791d4d41d0a6817d23f")
],
},
{
"varName" : "Var2",
"attrs" : [
ObjectId("60591791d4d41d0a6817d22a"),
ObjectId("60591791d4d41d0a6817d255"),
ObjectId("6058e94c3994d04d28639622"),
ObjectId("60591791d4d41d0a6817d23f")
],
},
],
"storeId" : "9acdq9zgke49pw85"
}
Let´s say I need to check if this if this _id exists "6058e94c3994d04d28639616" in all arrays named attrs.
My aggregation query goes like this:
db.product.aggregate([
{
$match: {
storeId,
},
},
{
$project: {
_id: 0,
attrs: 1,
'variations.attrs': 1,
},
},
{
$project: {
attrs: 1,
vars: '$variations.attrs',
},
},
{
$unwind: '$vars',
},
{
$project: {
attr: {
$concatArrays: ['$vars', '$attrs'],
},
},
},
]);
which results in this:
[
{
attr: [
6058e94c3994d04d28639616,
6058e94c3994d04d28639627,
6058e94c3994d04d28639622,
6058e94c3994d04d2863962e,
6058e94c3994d04d28639616,
6058e94c3994d04d28639627,
6058e94c3994d04d28639622,
60591791d4d41d0a6817d23f,
60591791d4d41d0a6817d22a,
60591791d4d41d0a6817d255,
6058e94c3994d04d28639622,
60591791d4d41d0a6817d23f
]
},
{
attr: [
60591791d4d41d0a6817d22a,
60591791d4d41d0a6817d255,
6058e94c3994d04d28639622,
60591791d4d41d0a6817d23f,
6058e94c3994d04d28639624,
6058e94c3994d04d28639627,
6058e94c3994d04d28639628,
6058e94c3994d04d2863963e
]
}
]
Assuming I have two products in my DB, I get this result. Each element in the outermost array is a different product.
The last bit, which is checking for this key "6058e94c3994d04d28639616", I could not find a way to do it with $group, since I dont have keys to group on.
Or with $match, adding this to the end of the aggregation:
{
$match: {
attr: "6058e94c3994d04d28639616",
},
},
But that results in an empty array. I know that $match does not query arrays like this, but could not find a way to do it with $in as well.
Is this too complicated of a Schema? I cannot have the original data embedded, since it is mutable and I would not be happy to change all products if something changed.
Will this be very expensive if I had like 10000 products?
Thanks in advance
You are trying to compare string 6058e94c3994d04d28639616 with ObjectId. Convert the string to ObjectId using $toObjectId operator when perform $match operation like this:
{
$match: {
$expr: {
$in: [{ $toObjectId: "6058e94c3994d04d28639616" }, "$attr"]
}
}
}

How to update document in an object in an array in an object in an array using MongoDB?

Using MongoDB queries:
1. How do I find the object with _id A3?
2. How would I update object with _id A3 to A4?
Please note: Database is an array of objects where one key has an array of objects
[{
"project_name": "ProjectA",
"issues":[{"issue_title":"TitleA1", "_id":"A1"},
{"issue_title":"TitleA2","_id":"A2"},
{"issue_title":"TitleA3","_id":"A3"}]
},
{
"project_name": "ProjectB",
"issues":[{"issue_title":"TitleB1", "_id":"B1"},
{"issue_title":"TitleB2","_id":"B2"},
{"issue_title":"TitleB3","_id":"B3"}]
}
]
You can use the dot notation to $match your object by nested _id and use $unwind with $replaceRoot to promote nested object to the top level:
db.collection.aggregate([
{
$unwind: "$issues"
},
{
$match: { "issues._id": "A3" }
},
{
$replaceRoot:{ newRoot: "$issues" }
}
])
Mongo Playground
the positional operator can be used to update that one:
db.col.updateOne({ "project_name": "ProjectA", "issues._id": "A3" }, { $set: { "issues.$._id": "A4" } })

mongo update : upsert an element in array

I would like to upsert an element in an array, based on doc _id and element _id. Currently it works only if the element is allready in the array (update works, insert not).
So, these collection:
[{
"_id": "5a65fcf363e2a32531ed9f9b",
"ressources": [
{
"_id": "5a65fd0363e2a32531ed9f9c"
}
]
}]
Receiving this request:
query = { _id: '5a65fcf363e2a32531ed9f9b', 'ressources._id': '5a65fd0363e2a32531ed9f9c' };
update = { '$set': { 'ressources.$': { '_id': '5a65fd0363e2a32531ed9f9c', qt: '153', unit: 'kg' } } };
options = {upsert:true};
collection.update(query,update,options);
Will give this ok result:
[{
"_id": "5a65fcf363e2a32531ed9f9b",
"ressources": [
{
"_id": "5a65fd0363e2a32531ed9f9c",
"qt": 153,
"unit": "kg"
}
]
}]
How to make the same request work with these initial collections:
[{
"_id": "5a65fcf363e2a32531ed9f9b"
}]
OR
[{
"_id": "5a65fcf363e2a32531ed9f9b",
"ressources": []
}]
How to make the upsert work?
Does upsert works with entire document only?
Currently, I face this error:
The positional operator did not find the match needed from the query.
Thanks
I also tried to figure out how to do it. I found only one way:
fetch model by id
update array manually (via javascript)
save the model
Sad to know that in 2018 you still have to do the stuff like it.
UPDATE:
This will update particular element in viewData array
db.collection.update({
"_id": args._id,
"viewData._id": widgetId
},
{
$set: {
"viewData.$.widgetData": widgetDoc.widgetData
}
})
$push command will add new items

Filtering an embedded array in MongoDB

I have a Mongodb document that contains an an array that is deeply imbedded inside the document. In one of my action, I would like to return the entire document but filter out the elements of that array that don't match that criteria.
Here is some simplified data:
{
id: 123 ,
vehicles : [
{name: 'Mercedes', listed: true},
{name: 'Nissan', listed: false},
...
]
}
So, in this example I want the entire document but I want the vehicles array to only have objects that have the listed property set to true.
Solutions
Ideally, I'm looking for a solution using mongo's queries (e.g. `$unwind, $elemMatch, etc...) but I'm also using mongoose so solution that uses Mongoose is OK.
You could use aggregation framework like this:
db.test312.aggregate(
{$unwind:"$vehicles"},
{$match:{"vehicles.name":"Nissan"}},
{$group:{_id:"$_id",vehicles:{$push:"$vehicles"}}}
)
You can use $addToSet on the group after unwinding and matching by listed equals true.
Sample shell query:
db.collection.aggregate([
{
$unwind: "$vehicles"
},
{
$match: {
"vehicles.listed": {
$eq: true
}
}
},
{
$group: {
_id: "$id",
vehicles: {
"$addToSet": {
name: "$vehicles.name",
listed: "$vehicles.listed"
}
}
}
},
{
$project: {
_id: 0,
id: "$_id",
vehicles: 1
}
}
]).pretty();

MongoDB: How do I update a single subelement in an array, referenced by the index within the array?

I'm trying to update a single subelement contained within an array in a mongodb document. I want to reference the field using its array index (elements within the array don't have any fields that I can guarantee will be unique identifiers). Seems like this should be easy to do, but I can't figure out the syntax.
Here's what I want to do in pseudo-json.
Before:
{
_id : ...,
other_stuff ... ,
my_array : [
{ ... old content A ... },
{ ... old content B ... },
{ ... old content C ... }
]
}
After:
{
_id : ...,
other_stuff ... ,
my_array : [
{ ... old content A ... },
{ ... NEW content B ... },
{ ... old content C ... }
]
}
Seems like the query should be something like this:
//pseudocode
db.my_collection.update(
{_id: ObjectId(document_id), my_array.1 : 1 },
{my_array.$.content: NEW content B }
)
But this doesn't work. I've spent way too long searching the mongodb docs, and trying different variations on this syntax (e.g. using $slice, etc.). I can't find any clear explanation of how to accomplish this kind of update in MongoDB.
As expected, the query is easy once you know how. Here's the syntax, in python:
db["my_collection"].update(
{ "_id": ObjectId(document_id) },
{ "$set": { 'documents.'+str(doc_index)+'.content' : new_content_B}}
)
Update of an array element referenced by an index (e.g. 1 ) in Mongo Shell can also be done by directly indicating the index value:
db.my_collection.update(
{_id : "document_id"},
{$set : {"my_array.1.content" : "New content B"}}
)
In mongo style, using '$' positional operator.
Check out this link for details.
db.my_collection.update(
{_id: ObjectId(document_id), my_array.1 : 1 },
{ $set: { "my_array.$.content" : "NEW content B" } }
)
When it's required to update an array element without knowing it's actual index but having a unique identifier of the element:
// Modify a comment in a bucket
db.POST_COMMENT.update(
{
"_id": ObjectId("5ec424a1ed1af85a50855964"),
"bucket.commentId": "5eaf258bb80a1f03cd97a3ad_lepf4f"
},
{
$set: {
"bucket.$.text": "Comment text changed",
"bucket.$.createdDate": ISODate("2015-12-11T14:12:00.000+0000")
}
}
)
Here "bucket.commentId" is the unique identifier of an array element.
A neat way to do it in Javascript, with backticks, is:
const index = 1;
... { $set: { [`myArray.${index}.value`]: "new content"} }, ...
db.my_collection.update(
{_id: ObjectId(document_id), my_array : { ... old content A ... } },
{ $set: { "my_array.$.content" : "NEW content B" } }
)
When it's required to update an array element without knowing it's an actual index but having a unique identifier of the element
db.getCollection('profiles').update(
{
'userId':'4360a380-1540-45d9-b902-200f2d346263',
'skills.name':'css'
},
{
$set: {'skills.$.proficiencyLevel': 5}
},
{
multi: true
}
)
If you want to update the authorName of the testimonial having _id = 60c4918d74c30165ba585c14 from the following document:
"business": {
"ownerId": "60a5ebad7432d91b853c0277",
"testimonials": [
{
"_id": "60c4912877dd5664f2201b08",
"authorName": "user1",
"authorBio": "User from 10 years",
"image": "user1/img1",
"review": "asdfiuahsdfpoiuashdpfoaspdlfkjn;alsfpuoh"
},
{
"_id": "60c4918d74c30165ba585c14",
"authorName": "user2",
"authorBio": "User from 3 years",
"image": "user/img1",
"review": "asdpfuahsfljnsadfoihsf."
}
],
"createdAt": "2021-06-11T20:12:56.666Z",
"updatedAt": "2021-06-12T11:11:56.696Z",
}
Then the following mongoose query works:
await BusinessModel.updateOne(
{
'_id': Mongoose.Types.ObjectId(businessId),
'testimonials._id': Mongoose.Types.ObjectId('60c4918d74c30165ba585c14')
},
{
$set: { 'testimonials.$.authorName' : 'new author name' }
}
);
Also refer to https://docs.mongodb.com/drivers/node/fundamentals/crud/write-operations/embedded-arrays/
You can use the updateOne function of mongoDB passing the index of the element in array, if the key of old content B is "value" per example:
[
...
"value" : "old content A"
"value" : "old content B"
"value" : "old content C"
...
]
the command should be like this:
db.collection.updateOne({"_id" : "...,"},{$set: {"my_array.1.value": "NEW content B"}})
If you have a "plain" array containing simple strings, this did the trick:
db.paintings.insertMany([
{_id: 1, colors: ["red", "blue", "green"]},
{_id: 2, colors: ["red", "yellow"]}
db.paintings.updateMany(
{colors: "red"},
{$set: {"colors.$": "magenta"}})
the positional $ operator acts as a placeholder for the first element that matches the query document
Source

Resources