MongoDB v5.0.5 reference existing field in updateMany() - mongodb

First time using MongoDB and I'm having an issue that I would appreciate some help with please.
Let's say I have a collection called "students" with documents in the collection structured as followed:
{
"_id": ObjectId("12345"),
"Name": "Joe Bloggs",
"Class_Grade": "b"
"Homework_Grade": "c",
}
I want to create an embedded document called "Grades" that contains the class and homework grade fields and applies this to every document in the collection to end up with:
{
"_id": ObjectId("12345"),
"Name": "Joe Bloggs",
"Class_Grade": "b"
"Homework_Grade": "c",
"Grades": {
"Class_Grade": "b",
"Homework_Grade": "c",
}
}
I have been trying to achieve this using updateMany() in MongoShell:
db.students.updateMany({}, {$set: {Grades: {"Class_Grade": $Class_Grade, "Homework_Grade": $Homework_grade"}}})
However, in doing so, I receive Reference Error: $Class_Grade is not defined. I have tried amending the reference to $students.Class_Grade and receive the same error.
Your advice would be greatly appreciated

There are a few mistakes in your query,
if you want to use the internal existing field's value, you need to use an update with aggregation pipeline starting from MongoDB 4.2, you need to wrap the update part in attay bracket [], as i added query.
use quotation in field name that you want to use from internal field ex: "$Class_Grade"
you have used field $Homework_grade, and in your documents it is G is capital in Grade so try to use exact field name $Homework_Grade
db.students.updateMany({},
[
{
$set: {
Grades: {
"Class_Grade": "$Class_Grade",
"Homework_Grade": "$Homework_Grade"
}
}
}
])
Playground

Related

Trying to fetch data from Nested MongoDB Database?

I am beginner in MongoDB and struck at a place I am trying to fetch data from nested array but is it taking so long time as data is around 50K data, also it is not much accurate data, below is schema structure please see once -
{
"_id": {
"$oid": "6001df3312ac8b33c9d26b86"
},
"City": "Los Angeles",
"State":"California",
"Details": [
{
"Name": "Shawn",
"age": "55",
"Gender": "Male",
"profession": " A science teacher with STEM",
"inDate": "2021-01-15 23:12:17",
"Cars": [
"BMW","Ford","Opel"
],
"language": "English"
},
{
"Name": "Nicole",
"age": "21",
"Gender": "Female",
"profession": "Law student",
"inDate": "2021-01-16 13:45:00",
"Cars": [
"Opel"
],
"language": "English"
}
],
"date": "2021-01-16"
}
Here I am trying to filter date with date and Details.Cars like
db.getCollection('news').find({"Details.Cars":"BMW","date":"2021-01-16"}
it is returning details of other persons too which do not have cars- BMW , Only trying to display details of person like - Shawn which have BMW or special array value and date too not - Nicole, rest should not appear but is it not happening.
Any help is appreciated. :)
A combination of $match on the top-level fields and $filter on the array elements will do what you seek.
db.foo.aggregate([
{$match: {"date":"2021-01-16"}}
,{$addFields: {"Details": {$filter: {
input: "$Details",
as: "zz",
cond: { $in: ['BMW','$$zz.Cars'] }
}}
}}
,{$match: {$expr: { $gt:[{$size:"$Details"},0] } }}
]);
Notes:
$unwind is overly expensive for what is needed here and it likely means "reassembling" the data shape later.
We use $addFields where the new field to add (Details) already exists. This effectively means "overwrite in place" and is a common idiom when filtering an array.
The second $match will eliminate docs where the date matches but not a single entry in Details.Cars is a BMW i.e. the array has been filtered down to zero length. Sometimes you want to know this info so if this is the case, do not add the final $match.
I recommend you look into using real dates i.e. ISODate instead of strings so that you can easily take advantage of MongoDB date math and date formatting functions.
Is a common mistake think that find({nested.array:value}) will return only the nested object but actually, this query return the whole object which has a nested object with desired value.
The query is returning the whole document where value BMW exists in the array Details.Cars. So, Nicole is returned too.
To solve this problem:
To get multiple elements that match the criteria you can do an aggregation stage using $unwind to separate the different objects into array and match by the criteria you want.
db.collection.aggregate([
{
"$match": { "Details.Cars": "BMW", "date": "2021-01-26" }
},
{
"$unwind": "$Details"
},
{
"$match": { "Details.Cars": "BMW" }
}
])
This query first match by the criteria to avoid $unwind over all collection.
Then $unwind to get every document and $match again to get only the documents you want.
Example here
To get only one element (for example, if you match by _id and its unique) you can use $elemMatch in this way:
db.collection.find({
"Details.Cars": "BMW",
"date": "2021-01-16"
},
{
"Details": {
"$elemMatch": {
"Cars": "BMW"
}
}
})
Example here
You can use $elemenMatch into query or projection stage. Docs here and here
Using $elemMatch into query the way is this:
db.collection.find({
"Details": {
"$elemMatch": {
"Cars": "BMW"
}
},
"date": "2021-01-16"
},
{
"Details.$": 1
})
Example here
The result is the same. In the second case you are using positional operator to return, as docs says:
The first element that matches the query condition on the array.
That is, the first element where "Cars": "BMW".
You can choose the way you want.

Pymongo access specific field value within nested dict

In Pymongo application, while iterating through every document of the collection, how to access a specific field value of the JSON structure?
{
"_id": {
"$oid": "5e1c2b0bacbdaehujjjbdsh"
},
"a": {
"data_type": "abc",
"data_format": "xyz",
"data_version": "1",
},
"b": "123",
"c": "345"
}
Based on the following code snippet, how do I access the value associated with the key 'data_format' which is nested within the key 'a' ---
for document in col.find():
data_format_val = document['a']['data_format'] # not working
Relatively new to Mongodb query commands.
It's possible that some of the documents of the collection may not have the key 'a'.
Try using $exists to make sure the field is present like this: Syntax: { field: { $exists: } }

pymongo db query with multiple conditions- $and $exists

An example document looks like this
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"Type": "f",
"runTime": ISODate("2016-12-21T13:34:00.000+0000"),
"data" : {
"PRICES SPOT" : [
{
"value" : 29.64,
"timeStamp" : ISODate("2016-12-21T23:00:00.000+0000")
},
{
"value" : 29.24,
"timeStamp" : ISODate("2016-12-22T00:00:00.000+0000")
},
{
"value" : 29.81,
"timeStamp" : ISODate("2016-12-22T01:00:00.000+0000")
},
{
"value" : 30.2,
"timeStamp" : ISODate("2016-12-22T02:00:00.000+0000")
},
{
"value" : 29.55,
"timeStamp" : ISODate("2016-12-22T03:00:00.000+0000")
}
]
}
}
My MongoDb has different Type of documents, I'd like to get a cursor for all of the documents that are from a time range that are of type: "f" but that actually exist. There are some documents in the database that broke the code I had previously(which did not check if PRICES SPOT existed).
I saw that I can use $and and $exists from the documentation. However, I am having trouble setting it up because of the range, and the nesting. I am using pyMongo as my python driver and also noticed here that I have to wrap the $and and $exists in quotes.
My code
def grab_forecast_cursor(self, model_dt_from, model_dt_till):
# create cursor with all items that actually exist
cursor = self._collection.find(
{
"$and":[
{'Type': 'f', 'runTime': {"$gte": model_dt_from, "$lte": model_dt_till}
['data']['PRICES SPOT': "$exists": true]}
]})
return cursor
This results in a Key Error it cannot find data. A sample document that has no PRICE SPOT looks exactly like the one I posted in the beginning, just without that respectively.
In short.. Can someone help me set up a query in which I can grab a cursor with all the documents of a certain type but that actually have respected contents nested in.
Update
I added a comma after the model_dt_till and have now a syntax error.
def grab_forecast_cursor(self, model_dt_from, model_dt_till):
# create cursor with all items that actually exist
cursor = self._collection.find(
{
"$and":[
{'Type': 'f', 'runTime': {"$gte": model_dt_from, "$lte": model_dt_till},
['data']['PRICES SPOT': "$exists": true]}
]})
return cursor
You're trying to use Python syntax to denote the path to a data structure, but the "database" want's it's syntax for the "key" using "dot notation":
cursor = self._collection.find({
"Type": "f",
"runTime": { "$gte": model_dt_from, "$lte": model_dt_till },
"data.PRICES SPOT.0": { "$exists": True }
})
You also don't need to write $and like that as ALL MongoDB query conditions are already AND expressions, and part of your statement was actually doing that anyway, so make it consistent.
Also the check for a "non-empty" array is 'data.PRICES SPOT.0' with the added bonus that not only do you know it "exists", but also that it has at least one item to process within it
Python and JavaScript are almost identical in terms of object/dict construction, so you really should be able to just follow the general documentation and the many samples here that are predominantly JavaScript.
I personally even try to notate answers here with valid JSON, so it could be picked up and "parsed" by users of any language. But here, python is just identical to what you could enter into the mongo shell. Except for True of course.
See "Dot Notation" for an overview of the syntax with more information at Query on Embedded / Nested Documents

Pymongo - Get a Nested Collection and Updating it

I want to find a nested collection in my MongoDB database and update it from Pymongo. Here is how my document tree is structured:
{
"name_list" : [
{
"_id" : ...,
"name" : "Joe Edwards",
"aliases" : [
"Allen Dingus",
"Edward McPickles"
]
},
...
]
}
The collection I wish to access is aliases. Currently, my code uses the find() function to locate the element in the name_list with the name key of "Joe Edwards". What I'm getting in return is a cursor object; that is expected. However, I cannot find a way to access the aliases collection and update it (I'm looking for something like db.name_list.find(...)[0].aliases.update(...) so that I can just manipulate aliases directly).
I've looked over the PyMongo documentation and a lot of posts on Stack Exchange as well but I found nothing similar. My temporary fix is just to access the name_list and update the whole document belonging to "Joe Edwards", but it isn't really elegant. Can someone please help?
Thanks.
MongoDB tries to align the operators of the drivers (Pymongo included) with the operators for the mongo shell. In the best case, the mongo shell operation looks exactly like the Pymongo version of the command. So if you can't find an operator that you need, chances are you may be able to find it in the Operator documentation page
Regarding your question, depending on what you want to do, there are a couple of options available to you:
Updating an existing array element
This can be done using the $ update positional operator:
db.name_list.update_one({'aliases':'Allen Dingus'}, {'$set': {'aliases.$': 'Alias 1'}})
Note the aliases.$ operation, which will update an alias of "Allen Dingus" and replace it with "Alias 1". With your example document, this will result in:
{
"_id": ...,
"name": "Joe Edwards",
"aliases": [
"Alias 1",
"Edward McPickles"
]
}
Adding a new array element
This can be done using the $push operator or the $addToSet operator:
db.test.update_one({'aliases':'Allen Dingus'}, {'$push': {'aliases': 'Alias 2'}})
This will search for an alias "Allen Dingus" and add the new entry into the array. With your example document, this will result in:
{
"_id": ...,
"name": "Joe Edwards",
"aliases": [
"Allen Dingus",
"Edward McPickles",
"Alias 2"
]
}

Mongo : Trouble updating a Mongo DB row

I am trying to update a row in Mongo DB .
I have a collection named users
db.users.find()
{ "_id" : ObjectId("50e2efe968caee13be412413"), "username" : "sss", "age" : 32 }
I am trying to update the row with the username as Erinamodobo and modify the age to 55
I have tried the below way , but its not working .
db.users.update({ "_id": "50e2efe968caee13be412413" }, { $set: { "username": "Erinamodobo" } });
Please let me know where i am making the mistake ??
Pass in the _id as an ObjectId if you're using the mongo shell, otherwise, it won't find the existing user.
db.users.update({"_id": ObjectId("50e2efe968caee13be412413")},
{ "$set" :
{ "username": "Erinamodobo", "age" : "55" }})
With this query you are updating the name itself.
Notice that the syntax of an update is the following:
db.COLLECTION.update( {query}, {update}, {options} )
where query selects the record to update and update specify the value of the field to update.
So, in your case the correct command is:
db.users.update({ "name": "Erinamodobo" }, { $set: { "age": 55 } });
However, I suggets you to read the mongodb documentation, is very well written (http://docs.mongodb.org/manual/applications/update/)
As an extension to #WiredPrairie, even though he states the right answer he doesn't really explain.
The OjbectId is not a string, it is actually a Object so to search by that Object you must supply an Ojbect of the same type, i.e. here an ObjectId:
db.users.update({ "_id": ObjectId("50e2efe968caee13be412413") }, { $set: { "username": "Erinamodobo" } });
The same goes for any specific BSON type you use from Date to NumberLong you will need to wrap the parameters in Objects of the type.