3

我有几百个键,都是相同的模型,我已经预先计算了:

candidate_keys = [db.Key(...), db.Key(...), db.Key(...), ...]

其中一些键是指数据存储中的实际实体,而有些则不是。我希望确定哪些键确实对应于实体。

不必知道实体内的数据,只需知道它们是否存在即可。

一种解决方案是使用 db.get():

keys_with_entities = set()
for entity in db.get(candidate_keys):
  if entity:
    keys_with_entities.add(entity.key())

但是,此过程将从商店中获取所有实体数据,这是不必要且昂贵的。

第二个想法是使用带有IN过滤器的查询key_name,手动获取 30 个块以满足IN伪过滤器的要求。IN但是,过滤器不允许仅键查询。

有没有更好的办法?

4

3 回答 3

3

App Engine 数据存储区不直接支持 IN 过滤器;它们是在客户端库中实现的便利。一个包含 30 个值的 IN 查询被转换为 30 个相等查询,每个查询对应一个值,从而产生 30 个常规查询!

由于往返时间和甚至仅键查询的费用,我怀疑您会发现简单地尝试在一次获取中获取所有实体是最有效的。但是,如果您的实体很大,您可以进行进一步优化:对于您插入的每个实体,插入一个空的“存在”实体作为该实体的子实体,并在查询中使用它。例如:

foo = AnEntity(...)
foo.put()
presence = PresenceEntity(key_name='x', parent=foo)
presence.put()
...
def exists(keys):
  test_keys = [db.Key.from_path('PresenceEntity', 'x', parent=x) for x in keys)
  return [x is not None for x in db.get(test_keys)]
于 2009-10-22T14:04:28.683 回答
0

在这一点上,我唯一的解决方案是使用 键手动查询keys_only=True,每个键一次。

for key in candidate_keys:
  if MyModel.all(keys_only=True).filter('__key__ =', key).count():
    keys_with_entities.add(key)

这实际上可能比仅批量加载实体并丢弃它们要慢,尽管批量加载也会影响Data Received from API配额。

于 2009-10-22T13:29:21.030 回答
0

如何不这样做(根据尼克约翰逊的回答更新):

我也在考虑添加一个参数,以便能够使用IN过滤器对其进行扫描。

class MyModel(db.Model):
  """Some model"""
  # ... all the old stuff
  the_key = db.StringProperty(required=True) # just a duplicate of the key_name

#... meanwhile back in the example

for key_batch in batches_of_30(candidate_keys):
  key_names = [x.name() for x in key_batch]
  found_keys = MyModel.all(keys_only=True).filter('the_key IN', key_names)
  keys_with_entities.update(found_keys)

应该避免这种情况的原因是属性上的 IN 过滤器按顺序执行索引扫描,并在IN集合中的每个项目上查找一次。每次查找需要 160-200 毫秒,因此很快就会变成非常慢的操作。

于 2009-10-22T13:36:51.653 回答