Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mongodb #13

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import _root_.sbt.Keys._

name := "lyrics-engine"

version := "1.0-SNAPSHOT"

name := "lyrics-engine"

lazy val commonSettings = Seq(
Expand All @@ -16,10 +22,17 @@ lazy val parser = (project in file("parser"))
.dependsOn(domain)
lazy val api = (project in file("api"))
.settings(commonSettings: _*)

lazy val persistence = (project in file("persistence"))
.settings(commonSettings: _*)
.dependsOn(domain)

lazy val root = (project in file("."))
.settings(commonSettings: _*)
.dependsOn(api)
.aggregate(domain, parser, api)
.aggregate(domain, parser, api, persistence)
.enablePlugins(UniversalPlugin, JavaAppPackaging)




24 changes: 24 additions & 0 deletions persistence/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import sbt.Keys._

name := "persistence"

organization := "scalalab3.lyricsengine"

version := "0.1.0-SNAPSHOT"

scalaVersion := "2.11.8"

resolvers += "releases" at "https://oss.sonatype.org/content/groups/scala-tools"

val specsV = "3.7.2"
val scalaTestV = "2.2.6"

libraryDependencies ++=
Seq(
"org.scalatest" %% "scalatest" % scalaTestV,
"org.specs2" %% "specs2-core" % specsV,
"org.specs2" %% "specs2-matcher-extra" % specsV,
"org.mongodb" %% "casbah" % "3.1.1",
"com.typesafe" % "config" % "1.3.0"
)

10 changes: 10 additions & 0 deletions persistence/src/main/resources/application.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
mongo {
host= "localhost"
port= "8080"
user=""
password=""
dbname= "lyrics"
songscollection= "songs"
wordsdefinitions= "wordsdefinitions"
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package scalalab3.lyricsengine.persistence

import scalalab3.lyricsengine.domain._

/**
* Created by annie on 5/17/16.
*/
trait StorageComponent {

val storage: Storage

trait Storage {

def addSongs(songsToAdd: Seq[Song], version: Option[Int] = None)

def addWordsDefinition(wd: WordsDefinition, version: Option[Int] = None)

def findSongs(version: Option[Int] = None): Seq[Song]

def findWordsDefinitions(version: Option[Int] = None): Seq[Map[Int, String]]

def countSongs(): Int

def countWD(): Int

def getLastVersion():Option[Int]
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package scalalab3.lyricsengine.persistence

import com.mongodb.casbah.Imports._

import scalalab3.lyricsengine.domain._
import scalalab3.lyricsengine.persistence.mongo.MongoContext

trait StorageComponentImpl extends StorageComponent {

override val storage: Storage


class StorageImpl(implicit context: MongoContext) extends Storage {

def addSongs(songsToAdd: Seq[Song], version: Option[Int] = None) = {
val builder = context.songsCollection.initializeOrderedBulkOperation //will automatically split the operation into batches
for {
song <- songsToAdd
} builder.insert(songToMongoDBObj(song, version))
val result = builder.execute()
}

def addWordsDefinition(wd: WordsDefinition, version: Option[Int] = None) = {
val builder = context.wdCollection.initializeOrderedBulkOperation
builder.insert(wdToMongoDbObject(wd, version))

val result = builder.execute()
}

def findSongs(version: Option[Int] = None): Seq[Song] = {

val query = version match {
case Some(v) => MongoDBObject("version" -> v)
case None => MongoDBObject("version" -> null)
}
val result = context.songsCollection.find(query)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вообще здесь можно обойтись без паттерн-матчинга. version.getOrElse(null) вполне хватит.


val songsSet = for {
song <- result
} yield songFromMongoDBObj(song)

songsSet.toSeq
}

def findWordsDefinitions(version: Option[Int] = None): Seq[Map[Int, String]] = {
Copy link

@kstep kstep May 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне кажется, что findWordsDefinitions и findSongs можно выразить через одну и ту же базовую функцию.

val query = version match {
case Some(v) => MongoDBObject("version" -> version.get)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Здесь уже есть развёрнутое v, не надо делать version.get.

case None => MongoDBObject("version" -> null)
}
val result = context.wdCollection.find(query)

val wdSet = for {
wd <- result
} yield wdFromMongoDBObj(wd)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

С простым result.map(wdFromMongoDBObj) это будет выглядеть гораздо проще.


wdSet.toSeq
}

def countSongs(): Int = context.songsCollection.find().count()

def countWD(): Int = context.wdCollection.find().count()

private def songFromMongoDBObj(obj: MongoDBObject): Song = {
val _msdTrackId = obj.getAs[String]("msdTrackId").get
val _mxmTrackId = obj.getAs[String]("mxmTrackId").get

val _words = obj.getAs[Map[Int, Int]]("words").get
Copy link

@kstep kstep May 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Раз уж всё равно здесь делаете сразу .get, то можно было бы просто использовать obj.as[T]("field").
Или, если нужно избежать исключений, можно использовать .getOrElse(defaultValue).


Song(msdTrackId = _msdTrackId, mxmTrackId = _mxmTrackId, words = _words)

}

private def wdFromMongoDBObj(obj: MongoDBObject): Map[Int, String] = {

val version = obj.getAs[Int]("version").getOrElse(null)

val words = obj.getAs[Map[String, String]]("wordsDefinitions").get.map { case (k, v) => (k.toInt, v.toString) }
words
}

private def songToMongoDBObj(song: Song, version: Option[Int]): MongoDBObject = {
val songBuilder = MongoDBObject.newBuilder
songBuilder +=("msdTrackId" -> song.msdTrackId,
"mxmTrackId" -> song.mxmTrackId,
"words" -> getWDefinitions(song),
"version" -> version.getOrElse(getLastVersion)
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

songBuilder.result()
}

private def wdToMongoDbObject(wd: WordsDefinition, version: Option[Int]): MongoDBObject = {
val wdBuilder = MongoDBObject.newBuilder
wdBuilder +=(
"wordsDefinitions" -> transformWordsDef(wd),
"version" -> version.getOrElse(getLastVersion.getOrElse(null))
)
wdBuilder.result()
}

private def transformWordsDef(w: WordsDefinition): MongoDBObject = {
val collection = MongoDBObject.newBuilder
val transformed = w.map { case (k, v) => (k.toString, v) }
collection ++= transformed
collection.result()
}

private def getWDefinitions(song: Song): MongoDBObject = {
val words = MongoDBObject.newBuilder
val songWords = song.words.map { case (k, v) => (k.toString, v) }
words ++= songWords
words.result()
}

def getLastVersion: Option[Int] = {
val query = MongoDBObject() // All documents
val fields = MongoDBObject("version" -> 1) // Only return `version`
val orderBy = MongoDBObject("version" -> -1) // Order by version descending

val result = context.wdCollection.findOne(query, fields, orderBy)

result match {
case Some(res) => res.getAs[Number]("version").map(_.intValue())
case None => None
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Паттерн-матчинг не нужен. Нужен result.flatMap().

}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package scalalab3.lyricsengine.persistence.mongo

import com.typesafe.config.ConfigFactory

import constants.DefaultConfigValues._
import constants.DefaultConfigKeys._
import scala.util.Try

case class MongoConfig(host: String = defaultHost,
port: Int = defaultPort,
user: String = defaultUser,
password: String = defaultPassword,
dbName: String = defaultDbName,
songsCollection: String = defaultSongsCollection,
wdCollection: String = defaultWDCollection)

object MongoConfig {
private val config = ConfigFactory.load()

def load(): MongoConfig =
MongoConfig(
getString(host, defaultHost),
getInt(port, defaultPort),
getString(user, defaultUser),
getString(password, defaultPassword),
getString(dbname, defaultDbName),
getString(songsCollection, defaultSongsCollection),
getString(wordsDefinitions, defaultWDCollection)
)
private def getString(key: String, defaultValue: String) = Try(config.getString(key)).getOrElse(defaultValue)
private def getInt(key: String, defaultValue: Int) = Try(config.getInt(key)).getOrElse(defaultValue)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package scalalab3.lyricsengine.persistence.mongo

import com.mongodb.BasicDBObjectBuilder
import com.mongodb.casbah.Imports._
import com.mongodb.casbah.MongoClient

class MongoContext(val config: MongoConfig) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

С конфигами вы очень намудрили.
Не понял смысла выносить отдельно DefaultConfigKeys и DefaultConfigValues. Обычно это всё задаётся в одном месте, в данном случае application.conf файле. У вас получилось очень размазано.
Сам файл поддерживает возможность задавать дефолтные значения, хардкодить их где-то еще не надо. Также нужно оставить возможность переопределять их переменными среды (environment variables).
Более того, хост и порт из config объекта который сюда передаётся нигде не используются! У меня блин час ушёл пока настраивал докер и не мог понять почему адрес не переопределяется :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DefaultConfugKeys используется, чтобы вычитать из application.conf значения. DefaultConfigValues используется, чтобы подставить дефолтное значение, если в application.conf что-то не задано. А так все вычитывается из application.conf, да

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мой поинт в том, что выносить в переменные значения ключей из application.conf не стоит. Также не стоит хранить дефолтные значения в переменных, синтаксис application.conf позволяет прописать их прямо там. Ну и самое странное, что некоторые из настроек которые вы задали вообще не используются.


val mongoClient = MongoClient()
val mongoDB = mongoClient(config.dbName)

implicit val options: DBObject = BasicDBObjectBuilder.start().add("capped", true).add("size", 2000000000l).get()

def songsCollection =
if (mongoDB.collectionExists(config.songsCollection)) {
mongoDB(config.songsCollection)
} else {
mongoDB.createCollection(config.songsCollection, options)
mongoDB(config.songsCollection)
}

def wdCollection = {
if (mongoDB.collectionExists(config.wdCollection)) {
mongoDB(config.wdCollection)
} else {
mongoDB.createCollection(config.wdCollection, options)
mongoDB(config.wdCollection)
}
}

def drop = {
mongoDB.dropDatabase()
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package scalalab3.lyricsengine.persistence.mongo.constants

object DefaultConfigKeys {
val mongo = "mongo"
val host = s"$mongo.host"
val port = s"$mongo.port"
val user = s"$mongo.user"
val dbname = s"$mongo.dbname"
val password = s"$mongo.password"
val songsCollection = s"$mongo.songscollection"
val wordsDefinitions = s"$mongo.wordsdefinitions"

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package scalalab3.lyricsengine.persistence.mongo.constants

object DefaultConfigValues {
val defaultHost = "localhost"
val defaultPort = 27017
val defaultUser = "admin"
val defaultPassword = ""
val defaultDbName = "lyrics"
val defaultSongsCollection = "songs"
val defaultWDCollection = "songs"
}
Loading