2012-09-28

MongoDB with Morphia (and Scala)

I've been working on my Scala application that connects to MySQL on one end (using EclipseLink and JPA), and MongoDB on the other (using Morphia). The source code is available on GitHub. In this post I will show you my setup.

build.gradle

Dependencies...
// ...

repositories {
   mavenLocal()
   mavenCentral()
   mavenRepo name: "EclipseLink", url: "http://download.eclipse.org/rt/eclipselink/maven.repo/"
   mavenRepo name: "MongoDB", url: "http://morphia.googlecode.com/svn/mavenrepo"
   mavenRepo name: "Audiolicious", url: "https://github.com/m1key/repo/raw/master/releases"
}

dependencies {

   compile group: 'org.eclipse.persistence', name: 'eclipselink', version: '2.3.0'
   compile group: 'org.eclipse.persistence', name: 'javax.persistence', version: '2.0.0'
   compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.18'
   compile group: 'org.mongodb', name: 'mongo-java-driver', version: '2.7.3'
   compile group: 'com.google.code.morphia', name: 'morphia', version: '0.99'

   // …
}

// ...

Persistence provider

This is how you expose Mongo to your repositories (or DAOs).
package me.m1key.audioliciousmigration.persistence.mongodb

import com.google.code.morphia.Datastore
import com.mongodb.Mongo
import com.google.code.morphia.Morphia
import me.m1key.audioliciousmigration.entities.mongodb.MongoDbSong

class ProductionMorphiaMongoDbPersistenceProvider extends MorphiaMongoDbPersistenceProvider {

 private var datastore: Datastore = null
 private var mongo: Mongo = null

 def initialise(): Unit = {
    mongo = new Mongo("localhost", 27017)
    datastore = new Morphia().map(classOf[MongoDbSong]).createDatastore(mongo, "audiolicious")
    datastore.ensureIndexes()
 }

 def getDatastore(): Datastore = {
    return datastore
 }

 def getMongo(): Mongo = {
    return mongo
 }

}

Repository using getDatastore

This is a repository which uses the MongoDB datastore.
package me.m1key.audioliciousmigration.repository

import com.google.inject.Inject
import me.m1key.audioliciousmigration.persistence.mongodb.MorphiaMongoDbPersistenceProvider
import scalaj.collection.Imports._
import me.m1key.audioliciousmigration.entities.mongodb.MongoDbSong
import com.google.code.morphia.query.UpdateOperations
import com.google.code.morphia.query.Query

class MorphiaMongoDbRepository @Inject() (private val persistenceProvider: MorphiaMongoDbPersistenceProvider) {

 def save(song: MongoDbSong): Unit = {
    val datastore = persistenceProvider.getDatastore()

    val selectQuery = datastore.createQuery(classOf[MongoDbSong])
     .field("name").equal(song.name).field("albumName").equal(song.albumName)
     .field("artistName").equal(song.artistName).field("songKey").equal(song.songKey);
    val existingSong = selectQuery.get()

    if (existingSong != null) {
     var ops = datastore.createUpdateOperations(classOf[MongoDbSong])

     if (song.genre != null) {
       ops = ops.set("genre", song.genre)
     }
     if (song.year != 0) {
       ops = ops.set("year", song.year)
     }
     if (song.songArtistName != null) {
       ops = ops.set("songArtistName", song.songArtistName)
     }

     val stat = song.statsList.get(0)
     existingSong.addOrEditStats(stat.libraryUuid, stat.percentage, stat.playCount, stat.skipCount)
     ops.set("statsList", existingSong.statsList)

     val updatedCount = datastore.update(selectQuery, ops).getUpdatedCount()

     if (updatedCount != 1) {
       println("Warning. Song updated [%d] times while expected once: [%s]".format(updatedCount, song))
     }
    } else {
     datastore.save(List(song).asJava)
    }
 }

}

getMongo

Here's code that uses getMongo for executing a raw query.
 def mine(): Option[Int] = {
    val mongo = persistenceProvider.getMongo
    val result = mongo.getDB("audiolicious").eval(query)
    result match {
     case double: java.lang.Double => return Some(double.intValue())
     case _ =>
       println("Error while obtaining stats. Result of unknown type [%s].".format(result))
       return None;
    }
 }

Putting it all together...

Before code using datastore or mongo can be executed, the following call has to be made.
    val mongoDbPersistence = injector.getInstance(classOf[MorphiaMongoDbPersistenceProvider])
    mongoDbPersistence.initialise
(This example is using Guice) I don't know if this is the best way of doing things, but there's little documentation available. This way works. If you have a better idea - definitely let me know!

3 comments:

  1. Hey Michał,
    I think that since you're using Scala, you should go with the best scala DSL there is for MongoDB, namely -> https://github.com/foursquare/rogue . I mean, why use Scala if you don't leverage what it can do? Your example rewritten to Rogue would be something like:

    val mds = MongoDbSong (_.name eqs song.name) and (_.albumName eqs song.albumName) and (_.artistName eqs song.artistName) get()

    And it's... Typesafe! :-) Really, give it a shot - you won't regret it.

    ReplyDelete
    Replies
    1. The development of artificial intelligence (AI) has propelled more programming architects, information scientists, and different experts to investigate the plausibility of a vocation in machine learning. Notwithstanding, a few newcomers will in general spotlight a lot on hypothesis and insufficient on commonsense application. machine learning projects for final year In case you will succeed, you have to begin building machine learning projects in the near future.

      Projects assist you with improving your applied ML skills rapidly while allowing you to investigate an intriguing point. Furthermore, you can include projects into your portfolio, making it simpler to get a vocation, discover cool profession openings, and Final Year Project Centers in Chennai even arrange a more significant compensation.

      Data analytics is the study of dissecting crude data so as to make decisions about that data. Data analytics advances and procedures are generally utilized in business ventures to empower associations to settle on progressively Python Training in Chennai educated business choices. In the present worldwide commercial center, it isn't sufficient to assemble data and do the math; you should realize how to apply that data to genuine situations such that will affect conduct. In the program you will initially gain proficiency with the specialized skills, including R and Python dialects most usually utilized in data analytics programming and usage; Python Training in Chennai at that point center around the commonsense application, in view of genuine business issues in a scope of industry segments, for example, wellbeing, promoting and account.

      The Nodejs Projects Angular Training covers a wide range of topics including Components, Angular Directives, Angular Services, Pipes, security fundamentals, Routing, and Angular programmability. The new Angular TRaining will lay the foundation you need to specialise in Single Page Application developer. Angular Training

      Delete
  2. Morphia doesn't seem to be doing so well, it's been going nowhere for a while, so I guess I might as well give Rogue a try. :)
    Thanks Konrad!

    ReplyDelete