2010-10-18 17 views
6

Esiste un modo intelligente per evitare di eseguire una query costosa con una clausola IN in casi come il seguente?Minimizza sottoquery con query IN su AppEngine (python)

Sto utilizzando Google App Engine per creare un'applicazione Facebook e ad un certo punto ho (ovviamente) bisogno di interrogare il datastore per ottenere tutte le entità che appartengono a uno degli amici di Facebook dell'utente specificato.

Supponiamo che io sono un paio di entità modellato come ad esempio:

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

e

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

Ad un certo punto mi interrogo Facebook per ottenere la lista di amici di un determinato utente e ho bisogno di eseguire la seguente query

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

Se l'ho fatto, AppEngine eseguiva una sottoquery per ogni i d in friend_ids, probabilmente superando il numero massimo di sottoquery qualsiasi query può spawn (30).

Esiste un modo migliore per eseguire questa operazione (riducendo al minimo il numero di query)? Capisco che non ci siano relazioni e join usando il datastore ma, in particolare, prenderei in considerazione l'aggiunta di nuovi campi alla classe User o Thing se aiuta a semplificare le cose.

risposta

5

Io non credo che ci sia una soluzione elegante, ma si potrebbe provare questo:

Sul modello utente, usare Facebook ID come il nome della chiave, e memorizzare l'elenco di ciascun utente di cose in un ListProperty.

class Thing(db.Model): 
    ... 

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

creazione di entità sarebbe andata in questo modo:

user = User.get_or_insert(my_facebook_id) 

thing = Thing() 
thing.put() 

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

Recupero prende 2 domande:

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 Un'altra opzione è quella di rendere le cose child a User per le query degli antenati per un tipo specifico di cosa da restituire. L'uso di key_names è fondamentale per questo davvero funzionante. – kevpie

+0

È grandioso, ho persino reso le cose come bambini da utente come suggerito da kevpie. Ho dovuto affrontare un paio di altri problemi, tuttavia: a) Non memorizzo un'entità Utente per ogni friend_id, quindi devo filtrare i valori None che ottengo quando eseguo query usando get_by_key_name; b) Devo filtrare le cose anche da altri campi, ma lo faccio sulle entità che prelevo dopo averle ricevute dal DataStore. C'è un modo migliore per farlo? – abahgat

+0

Assicurati di guardare i discorsi di Nick pubblicati nella sua risposta. Potresti voler usare un'entità indice combinata con una proprietà lista. Questo è mostrato nel primo discorso pubblicato da Nick. – kevpie

3

This Google I/O talk da Brett Slatkin affronta la situazione esatta hai a che fare con. Vedi anche his follow up talk quest'anno.

+0

Ho paura di pubblicare due volte lo stesso link all'ultimo discorso. Ti riferivi a questo? http://www.google.com/events/io/2009/sessions/BuildingScalableComplexApps.html – abahgat

+0

Sì, lo ero. Siamo spiacenti, corretto. –