Google App Engine Datastore:如果父键未知,如何通过 ID/名称获取实体?

Posted

技术标签:

【中文标题】Google App Engine Datastore:如果父键未知,如何通过 ID/名称获取实体?【英文标题】:Google App Engine Datastore: How to get entity by ID/Name if parent key is unknown? 【发布时间】:2012-12-27 10:31:21 【问题描述】:

有两种实体:User 和 Trip。 User 是 Trip 的父级, Trip 是 User 的子级。

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

如果父键未知,如何通过 ID/名称获取实体?

【问题讨论】:

+1 - 这是一个完全有效的问题。谁把 -1 - 解释一下? 为什么不使用搜索? [见][1] [1]:***.com/questions/12675664/… @Lapteuh - 你有没有看过你所指的答案?他们提出的查询需要完整的父键(种类+id/名称),而这正是 OP 所没有的。 那么,我们该怎么办?将键值发送给客户端并使用该键检索对象? @nurp 我认为应该将用户 ID 存储在会话中,然后将旅行 ID 传递给客户端。因此,当客户端想要更新行程时,会将行程 ID 发送回服务器,然后服务器从会话中获取用户 ID,以创建完整的行程密钥。例如: Key userKey = KeyFactory.createKey(TABLE_USER, userId);密钥tripKey = KeyFactory.createKey(userKey, TABLE_TRIP, tripId); 【参考方案1】:

我想出了 3 个可以解决这个问题的替代方案,恕我直言,这是一个非常重要的替代方案。

---- 第一种选择----

如果您的行程 ID 是根据其他属性计算的,则有一种方法。而不是通过其id 获取Trip,而是从其他计算属性中获取它。假设您的 Trip 的 id 是由某个规范名称(您从其完整标题推断出的 URN)计算的,例如如果 Trip 的全名是

珠穆朗玛峰之旅

您的规范名称可能是voyage-to-the-everest,这是您用作密钥名称的字符串。因此,不要使用datastore.get 获取元素,而是使用:

@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();

无论父 (User) 是谁,这都会获取实体 (Trip)。

---- 第二种选择----

在访问一个元素之前,您可能首先必须列出它们,然后选择一个并转到访问链接。正如我们所知,使用任务的 id 是不够的,因为它仅对其父级 (User) 是唯一的,但您可以使用 url 安全 id 而不是显示 id

entity.getKey().toUrlSafe()

所以在从实体到对象的转换中,分配Task 元素,这个id 在base-64 encode 中编码。要从 url 安全使用中取回密钥

Key.fromUrlSafe

它将保证您将始终使用全局唯一 ID。

---- 第三种选择----

使用HATEOAS可以指定访问Task的链接,所以如果任务有一些id,比如parentIduserId,这基本上是获取他的父节点的id,这可能很容易你建立一个指向这样的网址的链接

http://base-url.com/users/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
    
  

如果您使用parentId 而不是userId,则可以使用所有节点指定是否有父节点的接口来解决。使用parent 属性甚至可以更灵活地定义整个父层次结构:

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

虽然强烈建议使用 HATEOAS,但您可以推断出具有 json 结构的相同 url,例如

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

【讨论】:

【参考方案2】:

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

除非您指定祖先键,否则 query with key filter 也不会找到具有父级的实体。

【讨论】:

【参考方案3】:

你可以这样做:

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

【讨论】:

不,除非您在 key 中指定父级,否则它不会找到任何东西。【参考方案4】:

如果您在“根”实体下创建所有“用户”实体并向“旅行”实体添加“uuid”属性,则可以查找具有指定 UUID 的单个“旅行”。

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

【讨论】:

【参考方案5】:

@peter-knego 在最初的问题中:用户是 Trip 的父母。要通过 id 获取实体,您需要使用父项重建密钥以获取完整密钥。但是您可以避免这种情况,只需为 Trip 的完整密钥分配 id。您可以使用分配的 id 构造完整的密钥。这是我的逻辑。

【讨论】:

以上是关于Google App Engine Datastore:如果父键未知,如何通过 ID/名称获取实体?的主要内容,如果未能解决你的问题,请参考以下文章

Google Cloud 中的 Google Compute Engine、App Engine 和 Container Engine 有啥区别?

连接 Google App Engine 和 Google Compute Engine

Google App Engine Flexible 和 Google Container Engine 之间的区别?

如何在 Google Cloud App Engine 上使用 PubSub 创建订阅者,该订阅者通过 Publisher 从 Google Cloud App Engine Flex 收听消息?

将 Meteor 部署到 Google App Engine 2017

Google BigQuery 的 Google App Engine 授权