LoginSignup
33
28

More than 5 years have passed since last update.

DatastoreからGetした時に余計なPropertyがある場合エラーになるが無視してもいい

Last updated at Posted at 2016-10-06

タイトルのまんまです。

話の前提として、以下のパッケージを使っているものとします。

google.golang.org/appengine/datastore

SDK組み込みの appengine/datastore や Cloud Datastore用の cloud.google.com/go/datastore については言及しませんが、多分似たような設計になってると思うので流用可能なテクではないでしょうか。

モチベーション

GAE/J+Slim3とかだとKindのSchemaの変更は大変気軽にできました。
新規追加のプロパティはデフォルト値になりますし、削除したプロパティはインスタンスに変換される時に無視されるからです。

一方、Goは違います。
Entityに存在するプロパティに対応するフィールドがstructにないと、エラーになります。
このため、GAE/Goでは不要になったプロパティを削除できず、ずっと残す運用をしていました。
具体的に以下のような感じです。

type Sample struct {
    ID     int64  `goon:"id" datastore:"-"`
    Name   string
}

これを以下のように変更して適当に検索かけて使ってるところ消す。

type Sample struct {
    ID               int64   `goon:"id" datastore:"-"`
    DeprecatedName   string  `datastore:"Name"`
}

もしくは、 datastore.PropertyLoadSavor を使って読み出し時に該当のプロパティを抜いてから変換します。
Goの場合 ps = ps.Filter(p => p.Name != "Name") みたいなコードが書きづらいのでひたすらめんどくさいです。

何も考えずにプロパティ削りたい。

対応

structに対し、 datastore.PropertyLoadSavor を実装します。
まずは今まで書いていたであろう実装を示しておきます。

func (entity *Foo) Load(p []datastore.Property) error {
    if err := datastore.LoadStruct(entity, p); err != nil {
        return err
    }

    // 何かやったりする

    return nil
}

func (entity *Foo) Save() ([]datastore.Property, error) {
    // 何かやったりする

    p, err := datastore.SaveStruct(entity)
    if err != nil {
        return nil, err
    }
    return p, nil
}

これを以下のように変更します。

func (entity *Foo) Load(p []datastore.Property) error {
    err := datastore.LoadStruct(entity, p)
    if fmerr, ok := err.(*datastore.ErrFieldMismatch); ok && fmerr != nil && fmerr.Reason == "no such struct field" {
        // ignore
    } else if err != nil {
        return err
    }

    // 何かやったりする

    return nil
}

func (entity *Foo) Save() ([]datastore.Property, error) {
    // 何かやったりする

    p, err := datastore.SaveStruct(entity)
    if err != nil {
        return nil, err
    }
    return p, nil
}

これだけでOKです。

これで大丈夫な理由

このへん

We don't return early, as we try to load as many properties as possible.
It is valid to load an entity into a struct that cannot fully represent it.
That case returns an error, but the caller is free to ignore it.

要するに、 datastore.LoadStruct した時に *datastore.ErrFieldMismatch が返ってきても途中で処理打ち切ったりせずにお尻まで処理してからエラー返してるので無視してもデータが壊れてることはないゾ!という話。

Reasonの値をチェックしているのは若干神経質な気もするので、おおらかな性格の人はReasonはみなくても多分大丈夫なんじゃないでしょうか。

感想

無視するために独自の datastore.LoadStruct 実装しなきゃかなと思って結構手を動かしてたんだけどある時 datastore.ErrFieldMismatch の存在とソレに関わる処理を見つけて無駄な作業だった事を知ってしまいたいへん悲しい(有益な発見

とりあえず、これから本番環境に突っ込んでみる予定です。

33
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
28