MongoDB using an OR clause in mongoengine

MattoTodd picture MattoTodd · Nov 18, 2011 · Viewed 12.8k times · Source

I'm using python's mongoengine to query MongoDB, and have loved it for the most part, but I'm having an issue with an advanced query.

Here's my model

class ContentItem(Document):
    account = ReferenceField(Account)
    creator = ReferenceField(User)
    public = BooleanField(default=False) 
    last_used = DateTimeField(default=datetime.now)

I would like to make a query for all ContentItem's that are of a particular account, and are either created by the logged in user or are public. Here's the query I wrote

query = ContentItem.objects.filter( (Q(account=account) & Q(public=True)) |  (Q(account=account) & Q(creator=logged_in_user)) ).order_by('-last_used')

or:

query = ContentItem.objects.filter( Q(account=account) & ( Q(public=True) |  Q(creator=logged_in_user) ) ).order_by('-last_used')

But these seem to be XOR where if either public, or the creator but not both. Is this expected?

Am I overlooking something? Should I do this directly with mongodb instead of mongoengine?

My current workaround is to make two different queries and combine the results, but as the # of Content Items gets larger the result is taking a long time to come back because I need to get all items before I can order them, thereby losing all the benefit of (django) paginated results.

Answer

apiguy picture apiguy · Nov 21, 2011

The mongoengine documentation is apparently incorrect in this case. Instead of using the bitwise operators "&" and "|", you should use the standard operators "and" and "or".

So your first query becomes:

query = ContentItem.objects.filter( (Q(account=account) and Q(public=True)) or  (Q(account=account) and Q(creator=logged_in_user)) ).order_by('-last_used')