3

I am new to Datastore and I am trying to create a simple app that tracks books borrowing.

I would like the DB schema to be as follows:

books:
 book_id
  name:
  borrowing:
   timestamp:
    user_id
    firstname
    email

users:
 name:
 borrowed:
  timestamp:
   book_id

I am using the console to configure the DB.

I have defined two Kinds: Books and User

Business logic:

  • 1 book can be borrowed by n users.
  • 1 user can borrow n books

Basically, when a user borrows a book I want the two following inserts to occur:

  • a new borrowing entry is appended to the book entity of type Book, with the current timestamp as key and user_id, firstname and email as properties
  • a new borrowed entry is appended to the user entity of ty User, with the current timestamp as key and the book_id as property

How can I achieve this (managing nested lists children) with Datastore? Through embedded entities? Parent ref?

Here is an example of what I expect:

{
  "books": {
      "book1": {
          "name": "book number 1",
          "borrowing": {
              "1234567890": {
                  "user_id": "user1",
                  "firstname": "john",
                  "email": "john@example.com"
              },
              "2234567890": {
                  "user_id": "user2",
                  "firstname": "robin",
                  "email": "robin@example.com"
              }
          }
      }
      ...
  },
    "users": {
        "user1": {
            "firstname": "robin",
            "email": "robin@example.com",
            "borrowed": {
                "1234567890": {
                    "book_id": "book1"
                },
                "3247829398": {
                    "book_id": "book99"
                }
            }
        },
        ...
    }
}
Manuel RODRIGUEZ
  • 2,131
  • 3
  • 25
  • 53

1 Answers1

3

In general this is not a scalable approach: every time a user borrows a book you'd have to re-write both the user and book entities, which will get progressively slower as both entities will keep growing.

I'd suggest a different approach: add a new type to your schema, let's call it borrowed_book, representing a book boorowed by a user:

borrowed_book:
  book_id
  user_id
  timestamp

Now every time a user borrows a book you'd simply create one such borrowed_book entity, pointing to both the book and the user. No changes to the user or the book entities. And no nesting required.

Side note: I'd place the firstname and email properties under the user entity type, they don't really belong to the borrowing event where they would be duplicated every time such event occurs for the same user.

Also try to not get confused by the ancestry - it is not required for establishing relationships, see E-commerce Product Categories in Google App Engine (Python)

Dan Cornilescu
  • 39,470
  • 12
  • 57
  • 97
  • (I probably should/will put this in a new question): I have a follow up question with your proposed design. If we want to have a feature to support a query like "what are the books user A has ever borrowed that have a category of 'picture book' and published before 2000". Is it possible to have a query in the current setup? So I guess we have to copy those related properties (publish_year and category) from book entity to borrowed_book entity. In that case, if there is update on a book's publish_year, how could borrowed_book be updated automatically? – greeness May 03 '18 at 07:36
  • 1
    Indeed, a single query wouldn't be enough. It could be done with 2 queries and intersecting the results. If you copy the book info into the borrowed info you could handle a book update with a push task making a query(+cursor) for borrowed info by book_id and updating each result. – Dan Cornilescu May 03 '18 at 12:08