ndb retrieving entity key by ID without parent
Asked Answered
P

4

7

I want to get an entity key knowing entity ID and an ancestor. ID is unique within entity group defined by the ancestor. It seems to me that it's not possible using ndb interface. As I understand datastore it may be caused by the fact that this operation requires full index scan to perform. The workaround I used is to create a computed property in the model, which will contain the id part of the key. I'm able now to do an ancestor query and get the key

class SomeModel(ndb.Model):
    ID = ndb.ComputedProperty( lambda self: self.key.id() )

    @classmethod
    def id_to_key(cls, identifier, ancestor):
        return cls.query(cls.ID == identifier,
                         ancestor = ancestor.key ).get( keys_only = True)

It seems to work, but are there any better solutions to this problem?

Update It seems that for datastore the natural solution is to use full paths instead of identifiers. Initially I thought it'd be too burdensome. After reading dragonx answer I redesigned my application. To my suprise everything looks much simpler now. Additional benefits are that my entities will use less space and I won't need additional indexes.

Perkoff answered 18/10, 2012 at 12:39 Comment(2)
Sounds like you got the right answer!Angara
ID is not guaranteed unique within an entity group - only for a given parent entity.Schematize
A
8

I ran into this problem too. I think you do have the solution.

The better solution would be to stop using IDs to reference entities, and store either the actual key or a full path.

Internally, I use keys instead of IDs.

On my rest API, I used to do http://url/kind/id (where id looked like "123") to fetch an entity. I modified that to provide the complete ancestor path to the entity: http://url/kind/ancestor-ancestor-id (789-456-123), I'd then parse that string, generate a key, and then get by key.

Ardor answered 18/10, 2012 at 15:2 Comment(4)
You are right. Everything is easier with full ancestor path. It seems that this is the right way to do such things in datastore. ThanksPerkoff
Don't forget there is a "key" type in the datastore you can use to store actual keys (KeyProperty).Unfrock
In a side note, Java has these methods to serialize and deserialize keysVoroshilovsk
kurl = instance.key.urlsafe() -> instance = nb.Key(urlsafe=kurl) I know urlsafe() generates a ugly string but it makes the jobBrief
R
3

Since you have full information about your ancestor and you know your id, you could directly create your key and get the entity, as follows:

my_key = ndb.Key(Ancestor, ancestor.key.id(), SomeModel, id)
entity = my_key.get()

This way you avoid making a query that costs more than a get operation both in terms of money and speed.

Hope this helps.

Raseta answered 18/10, 2012 at 13:10 Comment(2)
It is a good solution if ancestor is also a parent, but it's not my casePerkoff
Since you used the term "ancestor", I thought you meant that it was a parent. If this is not the case, then your approach I think is the correct one.Raseta
M
1

I want to make a little addition to dargonx's answer.

In my application on front-end I use string representation of keys:

str(instance.key())

When I need to make some changes with instence even if it is a descendant I use only string representation of its key. For example I have key_str -- argument from request to delete instance':

instance = Kind.get(key_str)
instance.delete()
Milliary answered 11/4, 2013 at 7:17 Comment(0)
A
1

My solution is using urlsafe to get item without worry about parent id:

pk = ndb.Key(Product, 1234)
usafe = LocationItem.get_by_id(5678, parent=pk).key.urlsafe()
# now can get by urlsafe
item = ndb.Key(urlsafe=usafe)
print item
Aristippus answered 10/2, 2017 at 16:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.