Retrieve a single property from document - python

Good day everyone.
Suppose we have a collection and a document which looks something like this:
test_doc = {
"ID" : "123",
"a" : [
{
'x' : "/",
'y' : "2000",
'z' : "1000"
},
{
'x' : "/var",
'y' : "3500",
'z' : "3000"
}
]
}
What i need is to retrieve a single property a.z .
In MongoDB i'm using the following query:
db.testcol.find({"ID":"123","a.x":"/"},{'a.z':1})
which returns this:
{ "_id" : ObjectId("skipped"), "a" : [ { "z" : "1000" }, { "z" : "3000" } ] }
As you can see it returns all the z properties, but i need only the first one or the second when condition is {"ID":"123","a.x":"/var"}
So, the question is: how do i get a single property in this situation? Is it just a matter of bad design or should i somehow process the returned document in code (python)? Any suggestions will be much appreciated.

In MongoDB 2.0 and older, this is not possible. What you want to do is return a specific element of the array - but that is not what your projection is actually doing, it will just return the whole array and then the z element of each one.
However, with 2.2 (rc2 as of writing this answer), things have gotten a bit better. You can now use $elemMatch as part of your projection (see SERVER-2238 for details) so that you only pull back the required array element. So, try something like this:
db.foo.find({"ID":"123",'a':{$elemMatch:{'x':"/"}}},{_id : 0, 'a.$': 1})
//returns
{ "a" : [ { "x" : "/", "y" : "2000", "z" : "1000" } ] }
Or, just use $elemMatch in the projection itself, which you may think is cleaner:
db.foo.find({"ID":"123"},{_id : 0, 'a':{$elemMatch:{'x':"/"}}})
//returns
{ "a" : [ { "x" : "/", "y" : "2000", "z" : "1000" } ] }
So, now, at least the array returned is only the one containing only the entries you want and you can simply reference the relevant z element (elemMatch projections on a subdocument are not yet supported).
Last but not least, in 2.2 we have the aggregation framework, and one of the things it can do (with the $project operator, is to reshape your documents and change sub documents and array elements into top level arrays. To get your desired result, you would do something like this:
db.foo.aggregate(
{$match : {"ID":"123"}},
{$unwind : "$a"},
{$match : {"a.x":"/"}},
{$project : {_id : 0, z : "$a.z"}}
)
The result looks like this:
{ "result" : [ { "z" : "1000" } ], "ok" : 1 }

Related

Mongodb Python: How to use $push overwrites existing value rather than adds to the end of an array within a document

I have a document like this
{ "_id" : 23, "local_id" : 1234, "global_id" : [ "P123", "P345" ] }
If I want to $push new value to the array has the key “global_id” then I can do this
collection.update_one({‘local_id’: l_pid}, {’$push’: {‘global_id’: "P678"}})
and the document looks sth like this , for example : (push P678 to the array)
{ “_id” : 23, “local_id” : 1234, “global_id” : [ “P123”, “P345”,
“P678” ] }
But next time when the same key of “global_id” comes in it keeps appending to the end of array like : (this time the same P678 comes in)
{ “_id” : 23, “local_id” : 1234, “global_id” : [ “P123”, “P345”,
“P678” , “P678”] }
I want it to overwrite to existing value, and the array has to have unique value, the value can’t be the same.
How can I do it?
Thanks
Base on #rickhg12hs answer, use $addToSet solve my issue
collection.update_one({‘local_id’: l_pid}, {’$addToSet’: {‘global_id’: "P678"}})

How to use PyMongo find() to search nested array attribute?

Using PyMongo, how would one find/search for the documents where the nested array json object matches a given string.
Given the following 2 Product JSON documents in a MongoDB collection..
[{
"_id" : ObjectId("5be1a1b2aa21bb3ceac339b0"),
"id" : "1",
"prod_attr" : [
{
"name" : "Branded X 1 Sneaker"
},
{
"hierarchy" : {
"dept" : "10",
"class" : "101",
"subclass" : "1011"
}
}
]
},
{
"_id" : ObjectId("7be1a1b2aa21bb3ceac339xx"),
"id" : "2",
"prod_attr" : [
{
"name" : "Branded Y 2 Sneaker"
},
{
"hierarchy" : {
"dept" : "10",
"class" : "101",
"subclass" : "2022"
}
}
]
}
]
I would like to
1. return all documents where prod_att.hierarchy.subclass = "2022"
2. return all documents where prod_attr.name contains "Sneaker"
I appreciate the JSON could be structured differently, unfortunately that is not within my control to change.
1. Return all documents where prod_attr.hierarchy.subclass = "2022"
Based on the Query an Array of Embedded Documents documentation of MongoDB you can use dot notation concatenating the name of the array field (prod_attr), with a dot (.) and the name of the field in the nested document (hierarchy.subclass):
collection.find({"prod_attr.hierarchy.subclass": "2022"})
2. Return all documents where prod_attr.name contains "Sneaker"
As before, you can use the dot notation to query a field of a nested element inside an array.
To perform the "contains" query you have to use the $regex operator:
collection.find({"prod_attr.name": {"$regex": "Sneaker"}})
Another option is to use the MongoDB Aggregation framework:
collection.aggregate([
{"$unwind": "$prod_attr"},
{"$match": {"prod_attr.hierarchy.subclass": "2022"}}
])
the $unwind operator creates a new object for each object inside the prod_attr array, so you will have only nested documents and no array (check the documentation for details).
The next step is the $match operator that actually perform a query on the nested object.
This is a simple example but playing with the Aggregators Operators you have a lot of flexibility.

Adding new number of dictionary based on the value of other dictionaries

My main objective is as below:
(1) Calculate the number of "id", "id_value". Then find what which value is not present in "id_value" based on "id". Example, "id" = [1,2,3,4], "id_value"=[1,2]. Then add a new dictionary of "id_value" into the main({"id_value":3},{"id_value":4}).
I tried to use set to find the difference between value within these two variables but with error. Could anyone please assist me.
main = [ {
"y" : {
"a" : [ {
"u" : [ {
"y" : 0
"x" : [ {
"check" : {
"value" : 0
My code is as below:
forid= []
forvalueid= []
for x in main[0]["x"]:
for y in x["example"]:
forid.append(y["id"])
I am getting error for "lambda cannot contain assignment"... can I know why?
* I solved this Thank you for commenting
Not sure if that will produce what you intend, but this is the lambda free version of your current code:
def func(x):
y["id_value"] = x
map(func, addvalue(forvalueid))

MongoDB how do I make an array with custom keys containing subdocument

Alright so using Python and MongoDB I am trying to embed a subdocument within an array with a custom key value in the array. I was playing around with all sorts of different ways to do this and I couldn't figure out what I was doing wrong so I temporarily settled on the working code below. Numerous attempts always lead to the error:
in _check_write_command_response
raise OperationFailure(error.get("errmsg"), error.get("code"), error) pymongo.errors.OperationFailure: The dotted field 'first.rule'
in 'followedBy..first.rule' is not valid for storage.
Code:
citizens.update(
{"_id" : userPush},
{"$push": {"followedBy":[field[1], field[2], field[3], field[0]]}})
Produces:
"_id" : ObjectId("5…asfd"),
"uName" : "tim0",
"fName" : "tim",
"lName" : "lost",
"pic" : null,
"bio" : "I <3 MongoDB",
"followedBy" : [
[
"BobTheBomb",
"bobby",
"knight",
NumberInt(2)
],
[
"Robert",
"DROP",
"TABLE",
NumberInt(6)
]
This is what I want:
"_id" : ObjectId("5…asfd"),
"uName" : "tim0",
"fName" : "tim",
"lName" : "lost",
"pic" : null,
"bio" : "I <3 MongoDB",
"followedBy" : [
"BobTheBomb": {
"fName" : "bobby",
"lName" : "knight",
"uID" : NumberInt(2)
},
"Robert": {
"fName" : " DROP ",
"lName" : " TABLE ",
"uID" : NumberInt(6)
}
]
You will need to build that data structure, currently you are saying that "followedBy" is only a list.
so try:
citizens.update(
{"_id" : userPush},
{"$push": {"followedBy":{field[1]: { "fName":field[2], "lName":field[3], "uID":field[0]}}}})
Remove the list and replace it with a dict.
I do hope this helps.
I have realised that I have not given you valid json, I have tested this:
citizens.update(
{"_id" : userPush},
{$push:
{"followedBy":
[
{field[1]:
{ "fName": field[2], "lName": field[3], "uID": field[0]}
}
]
}
})
And it worked...
You might find that the error is caused by the modifier you are using, I found the following on a blog:
MongoDB provides several different modifiers you can use to update documents in place, including the following (for more details see updates):
- $inc Increment a numeric field (generalized; can increment by any number)
- $set Set certain fields to new values
- $unset Remove a field from the document
- $push Append a value onto an array in the document
- $pushAll Append several values onto an array
- $addToSet Add a value to an array if and only if it does not already exist
- $pop Remove the last (or first) value of an array
- $pull Remove all occurrences of a value from an array
- $pullAll Remove all occurrences of any of a set of values from an array
- $rename Rename a field
- $bit Bitwise updates
you might find that because you are inserting many items that you would rather want to use $pushAll or $addToSet rather than $push... Just a speculation...

Reach to array subdocument in mongodb

How to reach to the last element of scores array with score value: 64.8 ???
I try to use $pull operator,but I have 200 documents like this form. So, I can't make use of exact value of latest element of scores array.
{
"_id" : 198,
"name" : "Timothy Harrod",
"scores" : [
{
"type" : "exam",
"score" : 11.9075674046519
},
{
"type" : "quiz",
"score" : 20.51879961777022
},
{
"type" : "homework",
"score" : 55.85952928204192
},
{
"type" : "homework",
"score" : 64.85650354990375
} ]
}
Easiest way would be to put that document into a python dict if it's in string format:
import json
doc_json = json.loads(doc_string)
Then to access the LAST element of the scores array, just do:
last_element = doc_json['scores'][-1]
The ['scores'] tells us to get the 'scores' array from our json_doc, and the [-1] tells us to access the last element of this 'scores' array. From there, if you wanted the score from the last_element, you would just do:
last_score = last_element['score']
You could have also gotten it straight by doing:
last_score = doc_json['scores'][-1]['score']

Categories