2018年5月2日水曜日

Protocol Buffers オブジェクトを MongoDB に格納する


Protocol Buffers の IDL で定義された型のオブジェクトを MongoDB に格納できるかどうかを検証しています。gRPC で受け取ったオブジェクトをそのまま MongoDB に格納するようなストーリーを想定しています。
単純な型であれば特に問題なく格納できるのですが、やはりいろいろな前提や制限があるようです。

MongoDB 公式の C# 実装である MongoDB.Driver (2.5.1) を使用しています。BSON ではなく、タイプセーフな方法でどのように実装できるかを考えます。MongoDB.Driver はバージョンによって仕様が大きく異なるようで、注意が必要です。

 

 Id プロパティ・フィールドが必要


ドキュメントとして格納するルートオブジェクトの型に Id という名称のプロパティやフィールドが存在しない場合、"Id Element '_id' does not match any field or property" というエラーが発生します。

Id の値が _id というフィールドで格納されます。Id が存在しなくても _id というフィールドが自動的に追加されて格納されるのですが、Find メソッドなどでエラーが発生します。事実上、Id が必須であると思われます。

なお、Id の型によって MongoDb に格納されるときの型が変わります。System.Guid を使いたいところですが、Protocol Buffers ではサポートされていません。
  • System.String → 文字列
  • System.Guid → バイト配列
  • proto ファイルの message から定義された型 → ネストされたオブジェクト

 

repeated フィールドは格納不可


repeated が指定されたフィールドを含む場合、UpdateMany メソッドなどで "Unable to determine the serialization information" というエラーが発生します。

repeated が指定されているフィールドは Repeatable<T> 型のプロパティとして定義されますが、setter がないためエラーになるようです。Repeatable<T> 型自体がシリアライズできないわけではなく、setter があれば Array として格納できます。一方、InsertMany メソッドなどでは setter がないプロパティは格納対象外になるようで、エラーは発生しませんが格納もされません。

Grpc.Tools で自動生成するたびにソースコードを修正するのは現実的ではありませんので、partial ファイルに setter つきのプロパティを追加する方法が妥当でしょうか。とはいえ、一つ一つ定義するのも確実性に欠けます。自動生成の過程で解決できるのがベストだと思います。

Grpc.Toolsで自動生成されたコード
public RepeatedField Tags {
  get { return tags_; }
}

partial ファイルで追加したコード
[BsonElement("Tag")]
public RepeatedField TagsSerializable {
  get { return Tags; }
  private set {
    Tags.Clear();
    if (value!=null) { Tags.AddRange(value); }
  }
}
この場合、FilterDefinition や UpdateDefinition で Tags プロパティの代わりに TagsSerializable プロパティを使うことになります。

2018/05/03 追記
setter のスコープは private でもよいようです。


0 件のコメント:

コメントを投稿

paiza のスキルチェックをやってみました

いまさら感はありますが、 paiza のスキルチェックをやってみました。指定された時間内にコードを書いてユニットテストにかけ、その結果を基に評価を数値化してくれるというものですが、ゲーム感覚で空き時間を見つけて進めていこうと考えています。 どうやら時間が短いほど高い評価を得...