Supposed I have a mongo document that looks similar to the following:
{
'foo':1
'listOfLists' : [ [1,2],[3,4] ]
}
(Yes I am aware this isn't how it "really" looks but it should be simple enough for explanation purposes.)
If I wanted to write a query that would check to see if the listsOfLists list object contains the combination of [3,4], how could I go about doing that?
Could I do something like
collection.find({'listsOfLists' : {'$elemMatch' : [3,4] } })
collection.find({ 'listsOfLists': [3,4] }).
It's just a "direct match" on the property. MongoDB will look at each array element automatically. You don't need $elemMatch here.
If you were to use it, you need an operator expression, such as $eq:
collection.find({ 'listsOfLists': { '$elemMatch': { '$eq': [3,4] } } }).
But that of course is not required unless there are "two or more" conditions that actually need to match on the array elements. Which is what $elemMatch is actually for.
Related
I'm trying to get the documents that surround a match in pyMongo. So I would search for a string and get the matches and the entries that are around this match (using the '_index' well, index), so the user has some context on the result.
I'm trying to do it using $setWindowFields to no success, as I'm getting no results. Probably I'm using the wrong syntax?. This is the aggregation that I'm trying:
show_near = ([{'$setWindowFields':{
'partitionBy':None,
'sortBy': {'_index':1},
'output':{
'nearIds':{
'$addToSet':'$_id',
'window':{'documents':[-2,2]}
}
}
}
},
{
'$match':
{field:{'$regex':f'({s})'}}
},
{'$lookup':
{'from':'collection',
'localField':'nearIds',
'foreignField':'_id',
'as':'nearDocs'}
},
{'$unwind':'$nearDocs'},
{'$replaceRoot':{
'newRoot':'$nearDocs'}}])
cursor = self.collection.aggregate(show_near)
Where 's' is the string I want to match and '_index' is the order of the entries.
Any idea? Maybe there is another method to do this? This feature looks perfect for what I want, but maybe I'm mistaken and there is another way. I've tried going back and forth with $gte and $lte, but is not feasible when results start to pile up.
Thanks!
I have a question about how to manipulate a document in PyMongo to have it list all of its current keys, and I'm not quite sure how to do it. For example, if I had a document that looked like this:
{
"_id" : ObjectID("...")
"name": ABCD,
"info": {
"description" : "XYZ",
"type" : "QPR"
}
}
and I had a variable "document" that had this current document as its value, how could I write code to print the three keys:
"_id"
"name"
"info"
I don't want it to list the values, simply the names. The motivation for this is that the user would type one of the names and my program would do additional things after that.
As mentioned in the documentation:
In PyMongo we use dictionaries to represent documents.
So you can get all keys using .keys():
print(document.keys())
Using Python we can do the following which is to fetch all the documents in a variable as mydoc
mydoc = collections.find()
for x in mydoc:
l=list(x.keys())
print(l)
Using this we can get all the keys as a list and then we can use them for further user's need
the document is a python dictionary so you can just print its keys
e.g.
document = db.collection_name.find_one()
for k in document:
print(k)
it is quite complicated with the nested documents, but please let me know if you all has any solutions, thanks.
To summarize, I would like to:
Add a value to an array (without duplication), and the array is within a sub-document, that is within an array of a main document. (Document > Array > Subdoc > Array)
The subdocument itself might not exist, so if not exist, the subdocument itself need to be added, i.e. UpSert
The command be the same for both action (i.e. adding of value to subdoc's array, and adding of subdoc)
I have tried the following, but it doesn't work:
key = {'username':'user1'}
update1 = {
'$addToSet':{'clients':{
'$set':{'fname':'Jessica'},
'$set':{'lname':'Royce'},
'$addToSet':{'cars':'Toyota'}
}
}
}
#the document with 'Jessica' and 'Royce' does not exist in clients array, so a new document should be created
update2 = {
'$addToSet':{'clients':{
'$set':{'fname':'Jessica'},
'$set':{'lname':'Royce'},
'$addToSet':{'cars':'Honda'}
}
}
}
#now that the document with 'Jessica' and 'Royce' already exist in clients array, only the value of 'Honda' should be added to the cars array
mongo_collection.update(key, update1 , upsert=True)
mongo_collection.update(key, update2 , upsert=True)
error message: $set is not valid for storage
My intended outcome:
Before:
{
'username':'user1',
'clients':[
{'fname':'John',
'lname':'Baker',
'cars':['Merc','Ferrari']}
]
}
1st After:
{
'username':'user1',
'clients':[
{'fname':'John',
'lname':'Baker',
'cars':['Merc','Ferrari']},
{'fname':'Jessica',
'lname':'Royce',
'cars':['Toyota']}
]
}
2nd After:
{
'username':'user1',
'clients':[
{'fname':'John',
'lname':'Baker',
'cars':['Merc','Ferrari']},
{'fname':'Jessica',
'lname':'Royce',
'cars':['Toyota','Honda']}
]
}
My understanding says you won't be able to completely achieve intended solution directly. You can very well do nested update or upsert but duplication check probably not, as there is no direct way to check item contains in a array document.
For upsert operation you can refer mongodb update operation doc or bulk operation. And for duplication probably you need to have separate logic to identify.
I am a noob at Python and MongoDB and would really appreciate your help with my problem. My collection in MongoDB looks like this:
{
"Segments" : [
{
Devices : [
"IP" : "",
"Interfaces" :
[
{
"Name" :""
}
],
],
"DeviceName" : "",
"SegmentName" : ""
}
]
}
I have an object like so:
Node Details: {'node:98a': ['Sweden', 'Stockholm', '98a-3470'], 'node:98b': ['Denmark', 'Copenhagen', '98b-3471', '98b-3472']}
I need to update the 'Name' within 'Interfaces' part in the collection above, with values from the Node Details dictionary. I have tried using $set, $addToSet, $push etc., but nothing is helping. I have already added the Segment and DeviceName information.
The output should be as follows:
{
"Segments" : [
{
Devices : [
{
"Interfaces" :
[
{
"Name" :"98a-3470"
}
],
"DeviceName" : "node:98a",
}
{
"Interfaces" :
[
{
"Name" :"98b-3471"
},
{
"Name" :"98b-3472"
}
],
"DeviceName" : "node:98b",
}
],
"SegmentName" : "segmentA"
}
]
}
Any help would be greatly appreciated. I have tried a lot in the MongoDB shell and also on Google, but to no avail. Thank you all.
Regards,
trupsster
[[ EDITED ]]
Okay, here is what I have got so far after continuing to poke around after posing the question: I used the following query in MongoDB shell:
db.test.mycoll.update({'Segments.SegmentName':'segmentA','Segments.Devices.Name':'node:98a'}, {$set: {"Segments.$.Devices.0.Interfaces.Name" : "98b-3470"}})
Now this inserted in the correct place as per my 'schema', but when I try to add the second interface, it simply replaces the earlier one. I tried using $push (complained about it not being an array), and $addToSet (showed another error), but none helped. Can you please help me from this point on?
Thanks,
trupsster
[[ Edited again ]]
I found the solution! Here is what I did:
To add an interface to an existing device:
db.test.mycoll.update({'Segments.SegmentName':'segmentA','Segments.Devices.Name':'node:98a'}, {$addToSet: {"Segments.$.Devices.0.Interfaces.Name" : "98a-3471"}})
Now, to append to the dict with a new 'Name' within the array 'Interfaces':
db.test.mycoll.update({'Segments.SegmentName':'segmentA','Segments.Devices.Name':'node:98a'}, {$addToSet: {"Segments.$.Devices.0.Interfaces" : {"Name" : "98a-3472"}}})
As you can see, I used $addToSet.
Now, next step was to add the same information (with different values) to 2nd device, which was done like so:
db.test.mycoll.update({'Segments.SegmentName':'segmentA','Segments.Devices.Name':'node:98b'}, {$addToSet: {"Segments.$.Devices.1.Interfaces" : {"Name" : "98b-3473"}}})
So that was it! I am so chuffed with myself! Thank you all who took time to read my problem. I hope my solution will help someone.
Regards,
trupsster
You did not say what you actually tried. To access a sub-document inside an array, you need to use dot notation with numeric indices. So to address the Name field in your example:
Segments.Devices.0.Interfaces.0.Name
Did you try that? Does it work?
I started reading about underscore.js today, it is a library for javascript that adds some functional programming goodies I'm used to using in Python. One pretty cool shorthand method is pluck.
Indeed in Python I often need to pluck out some specific attribute, and end up doing this:
users = [{
"name" : "Bemmu",
"uid" : "297200003"
},
{
"name" : "Zuck",
"uid" : "4"
}]
uids = map(lambda x:x["uid"], users)
If the underscore shorthand is somewhere in Python, this would be possible:
uids = pluck(users, "uid")
It's of course trivial to add, but is that in Python somewhere already?
Just use a list comprehension in whatever function is consuming uids:
instead of
uids = map(operator.itemgetter("uid"), users)
foo(uids)
do
foo([x["uid"] for x in users])
If you just want uids to iterate over, you don't need to make a list -- use a generator instead. (Replace [] with ().)
For example:
def print_all(it):
""" Trivial function."""
for i in it:
print i
print_all(x["uid"] for x in users)
From funcy module (https://github.com/Suor/funcy) you can pick pluck function.
In this case, provided that funcy is available on your host, the following code should work as expected:
from funcy import pluck
users = [{
"name" : "Bemmu",
"uid" : "297200003"
},
{
"name" : "Zuck",
"uid" : "4"
}]
uids = pluck("uid", users)
Pay attention to the fact that the order of arguments is different from that used with underscore.js