Saving python list in mongodb - python

I'm trying to save a python list to mongodb using pymongo, but the saved document has a null value instead of the list.
db.testcoll.save({"test" : [12345]})
results in
{ "_id" : ObjectId("50b53e7b71340f4ad774897a"), "test" : null }
A couple of other questions suggested using a dictionary instead, but dict values also result in null. Both array and dict values work fine when entered directly in the mongo client. Is this a known gap in the pymongo client?

Works well on my pymongo 2.2.1. That must have been an issue in the eariler pymongo releases. Try to upgrage it.

Related

Python and MongoDB Convert data type from string to long

I am new to mongodb and pymongo. Have a small clarification in converting the field datatype from string to long. Fieldname = sc_2g
The following is working fine using mongoshell.
db.collection.aggregate({$set: {sc_g: { $toLong: "$sc_g" }}},{$out:"collection"})
but i need equivalent in python. Can anyone help..?
You can use aggregation too in pymongo.
pipeline = {"$set": {"sc_g": { "$toLong": "$sc_g" }}}, {"$out":"collection"}
list(db.collection.aggregate(pipeline))

including a NumberInt in a dict for pymongo

I need to load a list of dicts (see below) into a mongoDB. Within mongo, you have to define an int type as NumberInt(). Python doesn't recognize this as a valid type for a dict. I've found pages on custom encoding for pymongo that don't actually do what I need. I'm totally stuck. Someone has to have encountered this before!
Need to insert a list of dicts like this into mongoDB from python.
agg = {
'_id' : unique_id_str,
'total' : NumberInt(int(total)),
'mode' : NumberInt(int(mymode))
}
You should be able to just insert the dict with an int, I've never needed to use NumberInt to insert documents using pymongo.
Also, fwiw, folks at mongodb told me that letting mongo create the _id itself tends to be more efficient but obviously it may work better for you to define in your case.
agg = {
'_id' : unique_id_str,
'total' : int(total),
'mode' : int(mymode)
}
should work

ObjectID generated by server on pymongo

I am using pymongo (python module for mongodb).
I want the ObjectID to be created automatically by the server, however it seems to be created by pymongo itself when we don't specify it.
The problem it raises is that I use ObjectID to sort by time (by just sorting by the _id field). However it seems that it is using the time set on each computer so we cannot truly rely on it.
Any idea on how to solve this problem?
If you call save and pass it a document without an _id field, you can force the server to add the _id instead of the client by setting the (enigmatically-named) manipulate option to False:
coll.save({'foo': 'bar'}, manipulate=False)
I'm not Python user but I'm afraid there's no way to generate _id by server. For performance reasons _id is always generated by driver thus when you insert a document you don't need to do another query to get the _id back.
Here's a possible way you can do it by generating a int sequence _id, just like the IDENTITY ID of SqlServer. To do this, you need to keep a record in you certain collection for example in my project there's a seed, which has only one record:
{_id: ObjectId("..."), seqNo: 1 }
The trick is, you have to use findAndModify to keep the find and modify in the same "transaction".
var idSeed = db.seed.findAndModify({
query: {},
sort: {seqNo: 1},
update: { $inc: { seqNo: 1 } },
new: false
});
var id = idSeed.seqNo;
This way you'll have all you instances get a unique sequence# and you can use it to sort the records.

Pymongo find if value has a datatype of NumberLong

