MongoDB: Updating a document with existing fields using aggregation - mongodb

I am new to mongodb, I am learning from some Udemy courses and I want to know how I can update a document existing field without overwriting it.
I have the following collection with these documents:
enter image description here
I want to add new warehouses in the "item":"drafts" within the stock field.
What I am trying is:
enter image description here
And giving the output it seems that is working, but when I do again db.matrices.find(), what I get is the exactly same output that in the first image.
How can I update it? I have tried also the update method, but does not do what I want to do.
Thanks!
PD: I am using linux mint, with mongo v5.0.3, and mongosh v1.1.1

You are using the aggregate pipeline, this does not update the document in the DB, it just retrieves the result. starting in Mongo version 4.2+ you can now use an aggregation pipeline ( with some limitations ) to update a document, like so:
db.collection.updateOne({
item: "drafts"
},
[
{
$set: {
stock: {
$concatArrays: [
"$stock",
[
{
"warehouse": "A",
qty: 20
}
]
]
}
}
}
])
Mongo Playground
I will just say that this specific update is very simple, there is no need to use an aggregation pipeline for it. a simple use of the $push operator in the update field will suffice in a "normal" update:
db.collection.updateOne({
item: "drafts"
},
{
$push: {
stock: {
"warehouse": "A",
qty: 20
}
}
})
Mongo Playground

Related

Push an object to a nested array within a document but use a value from a field in the root document

