[Scala] Joda DateTimeをBSON ISODate形でMongoDBに登録したい

  • このエントリーをはてなブックマークに追加

Joda DateTimeって最近はもうトレンドじゃない?
とにもかくにも、今現在進行形で使っていてMongoDBにISODateとして保存したい。
けど流石に公式サポートされてる型じゃないんでCan't find a codec for class org.joda.time.DateTime.って怒られちゃいます。(下記コード)
わざわざ文字列にするのもあれだし、ということでスマートにコーデックを作る方法です。

import org.mongodb.scala._
import org.mongodb.scala.bson.codecs.Macros._
import org.mongodb.scala.bson.codecs.DEFAULT_CODEC_REGISTRY
import org.bson.codecs.configuration.CodecRegistries.{fromRegistries, fromProviders}
import scala.concurrent.duration._
import org.joda.time.{DateTime, DateTimeZone}
import org.joda.time.format.DateTimeFormat

case class Post(title: String, body: String, createAt: DateTime)

val tz = DateTimeZone.forID("Asia/Tokyo")
val format = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").withZone(tz)

val codecRegistry = fromRegistries(fromProviders(classOf[Post]), DEFAULT_CODEC_REGISTRY)
val uri = "mongodb://root:password@localhost:27017/?authSource=admin"
val db = MongoClient(uri).getDatabase("example_db")
val posts: MongoCollection[Post] = db.withCodecRegistry(codecRegistry).getCollection("posts")

val post = Post("記事のタイトル", "本文だよ!", format.parseDateTime("2020-10-01 10:00:00"))

// 【ここでエラー!】 org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.joda.time.DateTime.
Await.result(posts.insertOne(post).toFuture, 1 second)

コーデック実装

ほいじゃま、コーデック作りましょう。
BsonWriter.writeDateTimeでミリ秒からISODateに変換してくれます。
逆にBsonReader.readDateTimeはミリ秒を返してくれるので、DateTimeのコンストラクタにそのまま食わせればOK。

import org.bson.codecs.{Codec, DecoderContext, EncoderContext}
import org.bson.{BsonReader, BsonWriter}
import org.joda.time.DateTime

/**
  * Joda DateTimeをBSONのISODateTime形式に相互変換するコーデック
  */
class JodaCodec extends Codec[DateTime] {
  override def encode(writer: BsonWriter, value: DateTime, encoderContext: EncoderContext): Unit = writer.writeDateTime(value.getMillis)
  override def decode(reader: BsonReader, decoderContext: DecoderContext): DateTime = new DateTime(reader.readDateTime)
  override def getEncoderClass: Class[DateTime] = classOf[DateTime]
}

そしたら先のコードのコーデックを追加して完了!

import org.bson.codecs.configuration.CodecRegistries

// 中略

val codecRegistry = fromRegistries(
  fromProviders(classOf[Post]),
  DEFAULT_CODEC_REGISTRY,
  CodecRegistries.fromCodecs(new JodaCodec)
)

結果

上記の結果で登録されたドキュメントはこんな感じ。ばっちりですね!
もちろんfindも問題ないですよ!

{
    "_id" : ObjectId("5f6d72a791a89d611a21174d"),
    "title" : "記事のタイトル",
    "body" : "本文だよ!",
    "createAt" : ISODate("2020-10-01T01:00:00.000Z")
}
  • このエントリーをはてなブックマークに追加

SNSでもご購読できます。




コメントを残す