Datastoreのnamespaceを変更して複製するには?


NamespaceごとにDatastoreを複製した

GCPのDatastoreにはnamespaceによってパーティションを作る機能があります。一つのnamespaceにあるものを丸ごと違うnamespaceに移す、というのはどのようにやればいいのか。やることがあったので書いておきます。

Namespaceを切り替えることはできない?

そもそも最初にやりたかったのは、datastoreに保存された全てのEntityのnamespaceを変更できないかということ。
ただ、どうやらできない?

Namespace is the part of the Key. So you can't change or copy all data from one namespace to another. As I understand all you can do is to fetch all objects from one namespace and create NEW objects with the same properties in another namespace.

というわけで、デフォルトのnamespaceから新しくnamespaceを切って丸ごとコピーする方法をやってみました。

namespace_manager

namespaceを変えるには、namespace_managerを使います。

import logging
from google.appengine.api import namespace_manager

#現在のnamespaceを知るには
current_namespace = namespace_manager.get_namespace()
# defaultは空
logging.debug(current_namespace)
# namespaceをnew_namespaceに設定
namespace_manager.set_namespace('new_namespace')
# 以下でput()したら設定したnamespaceで保存される

保存したいもエンテティはこんな形式だと仮定します。idはitemをそのまま使うということにしておきます。

class Item(ndb.Model, NDBMixin):
    item = ndb.StringProperty()
    message = ndb.StringProperty()

namespaceを変更した場所に丸ごと保存するには?

前のnamespaceの状態で、datastoreから引き出してきて、set_namespaceしたあとに保存します。

# 全件取得
items = Item.query().fetch()
# namespaceを変更
namespace_manager.set_namespace('new_namespace')
entities = (Category(
    id=item.item,
    item=item.item,
    message=item.message,
    ) for item in items)
# 一括保存
ndb.put_multi(entities)

parentキーも保存する場合

複数のカインド含めて丸ごと複製したいときに、parentキーを持っていたりすることもあります。その際には、もちろんですがキーリファレンスも作り直して保存してあげる必要があります。

class ChildItem(ndb.Model, NDBMixin):
    item = ndb.StringProperty()
children = ChildItem.query().fetch()
entities = (ChildItem(
    id=child.item,
    parent=ndb.Key(Item, item.key.parent().get().item),
    item=child.item,
    ) for child in children)
ndb.put_multi(entities)

こんな感じでやってみましたが、他にいい方法がありそうな気もします。。