Наиболее эффективный способ сопоставить определенное количество элементов в базе данных.Model ListProperty
-
20-09-2019 - |
Вопрос
В отношении это другой, но не несвязанный вопрос Я позаимствую примеры моделей.
class Foo(db.Model): bars = db.ListProperty(db.Key)
class Bar(db.Model): pass
Если у меня есть определенная сущность Foo, и я хочу получить все другие сущности foo, также содержащие определенный ключ bar в своем свойстве bars ListProperty , я бы использовал следующий запрос:
related_foos = Foo.all().filter('bars', bar_entity).fetch(fetch_count)
Что делать, если я хочу найти все другие объекты модельного типа Foo, которые имеют по крайней мере N количество совпадающих объектов bar?Очевидный способ сделать это с помощью цикла for повлек бы за собой резкую неэффективность, и, возможно, было бы лучше на самом деле изменить саму модель, чтобы упростить это, но не кажется очевидным, как это сделать.
Решение
Учитывая запись foo, которая имеет 10 bar_entities, и поиск всех записей foo, которые содержат по крайней мере 2 из этих 10 объектов, приведет к 45 возможным значениям равенства 10!/(2!*(10-2)!)=45.
Это можно вывести за 10_C_(2-1)=10 чтений.
SELECT * from table WHERE bar="1" AND bar in ["2", "3", "4", "5", "6", "7", "8", "9", "0"]
SELECT * from table WHERE bar="2" AND bar in ["3", "4", "5", "6", "7", "8", "9", "0"]
SELECT * from table WHERE bar="3" AND bar in ["4", "5", "6", "7", "8", "9", "0"]
etc.
Чтобы сократить это до одного чтения, потребовалось бы, чтобы при добавлении записи foo вы заполнили отдельную таблицу, в которой были все 2 комбинации для данной записи.
Say you had
foo_table
foo1 [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
foo2 [1, 3, 4]
foo3 [1, 2, a]
foo4 [b, 6, c]
foo_combo_2_table
Parent Combination
foo1 12
foo1 13
... and all 45 foo1 combinations each in its own row
foo2 13
foo2 14
foo2 34
foo3 12
foo3 1a
foo3 2a
etc.
Now you can do a
indexes = SELECT __KEY__ from foo_combo_2_table WHERE combination IN [12, 13, 14, 15, ... all 45]
keys = [k.parent() for k in indexes] # you would need to filter for duplicates
Таким образом, вы не столкнетесь с какими-либо взрывоопасными проблемами с индексом.
Если бы вы также хотели выполнить любые 3 или любые 4 объекта, кроме как для каждого из них, вам нужно было бы создать foo_combo_n_table или выполнить 10_C_(n-1) количество операций чтения.
Другие советы
Вы можете просто повторно применить один и тот же фильтр:
related_foos = Foo.all().filter('bars', bar_entity).filter('bars', bar_entity_2).fetch(fetch_count)
Или, управляемый данными:
q = Foo.all()
for bar in bar_entities:
q.filter('bars', bar)
related_foos = q.fetch(fetch_count)
Если вы не применяете к запросу никаких неравенств или порядков сортировки, хранилище данных сможет выполнять запросы, используя встроенные индексы и стратегию объединения слиянием, независимо от того, сколько фильтров вы примените.Однако, если вам нужно неравенство или порядок сортировки, вам нужно будет иметь индекс для каждого количества столбцов, по которым вы, возможно, захотите отфильтровать, что приводит к резкому увеличению индексов (и этого лучше избегать!).