8
votes

My background with designing data stores comes from Core Data on iOS, which supports properties having a one-to-many relationship with another entity.

I'm working on an App Engine project which currently has three entity types:

  • User, which represents a person using the app.
  • Project, which represents a project. A User may be associated with many projects.
  • Post, which is the main content behind a Project. A Project may have many posts.

Currently, User has a property, projects, that is a one-to-many relationship to Project entities. Project has a property, posts, that is a one-to-many relationship to Post entities.

In this case, is Datastore's Reference Property or NDB's Structured Property better for the job (and how are the two conceptually different)? Is there a better way to structure my data?

2

2 Answers

11
votes

By reference property you probably mean Key Property. This is a reference to another datastore entity. It is present in both db and ndb APIs. Using these, you can model a many to one relationship by pointing many entities to the key of another entity.

Structured property is a completely different beast. It allows you to define a data structure, and then include it within another entity.

Here's an example from the docs where you include multiple addresses for a single contact:

class Address(ndb.Model):
  type = ndb.StringProperty() # E.g., 'home', 'work'
  street = ndb.StringProperty()
  city = ndb.StringProperty()

class Contact(ndb.Model):
  name = ndb.StringProperty()
  addresses = ndb.StructuredProperty(Address, repeated=True)

guido = Contact(name='Guido',
                addresses=[Address(type='home',
                                   city='Amsterdam'),
                           Address(type='work',
                                   street='Spear St',
                                   city='SF')])

guido.put()

For your specific application I'd recommend using NDB (it's always best to use the latest version of the api available), with the following:

Post model included under Project model as a repeated structured property. Users include a repeated KeyProperty that contains the keys of the Projects they have permissions to.

To make it a bit more complex, you can create a another model to represent projects and permissions/roles, and then include that as a repeated structured property within the user model.

The main reason you want to hang on to the keys, is to keep the data accessible in light of HRDs eventual consistency.

Let me know if you need any more help on this.

EDIT:

To clarify, here's the proposed structure:

Models:

  • User
  • User-Project-Mapping (optional, needed to handle permissions)
  • Project
  • Post

User model should contain User-Project-Mapping as repeated structured property.

Project model should contain Post as repeated structured property.

User-Project-Mapping only needs to contain Key reference to the Project and relevant permissions representation.

Since this sounds like a commercial project, if you'd like further help with this, I'll gladly consult for you. Hope you have enough to succeed!

3
votes

There is another point that was not mentioned and might be relevant: entities inserted in a StructuredProperty "are not full-fledged entities", as mentioned in this part of the docs. Below is the complete quote (it refers to the same example mentioned in the answer by @Sologoub):

Although the Address instances are defined using the same syntax as for model classes, they are not full-fledged entities. They don't have their own keys in the Datastore. They cannot be retrieved independently of the Contact entity to which they belong.

This may cast some limitations in the design given that you cannot reuse an entity's property without duplicating data. The KeyProperty, on the other side, refers to another entity's key and therefore represents entities relationship in a more "relational" way. And KeyProperties can also be repeated: just include the repeated=True parameter.