(py)Mongo查询返回按相关字段分组的匹配值?

时间:2013-12-03 23:12:45

标签: python mongodb python-3.x pymongo

(随意重命名问题)

我在mongo集合中有一些类似于:

的数据
{
    ...
    'tree_id': 'ABC123',
    'serial_id': 'XYZ789',
    ...
}

从json请求中,我最终获得了大量的serial_id值(称为wantedIDs)。我需要创建一个类似于:

的字典(在python中)
{'tree_id_1': [...all the serial_ids that matched tree_id_1...],
 'tree_id_2': [...all the serial_ids that matched tree_id_2...],
 ...}

我可以做类似的事情:

myMap = defaultdict(list)
for doc in client.database.collection.find({'serial_id': {'$in': wantedIDs}}):
    myMap[doc['tree_id']].append(doc['serial_id'])

我想知道是否有更多的pythonic或mongorific方式这样做?我是一个非常新手的pymongo用户。我也很好奇$in不是要走的路,如果有的话。 wantedIDs的列表可能非常大。

2 个答案:

答案 0 :(得分:0)

  

我想知道是否有更多的pythonic或mongorific方式这样做?

是的,有。寻找MongoDB Aggregation Framework。 Pymongo集合有一种aggregate方法用于您尝试执行的操作(请参阅更新)。

  

我也很好奇,如果有的话,$ in是不可行的。

好吧,我不完全确定$in,但我认为您可以将数据放入其中,直到达到BSON文档大小限制为16兆字节。无论如何,我建议不要接近这个限制,因为它可能会减慢你的应用程序和MongoDB服务器的速度。

<强>更新

聚合无法解决潜在的大$in问题。 find使用的相同查询将进入聚合管道的$match运算符,因此适用相同的限制。

如果您检测到serial_id的列表太大,或者因为它会达到BSON限制,或者因为它会导致wantedIDs,那么您可以做的一件事就是根本不按$in进行过滤MongoDB几乎扫描整个集合。

您还可以在MongoDB中执行单个聚合,并将树ID的整个映射带到应用程序中,然后在Python代码中进行匹配。该聚合的结果也不能超过BSON最大文档大小。如果集合变化不大,您可能会尝试缓存聚合结果以提高性能。

当然,所有这些都可能是过早的优化。在不知道收集/应用的细节的情况下,很难说清楚。

除此之外,如果不重新考虑应用程序或重组数据以完全避免{{1}},您就无法做到更多。

答案 1 :(得分:0)

聚合框架可以让你关闭:

>>> db.collection.insert({'tree_id': 'ABC123', 'serial_id': 1})
ObjectId('52b105e3ca1ce9bb42202f63')
>>> db.collection.insert({'tree_id': 'ABC123', 'serial_id': 2})
ObjectId('52b105e4ca1ce9bb42202f64')
>>> db.collection.insert({'tree_id': '2', 'serial_id': 1})
ObjectId('52b105f8ca1ce9bb42202f65')
>>> db.collection.aggregate([{
    '$group': {
        '_id': '$tree_id',
        'serial_ids': {'$push': '$serial_id'}
     }
}])
{u'ok': 1.0,
    u'result': [{u'_id': u'2', u'serial_ids': [1]},
                {u'_id': u'ABC123', u'serial_ids': [1, 2]}
    ]
}

从这里开始,用Python完成:

dict([(row['_id'], row['serial_ids']) for row in result['result']])