Question

I am trying get ordered dictionaries in Pymongo. I have read it can be done with bson.son.Son. The Docs are Here

However, I can't seem to make it work. There is not much on google about it. There are some discussions on configuring pymongo first to tell it to use SON objects but no examples. A friend suggested passing a param when you do a find. He couldn't remember.

I'm able to create the SON objects. But when they get inserted into the DB and then come back out they are just plain dicts.

I'm not sure really what code example to give you because I really don't know where to start. The below snippet creates an empty SON object every time I add a new user. The 'sub_users' object was also created with SON. When I read the account document back from the DB they are just normal python dicts.

    account['sub_users'][sub_user_name] = bson.SON()
    with mongo_manager.Collection(CFG.db, 'Users') as users:
        users.save(account)

Maybe a param past to the find like this to configure? This was my friends suggestion but he could not remember.

with mongo_manager.Collection(CFG.db, 'Users') as users:                                 
    account = users.find_one({'_id': _id, 'DOC':'OrderedDict})

Any ideas?

Was it helpful?

Solution

You can use bson.son.SON or OrderedDict to store ordered dict.

And retrive data with as_class=OrderedDict option.

Here is an example:

from collections import OrderedDict
from pymongo import MongoClient
import bson

client = MongoClient()
sample_db = client['sample']
test_col = sample_db['test']

test_col.drop()

data = OrderedDict([("one", 1), ("two", 2), ("three", 3), ("four", 4)])
test_col.insert(data)
print(list(test_col.find({}, {'_id': 0}, as_class=OrderedDict)))

test_col.drop()

data = bson.son.SON([("one", 1), ("two", 2), ("three", 3), ("four", 4)])
test_col.insert(data)
print(list(test_col.find({}, {'_id': 0}, as_class=OrderedDict)))

Output:

[OrderedDict([(u'one', 1), (u'two', 2), (u'three', 3), (u'four', 4)])]
[OrderedDict([(u'one', 1), (u'two', 2), (u'three', 3), (u'four', 4)])]

OTHER TIPS

This solution above is correct for older versions of MongoDB and the pymongo driver but it no longer works with pymongo3 and MongoDB3+ You now need to add document_class=OrderedDict to the MongoClient constructor. Modifying the above answer for pymongo3 compatibility.

from collections import OrderedDict
from pymongo import MongoClient
import bson

client = MongoClient(document_class=OrderedDict)
sample_db = client['sample']
test_col = sample_db['test']

test_col.drop()

data = OrderedDict([("one", 1), ("two", 2), ("three", 3), ("four", 4)])
test_col.insert(data)
print(list(test_col.find({}, {'_id': 0})))

test_col.drop()

data = bson.son.SON([("one", 1), ("two", 2), ("three", 3), ("four", 4)])
test_col.insert(data)
print(list(test_col.find({}, {'_id': 0})))

Output:

[OrderedDict([(u'one', 1), (u'two', 2), (u'three', 3), (u'four', 4)])]
[OrderedDict([(u'one', 1), (u'two', 2), (u'three', 3), (u'four', 4)])]

A standard find() in PyMongo will not return an object who's fields are in the same order as that object, if you retrieved it via mongo shell.

This is because, the default type returned is a Dict and the order is not defined.

You can use SON as suggested. Here is how I did it. Now the field order will be respected.

This is for pymongo==3.4.0

from bson.codec_options import CodecOptions
from bson.son import SON

opts = CodecOptions(document_class=SON)
collection_son = mongo.db.collection.with_options(codec_options=opts)

collection_son.find_one({"imsid": '12345'})

In PyMongo v3.2 insert() has been deprecated and in this example it should be replaced with insert_one(). Updated code is below:

from collections import OrderedDict
from pymongo import MongoClient
import bson

client = MongoClient(document_class=OrderedDict)
sample_db = client['sample']
test_col = sample_db['test']

test_col.drop()

data = OrderedDict([("one", 1), ("two", 2), ("three", 3), ("four", 4)])
test_col.insert_one(data)
print(list(test_col.find({}, {'_id': 0})))

test_col.drop()

data = bson.son.SON([("one", 1), ("two", 2), ("three", 3), ("four", 4)])
test_col.insert_one(data)
print(list(test_col.find({}, {'_id': 0})))

Output:

[OrderedDict([(u'one', 1), (u'two', 2), (u'three', 3), (u'four', 4)])]
[OrderedDict([(u'one', 1), (u'two', 2), (u'three', 3), (u'four', 4)])]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top