Mongo Embedded Document Query

。_饼干妹妹 提交于 2019-12-21 14:32:43

问题


I've 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)

I want to filter the UserTasks document by checking whether the flag value (from Tasks Document) of the given task_id is 0 or 1, given the task_id and user_id. So I query in the following way:-

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

This fetches me an UserTask object.

Now I loop around the task list and first I get the equivalent task and then check its flag value in the following manner.

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

Is there any better/direct way of querying UserTasks Document in order to fetch the flag value of Tasks Document.

PS : I could have directly fetched flag value from the Tasks Document, but I also need to check whether the task is associated with the user or not. Hence I directly queried the USerTasks document.


回答1:


Can we directly filter on a document with the ReferenceField's fields in a single query?

No, its not possible to directly filter a document with the fields of ReferenceField as doing this would require joins and mongodb does not support joins.

As per MongoDB docs on database references:

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

From another page on the official site:

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.

So in 1 query, we can't both filter tasks with a particular flag value and with the given user_id and task_id on the UserTasks model.

How to perform the filtering then?

To perform the filtering as per the required conditions, we will need to perform 2 queries.

In the first query we will try to filter the Tasks model with the given task_id and flag. Then, in the 2nd query, we will filter UserTasks model with the given user_id and the task retrieved from the first query.

Example:

Lets say we have a user_id, task_id and we need to check if the related task has flag value as 0.

1st Query

We will first retrive the my_task with the given task_id and flag as 0.

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

2nd Query

Then in the 2nd query, you need to filter on UserTask model with the given user_id and my_task object.

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

You should perform 2nd query only if you get a my_task object with the given task_id and flag value. Also, you will need to add error handling in case there are no matched objects.

What if we have used EmbeddedDocument for the Tasks model?

Lets say we have defined our Tasks document as an EmbeddedDocument and the tasks field in UserTasks model as an EmbeddedDocumentField, then to do the desired filtering we could have done something like below:

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

Getting the particular my_task from the list of tasks

The above query will return a UserTask document which will contain all the tasks. We will then need to perform some sort of iteration to get the desired task.

For doing that, we can perform list comprehension using enumerate(). Then the desired index will be the 1st element of the 1-element list returned.

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



回答2:


@Praful, based on your schema you need two queries because mongodb does not have joins, so if you want to get "all the data" in one query you need a schema which fit that case. ReferenceField is a special field which does a lazy load of the other collection (it requires a query).

Based on the query you need, I recommend you to change your schema to fit that. The idea behind NOSQL engines is "denormalization" so it is not bad to have a list of EmbeddedDocument. EmbeddedDocument can be a smaller document (denormalized version) with a set of fields instead of all of them.

If you do not want to load the whole document into memory while querying you can exclude that fields using a "projection". Supossing your UserTasks has a list of EmbeddedDocument with the task you could do:

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

I hope it helps you.

Good luck!



来源:https://stackoverflow.com/questions/32746083/mongo-embedded-document-query

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!