2012-12-18 10 views
14

Meine Frage ist über die Modellierung von Eins-zu-viele-Beziehungen in ndb. Ich verstehe, dass dies auf (mindestens) zwei verschiedene Arten geschehen kann: mit einer wiederholten Eigenschaft oder mit einem "Fremdschlüssel". Ich habe unten ein kleines Beispiel erstellt. Grundsätzlich haben wir einen Artikel, der eine beliebige Anzahl von Tags haben kann. Nehmen wir an, dass ein Tag entfernt werden kann, aber nicht geändert werden kann, nachdem es hinzugefügt wurde. Nehmen wir an, wir kümmern uns nicht um Transaktionssicherheit.ndb Modellierung eins-zu-viele: Vorteile von wiederholten KeyProperty vs Fremdschlüssel

Meine Frage ist: Was ist die bevorzugte Art der Modellierung dieser Beziehungen?

Meine Überlegungen:

  • Approach (A) erfordert zwei Schreibvorgänge für jeden Tag, das zu einem Artikel (eine für die Artikel und eine für den Tag) hinzugefügt wird, während (B) nähern nur erfordert man schreibt (nur das Tag).
  • Approach (A) nutzt Caching-Mechanismus des NDB, wenn für einen Artikel während bei Ansatz alle Schlüsselpaare zu holen (B) eine Abfrage erforderlich ist (und zusätzlich einige benutzerdefinierte Caching)

gibt es einige Dinge dass ich hier vermisse, irgendwelche anderen Überlegungen, die berücksichtigt werden sollten?

Vielen Dank für Ihre Hilfe.

Beispiel (A):

class Article(ndb.Model): 
    title = ndb.StringProperty() 
    # some more properties 
    tags = ndb.KeyProperty(kind="Tag", repeated=True) 

    def create_tag(self): 
     # requires two writes 
     tag = Tag(name="my_tag") 
     tag.put() 
     self.tags.append(tag) 
     self.put() 

    def get_tags(self): 
     return ndb.get_multi(self.tags) 

class Tag(ndb.Model): 
    name = ndb.StringProperty() 
    user = ndb.KeyProperty(Kind="User") # User that created the tag 
    # some more properties 

Beispiel (B):

class Article(ndb.Model): 
    title = ndb.StringProperty() 
    # some more properties 

    def create_tag(self): 
     # requires one write 
     tag = Tag(name="my_tag", article=self.key) 
     tag.put() 

    def get_tags(self): 
     # obviously we could cache this query in memcache 
     return Tag.gql("WHERE article :1", self.key) 

class Tag(ndb.Model): 
    name = ndb.StringProperty() 
    article = ndb.KeyProperty(kind="Article") 
    user = ndb.KeyProperty(Kind="User") # User that created the tag 
    # some more properties 
+2

Erwägen Sie, die Leistung mit appstats zu überprüfen, da, während Ihre spezifische Frage hier eine spezifische Antwort hat, diese wahrscheinlich mehr mit Ihrer tatsächlichen Nutzung zusammenhängt und appstats Ihnen sagen kann, welche der obigen Optionen im wirklichen Leben effizienter sind. https://developers.google.com/appengine/docs/python/tools/appstats –

+2

würden Sie neue Tags für jeden Artikel erstellen, auch wenn es das gleiche Tag hat? Ich würde für die Option "A" gehen, weil Sie in der Lage sein werden, das gleiche "Tag" für jeden Artikel zu verwenden, und Sie werden in der Lage sein, "Artikel" nach dem Tag abzufragen. – aschmid00

+0

@PaulC danke. In der Tat habe ich mit Appstats überprüft und in meinem Fall Option B ist effizienter (1 schreiben vs 2). Da die Optimierung jedoch nur gering ist, bin ich nicht sicher, ob es sich lohnt, den dokumentierten Weg (dh Option A) aufzugeben, um eine 1: n-Beziehung zu lösen. –

Antwort

6

Haben Sie sich auf der folgenden über Structured Propertieshttps://developers.google.com/appengine/docs/python/ndb/properties#structured verwenden. Die kurze Diskussion dort über Contact und Addresse kann Ihr Problem vereinfachen. Siehe auch https://developers.google.com/appengine/docs/python/ndb/queries#filtering_structured_properties. Die Diskussionen sind sehr kurz.

Auch Blick auf die Tatsache, dass Joins nicht erlaubt sind, sieht Option A besser aus.

+0

Danke für Ihre Antwort, strukturierte Eigenschaften könnten den Job erledigen, aber in meinem speziellen Fall glaube ich nicht, dass sie die beste Lösung wären. Was meinst du mit "Vorausschauen auf die Tatsache, dass Joins nicht erlaubt sind"? Ist das eine GAE-Richtlinie? –

+1

Ja, das ist eine Einschränkung des Datenspeichers. siehe https://developers.google.com/appengine/docs/python/datastore/queries. "Insbesondere Joins und Aggregatabfragen werden in der Datastore-Abfrage-Engine nicht unterstützt." Der Datenspeicher enthält weitere Einschränkungen, mit denen Sie wahrscheinlich vertraut sein sollten: https://developers.google.com/appengine/docs/python/datastore/queries#Restrictions_on_Queries – kasavbere

1

Wie bereits erwähnt, gibt es keine Joins in Datastore. Daher gilt der Begriff "Foreign Key" nicht. Sie können die Query-Klasse verwenden, um den Datenspeicher nach dem richtigen Tag abzufragen.

Zum Beispiel, wenn Sie Endpunkte verwenden, dann:

class Tag(ndb.model): 
    user = ndb.UserProperty() 

Und die während der Anforderung tun:

query.filter(Tag.user == endpoints.get_current_user()) 
0

Approach (A) sollte in den meisten Fällen bevorzugt sein. Während zwei Schreibvorgänge zum Hinzufügen eines Tags erforderlich sind, ist dies wahrscheinlich weniger häufig als das Lesen der Tags. Solange Sie nicht über eine große Anzahl von Tags verfügen, sollten sie alle in die wiederholte Key-Eigenschaft passen.

Wie Sie erwähnt haben, ist das Abrufen der Tags mit ihren Schlüsseln viel schneller als das Ausführen einer Abfrage.Auch, wenn Sie nur die Tag-Namen und die Benutzer benötigen, können Sie den Tag mit den User als Mutterschlüssel und die Name als den Tag-ID erstellen:

User -> Name -> Tag 

diesen Tag zu erstellen, verwenden Sie:

tag = Tag(id=name, parent=user, ...) 
article.tags.push(tag) 
ndb.put_multi([tag, article]) 

Dann, wenn Sie die Tags abrufen,

for tag in article.tags: 
    user = tag.parent() 
    name = tag.id() 

Dann wird jeder Schlüssel, den Sie in Article.tags gespeichert würde die Benutzerschlüssel enthalten und die 0.123.Name! Dies würde Sie davor bewahren, die Tag zu lesen, um diese Werte zu erhalten.