La façon la plus efficace pour correspondre à un certain nombre d'éléments dans un db.Model ListProperty
-
20-09-2019 - |
Question
En ce qui concerne cette question différente, mais pas sans rapport je vais emprunter les modèles par exemple.
class Foo(db.Model): bars = db.ListProperty(db.Key)
class Bar(db.Model): pass
Si j'ai une certaine entité Foo et je veux obtenir toutes les autres entités foo contenant également un certain bar clé dans ses bars ListProperty, j'utiliser la requête suivante:
related_foos = Foo.all().filter('bars', bar_entity).fetch(fetch_count)
Qu'en est-il si je veux trouver toutes les autres entités de Foo type de modèle qui ont au moins nombre N d'appariement des entités de bar? La façon évidente de le faire avec une boucle for impliquerait l'inefficacité drastiques, et il pourrait être préférable de changer réellement le modèle lui-même pour rendre cela plus facile, mais il ne semble pas évident de le faire.
La solution
Étant donné un dossier foo qui a 10 bar_entities et la recherche de tous les enregistrements foo qui ont au moins 2 de ces 10 entités entraînerait 45 valeurs possibles égalité 10! / (2! * (10-2)!) = 45.
Ceci peut être déduit dans 10_C_ (2-1) = 10 se lit comme suit.
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.
Pour réduire à une lecture, il faudrait que lorsqu'un enregistrement foo est ajouté que vous remplissez une table séparée qui avait toutes les 2 combinaisons pour un enregistrement donné.
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
De cette façon, vous obtenez l'habitude dans toutes les questions d'index explosant.
Si vous voulez aussi faire des 3 ou 4 les entités que pour chacun d'entre eux, vous devez créer un foo_combo_n_table ou faire un 10_C_ (n-1) nombre de lectures.
Autres conseils
Vous pouvez simplement appliquer le même filtre à plusieurs reprises:
related_foos = Foo.all().filter('bars', bar_entity).filter('bars', bar_entity_2).fetch(fetch_count)
Ou, basée sur les données:
q = Foo.all()
for bar in bar_entities:
q.filter('bars', bar)
related_foos = q.fetch(fetch_count)
Si vous n'appliquez pas d'inégalités ou les ordres de tri à la requête, le magasin de données sera en mesure d'exécuter les requêtes en utilisant le construit dans les index et la fusion stratégie de jointure, quel que soit le nombre de filtres que vous appliquez. Si vous avez besoin d'un ordre d'inégalité ou de tri, cependant, vous aurez besoin d'avoir un index pour chaque nombre de barres que vous pourriez vouloir filtrer, ce qui conduit à des indices d'exploser (et donc il vaut mieux éviter!)