I've been scratching my head with this problem. I've attempted to search for a solution but I didn't find anything relating to my specific use case.
Would anyone be able to help me out?
Say I have a collection of "discount" documents, and importantly they have an "amount" field to say how much the discount is worth. Whenever a discount is redeemed I currently want to track what the worth was at the time of the redemption.
To do this I've been attempting to use the following code:
await datastore.collection('discounts').updateOne(
{
$expr: { $gt: [ '$maxUses', '$uses' ] },
...criteria
},
{
$set: {
uses: 1
},
$push: {
redemptions: {
name: concatNames(user),
email: user.email,
amount: '$amount', // <-- use amount from root document
when: new Date()
}
}
}
)
Unfortunately $amount does not pull the value from the root document, instead it just becomes "$amount" as a string. I've also attempted to convert this update to use a pipeline but $push is not a valid pipeline stage.
Here's a quick Mongo playground link.
Thanks in advance.
In order to refer to another fields value, you'll need to use the aggregation pipeline form of update. However, '$push' is an update operator, not an aggregation operator.
$concatArrays gets most of the way there like
{$set: {redepmtions: {$concatArrays: [ "$redemptions", [{amount: "$amount"}]}}
That will throw an error if $redemptions doesn't already exist, so use $cond to subsitute an empty array in that case:
.updateOne(
{ ...criteria },
[{$set: {
redemptions: {$concatArrays: [
{$cond: [{$eq: ["array", {$type: "$redemptions"}]}, "$redemptions", []]},
[{amount: "$amount"}]
]}
}}]
)
Playground

How to build a MongoDB query that combines two field temporarily?

I have a schema which has one field named ownerId and a field which is an array named participantIds. In the frontend users can select participants. I'm using these ids to filter documents by querying the participantIds with the $all operator and the list of participantsIds from the frontend. This is perfect except that the participantsIds in the document don't include the ownerId. I thought about using aggregate to add a new field which consists of a list like this one: [participantIds, ownerId] and then querying against this new field with $all and after that delete the field again since it isn't need in the frontend.
How would such a query look like or is there any better way to achieve this behavior? I'm really lost right now since I'm trying to implement this with mongo_dart for the last 3 hours.
This is how the schema looks like:
{
_id: ObjectId(),
title: 'Title of the Event',
startDate: '2020-09-09T00:00:00.000',
endDate: '2020-09-09T00:00:00.000',
startHour: 1,
durationHours: 1,
ownerId: '5f57ff55202b0e00065fbd10',
participantsIds: ['5f57ff55202b0e00065fbd14', '5f57ff55202b0e00065fbd15', '5f57ff55202b0e00065fbd13'],
classesIds: [],
categoriesIds: [],
roomsIds: [],
creationTime: '2020-09-10T16:42:14.966',
description: 'Some Desc'
}
Tl;dr I want to query documents with the $all operator on the participantsIds field but the ownerId should be included in this query.
What I want is instead of querying against:
participantsIds: ['5f57ff55202b0e00065fbd14', '5f57ff55202b0e00065fbd15', '5f57ff55202b0e00065fbd13']
I want to query against:
participantsIds: ['5f57ff55202b0e00065fbd14', '5f57ff55202b0e00065fbd15', '5f57ff55202b0e00065fbd13', '5f57ff55202b0e00065fbd10']
Having fun here, by the way, it's better to use Joe answer if you are doing the query frequently, or even better a "All" field on insertion.
Additional Notes: Use projection at the start/end, to get what you need
https://mongoplayground.net/p/UP_-IUGenGp
db.collection.aggregate([
{
"$addFields": {
"all": {
$setUnion: [
"$participantsIds",
[
"$ownerId"
]
]
}
}
},
{
$match: {
all: {
$all: [
"5f57ff55202b0e00065fbd14",
"5f57ff55202b0e00065fbd15",
"5f57ff55202b0e00065fbd13",
"5f57ff55202b0e00065fbd10"
]
}
}
}
])
Didn't fully understand what you want to do but maybe this helps:
db.collection.find({
ownerId: "5f57ff55202b0e00065fbd10",
participantsIds: {
$all: ['5f57ff55202b0e00065fbd14',
'5f57ff55202b0e00065fbd15',
'5f57ff55202b0e00065fbd13']
})
You could use the pipeline form of update to either add the owner to the participant list or add a new consolidated field:
db.collection.update({},[{$set:{
allParticipantsIds: {$setUnion: [
"$participantsIds",
["$ownerId"]
]}
}}])

MongoDB querying aggregation in one single document

I have a short but important question. I am new to MongoDB and querying.
My database looks like the following: I only have one document stored in my database (sorry for blurring).
The document consists of different fields:
two are blurred and not important
datum -> date
instance -> Array with an Embedded Document Object; Our instance has an id, two not important fields and a code.
Now I want to query how many times an object in my instance array has the group "a" and a text "sample"?
Is this even possible?
I only found methods to count how many documents have something...
I am using Mongo Compass, but i can also use Pymongo, Mongoengine or every other different tool for querying the mongodb.
Thank you in advance and if you have more questions please leave a comment!
You can try this
db.collection.aggregate([
{
$unwind: "$instance"
},
{
$unwind: "$instance.label"
},
{
$match: {
"instance.label.group": "a",
"instance.label.text": "sample",
}
},
{
$group: {
_id: {
group: "$instance.label.group",
text: "$instance.label.text"
},
count: {
$sum: 1
}
}
}
])

Update array of document embedded

A question if I want to update all documents embedded in a document, how could I do? because when you run the following command
.update({'sites':{$elemMatch:{'status':true}}},{$set:{'sites.$.status': false}},{multi:true})
only the first found embedded document is updated
documents example:
{
'_id': 1,
sites: [
{'status':true,'url':'http://google.com'},
{'status':true,'url':'https://university.mongodb.com'},
{'status':true,'url':'https://docs.mongodb.org'}
]}
This isn't possible, the closes you can get is to update the entire element. e.g.:
db.test.update({
_id: 1
},
{
$set: {
sites: [
{'status':true,'url':'http://1.example.com'},
{'status':true,'url':'http://2.example.com'},
{'status':true,'url':'http://3.example.com'},
]
}
})

Refine/Restructure data from Mongodb query

Im using NodeJs, MongoDB Native 2.0+
The following query fetch one client document containing arrays of embedded staff and services.
db.collection('clients').findOne({_id: sessId}, {"services._id": 1, "staff": {$elemMatch: {_id: reqId}}}, callback)
Return a result like this:
{
_id: "5422c33675d96d581e09e4ca",
staff:[
{
name: "Anders"
_id: "5458d0aa69d6f72418969428"
// More fields not relevant to the question...
}
],
services: [
{
_id: "54578da02b1c54e40fc3d7c6"
},
{
_id: "54578da42b1c54e40fc3d7c7"
},
{
_id: "54578da92b1c54e40fc3d7c9"
}
]
}
Note that each embedded object in services actually contains several fields, but _id is the only field returned by means of the projection of the query.
From this returned data I start by "pluck" all id's from services and save them in an array later used for validation. This is by no means a difficult operation... but I'm curious... Is there an easy way to do some kind of aggregation instead of find, to get an array of already plucked objectId's directly from the DB. Something like this:
{
_id: "5422c33675d96d581e09e4ca",
staff:[
{
name: "Anders"
_id: "5458d0aa69d6f72418969428"
// More fields not relevant to the question...
}
],
services: [
"54578da02b1c54e40fc3d7c6",
"54578da42b1c54e40fc3d7c7",
"54578da92b1c54e40fc3d7c9"
]
}
One way of doing it is to first,
$unwind the document based on the staff field, this is done to
select the intended staff. This step is required due to the
unavailability of the $elemMatch operator in the aggregation
framework.
There is an open ticket here: Jira
Once the document with the correct staff is selected, $unwind, based on $services.
The $group, together $pushing all the services _id together in an array.
This is then followed by a $project operator, to show the intended fields.
db.clients.aggregate([
{$match:{"_id":sessId}},
{$unwind:"$staff"},
{$match:{"staff._id":reqId}},
{$unwind:"$services"},
{$group:{"_id":"$_id","services_id":{$push:"$services._id"},"staff":{$first:"$staff"}}},
{$project:{"services_id":1,"staff":1}}
])