MapReduce input_reader的ndb.Key过滤器

时间:2013-05-07 15:57:58

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

为input_reader使用新的Google App Engine MapReduce库过滤器我想知道如何通过ndb.Key过滤。

我读了这个post,我在过滤器元组中使用了datetime,string,int,float,但是我如何通过ndb.Key过滤?

当我尝试按ndb.Key过滤时,我收到此错误:

BadReaderParamsError: Expected Key, got u"Key('Clients', 406)"

或者这个错误:

TypeError: Key('Clients', 406) is not JSON serializable

我试图传递一个ndb.Key对象和ndb.Key的字符串表示。

以下是我的两个过滤元组:

样本1:

input_reader': {
  'input_reader': 'mapreduce.input_readers.DatastoreInputReader',
  'entity_kind': 'model.Sales',
  'filters': [("client","=", ndb.Key('Clients', 406))]
}

样本2:

input_reader': {
  'input_reader': 'mapreduce.input_readers.DatastoreInputReader',
  'entity_kind': 'model.Sales',
  'filters': [("client","=", "%s" % ndb.Key('Clients', 406))]
}

4 个答案:

答案 0 :(得分:2)

这有点棘手。

如果查看code on Google Code,您可以看到mapreduce.model定义了一个JSON_DEFAULTS dict,它确定了在JSON序列化/反序列化中获得特殊情况处理的类:默认情况下,约会时间。因此,您可以将ndb.Key类修补到那里,并为其提供执行序列化/反序列化的函数 - 类似于:

from mapreduce import model

def _JsonEncodeKey(o):
  """Json encode an ndb.Key object."""
  return {'key_string': o.urlsafe()}

def _JsonDecodeKey(d):
  """Json decode a ndb.Key object."""
  return ndb.Key(urlsafe=d['key_string'])

model.JSON_DEFAULTS[ndb.Key] = (_JsonEncodeKey, _JsonDecodeKey)
model._TYPE_IDS['Key'] = ndb.Key

您可能还需要重复最后两行来修补mapreduce.lib.pipeline.util

另请注意,如果执行此操作,则需要确保在运行mapreduce的任何部分的任何实例上运行:最简单的方法是编写一个导入上述注册码的包装脚本,以及mapreduce.main.APP,并覆盖app.yaml中的mapreduce网址以指向您的包装。

答案 1 :(得分:1)

根据DatastoreInputReader创建自己的输入阅读器,class DatastoreKeyInputReader(input_readers.DatastoreKeyInputReader): """Augment the base input reader to accommodate ReferenceProperty filters""" def __init__(self, *args, **kwargs): try: filters = kwargs['filters'] decoded = [] for f in filters: value = f[2] if isinstance(value, list): value = db.Key.from_path(*value) decoded.append((f[0], f[1], value)) kwargs['filters'] = decoded except KeyError: pass super(DatastoreKeyInputReader, self).__init__(*args, **kwargs) 知道如何解码基于密钥的过滤器:

def encode_filters(filters):
    if filters is not None:
        encoded = []
        for f in filters:
            value = f[2]
            if isinstance(value, db.Model):
                value = value.key()
            if isinstance(value, db.Key):
                value = value.to_path()
            entry = (f[0], f[1], value)
            encoded.append(entry)
        filters = encoded

    return filters

在过滤器上运行此功能,然后将其作为选项传递:

{{1}}

答案 2 :(得分:1)

您是否了解to_old_key()和from_old_key()方法?

答案 3 :(得分:1)

我遇到了同样的问题,并提出了计算属性的解决方法。

您可以使用密钥ID向Sales模型添加新的ndb.ComputedProperty。 ID只是字符串,所以你不会有任何JSON问题。

client_id = ndb.ComputedProperty(lambda self: self.client.id())

然后将该条件添加到mapreduce查询过滤器

input_reader': {
  'input_reader': 'mapreduce.input_readers.DatastoreInputReader',
  'entity_kind': 'model.Sales',
  'filters': [("client_id","=", '406']
}

唯一的缺点是,在调用put()参数之前,计算属性不会被索引和存储,因此您必须遍历所有Sales实体并保存它们:

for sale in Sales.query().fetch():
  sale.put()