我最近一直在玩弄 MongoDB 的聚合框架,并认为这将是解决我一直试图解决的问题的好方法。
因此,假设我正在编写讨论板软件,并且我有以下帖子文档结构:
{
'_id': ObjectId,
'created_at': datetime,
'poster_id': ObjectId,
'discussion_id': ObjectId,
'body': string
}
我在posts
集合中存储了以下(简化的)示例文档:
{
'_id': 1,
'created_at': '2013-08-18 12:00:00',
'poster_id': 1,
'discussion_id': 1,
'body': 'imma potato'
}
{
'_id': 2,
'created_at': '2013-08-18 13:00:00',
'poster_id': 1,
'discussion_id': 1,
'body': 'im still a potato'
}
{
'_id': 3,
'created_at': '2013-08-18 14:00:00',
'poster_id': 2,
'discussion_id': 1,
'body': 'you are definitely a potato'
}
{
'_id': 4,
'created_at': '2013-08-18 15:00:00',
'poster_id': 3,
'discussion_id': 1,
'body': 'Wait... he is potato?'
}
{
'_id': 5,
'created_at': '2013-08-18 16:00:00',
'poster_id': 2,
'discussion_id': 1,
'body': 'Yes! He is potato.'
}
{
'_id': 6,
'created_at': '2013-08-18 16:01:00',
'poster_id': 3,
'discussion_id': 1,
'body': 'IF HE IS POTATO... THEN WHO WAS PHONE!?'
}
我想要做的是返回一个独特的poster_id
s 地图,他们的最新帖子_id
按最新帖子降序排序。所以,最后,给定上面的示例代码,映射看起来非常类似于:
{
3:6,
2:5,
1:2
}
这是我使用 pymongo 实现的 MongoDB 聚合框架在 Python 中编写的一个方法示例:
def get_posters_with_latest_post_by_discussion_ids(self, discussion_ids, start=None, end=None, skip=None, limit=None, order=-1):
'''Returns a mapping of poster ids to their latest post associated with
the given list of discussion_ids. A date range, ordering and paging properties
can be applied.
'''
pipelines = []
if order:
pipelines.append({ '$sort': { 'created_at': order } })
if skip:
pipelines.append({ '$skip': skip })
if limit:
pipelines.append({ '$limit': limit })
match = {
'discussion_id': {
'$in': discussion_ids
}
}
if start and end:
match['created_at'] = {
'$gte': start,
'$lt': end
}
pipelines.append({ '$match': match })
pipelines.append({ '$project': { 'poster_id': '$poster_id' } })
pipelines.append({ '$group': { '_id': '$poster_id', 'post_id': { '$first': '$_id' } } })
results = self.db.posts.aggregate(pipelines)
poster_to_post_map = {}
for result in results['result']:
poster_to_post_map[result['_id']] = result['post_id']
return poster_to_post_map
现在我有了映射,我可以分别查询posters
和posts
集合以获取完整的文档,然后将它们组合在一起进行显示。
现在,问题不在于它不起作用,它确实……有点。假设我的帖子数量要多得多,我想翻阅带有最新帖子的海报列表。如果我的页面限制是“每页 10 个海报”并且在生成的 10 个文档中存在一个包含 2 个或更多帖子的海报,那么我实际上在我的地图中返回的项目少于 10 个。
例如,我有 10 个帖子,1 个海报在初始结果中有 3 个帖子。然后,聚合框架将丢弃其他 2 个帖子并将最新的帖子与该用户相关联,从而生成包含 8 个条目的地图,而不是 10 个。
这非常令人沮丧,因为我无法可靠地对结果进行分页。我也无法准确确定我是否在结果的最后一页,因为一组结果可能会或可能不会返回 0 个或更多匹配项。
什么,如果有的话,我在这里做错了吗?
我要完成的工作很简单,聚合框架似乎非常适合我的问题。
如果它是传统关系数据库上的存储过程,这将非常简单,但是当我们转向无模式文档存储时,这就是我们所牺牲的;关系在数据库的上下文之外进行管理。
无论如何,代码应该很容易理解,我会回答你可能有的任何问题。
无论哪种方式,感谢您花时间阅读。:)
编辑:已解决
以下是面向未来观众的解决方案要点:https ://gist.github.com/wilhelm-murdoch/6260469