When I execute "db.abc.find()" in mongo shell it returns the following answer:
db.abc.find()
{ "_id" : ObjectId("56a942bfec926681f17f09b6"), "name" : "foo" }
But when I execute the same command via PyMongo's eval method I receive a different answer:
>>> from pymongo import MongoClient
>>> client = MongoClient()
>>> db = client.test
>>> db.eval('db.abc.find()')
{u'_special': False, u'_options': 0.0, u'_ns': u'test.abc', u'_db': {u'_mongo': {u'slaveOk': False, u'host': u'EMBEDDED'}, u'_name': u'test'}, u'_skip': 0.0, u'_numReturned': 0.0, u'_query': {}, u'_limit': 0.0, u'_mongo': {u'slaveOk': False, u'host': u'EMBEDDED'}, u'_collection': {u'_shortName': u'abc', u'_db': {u'_mongo': {u'slaveOk': False, u'host': u'EMBEDDED'}, u'_name': u'test'}, u'_mongo': {u'slaveOk': False, u'host': u'EMBEDDED'}, u'_fullName': u'test.abc'}, u'_cursor': None, u'_fields': None, u'_batchSize': 0.0}
What this is happening?
How to fix it?
Well, eval() is definitely evil and is actually deprecated.
You should wrap the code into a function to make it work:
from bson import Code
db.eval(Code('function () { return db.abc.find(); }'))
Why don’t you just call db.abc.find() from Python and get you document(s) from cursor?
[doc for doc in db.abc.find()]
Or still:
db.abc.find_one()
Related
I know we can use Mongo shell to enable(or disable) change streams for Amazon Document DB. Is it possible to enable the change streams from AWS Console or MongoDB Driver?
AWS Console: No. I don't think this is controlled by DocumentDB cluster params.
MongoDB Drivers: Yes
DocumentDB Change Streams uses runCommand method to enable and disable changestreams.
In the documentation, the command used is adminCommand which is just calling runCommand on admin database. Since almost all drivers support running commands over database, you should be able to enable/disable change streams using any driver.
Following code uses pymongo to enable changestreams:
>>> from pymongo import MongoClient
>>> client = MongoClient("mongodb://<username>:<password>#xxxxxx.xxxxxx.us-east-1.docdb.amazonaws.com:27017/test_db?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0&readPreference=secondaryPreferred")
>>> client['admin'].command('aggregate', 1, pipeline=[{'$listChangeStreams': 1}], cursor={})
{'waitedMS': 0, 'cursor': {'firstBatch': [], 'id': 0, 'ns': 'admin.$cmd'}, 'ok': 1.0}
>>> client['admin'].command('modifyChangeStreams', 1, database='bar', collection='foo', enable=True)
{'ok': 1.0}
>>> client['admin'].command('aggregate', 1, pipeline=[{'$listChangeStreams': 1}], cursor={})
{'waitedMS': 0, 'cursor': {'firstBatch': [{'database': 'bar', 'collection': 'foo'}], 'id': 0, 'ns': 'admin.$cmd'}, 'ok': 1.0}
You can enable changestreams on your Amazon DocumentDB cluster using the modifyChangeStream API. To enable/disable changestreams using the mongo shell -
//Enable change streams for the collection "foo" in database "bar"
db.adminCommand({modifyChangeStreams: 1,
database: "bar",
collection: "foo",
enable: true});
//Enable change streams for all collections in database "bar"
db.adminCommand({modifyChangeStreams: 1,
database: "bar",
collection: "",
enable: true});
//Enable change streams for all collections in all databases in a cluster
db.adminCommand({modifyChangeStreams: 1,
database: "",
collection: "",
enable: true});
To enable change streams from your application, you can use #krg265's suggestion.
I am trying to run a python code to update a collection with arrays. The below statement give error wtih pymongo . Please guide
db.students.update({}, {'$set': {"grades.$[element]": 100}}, {'multi': true, 'arrayFilters': [{"element": { '$gte': 100}}]} )
tried : multi=True tried : multi:True
I am getting the below error :
common.validate_boolean("upsert", upsert)
File "F:\Program Files\Python3.7\lib\site-packages\pymongo\common.py", line 159, in validate_boolean
raise TypeError("%s must be True or False" % (option,))
TypeError: upsert must be True or False
Pymongo's syntax is a tad different than Mongo's syntax, you should write it like this:
db.students.update({}, {'$set': {"grades.$[element]": 100}}, multi=True, array_filters=[{"element": {'$gte': 100}}])
Also update is deprecated, and in your case you should use update_many instead.
db.students.update_many({}, {'$set': {"grades.$[element]": 100}}, array_filters=[{"element": {'$gte': 100}}])
I want to check whether a role exists in a mongodb, before I create a new one . I tried to do it the following way:
result = self.client[database].command("getRole", name=app_name)
Unfortunately I get the following error:
msg = msg or "%s"
raise OperationFailure(msg % errmsg, code, response)
pymongo.errors.OperationFailure: no such command: 'getRole', bad cmd: '{ getRole: 1, name: "test" }'
I am referring to this database command: https://docs.mongodb.com/manual/reference/method/db.getRole/
For createRole I can execute the command: https://docs.mongodb.com/manual/reference/method/db.createRole/#db.createRole
Shell methods db.* are different from Database commands.
Using the roleInfo command you can get information for a particular role.
db.command({
'rolesInfo': {'role': 'noremove','db': 'test'},
'showPrivileges': True, 'showBuiltinRoles': True
})
The above command returns a result in this form when there is a matching role:
{'ok': 1.0,
'roles': [{'db': 'test',
'inheritedPrivileges': [{'actions': ['find', 'insert', 'update'],
'resource': {'collection': 'test', 'db': 'test'}}],
'inheritedRoles': [],
'isBuiltin': False,
'privileges': [{'actions': ['find', 'insert', 'update'],
'resource': {'collection': 'test', 'db': 'test'}}],
'role': 'noremove',
'roles': []}]}
When there is no matching role, you get this result:
{'ok': 1.0, 'roles': []}
Checking that a role exists falls to checking for the length of the "roles" list in the returned result as follow:
noremove_role = db.command({
'rolesInfo': {'role': 'noremove','db': 'test'},
'showPrivileges': True, 'showBuiltinRoles': True
})
if not len(noremove_role['roles']):
# create role
pass
Is there a better way?
Yes, in keeping with ask forgiveness not permission philosophy, create the role and handle the resulting exception from trying to add an existing role.
from pymongo.errors import DuplicateKeyError
import logging
logger = logging.getLogger()
try:
db.command(
'createRole', 'noremove',
privileges=[{
'actions': ['insert', 'update', 'find'],
'resource': {'db': 'test', 'collection': 'test'}
}],
roles=[])
except DuplicateKeyError:
logger.error('Role already exists.')
pass
I have a db with a bunch of collections.
Some of them have 'status' and some don't.
How can I insert 'status':'pending' into the collections that lack 'status' - but not overwrite the collections that already have a status?
Using pymongo / flask / python 2.7
I tried this:
orders = monDB.find('order')
for order in orders:
if not order['status']:
monDB.update('order', {'status':'pending'})
print 'success'
But nothing happens. What am I doing wrong?
Use $exists to check if the field exists and $set to create it if it doesn't. Assuming monDB is your collection:
monDB.update({'status': {'$exists' : False}}, {'$set' : {'status': 'pending'}}, multi=True)
In the mongo shell:
> db.myQueue.update({status: {$exists: false}}, {$set : {status: 'pending'}}, false, true)
See http://docs.mongodb.org/manual/reference/operators/ and http://docs.mongodb.org/manual/applications/update/#crud-update-update.
Why does this work:
# mongo dbname
MongoDB shell version: 1.8.3
connecting to: nextmuni_staging
> db.collection.find()
{ "foo" : "bar" }
> bye
While this does not work:
# mongo localhost/dbname --eval 'db.collection.find()'
MongoDB shell version: 1.8.3
connecting to: localhost/dbname
DBQuery: dbname.collection -> undefined
It should be exactly the same, no?
Thanks!
The return val of db.collection.find() is a cursor type. Executing this command from within the shell will create a cursor and show you the first page of data. You can start going through the rest by repeating the 'it' command.
I think the scope of variables used during the execution of an eval'd script is only for the lifetime of the script (data can be persisted into collections of course) so once the script terminates those cursor variables no longer exist and so you would be able to send another eval script to page the data. So the behaviour you get during a shell session wouldn't really work from an eval script.
To get close to the behaviour you could run something like this:
mongo dbname --eval "db.collection.find().forEach(printjson)"
That shows you that the command does execute and produce a cursor which you can then iterate over sending the output to stdout.
Edit: I think the point I was trying to make was that the command you are issuing is working its just the output is not what you expect.
The printjson functions covers a lot of ground when scripting with mongo --eval '...'. Rather than chaining .forEach you can simply wrap your call.
$ mongo --eval 'db.stats_data.stats()' db_name
MongoDB shell version: 2.4.14
connecting to: db_name
[object Object]
$ mongo --eval 'db.stats_data.stats().forEach(printjson)' db_name
MongoDB shell version: 2.4.14
connecting to: db_name
Tue Jan 10 15:32:11.961 TypeError: Object [object Object] has no method 'forEach'
$ mongo --eval 'printjson(db.stats_data.stats())' db_name
MongoDB shell version: 2.4.14
connecting to: db_name
{
"ns" : "db_name.stats_data",
"count" : 5516290,
"size" : 789938800,
"avgObjSize" : 143.20110073980882,
"storageSize" : 1164914688,
"numExtents" : 18,
"nindexes" : 3,
"lastExtentSize" : 307515392,
"paddingFactor" : 1.0000000000000457,
"systemFlags" : 1,
"userFlags" : 0,
"totalIndexSize" : 1441559616,
"indexSizes" : {
"_id_" : 185292688,
"owner_id_key_idx" : 427678384,
"onwer_metric_key_idx" : 828588544
},
"ok" : 1
}