Question

Je me demande quel serait le meilleur moyen de concevoir une application sociale dans laquelle les membres effectuent des activités et suivent les activités d'autres membres à l'aide de Google AppEngine.

Pour être plus spécifique, supposons que nous avons ces entités:

  • Utilisateurs ayant des amis
  • Activités qui représentent les actions effectuées par les utilisateurs (disons que chacune a un message de chaîne et un objet ReferenceProperty à son utilisateur propriétaire, ou qu'elle peut utiliser une association parent via la clé de l'appengine)

La partie la plus difficile consiste à suivre les activités de vos amis, ce qui signifie regrouper les dernières activités de tous vos amis. Normalement, ce serait une jointure entre la table Activités et votre liste d'amis, mais ce n'est pas une conception viable sur appengine car il n'y a pas de jointure simulant celle-ci nécessitera de lancer N requêtes (où N est le nombre d'amis) et de les fusionner en mémoire - très coûteux et dépassera probablement le délai de demande ...)

Je songe actuellement à l'implémenter à l'aide de files d'attente dans la boîte de réception. La création d'une nouvelle activité déclenche un processus d'arrière-plan qui place la clé de la nouvelle activité dans la "boîte de réception". de chaque utilisateur suivant:

  • Obtention " Tous les utilisateurs qui suivent X " est une requête possible pour un moteur d'application
  • Une entrée de lot peu coûteuse dans une nouvelle "Boîte de réception". entité qui stocke essentiellement des n-uplets (utilisateur, clé d'activité).

Je serai heureux d’entendre des réflexions sur ce projet ou sur des suggestions alternatives, etc.

Était-ce utile?

La solution

Consultez Création d'applications complexes et évolutives sur le moteur d'applications ( pdf ), un conférence fascinante donnée à Google I / O par Brett Slatkin. Il aborde le problème de la construction d’un service de messagerie évolutif tel que Twitter.

Voici sa solution utilisant une propriété de liste:

class Message(db.Model):
    sender = db.StringProperty()
    body = db.TextProperty()

class MessageIndex(db.Model):
    #parent = a message
    receivers = db.StringListProperty()

indexes = MessageIndex.all(keys_only = True).filter('receivers = ', user_id)
keys = [k.parent() for k in indexes)
messages = db.get(keys)

Cette requête portant uniquement sur une clé recherche les index de message avec un destinataire égal à celui que vous avez spécifié sans désérialiser ni sérialiser la liste des destinataires. Ensuite, vous utilisez ces index pour récupérer uniquement les messages que vous souhaitez.

Voici la mauvaise façon de le faire:

class Message(db.Model):
    sender = db.StringProperty()
    receivers = db.StringListProperty()
    body = db.TextProperty()

messages = Message.all().filter('receivers =', user_id)

Cela est inefficace car les requêtes doivent décompresser tous les résultats renvoyés par votre requête. Ainsi, si vous avez renvoyé 100 messages contenant 1 000 utilisateurs dans chaque liste de destinataires, vous devez désérialiser 100 000 valeurs de propriété de liste (100 x 1 000). Beaucoup trop cher en latence de banque de données et en cpu.

Au début, j'ai été assez déconcerté par tout cela. J'ai donc écrit un petit tutoriel sur l'utilisation de la propriété list . Profitez de:)

Autres conseils

Je ne sais pas s'il s'agit du meilleur design pour une application sociale, mais jaiku a été transféré dans App Engine par son créateur original lors de l’acquisition de la société par Google, cela devrait donc être raisonnable.

Voir la section Acteurs et Tigres et Ours, oh mon dieu! dans design_funument.txt . Les entités sont définies dans common / models.py et les requêtes sont dans common / api.py .

Robert, à propos de votre solution proposée:

messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])

Je pense que le "corps" ndb.TextProperty ne peut pas être utilisé avec des projections car n'est pas indexé. Les projections ne prennent en charge que les propriétés indexées. La solution valide consisterait à gérer les 2 tables: Message et MessageIndex.

Je pense que cela peut maintenant être résolu avec les nouvelles requêtes de projection dans NDB.

class Message(ndb.Model):
    sender = ndb.StringProperty()
    receivers = ndb.StringProperty(repeated=True)
    body = ndb.TextProperty()

messages = Message.query(Message.receivers == user_id).fetch(projection=[Message.body])

Maintenant, vous n’avez plus à supporter le coût élevé de la désérialisation de la propriété de liste.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top