MongoDB с использованием предложения OR в mongoengine
Я использую python mongoengine для запроса MongoDB и любил его по большей части, но у меня проблема с расширенный запрос.
Здесь моя модель
class ContentItem(Document):
account = ReferenceField(Account)
creator = ReferenceField(User)
public = BooleanField(default=False)
last_used = DateTimeField(default=datetime.now)
Я хотел бы сделать запрос для всех ContentItem
, которые относятся к определенной учетной записи, и либо создаются зарегистрированным пользователем, либо являются общедоступными. Здесь запрос, который я написал
query = ContentItem.objects.filter( (Q(account=account) & Q(public=True)) | (Q(account=account) & Q(creator=logged_in_user)) ).order_by('-last_used')
или
query = ContentItem.objects.filter( Q(account=account) & ( Q(public=True) | Q(creator=logged_in_user) ) ).order_by('-last_used')
Но они кажутся XOR, если либо public
, либо creator
, но не оба. Ожидается ли это?
Я что-то пропускаю? Должен ли я делать это непосредственно с mongodb вместо mongoengine?
Мое текущее обходное решение состоит в том, чтобы сделать два разных запроса и объединить результаты, но по мере того, как количество элементов контента увеличивается, результат требует много времени, потому что мне нужно получить все предметы, прежде чем я могу их заказать, тем самым теряя все преимущества (django) paginated results.
Ответы
Ответ 1
Документация mongoengine, по-видимому, неверна в этом случае. Вместо использования побитовых операторов "&" и "|", вы должны использовать стандартные операторы "и" и "или".
Итак, ваш первый запрос будет выглядеть следующим образом:
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')
Ответ 2
Правильный способ выполнения запроса - использовать побитовые операции |
и &
способ, которым вы написали его в своем вопросе:
query = ContentItem.objects.filter( (Q(account=account) & Q(public=True)) | (Q(account=account) & Q(creator=logged_in_user)) ).order_by('-last_used')
Примечание: использование стандартных булевых операторов Python and
и or
будет работать не. Это объясняется в документации MongoEngine.
Ответ 3
https://github.com/MongoEngine/mongoengine/blob/master/tests/queryset/transform.py
строка 134
def test_raw_query_and_Q_objects (self):
query = Foo.objects(__raw__={'$nor': [{'name': 'bar'}]})._query
self.assertEqual(query, {'$nor': [{'name': 'bar'}]})
q1 = {'$or': [{'a': 1}, {'b': 1}]}
query = Foo.objects(Q(__raw__=q1) & Q(c=1))._query
self.assertEqual(query, {'$or': [{'a': 1}, {'b': 1}], 'c': 1})
Ответ 4
вы, вероятно, импортируете неправильный Q
from mongoengine.queryset.visitor import Q as mongo_Q
from django.db.models import Q as normal_Q