msawady’s engineering-note

なにも分からないエンジニアです。

【mongoDB】【Scala】mongoDB のインストール → Scala で CRUD する

次のテーマは mongoDB

  • twitter で呟いたりしたんですが、次のプロジェクトではmongoDB使います
  • ただ、自分のタスクはインフラ運用に近い部分なので、実際にどうクエリするかみたいなところは暫く触らなそう
  • なので、自習でやってみることにしました
  • とりあえずインストール→CRUDまでやってみる。

インストール

  • 以下のサイトからダウンロード、インストール。

    MongoDB Download Center | MongoDB

  • データ出力先、ログ出力先のフォルダを作成する

mkdir C\mongo\data
mkdir C\mongo\log
  • 実行ファイルにパスを通す。デフォルトだとC:\Program Files\MongoDB\Server\3.4\bin

  • 以下のコマンドを実行する。コマンドプロンプトは開きっぱなしにする。(.batファイルにしておくと便利)

mongod --dbpath c:\mongo\data --logpath c:\mongo\log\mongodb.log
mongo
  • テスト用のDBを作成 → 接続
use helloMongo

めっちゃ簡単ですね。

scalaCRUDする

依存関係にMongo Scala Driverを追加

build.sbtに以下を追加。

libraryDependencies += "org.mongodb.scala" %% "mongo-scala-driver" % "2.1.0

mongodb.github.io

Observable を処理するための Helper クラス作成

コピペ from 公式チュートリアル

mongo-scala-driver/Helpers.scala at master · mongodb/mongo-scala-driver · GitHub

import java.util.concurrent.TimeUnit

import scala.concurrent.Await
import scala.concurrent.duration.Duration

import org.mongodb.scala._

object Helpers {

  implicit class DocumentObservable[C](val observable: Observable[Document]) extends ImplicitObservable[Document] {
    override val converter: (Document) => String = (doc) => doc.toJson
  }

  implicit class GenericObservable[C](val observable: Observable[C]) extends ImplicitObservable[C] {
    override val converter: (C) => String = (doc) => doc.toString
  }

  trait ImplicitObservable[C] {
    val observable: Observable[C]
    val converter: (C) => String

    def results(): Seq[C] = Await.result(observable.toFuture(), Duration(10, TimeUnit.SECONDS))

    def headResult() = Await.result(observable.head(), Duration(10, TimeUnit.SECONDS))

    def printResults(initial: String = ""): Unit = {
      if (initial.length > 0) print(initial)
      results().foreach(res => println(converter(res)))
    }

    def printHeadResult(initial: String = ""): Unit = println(s"${initial}${converter(headResult())}")
  }

}

CRUD する

import org.mongodb.scala._
import org.mongodb.scala.model.Updates._
import org.mongodb.scala.model.Filters._
import Helpers._

object HelloMongo {

  def main(args: Array[String]): Unit = {

    // connect to //localhost:27017
    val mongoClient: MongoClient = MongoClient()
    val database: MongoDatabase = mongoClient.getDatabase("helloMongo")

    // create and get collection
    database.createCollection("stocks").results()
    val stocks: MongoCollection[Document] = database.getCollection("stocks")

    // insert
    stocks.insertOne(Document("code" -> "8410", "name" -> "セブン銀行")).results()
    println("=========after INSERT=========")
    stocks.count().printHeadResult("count: ")
    stocks.find().results().foreach(println(_))

    // update
    stocks.updateOne(equal("code", "8410"), set("tradingUnit", "100")).results()
    println("=========after UPDATE=========")
    stocks.count().printHeadResult("count: ")
    stocks.find().results().foreach(println(_))

    // delete
    stocks.deleteOne(equal("code", "8410")).results()
    println("=========after DELETE=========")
    stocks.count().printHeadResult("count: ")
    stocks.find().results().foreach(println(_))

    // drop connection
    stocks.drop().results()

  }
}

実行結果

=========after INSERT=========
count: 1
Document((_id,BsonObjectId{value=59d0dea2223c756f2445f7eb}), (code,BsonString{value='8410'}), (name,BsonString{value='セブン銀行'}))
=========after UPDATE=========
count: 1
Document((_id,BsonObjectId{value=59d0dea2223c756f2445f7eb}), (code,BsonString{value='8410'}), (name,BsonString{value='セブン銀行'}), (tradingUnit,BsonString{value='100'}))
=========after DELETE=========
count: 0

コード解説

  • コレクション(RDBでいうテーブル)を作成・取得
    database.createCollection("stocks").results()
    val stocks: MongoCollection[Document] = database.getCollection("stocks")
  • ドキュメント(RDBでいうレコード)をINSERT
stocks.insertOne(Document("code" -> "8410", "name" -> "セブン銀行")).results()
  • UPDATE

    updateOne だとwhere条件にマッチした最初のドキュメントしか更新されないので注意。 where にマッチした全てのドキュメントを更新したい場合はupdateManyを利用する。
    stocks.updateOne(equal("code", "8410"), set("tradingUnit", "100")).results()
  • DELETE

    update と同様に、deleteOneだとマッチした最初のドキュメントしか削除されない。全て消す場合はdeleteMany
    stocks.deleteOne(equal("code", "8410")).results()
  • コレクションの削除

    DROP TABLE がこんなあっさりアプリケーションから出来るってのは中々面白いと思いました。 一時テーブルをアプリ側から作成→削除まで行えるのは何かと便利そうです。
    stocks.drop().results()

感想

  • mongoDB のインストールの早さと簡単さに衝撃を受けました
  • Mongo Scala Driver は公式ドキュメントやサンプルコードがちゃんと揃っているので使いやすいですね
    • ライブラリの"品質" というと api の使い勝手やパフォーマンスが重視されるけど、こういうところが地味に大事だと思う。
  • 操作自体は単純なものの、これを使ったエンティティ設計どうするんだろう...というのはやってみないと分からなそう
    • Play Framework と組み合わせてポジション管理とかやってみようと思います

以上です。思ったよりもあっさり出来てびっくりしてます。来週には動くページを作れるように頑張ります。