I'm using the Pymongo driver and my documents look like this:
{
"_id" : ObjectId("5368a4d583bcaff3629bf412"),
"book_id" : NumberLong(23302213),
"serial_number" : '1122',
}
This works because the serial number is a string:
find_one({"serial_number": "1122"})
However, this doesn't:
find_one({"book_id": "23302213"})
Obviously its because the book_id has a datatype of NumberLong. How can execute the find method based on this datatype?
==================================================
Update:
Still can't get this to work, I can only find string values. Any advise would be much appreciated.
You need to ensure your data types are matching. MongoDB is strict about types. When you execute this:
find_one({"book_id": "23302213"})
you are asking MongoDB for documents with book_id equal to "23302213". As you are not storing the book_id as type string but as type long the query needs to respect that:
find_one({"book_id": long(23302213)})
If, for some reason, you have the ID as string in your app this would also work:
find_one({"book_id": long("23302213")})
Update
Just checked it (MacOS 64bit, MongoDB 2.6, Python 2.7.5, pymongo 2.7) and it works even when providing an integer.
Document in collection (as displayed by Mongo shell):
{ "_id" : ObjectId("536960b9f7e8090e3da4e594"), "n" : NumberLong(222333444) }
Output of python shell:
>>> collection.find_one({"n": 222333444})
{u'_id': ObjectId('536960b9f7e8090e3da4e594'), u'n': 222333444L}
>>> collection.find_one({"n": long(222333444)})
{u'_id': ObjectId('536960b9f7e8090e3da4e594'), u'n': 222333444L}
You can use $type:
http://docs.mongodb.org/manual/reference/operator/query/type/
INT is 16, BIGINT is 18

How do I update a Mongo document after inserting it?

Let's say I insert the document.
post = { some dictionary }
mongo_id = mycollection.insert(post)
Now, let's say I want to add a field and update it. How do I do that? This doesn't seem to work.....
post = mycollection.find_one({"_id":mongo_id})
post['newfield'] = "abc"
mycollection.save(post)
In pymongo you can update with:
mycollection.update({'_id':mongo_id}, {"$set": post}, upsert=False)
Upsert parameter will insert instead of updating if the post is not found in the database.
Documentation is available at mongodb site.
UPDATE For version > 3 use update_one instead of update:
mycollection.update_one({'_id':mongo_id}, {"$set": post}, upsert=False)
mycollection.find_one_and_update({"_id": mongo_id},
{"$set": {"newfield": "abc"}})
should work splendidly for you. If there is no document of id mongo_id, it will fail, unless you also use upsert=True. This returns the old document by default. To get the new one, pass return_document=ReturnDocument.AFTER. All parameters are described in the API.
The method was introduced for MongoDB 3.0. It was extended for 3.2, 3.4, and 3.6.
I will use collection.save(the_changed_dict) this way. I've just tested this, and it still works for me. The following is quoted directly from pymongo doc.:
save(to_save[, manipulate=True[, safe=False[, **kwargs]]])
Save a document in this collection.
If to_save already has an "_id" then
an update() (upsert) operation is performed and
any existing document with that "_id" is
overwritten. Otherwise an insert() operation is performed. In this
case if manipulate is True an "_id" will be added to to_save and this
method returns the "_id" of the saved document. If manipulate is False
the "_id" will be added by the server but this method will return
None.
According to the latest documentation about PyMongo titled Insert a Document (insert is deprecated) and following defensive approach, you should insert and update as follows:
result = mycollection.insert_one(post)
post = mycollection.find_one({'_id': result.inserted_id})
if post is not None:
post['newfield'] = "abc"
mycollection.save(post)
This is an old question, but I stumbled onto this when looking for the answer so I wanted to give the update to the answer for reference.
The methods save and update are deprecated.
save(to_save, manipulate=True, check_keys=True, **kwargs)ΒΆ Save a
document in this collection.
DEPRECATED - Use insert_one() or replace_one() instead.
Changed in version 3.0: Removed the safe parameter. Pass w=0 for
unacknowledged write operations.
update(spec, document, upsert=False, manipulate=False, multi=False,
check_keys=True, **kwargs) Update a document(s) in this collection.
DEPRECATED - Use replace_one(), update_one(), or update_many()
instead.
Changed in version 3.0: Removed the safe parameter. Pass w=0 for
unacknowledged write operations.
in the OPs particular case, it's better to use replace_one.

Categories

Resources