Google App Engine. Как я могу выполнить запрос Python ndb к результатам, возвращаемым запросом ndb?

Я использую фреймворк GAE python (webapp2) для проекта и у меня есть вопрос, связанный с запросом ndb. Позвольте мне привести пример, чтобы объяснить это ясно:

Вот модель ndb, которая у меня есть:

class Example(ndb.Model):
  userid = ndb.StringProperty()
  field1 = ndb.StringProperty()
  field2 = ndb.StringProperty()

Вот объекты, которые я создал, используя приведенную выше модель:

[Example(key=Key('Example', '1~A~B'), field1=u'B', field2=u'A', userid=u'1'),
 Example(key=Key('Example', '1~C~D'), field1=u'D', field2=u'C', userid=u'1'),
 Example(key=Key('Example', '2~A~B'), field1=u'B', field2=u'A', userid=u'2'),
 Example(key=Key('Example', '2~C~D'), field1=u'D', field2=u'C', userid=u'2'),
 Example(key=Key('Example', '3~A~B'), field1=u'B', field2=u'A', userid=u'3'),
 Example(key=Key('Example', '3~X~Y'), field1=u'Y', field2=u'X', userid=u'3'),
 Example(key=Key('Example', '4~C~D'), field1=u'D', field2=u'C', userid=u'4'),
 Example(key=Key('Example', '4~E~F'), field1=u'F', field2=u'E', userid=u'4'),
 Example(key=Key('Example', '5~A~B'), field1=u'B', field2=u'A', userid=u'5'),
 Example(key=Key('Example', '5~X~Y'), field1=u'Y', field2=u'X', userid=u'5')
]

Учитывая приведенные выше данные, я хочу найти следующее: - Найти все идентификаторы пользователей, для которых выполнены оба следующих условия:

field1='B', field2='A'
and field1='D', field2='C'

В приведенном выше примере следующие идентификаторы пользователей будут соответствовать этому критерию:

userid='1' 
userid='2'

Итак, у меня есть вопрос: можем ли мы найти вышеуказанный результат с помощью одного запроса ndb? Если да, то как?

Метод, который я знаю прямо сейчас, может быть достигнут только с помощью запроса OR ndb, где мы находим все записи, соответствующие

 Example.query(ndb.OR(ndb.AND(field1='B' AND field2='a'), ndb.AND(field1='D' AND field2='C')))

Затем выполните итерацию по результатам (в python), а затем получите логику для извлечения только тех идентификаторов пользователей, которые имеют field1 = «B», field2 = «A» и field1 = «D», field2 = «C».

Это не кажется эффективным способом по следующей причине: - Мы без необходимости извлекаем больше записей, чем необходимо, с комбинацией ИЛИ. - Если мы ограничим результаты с помощью fetch_page, невозможно узнать, сколько записей будет отфильтровано с использованием наших критериев. Это может даже привести к пустым записям в зависимости от нашего лимита.

ИЛИ, если у меня есть способ сначала найти все идентификаторы пользователей с полем1 = «B» и полем2 = «A», а затем напрямую запросить в этом результате, чтобы найти все идентификаторы пользователя с полем1 = «D» и поле2 = «C», которые будут также решить проблему. Итак, это запрос по результату запроса.

Оцените ваши входы/предложения. Спасибо.


person jar kir ang 1    schedule 01.07.2017    source источник


Ответы (1)


Да, операция ndb.OR не очень эффективна и может вызвать комбинаторный взрыв, см. обсуждение в Сочетание операций И ​​и ИЛИ.

Также имейте в виду, что вы запрашиваете Example элементов, а не идентификаторы пользователей. Это означает, что вы не можете запросить field1='D' and field2='C' в результатах запроса field1='B' and field2='A' — результат всегда будет пустым, потому что field1 не может быть одновременно и 'D', и 'B'.

Таким образом, вам действительно нужно выполнить два отдельных запроса, извлечь наборы идентификаторов пользователей в каждом из результатов, а затем проверить пересечение двух наборов.

Возможно можно использовать отдельные проекционные запросы. (экспериментально!) для более эффективного получения списка пользователей:

query_1 =  Example.query(ndb.AND(Example.field1='B' AND Example.field2='A'),
                         projection=[Example.userid], distinct=True))
query_2 =  Example.query(ndb.AND(Example.field1='D' AND Example.field2='C'),
                         projection=[Example.userid], distinct=True))

users_1 = set([example.userid for example in query_1.fetch()])
users_2 = set([example.userid for example in query_2.fetch()])

users_list = list(users_1 & users_2)

Примечание. На самом деле я не пробовал приведенный выше код, он основан только на документации.

person Dan Cornilescu    schedule 02.07.2017