【问题标题】:Best method to determine which of a set of keys exist in the datastore确定数据存储中存在一组键的最佳方法
【发布时间】:2009-10-22 13:18:21
【问题描述】:

我有几百个键,都是我预先计算好的同一个模型:

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

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

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

有没有更好的办法?

【问题讨论】:

  • 这些是完全任意的键还是有任何其他方法可以将另一个参数关联起来?
  • 嗨,让-卢。在我的具体情况下,我所知道的是它们都是同一个模型类的键;然而,一个通用的解决方案也将受到赞赏。如果我可以搜索并避免实际获取实体,我愿意用参数装饰它们。我希望这能回答你的问题。
  • 如果尼克的回答让你满意,你可以接受。
  • 谢谢,SilentGhost,我知道 :) 哦,你是在谈论我的标签。我是认真的!应该有一个由尼克约翰逊回答的标签!无论如何,我喜欢让问题保持活跃几个小时以鼓励更多参与,然后在它变慢时接受。

标签: python google-app-engine google-cloud-datastore


【解决方案1】:

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)]

【讨论】:

  • 谢谢。我会指出其他有此问题的人 stackoverflow.com/questions/1003247/… answer to another IN operator question 你解释说 db.get() 需要 20-40 毫秒,而使用 IN 过滤器获取需要 160-200 毫秒乘以查询中的属性数
  • 如果我错了,请纠正我,但我认为您可能想要return [x.parent() for x in db.get(test_keys) if x is not None],以便您真正获得指向实际实体的键。另外值得注意的是,此解决方案适用于任何模型的键。
  • 更正我的更正:[x.key().parent() for x in db.get(test_keys) if x is not None]
  • 好吧,在示例中,exists() 返回一个布尔数组,指示输入数组中的相应值是否存在于数据库中,而您的更改返回确实存在的键列表。哪个更有用取决于您需要它。 :)
【解决方案2】:

此时,我唯一的解决方案是使用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 配额。

【讨论】:

    【解决方案3】:

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

    我也在考虑添加一个参数,以便能够使用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 毫秒,因此很快就会变成非常慢的操作。

    【讨论】:

      猜你喜欢
      • 2012-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-21
      • 2011-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多