Объединение результатов нескольких запросов неравенства ndb

NDB Datastore запрещает множественные запросы на неравенство по разным свойствам. Чтобы обойти это, я подумал, что решением может быть объединение результатов нескольких независимых запросов. Я нашел этот вопрос 2011 года который рекомендует геохешинг, с которым я не знаком. Так что, возможно, сегодня есть лучшее решение.

Рассмотрим эти два вопроса:

q1 = User.query(User.age < 18).fetch()
q2 = User.query(User.city != 'New York City').fetch()

Я пытаюсь присоединиться к ним вот так:

results = set(q1).intersection(q2)

Однако я сталкиваюсь с TypeError: Model is not immutable.

Мои вопросы:

  • Есть ли лучший способ справиться с множественными фильтрами неравенства для разных свойств?
  • Если нет, как я могу решить TypeError выше?

Спасибо за помощь.


comment
Возможный дубликат запроса NDB App Engine с множественными неравенствами?   -  person Brent Washburne    schedule 06.11.2015
comment
Это в некоторой степени. Я прочитал этот ответ, и мне было любопытно, как можно объединить несколько запросов, о чем я частично и спрашиваю в этом вопросе.   -  person hyang123    schedule 06.11.2015
comment
Было бы более эффективно выполнять запрос только по ключам, затем вы можете создать набор на основе ключей, а затем получить только уникальный набор. (хотя кеширование ndb также снизило бы эффективность), но, как вы обнаружили, вы не можете создать набор моделей. Итак, вернемся к ключам.   -  person Tim Hoffman    schedule 06.11.2015


Ответы (3)


Если вы можете реструктурировать свою User модель, вы можете добавить еще несколько свойств, чтобы упростить запросы. Например, если вы запрашиваете те же возрастные диапазоны, создайте свойство, которое кодирует эти диапазоны:

age_range = ndb.IntegerProperty()    # 0 = 0-17, 1 = 18-29, 2 = 30-39, etc.

Тогда у вас может быть:

q1 = User.query(User.age_range == 0).query(User.city != 'New York City').fetch()

Если ваш набор данных достаточно мал, вы можете использовать подход @ TimHoffman:

q1 = User.query(User.age < 18).fetch(keys_only=True)
q2 = User.query(User.city != 'New York City').fetch(keys_only=True)
results = ndb.get_multi(set(q1).intersection(q2))

Более тяжелый подход, который позволяет масштабировать до больших наборов данных, - это MapReduce библиотека. Вы можете добавить несколько фильтров, чтобы уменьшить набор данных.

person Brent Washburne    schedule 06.11.2015

У меня была аналогичная проблема. Мой запрос был:

@classmethod:
def getUnReadMessages(cls, user, date)
    return cls.query(ndb.AND(cls.created <= date,
                             cls.receiver_key == user.key,
                             cls.status != READ))

Но appengine не позволил мне это сделать. Итак, я решил изменить одно неравенство на:

@classmethod:
def getUnReadMessages(cls, user, date)
    return cls.query(ndb.AND(cls.created <= date,
                             cls.receiver_key == user.key,
                             ndb.OR(cls.status == SEND,
                                    cls.status == RECEIVED)))

Проблема решена! Я надеюсь, это поможет вам.

person rcmstark    schedule 31.03.2016

Есть два варианта:

Измените свои модели данных

Добавьте дополнительные свойства или адаптируйте текущие, чтобы вы могли запрашивать объект в соответствии с ограничениями хранилища данных. Это может означать категоризацию непрерывных переменных.

Найдите обходной путь

Вы можете сначала сделать наиболее важный запрос, а затем вручную отфильтровать результат. Имейте в виду следующие соображения:

  • Вы можете использовать проекцию, чтобы сделать запрос более эффективным.
  • Сделайте результаты повторяемыми (итеративными).
  • Используйте get_multi для получения списка ключей.

Ваш код может выглядеть так:

query_iter = User.query(User.age < 18).iter(projection=[User.city])
query_keys = [u.key() for u in query_iter if u.city != 'New York City']
query = ndb.get_multi(query_keys)

Or

query = [u for u in User.query(User.age < 18).fetch() if u.city != 'New York City']
person Community    schedule 18.10.2017