Kappa Leonis
Kappa Leonis

Reputation: 661

Google App Engine Datastore: How to get entity by ID/Name if parent key is unknown?

There are two kinds of entity: User and Trip. User is parent to Trip and Trip is child to User.

For privacy consideration I am POSTing only Trip ID/Name. Because it is looks like a Trip Key contains encoded User ID/Name.

How to get entity by ID/Name if parent key is unknown?

Upvotes: 26

Views: 11409

Answers (6)

yukung
yukung

Reputation: 19

trip_query = datastore_client(kind='Trip')
trips = list(trip_query.fetch())
for trip in trips:
    # .key.id_or_name
    print(f'trip id/name:{trip.key.id_or_name}')

Upvotes: 0

EliuX
EliuX

Reputation: 12685

I worked out 3 alternatives that may solve this issue, which is a very important one IMHO.

---- 1st alternative ----

If your Trip's Id is calculated from another attribute, there is a way. Instead of getting the Trip by its id get it from that other calculated property. Let's imagine your Trip's id is calculated by some canonical name (A URN you infer from its full title), e.g. if the Trip's full name is

Voyage to the Everest

your canonical name may be voyage-to-the-everest and this is the String you use as name for the Key. So instead of getting the element using datastore.get use:

@Override
public Optional<Trip> findById(String tripCanonicalName) {
   StructuredQuery.PropertyFilter eqTripCanonicalName = StructuredQuery.PropertyFilter
                    .eq("canonicalName", tripCanonicalName);

   EntityQuery query = Query.newEntityQueryBuilder().setKind("Trip")
                    .setFilter(eqTripCanonicalName).setLimit(1).build();

   QueryResults<Entity> results = getDatastoreService().run(query);

   if (results.hasNext()) {
       return Optional.of(fromEntity(results.next()));
   }

   return Optional.empty();
}

this will get the entity (Trip) no matter whose parent (User) be.

---- 2nd alternative ----

Before accessing an element probably you first have to list them and then select one and go to an access link. As we know using the id of the task wont be enough because it will be unique only for its parent (User), but instead of showing that id you may use the url safe id:

entity.getKey().toUrlSafe()

so in the conversion from entity to object assign the Task element this id encoded in a base-64 encode. To get the key back from the url safe use

Key.fromUrlSafe

It will guarantee you will always gonna use a global unique id.

---- 3rd alternative ----

Using HATEOAS you can specify the link for accesing the Task, so if the task has some id such as parentId or userId which is basically getting his parent node's id, it could be very easy for you to stablish a link pointing to a url like this

http://base-url.com/users/{userId}/tasks/{taskId}

So in a HATEOAS request this could be indicated in the links, that indicates the allowed actions for the element, so for viewing the element use self, e.g

{
  "id": "voyage-to-the-everest",
  "name":"Voyage to the Everest",
  "userId": "my-traveler-user-id",
  "_links":{
    "self":{
      "href":"http://localhost:8080/users/my-traveler-user-id/tasks/voyage-to-the-everest
    }
  }
}

If instead of a userId you use a parentId you may work it out with an interface where all the nodes specify if they have a parent or not. Even it could be more flexible with a parent property where you define the whole parent hierarchy:

public interface DatastoreNode{
  String getParentId();
  String getParentKind();
  String getParentUrlTag();
  DatastoreNode getParent();
}

Although HATEOAS is strongly recommended you can infer the same url having a json structure such as

   {
      "id": "voyage-to-the-everest",
      "name":"Voyage to the Everest",
      "parent": {
           parentKind: "User",
           parentId: "my-traveler-user-id",
           parentUrlTag: "users",
           parent: {}  
       }
    }

Upvotes: 0

Peter Knego
Peter Knego

Reputation: 80340

You can't. Parent key is part of the entity key and you need a full key to get an entity.

Also query with key filter won't find entities with parents unless you specify the ancestor key.

Upvotes: 19

user1080381
user1080381

Reputation: 1786

You can do something like this:

public Entity GetEntity(String kind, String idName) 
        throws EntityNotFoundException{
    Key key = KeyFactory.createKey(kind, Long.parseLong(idName));
    return datastore.get(key);
}

Upvotes: 0

vharron
vharron

Reputation: 1207

If you create all of your "User" entities under a "Root" entity and add a "uuid" property to your "Trip" entity, you can look for the single "Trip" with the specified UUID.

Filter uuidFilter = new FilterPredicate("uuid", FilterOperator.EQUAL, uuid.toString());
Query q = new Query("Trip").setAncestor(root.getKey()).setFilter(uuidFilter);

Upvotes: 1

Arthur G
Arthur G

Reputation: 117

@peter-knego In initial question: User is parent for Trip. To get entity by id you need reconstruct key with parent to get full key. But you may avoid this, just allocate ids for full key of Trip. And you may construct full key with allocated id. This is my logic.

Upvotes: 0

Related Questions