type holyshared = Engineer<mixed>

技術的なことなど色々

Mongooseドキュメントのserialize/deserialize

Mongooseを利用している場合、読み込んだドキュメントをserialize/deserializeしたい場合があると思います。
serializeは雑にtoJSON、でいいと思うのですがdeserializeはどうするのか?

一つのやり方として、hydrateメソッドを使用するやり方があります。

const user = await User({
  name: 'name',
  password: 'password',
  createdAt: new Date(),
  updatedAt: new Date()
});
await user.save();

// JSONからUserドキュメントに戻す
const jsonOfUser = await user.toJSON();
const hydratedUser = User.hydrate(jsonOfUser);

console.log(hydratedUser); // User { name: ... }

ここで気になるのがhydrateした場合は、永続化済みかどうかのコンテキストがわかるのかという疑問があります。
通常はドキュメントのisNewプロパティ調べられますが、hydrateした場合はどうかということです。

検証してみる

試してみた結果はisNewプロパティがfalseになっていることがわかりました。

const user = await User({
  name: 'name',
  password: 'password',
  createdAt: new Date(),
  updatedAt: new Date()
});
console.log(user.isNew); // まだ永続化していないので、true
await user.save();
console.log(user.isNew); // 永続化したのでfalse

const jsonOfUser = await user.toJSON();
const hydratedUser = User.hydrate(jsonOfUser);

console.log(hydratedUser.isNew); // falseが返ってくるが根拠はなんだろうか?

しかし、isNewがfalseになる根拠はなんでしょうか?
これはMongooseのソースを読まないとわからないのでみてみます。(バージョンはv5.8.9)

// model.js
Model.hydrate = function(obj) {
  _checkContext(this, 'hydrate');

  const model = require('./queryhelpers').createModel(this, obj);
  model.init(obj);
  return model;
};

queryhelpersを読み込み、createModelを実行してモデルを初期化しているようです。
引数にisNewオプションを指定していますね。
なので、hydrateでドキュメントに戻した場合は強制的にisNewがfalseになります。

// queryhelpers.js
return new model(undefined, fields, {
  skipId: true,
  isNew: false,
  willInit: true
});

当たり前といえば当たり前ですね。
てっきりhydrateを使用する際の引数のオブジェクトにObjectIdが指定されていれば、永続化済みの状態にするのかと思っていたのですが そんなことはなかったです。