MongoDBからデータが取得できなくなってしまった

MongoDBからデータが取得できなくなったMongoDB
MongoDBからデータが取得できなくなった

はじめに

MongoDBのドキュメントをいじっていると、ある時、GETのHTTPリクエストを出してもドキュメントが返ってこなくなってしまいました。

結論からいうと、MongoDB内に収められているデータ型とアプリケーションサーバ側でハンドリングした時のデータ型が異なったため、クライアントアプリケーションまでデータが返ってこなかったのでした。
通常の運用ではおそらく起こり得ないパターンだと思いますが、コーディング上では起こり得るかもしれないです。今まで返ってきたのにある時突然返ってこなくなったときは、解決の足しにしていただければ幸いです。

状況の整理

私がやったこと

MongoDBのドキュメントを更新しました。タイトルでドキュメントを絞り込み、価格(price)を1.08倍しました。

もともと価格はInt型で保持していましたが、1.08倍ということでDouble型にキャストしました。

router.put(Book.self, at:"api/book") { (req, book) -> Future<HTTPStatus> in
  let client = try! req.make(MongoClient.self)
  let collection = client.db("vaporapp").collection("books", withType: Book.self)
  return try req.content.decode(Book.self).map(to: HTTPStatus.self) { book in
    let query: Document = try Document.init(fromJSON: "{\"title\":\"" + book.title + "\"}")
    let updatedBook: Document = try Document.init(fromJSON: "{\"$set\": {\"price\"" + String(Double(book.price) * 1.08) + "}}")
    _ = try! collection.updateOne(filter: query, update: updatedBook)
    return .ok
  }
}

そのままupdateOneで更新しました。

データモデル

struct Book: Content {
  var title: String
  var publisher: String
  var price: Int
}

価格(price)は確かにInt型ですね。

起こったこと

ドキュメント2つ目のpriceの値が整数ではなくなっています
ドキュメント2つ目のpriceの値が整数ではなくなっています
全件取得APIを叩いた結果(コンソール) 1つ目のドキュメントしか返ってきていません
1つ目のドキュメントしか返ってきていません

2件以上のドキュメントがあるコレクションに対して全件取得のAPIを叩いたにも関わらず、1件しか返ってきていません。
同名のタイトルや金額があるのでわかりにくいですが、要するに中途半端にドキュメントが得られるのです。全部返ってこないとかエラーが返ってくるとかならまだしも、中途半端に返してくるとは……

原因

型の不一致が原因です。もう白状したも同然ですが、犯人は私でした。

もともとはInt型で保持されたいた値をDouble型にキャストしたままMongoDBに保存してしまっています。

{\"$set\": {\"price\":" + String(Double(book.price) * 1.08) + "}}

MongoDBのドキュメントを保存する時、当たり前ですが型の確認なんてしてくれません。なので、Double型で保存するとそのまま保存されます。

読み出し時もそのまま返してくれます。そして、流れてきたデータがアプリケーションサーバでデータモデルに合わせて変換される際にはねられてしまうようです。

対策

データモデルの各プロパティのデータ型はクライアントアプリケーションからデータベースサーバまで一貫して統一させておきましょう。

今回のケースでは扱っていませんが、クライアントアプリケーションまでJSONで流してくるケースでも注意が必要そうです。まぁ、データ型を一貫して統一しておけばいいだけの話ですね。

まとめ

MongoDBは渡したデータをそのまま保存し、要求したデータはそのまま返してくれます。データ型チェックはコーディング時にきっちりとですね!

また、JSONからデータモデルに変換される時の挙動にも注意が必要です。
アプリケーションサーバにてデータモデルをContentプロトコルに準拠させているので、JSONとのエンコードやデコードはお手軽です。お手軽故に気が緩んでしまいかねません。データ型だけは要注意です。それにしても、適合しなかった型を持つプロパティだけがはねられるのではなく、ドキュメント自体がはねられるとは思わなかった……

それと、2進数と小数表現に関する古典的な注意点にもしっかりと引っかかっていますね。油断大敵!

タイトルとURLをコピーしました