Google app engine Google应用程序引擎数据存储:如果父密钥未知,如何通过ID/名称获取实体?

Google app engine Google应用程序引擎数据存储:如果父密钥未知,如何通过ID/名称获取实体?,google-app-engine,google-cloud-datastore,Google App Engine,Google Cloud Datastore,有两种实体:用户和旅行。用户是Trip的父级,Trip是用户的子级 出于隐私考虑,我只发布旅行ID/姓名。因为它看起来像一个Trip密钥包含编码的用户ID/名称 如果父项未知,如何按ID/名称获取实体?您不能。父键是实体键的一部分,需要完整键才能获取实体 除非您指定祖先密钥,否则也找不到具有父项的实体。@peter knego在初始问题中:用户是Trip的父项。若要按id获取实体,您需要使用父项重构密钥以获取完整密钥。但您可以避免这种情况,只需为行程的完整密钥分配ID即可。您可以使用分配的id构

有两种实体:用户和旅行。用户是Trip的父级,Trip是用户的子级

出于隐私考虑,我只发布旅行ID/姓名。因为它看起来像一个Trip密钥包含编码的用户ID/名称


如果父项未知,如何按ID/名称获取实体?

您不能。父键是实体键的一部分,需要完整键才能获取实体


除非您指定祖先密钥,否则也找不到具有父项的实体。

@peter knego在初始问题中:用户是Trip的父项。若要按id获取实体,您需要使用父项重构密钥以获取完整密钥。但您可以避免这种情况,只需为行程的完整密钥分配ID即可。您可以使用分配的id构造完整密钥。这是我的逻辑

如果在“根”实体下创建所有“用户”实体,并将“uuid”属性添加到“Trip”实体中,则可以查找具有指定uuid的单个“Trip”

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

您可以这样做:

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

我制定了3个备选方案来解决这个问题,这是IMHO非常重要的一个

----第一个备选方案--

如果您的旅行Id是根据另一个属性计算的,那么有一种方法。不要通过其
id
获取
Trip
,而是从其他计算属性获取它。让我们假设您的旅行id是由某个规范名称(您从其全名推断出的URN)计算的,例如,如果旅行的全名是

珠穆朗玛峰之旅

您的标准名称可能是
珠穆朗玛峰之旅
,这是您用作钥匙名称的字符串。因此,不要使用
数据存储获取元素。获取
使用:

@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();
}
因此,在从实体到对象的转换中,分配
任务
元素,该id编码为。要从url获取密钥,请安全使用

Key.fromUrlSafe
它将保证您将始终使用全球唯一id

----第三种备选方案--

使用可以指定用于访问
任务的链接
,因此,如果任务具有某种id,例如
parentId
userId
,基本上是获取其父节点的id,则可以很容易地建立指向如下url的链接

{userId}/tasks/{taskId}

因此,在HATEOAS请求中,这可以在链接中指示,这表示元素允许的操作,因此查看元素时使用
self
,例如

{
  "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
    }
  }
}
如果使用的不是
userId
,而是
parentId
,那么您可以使用一个接口来解决这个问题,其中所有节点都指定它们是否有父节点。甚至可以通过定义整个父层次结构的
parent
属性更灵活:

public interface DatastoreNode{
  String getParentId();
  String getParentKind();
  String getParentUrlTag();
  DatastoreNode getParent();
}
尽管强烈建议使用HATEOAS,但您可以推断相同的url具有json结构,例如

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

+这是一个完全正确的问题。谁放了-1-来解释一下?你为什么不使用搜索?[见][1][1]:@Lapteuh-你有没有看过你提到的答案?他们提出的查询需要完整的父键(kind+id/name),而这正是OP所没有的。那么,我们该怎么办呢?将键值发送给客户端并检索具有该键值的对象?@nurp我认为应该发生的是将用户ID存储在会话中,然后将Trip ID传递给客户端。因此,当客户机想要更新trip时,trip ID被发送回服务器,然后服务器从会话中获取用户ID,以创建完整的trip密钥。例如:Key userKey=KeyFactory.createKey(表\用户,用户ID);Key-tripKey=KeyFactory.createKey(userKey,TABLE\u TRIP,tripId);不,除非您在
键中指定父项,否则它不会找到任何内容。
   {
      "id": "voyage-to-the-everest",
      "name":"Voyage to the Everest",
      "parent": {
           parentKind: "User",
           parentId: "my-traveler-user-id",
           parentUrlTag: "users",
           parent: {}  
       }
    }