为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))]
}
答案 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()