【Scala】MongoScalaDriver経由で取得したDocumentをJson経由で自作クラスにマッピングする

2021/12/26に公開

概要

ScalaからMongoDBにアクセスするライブラリとして、MongoDB Scala Driverが用意されています。MongoDBScala Driverでは、DBとのインターフェースとしてDocumentのクラスが用意されています。使い方の概要については、【mongoDB】【Scala】mongoDB のインストール → Scala で CRUD するを参照ください。
今回はDocumentのクラスから、独自で定義したクラスにマッピングする実装をメモ書きします。

対応方針

  • Documentクラスはこちらの記事にある通りJsonに変換できますので、Jsonを経由してクラスにマッピングする方針とします。
  • MongoDBScalaDriverではこちらにある通り、コレクション毎のCodec機能も提供されていますが、今回は使用しない方針とします。(集計のクエリを投げるときは別途パースの処理が必要になるっぽいので)
  • Jsonのパースには、PlayFrameworkの機能を使用するものとします。
  • Observableを処理するためのHelperクラスについては、【mongoDB】【Scala】mongoDB のインストール → Scala で CRUD するの記事で、記載されている内容と同内容とします。

実装メモ

まずはマッピングしたいクラスと、PlayFrameworkのplay-jsonのcombinatorsで実装したJsonのパース用フォーマットを用意します。

<独自クラスとフォーマット>

import org.joda.time.DateTime
import play.api.libs.json.JodaReads.DefaultJodaDateTimeReads
import play.api.libs.json.JodaWrites.JodaDateTimeWrites
import play.api.libs.json._
import play.api.libs.functional.syntax._

object CategoryCollection {

  implicit val categoryCollectionFormat = (
    (__ \ "_id").format[String] ~
      (__ \ "name").format[String] ~
      (__ \ "register_user_id").format[String] ~
      (__ \ "register_date").format[DateTime]
    ) (CategoryCollection.apply, unlift(CategoryCollection.unapply))

}

case class CategoryCollection(id: String = "",
                              name: String = "",
                              registerUserId: String = "",
                              registerDate: DateTime = DateTime.now())

上記のJsonのパース用フォーマットを使用して、クラスからドキュメントを生成してinsertします。

<ドキュメントのinsert>

object CategoryRepository {

  import model.db.CategoryCollection._
  import repository.MongoDBHelper._ // Helperの内容は記載割愛

  // insert用のメソッド
  def insertCategory(name: String, userId: String): Unit = {
    // DB接続
    val mongoClient = MongoClient(config.getString("mongo.db.url"))
    val database = mongoClient.getDatabase(config.getString("mongo.db.database"))
    val collection = database.getCollection("category")
    
    // insert用のDocument作成
    val uuid = java.util.UUID.randomUUID.toString
    val nowDate = DateTime.now(DateTimeZone.UTC)
    val categoryCollection = CategoryCollection(
      id = uuid,
      name = name,
      registerUserId = userId,
      registerDate = nowDate
    )
    // クラスからJson文字列に変換
    val categoryCollectionJson = Json.toJson(categoryCollection).toString()
    val document = Document(categoryCollectionJson)
    
    // insertの発行
    collection.insertOne(document)
    // 接続クローズ
    mongoClient.close
  }
}

取得したドキュメントをJsonに変換した後に、クラスを取得します。

<ドキュメントのfind>

object CategoryRepository {

  import model.db.CategoryCollection._
  import repository.MongoDBHelper._ // Helperの内容は記載割愛

  // find用のメソッド
  def findCategory(name: String, userId: String): Seq[CategoryCollection] = {
    // DB接続
    val mongoClient = MongoClient(config.getString("mongo.db.url"))
    val database = mongoClient.getDatabase(config.getString("mongo.db.database"))
    val collection = database.getCollection("category")
    
    // Documentの取得
    val documents = collection.find(equal("register_user_id", userId)).sort(descending("register_date")).results
    // 接続クローズ
    mongoClient.close
    
    // Jsonへのパース
    for {
      doc <- documents
      category <- Json.parse(doc.toJson()).validate[CategoryCollection].asOpt
    } yield category
  }
}

Discussion