6

Y a-t-il une manière intelligente d'éviter de faire une requête coûteuse avec une clause IN dans des cas comme celui-ci? J'utilise Google App Engine pour créer une application Facebook et à un moment donné, je dois (évidemment) interroger le magasin de données pour obtenir toutes les entités appartenant à l'un des amis facebook de l'utilisateur donné.Réduire au minimum les sous-requêtes avec des requêtes IN sur AppEngine (python)

Supposons que j'ai deux ou trois entités modelée en tant que tel:

class Thing(db.Model): 
    owner = db.ReferenceProperty(reference_class=User, required=True) 
    owner_id = db.StringProperty(required=True) 
    ... 

et

class User(db.Model): 
    id = db.StringProperty(required=True) 
    ... 

À un certain moment je fais une recherche Facebook pour obtenir la liste des amis d'un utilisateur donné et je dois effectuer la requête suivante

# get all Thing instances that belong to friends 
query = Thing.all() 
query.filter('owner_id IN', friend_ids) 

Si je le faisais, AppEngine effectuerait une sous-requête pour chaque i d dans friend_ids, dépassant probablement le nombre maximum de sous-requêtes que n'importe quelle requête peut générer (30).

Existe-t-il une meilleure façon de procéder (réduire le nombre de requêtes)? Je comprends qu'il n'y a pas de relations et de jointures en utilisant le magasin de données mais, en particulier, je considérerais ajouter de nouveaux champs à la classe User ou Thing si cela facilite les choses.

Répondre

5

Je ne pense pas qu'il y ait une solution élégante, mais vous pouvez essayer ceci:

Sur le modèle de l'utilisateur, utilisez Facebook ID comme nom de clé et stocker la liste des choses de chaque utilisateur dans un ListProperty.

class Thing(db.Model): 
    ... 

class User(db.Model): 
    things = db.ListProperty(db.Key) 
    ... 

création d'entité irait comme ceci:

user = User.get_or_insert(my_facebook_id) 

thing = Thing() 
thing.put() 

user.things.append(thing.key()) 
user.put() 

récupération prend 2 requêtes:

friends = User.get_by_key_name(friend_ids) 
thing_keys = [] 

for friend in friends: 
    thing_keys.extend(friend.things) 

things = db.get(thing_keys) 
+0

+1 Une autre option consiste à faire en sorte que Things children to User permette de renvoyer des requêtes ancêtres pour un type spécifique de chose. L'utilisation de key_names est essentielle pour que cela fonctionne vraiment. – kevpie

+0

C'est génial, j'ai même fait Things Things to User comme suggéré par kevpie. J'ai dû faire face à quelques autres problèmes, cependant: a) Je ne stocke pas une entité Utilisateur pour chaque friend_id, donc j'ai besoin de filtrer les valeurs None que je reçois lors d'une requête en utilisant get_by_key_name; b) Je dois aussi filtrer les choses par d'autres champs, mais je le fais sur les entités que je récupère après les avoir récupérées dans le DataStore. Y a-t-il une meilleure façon de faire cela? – abahgat

+0

Assurez-vous de regarder les discussions que Nick a posté dans sa réponse. Vous pouvez utiliser une entité d'index combinée à une propriété de liste. Ceci est montré dans la première conversation postée par Nick. – kevpie

3

This Google I/O talk par Brett Slatkin aborde la situation exacte que vous avez affaire. Voir aussi his follow up talk cette année.

+0

Je crains que vous avez posté deux fois le même lien à la dernière conversation. Parlez-vous de celui-ci? http://www.google.com/events/io/2009/sessions/BuildingScalableComplexApps.html – abahgat

+0

Oui, j'étais. Désolé, corrigé. –

Questions connexes