2012-12-18 4 views
14

Moje pytanie dotyczy modelowania relacji jeden-do-wielu w ndb. Rozumiem, że można to zrobić na (przynajmniej) na dwa różne sposoby: z powtarzającą się własnością lub z "obcym kluczem". Poniżej stworzyłem mały przykład. Zasadniczo mamy artykuł, który może mieć dowolną liczbę znaczników. Załóżmy, że tag można usunąć, ale nie można go zmienić po dodaniu. Załóżmy też, że nie martwimy się bezpieczeństwem transakcyjnym.Modelowanie ndb jeden-do-wielu: zalety powtarzania KeyProperty kontra klucz obcy

Moje pytanie brzmi: jaki jest preferowany sposób modelowania tych relacji?

Moje rozważania:

  • Approach (A) wymaga dwóch pisze dla każdego znacznika, który jest dodawany do artykułu (jeden dla artykułu oraz jeden dla Tag) natomiast zbliżyć (b) wymaga tylko jeden pisze (tylko Tag).
  • Approach (A) wykorzystuje mechanizm buforowania NDB, gdy ściągam wszystkie tagi do artykułu natomiast w przypadku podejścia (b) jest wymagane zapytanie (a dodatkowo niektóre zwyczaj buforowania)

istnieją pewne rzeczy że tu jestem, jakieś inne uwagi, które należy wziąć pod uwagę?

Dziękuję bardzo za pomoc.

Przykład (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 

Przykład (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

Rozważ sprawdzanie wydajności za pomocą appstats, ponieważ podczas gdy twoje konkretne pytanie może mieć konkretną odpowiedź, prawdopodobnie bardziej odnosi się do twojego rzeczywistego użycia, a więc appstats może ci powiedzieć, które z powyższych opcji są bardziej skuteczne w życiu. https://developers.google.com/appengine/docs/python/tools/appstats –

+2

Czy utworzyć nowe tagi dla każdego artykułu, nawet jeśli jest to ten sam tag? wybrałbym opcję "A", ponieważ będziesz mógł użyć tego samego "tagu" dla każdego artykułu, a będziesz w stanie przesłać zapytanie "Artykuły" według tagu. – aschmid00

+0

@PaulC thanks. Rzeczywiście sprawdziłem z appstats iw moim przypadku opcja B jest bardziej wydajna (1 write vs 2). Ponieważ jednak optymalizacja jest niewielka, nie jestem pewien, czy warto byłoby zrezygnować z udokumentowanej metody (np. Opcji A) w celu rozwiązania relacji jeden do wielu. –

Odpowiedz

6

Pan spojrzał na następujący temat korzystania Structured Propertieshttps://developers.google.com/appengine/docs/python/ndb/properties#structured. Krótka dyskusja o numerach Contact i Addresse może uprościć Twój problem. Zobacz także https://developers.google.com/appengine/docs/python/ndb/queries#filtering_structured_properties. Dyskusje są bardzo krótkie.

Ponadto, patrząc na to, że łączenia są niedozwolone, opcja A wygląda lepiej.

+0

Dzięki za odpowiedź, strukturyzowane właściwości mogą wykonać zadanie, ale w moim konkretnym przypadku nie sądzę, by były najlepszym rozwiązaniem. Co rozumiesz przez "patrzenie w przyszłość na to, że łączenia nie są dozwolone"? Czy to jest polityka GAE? –

+1

Tak, jest to ograniczenie składnicy danych. zobacz https://developers.google.com/appengine/docs/python/datastore/queries. "w szczególności połączenia i kwerendy agregujące nie są obsługiwane w mechanizmie zapytań Datastore." Magazyn danych ma inne ograniczenia, które prawdopodobnie powinny być Ci znane: https://developers.google.com/appengine/docs/python/datastore/queries#Restrictions_on_Queries – kasavbere

1

Jak wspomniano wcześniej, nie ma żadnych połączeń w Datastore, więc nie ma zastosowania pojęcie "klucza obcego". Co można zrobić, to użyć klasy Query do wysłania zapytania do magazynu danych o poprawny Tag.

Na przykład, jeśli używasz punktów końcowych, a następnie:

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

a podczas wniosek zrobić:

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

Approach (A) powinny być preferowane w większości sytuacji. Chociaż do dodania tagu potrzebne są dwa zapisy, prawdopodobnie jest to znacznie rzadsze niż czytanie tagów. Dopóki nie masz dużej liczby tagów, wszystkie powinny pasować do powtarzanej właściwości Key.

Jak już wspomniano, pobieranie tagów według ich kluczy jest znacznie szybsze niż wykonanie zapytania.Ponadto, jeśli trzeba tylko nazwę tagu i użytkownika, można utworzyć tag z User jako klucza nadrzędnego i Name jak id tagu:

User -> Name -> Tag 

do stworzenia tego znacznika, należy użyć:

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

Następnie podczas pobierania tagów

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

Następnie każdy klucz został zapisany w Article.tags będzie zawierać klucz użytkownika i Tag imię! Pozwoli to zaoszczędzić od czytania w Tag, aby uzyskać te wartości.