我创建了一个REST服务,用于将数据从iPhone同步到我们的GAE。
在少数情况下,我们会在同一天获得双重条目。我相信我在Record
类的设计中犯了一个错误,并想在我尝试任何数据迁移之前仔细检查我的假设和可能的解决方案是否正确。
首先我浏览所有传入的json_records,如果它找到count == 1,则表示存在需要更新的现有条目(这就是有时错误的地方! )。然后它检查时间戳,只有在传入的时间戳更大时才更新它,否则它会忽略它。
for json_record in json_records:
recordsdb = Record.query(Record.user == user.key, Record.record_date == date_parser.parse(json_record['record_date']))
if recordsdb.count() == 1:
rec = recordsdb.fetch(1)[0]
if rec.timestamp < json_record['timestamp']:
....
rec.put()
elif recordsdb.count() == 0:
new_record = Record(user=user.key,
record_date = date_parser.parse(json_record['record_date']),
notes = json_record['notes'],
timestamp = json_record['timestamp'])
new_record.put()
如果我没有错,这种查询对象的方式并不保证它是最新版本。
recordsdb = Record.query(Record.user == user.key, Record.record_date == date_parser.parse(json_record['record_date']))
我相信GAE / Highreplication数据存储可以确保您面前有最新数据的唯一方法是使用密钥检索它。
因此,如果这个假设是正确的,我应该首先用日期字符串保存我的记录。
jsondate = date_parser.parse(json_record['record_date']
new_record = Record(id = jsondate.strftime("%Y-%m-%d")
user=user.key,
record_date = jsondate),
notes = json_record['notes'],
timestamp = json_record['timestamp'])
new_record.put()
当我必须查询该记录是否已经存在时,我会通过它的密钥得到它:
jsondate = date_parser.parse(json_record['record_date']
record = ndb.Key('Record', jsondate.strftime("%Y-%m-%d")).get()
现在如果record为null,那么我必须创建一个新记录。 如果记录!= null那么我必须更新它。
我的假设和解决方案是否正确? 如何使用日期字符串作为密钥来迁移此数据?
更新
我刚刚意识到我犯了另一个错误。我无法将记录设置为其日期字符串。因为每个用户都可以拥有一天的记录,这会导致密钥重复。
我认为解决问题的唯一方法是通过ancestor/parent
,我仍然试图解决这个问题。
更新2:
试着看看我是否理解Patrick的解决方案。如果它没有意义,或者有更好的方法,请纠正我。
我会在现有模型中添加is_fixed
标志:
class Record(ndb.Model)
user = ndb.KeyProperty(kind=User)
is_fixed = ndb.BooleanProperty()
...
然后我会通过游标查询现有记录,然后删除它们:
q = Record.query()
q_forward = q.order(Record.key)
cursor = None
while True:
records, cursor, more = q_forward.fetch_page(100)
if not records:
break;
for record in records:
new_record = Record(parent=user.key, ... )
new_record.is_fixed = True
new_record.put()
//now delete the old ones, I wonder if this would be an issue:
for old in Record.query()
if not old.is_fixed:
old.delete()
答案 0 :(得分:1)
由于您的查询始终是每个用户,因此我建议让用户成为用户的祖先。
正如您所提到的,您遇到的问题是最终一致性的结果 - 您的查询无法保证获得最新结果。使用祖先查询,结果将为strongly consistent。
需要注意的一件重要事情是,在实体组(单个祖先)中,每秒限制为1次更新。由于每个用户只有一条记录,这似乎不应该是一个问题。
您的代码实际上已经全部设置为用户祖先:
new_record = Record(parent=user.key, # Here we say that the ancestor of the record is the user
record_date =date_parser.parse(json_record['record_date']),
notes = json_record['notes'],
timestamp = json_record['timestamp'])
然后现在你可以使用强烈一致的查询:
Record.query(ancestor == user.key, Record.record_date == date_parser.parse(json_record['record_date']))
但是,更改现有记录的ID会遇到同样的问题。将祖先添加到实体有效地改变了将祖先作为前缀的关键。为此,您必须完成所有记录并以其用户为祖先创建新记录。您可以使用查询来批量获取结果(使用cursors前进),或者如果您有大量数据,则可能值得探索MapReduce library。