Mongo嵌入式文档查询

时间:2015-09-23 17:41:41

标签: python mongodb mongodb-query mongoengine

我有2个DynamicDocuments:

class Tasks(db.DynamicDocument):
    task_id = db.UUIDField(primary_key=True,default=uuid.uuid4)
    name = db.StringField()
    flag = db.IntField()

class UserTasks(db.DynamicDocument):
    user_id = db.ReferenceField('User')
    tasks = db.ListField(db.ReferenceField('Tasks'),default=list)

我希望通过检查给定task_id的UserTasks值(来自任务文档)是flag还是0来过滤1文档,给定task_id和用户身份。所以我用以下方式查询: -

obj = UserTasks.objects.get(user_id=user_id,tasks=task_id)

这会抓取一个UserTask对象。

现在我循环遍历任务列表,首先我获得等效任务,然后按以下方式检查其标志值。

task_list = obj.tasks
for t in task_list:
    if t['task_id'] == task_id:
        print t['flag']

是否有更好/直接的方式查询UserTasks文档以获取任务文档的标记值。

PS:我可以直接从Tasks文档中获取标记值,但我还需要检查该任务是否与用户相关联。因此,我直接查询了USerTasks文档。

2 个答案:

答案 0 :(得分:2)

我们可以直接过滤单个查询中包含ReferenceField's字段的文档吗?

不,无法使用ReferenceField字段直接过滤文档,因为这样做会需要连接,而mongodb不支持连接。

根据database references:上的MongoDB文档

  

MongoDB不支持连接。在MongoDB中,一些数据被非规范化,   或者与文档中的相关数据一起存储以消除对连接的需要。

来自官方网站上的另一个page

  

如果我们使用关系数据库,我们可以执行连接   用户和商店,并在一个查询中获取所有对象。的但   MongoDB不支持连接,因此有时需要一点点   反规范化。

     

关系纯粹主义者可能已经感到不安,就好像我们一样   违反一些普遍法律。但是请记住MongoDB   集合不等同于关系表;每个服务一个   独特的设计目标。规范化表提供原子,   孤立的数据块。然而,一份文件更为贴切   一个整体的对象。

因此,在1个查询中,我们无法在tasks模型上使用特定标记值和给定user_idtask_id过滤UserTasks

如何执行过滤?

要按照所需条件执行过滤,我们需要执行2次查询。

在第一个查询中,我们会尝试使用给定的Taskstask_id过滤flag模型。然后,在第二个查询中,我们将使用给定的UserTasks和从第一个查询中检索到的user_id过滤task模型。

示例:

假设我们有user_idtask_id,我们需要检查相关任务的flag值是否为0

第一次查询

我们将首先使用给定my_tasktask_id作为flag来检索0

my_task = Tasks.objects.get(task_id=task_id, flag=0) # 1st query

第二次查询

然后在第二个查询中,您需要使用给定的UserTaskuser_id对象过滤my_task模型。

my_user_task = UserTasks.objects.get(user_id=user_id, tasks=my_task) # 2nd query

只有获得具有给定my_tasktask_id值的flag对象时,才应执行第二次查询。此外,如果没有匹配的对象,您将需要添加错误处理。

如果EmbeddedDocument模型使用了Tasks怎么办?

假设我们已将Tasks文档定义为EmbeddedDocument,将tasks模型中的UserTasks字段定义为EmbeddedDocumentField,然后执行所需操作过滤我们可以做类似下面的事情:

my_user_task = UserTasks.objects.get(user_id=user_id, tasks__task_id=task_id, tasks__flag=0)

从任务列表中获取特定my_task

上述查询将返回一个UserTask文档,其中包含所有tasks。然后我们需要执行某种迭代来获得所需的任务。

为此,我们可以使用enumerate()执行列表理解。 然后,所需的索引将是返回的1元素列表的第1个元素。

my_task_index = [i for i,v in enumerate(my_user_task.tasks) if v.flag==0][0]

答案 1 :(得分:0)

@Praful,根据您的架构,您需要两个查询,因为mongodb没有连接,所以如果您想获得"所有数据"在一个查询中,您需要一个适合该情况的模式。 ReferenceField是一个特殊字段,它执行其他集合的延迟加载(它需要查询)。

根据您需要的查询,我建议您更改架构以适应这种情况。 NOSQL引擎背后的想法是"非规范化"所以有一个EmbeddedDocument列表也不错。 EmbeddedDocument可以是一个较小的文档(非规范化版本),包含一组字段而不是所有字段。

如果您不想在查询时将整个文档加载到内存中,可以使用"投影"排除这些字段。 使用UserTasks有一个EmbeddedDocument列表,其中包含您可以执行的任务:

UserTasks.objects.exclude('tasks').filter(**filters)

我希望它可以帮到你。

祝你好运!