Eagle
Eagle

Reputation: 1074

GAE: from RDBMS to NDB problems

I'm learning to work in GAE. I've read a lot of papers, all NDB docs from Google and asome questions here. I'm so used to SQL, but transform my way of think the last 20 years to NoSQL is a little hard for me, and all those different solutions gave here, drives me crazy.

I have the next simple structure: BOOKS than can have CHAPTERS CHAPTERS that can have VOTES For example, Book "Sentinel" can have 3 chapters, and every chapter will have 0, 8 and 12 votes each.

In a traditional SQL I just make foreign keys from VOTES to CHAPTERS and BOOKS, and from CHAPTERS to BOOKS.

I do this for my models:

class Book(ndb.Model):
    title = ndb.StringProperty(required=True)
    author = ndb.StringProperty(required=True)
    created = ndb.DateTimeProperty(auto_now_add=True)

    # Define a default ancestor for all the books
    @staticmethod
    def bookKey(group='books'):
        return ndb.Key(Book, group)

    # Search all
    @classmethod
    def getAll(cls):
        q = Book.query(ancestor=cls.bookKey())
        q = q.order(Book.title)
        books = q.fetch(100)
        return books

    @classmethod
    def byId(cls, id):
        book = Book.get_by_id(long(id), cls.bookKey())

    # Get all the Chapters for a book
    def getChapters(self):
        chapters = Chapter.query(ancestor=self).order(Chapter.number).fetch(100)
        return chapters

class Chapter(ndb.Model):
    """ All chapters that a book have """
    title = ndb.StringProperty(required=True)
    number = ndb.IntegerProperty(default=1)
    created = ndb.DateTimeProperty(auto_now_add=True)

    book = ndb.KeyProperty(kind=Book)

    # Search by Book (parent)
    @classmethod
    def byBook(cls, book, limit=100):
        chapter = book.getChapters()
        return chapter

    # Search by id
    @classmethod
    def byId(cls, id, book):
        return Chapter.get_by_id(long(id), parent=book)

class Vote(ndb.Model):
    """ All votes that a book-chapter have """
    value = ndb.IntegerProperty(default=1)

    book = ndb.KeyProperty(kind=Book)
    chapter = ndb.KeyProperty(kind=Chapter)

Well, my doubts are:

  1. Is this approach correct?
  2. The function bookKey() I've created is good to have a "Dummy Ancestor" in order to ensure that all entities are using ancestors?
  3. Must I define in the Vote class a reference for a book and for a chapter, as it was a foreign keys (just like I think I've done)?
  4. Is well defined the way to retrieve the chapters from a book? I mean, in the Chapter class the function byBook uses a function from the Book class. Or must I avoid to use functions from other entity to have a more clean code?
  5. how can I retrieve all the votes for a chapter?
  6. Which are the rigth ways to get the sum of all the votes for a especific chapter and for especific book?

Finally, I will display a single table with all my books. In the table I want to have the sum of all the votes for each book. For example:

Name | Votes Sentinel | 30 votes The witch | 4 votes

How can I get this info, especifically, the counted votes.

Then, clicking on the book name, I want to show all his chapters (I supose that is then when I must use the byBook function on Chapter model, right?).

Which is the GQL I need to obtain this kind of data?

Thanks in advance.

Upvotes: 2

Views: 315

Answers (1)

dragonx
dragonx

Reputation: 15143

Good start. GAE's datastore is kinda confusing. Because it's schemaless, I've found that dealing with entities is much more akin to dealing with objects/data structures in memory than dealing with database tables.

Here's a few things I'd do differently:

  • It appears you are creating all your books under a single ancestor. Terrible idea. Screws you over in terms of performance. Unless there is some transactional operation you need to do on a group of books that's not in your current code, this is not right.

  • From the Book.getChapters() function it appears that you want to make a book the ancestor of a bunch of chapters. This is probably a good use of an ancestor. I don't see the code where you create chapters, but make sure the appropriate book is specified as the ancestor.

  • I'd simply include a vote as an attribute inside a book or chapter. There's no need to make it a separate kind that you need to issue extra queries on.

  • If the number of chapters per book would be limited, I'd consider using a StructuredProperty for the chapters. StructuredProperties are essentially structured data within a parent entity (Book). You'd be limited by the maximum size of the Book entity (1MB), but if it fits, it'll save you the cost of doing extra queries, since you wouldn't be querying on chapters without the appropriate book anyways.

Upvotes: 2

Related Questions