Mongo 嵌入式文档查询

Mongo Embedded Document Query

我有 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 的 flag 值(来自任务文档)是 0 还是 [=17= 来过滤 UserTasks 文档],给定 task_id 和 user_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']

是否有任何better/direct查询UserTasks文档的方法以获取任务文档的标志值。

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

@Praful,根据您的架构,您需要两个查询,因为 mongodb 没有连接,所以如果您想在一个查询中获得 "all the data",您需要一个适合这种情况的架构。 ReferenceField 是一个特殊的字段,它对另一个集合进行延迟加载(它需要查询)。

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

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

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

希望对你有所帮助

祝你好运!

我们能否在单个查询中直接过滤具有 ReferenceField's 字段的文档?

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

根据 database references:

上的 MongoDB 文档

MongoDB does not support joins. In MongoDB some data is denormalized, or stored with related data in documents to remove the need for joins.

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

If we were using a relational database, we could perform a join on users and stores, and get all our objects in a single query. But MongoDB does not support joins and so, at times, requires bit of denormalization.

Relational purists may be feeling uneasy already, as if we were violating some universal law. But let’s bear in mind that MongoDB collections are not equivalent to relational tables; each serves a unique design objective. A normalized table provides an atomic, isolated chunk of data. A document, however, more closely represents an object as a whole.

因此,在 1 个查询中,我们不能同时使用特定标志值和 UserTasks 模型上的给定 user_idtask_id 来过滤 tasks

那么如何进行过滤呢?

要根据要求的条件执行过滤,我们需要执行 2 个查询。

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

示例:

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

第一次查询

我们将首先使用给定的 task_idflag 检索 my_task 作为 0.

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

第二次查询

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

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

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

如果我们对 Tasks 模型使用了 EmbeddedDocument 会怎样?

假设我们已经将 Tasks 文档定义为 EmbeddedDocument 并将 UserTasks 模型中的 tasks 字段定义为 EmbeddedDocumentField,然后执行我们可以做如下所需的过滤:

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

从任务列表中获取特定的 my_task

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

为此,我们可以使用 enumerate() 执行列表理解。 那么所需的索引将是 1 元素列表的第一个元素 returned.

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