3
votes

My (python) app is using several entities, many of them in a 1:1 relationship. For example:

class Main(ndb.Model):
    field1 = ndb.StringProperty()
    #peer_key = ndb.KeyProperty(kind='Peer')

class Peer(ndb.model):
    field2 = ndb.IntegerProperty()
    #main_key = ndb.KeyProperty(kind='Main')

Some of the Main entities may have a Peer entity (created after the Main entity) in exactly a 1:1 relationship.

I was thinking that at creation of the Peer entity I could simply specify a numerical ID equal to the corresponding Main entity's ID (auto-generated by the datastore and thus guaranteed to be unique):

main_entity = Main(field1='blah')
main_entity.put()

peer_entity = Peer(id=main_entity.key.id(), field2=10)
peer_entity.put()

This way I could significantly simplify my app's logic, without the need of storing and processing the entity keys to cross-reference these entities, especially when passing them across requests. For example, in a context where I have the main entity I could simply do:

peer_entity = Peer.get_by_id(main_entity.key.id())

Similarly, in a context where I have the peer entity:

main_entity = Main.get_by_id(peer_entity.key.id())

According to the documentation keys are just (kind, id) pairs, meaning as long as the kinds are different and the ids are unique I shouldn't have problems. The tests I've done so far (on the development server) appear to be working fine.

Is my thinking correct or am I missing something (and was just lucky so far in my testing)?

1
I believe you should be fine. What counts is the key and the key always includes the kind, id/name and possibly a parent, therefor you can reuse the id in a 1:1 relationship (although one might ask why you don't migrate the properties of Peer into Main or vice versa). You have to make sure though that no two requests generate the same id for the different kinds (however unlikely) which would create a racing condition. I recently (yesterday) created a unit test to proof the same you've done in Java (stackoverflow.com/questions/34909623) - konqi
I have different entities as the particular update logic would lead to many transaction collisions (and also they have different lifespans). Hm, right - need to think a bit more about the racing conditions. Thx. - Dan Cornilescu
The racing condition can only ever occur when you have two entities A and B and you can create A or B and then create the respective other. If you only ever create A and then B in order or the other way around (basically the case that Andrei Volgin just described in his answer) you're good. Then again, i assume you knew all this anyway - I've seen you around ;-) - konqi

1 Answers

3
votes

I use this approach all the time for many years, and never had any problems.

For example, every time you update an entity, every indexed field is updated too. For this reason, I often split a complex entity into "rarely updated" part and "frequently updated" part, and use different kinds but the same ID for both entities, e.g. AdEntity and AdCounterEntity. This way, as you correctly observed, the app logic is simplified as you need to remember only one ID to retrieve both entities as necessary.