Version 9, last updated by eltimn at 24 Feb 15:18 UTC

Define record class

class MainDoc private() extends MongoRecord[MainDoc] with ObjectIdPk[MainDoc] {
  def meta = MainDoc

  object name extends StringField(this, 12)
  object cnt extends IntField(this)
}

object MainDoc extends MainDoc with MongoMetaRecord[MainDoc]

Create record instance and save

val md1 = MainDoc.createRecord
  .name("md1")
  .cnt(5)
  .save

Find instance

val md = MainDoc.find("_id", md1.id.is)

MongoPk

All MongoRecords must define an id field. Currently, this is type Any, but it will be changed in a future version to MandatoryTypedField.

The MongoPk series of traits (available starting with version 2.4-M1) all provide an id for you. These all save the id field as _id in the database. ObjectId, UUID, String, Int, and Long implementations are available. If you require something more complicated, you can define the id field yourself.

Example id using a BsonRecord:

import net.liftweb.mongodb._

class MyPk private() extends BsonRecord[MyPk] {
  def meta = MyPk

  object keyA extends StringField(this, 100)
  object keyB extends StringField(this, 100)
}
object MyPk extends MyPk with BsonMetaRecord[MyPk]

class MyRecord private() extends MongoRecord[MyRecord] {
  def meta = MyRecord

  object id extends BsonRecordField(this, MyPk) {
    override def name = "_id"
  }
}
object MyRecord extends MyRecord with MongoMetaRecord[MyRecord]

val pk = MyPk.createRecord.keyA("a").keyB("b")
val rec = MyRecord.createRecord.id(pk).save

Migrating from MongoId to MongoPk

Prior to version 2.4-M1, the id requirement was handled by MongoId. Unfortunately, MongoId defines an _id field and defines the id field as _id.value. In order to migrate MongoRecord.id to type MandatoryTypedField, MongoId has to be removed. For this reason, it has been deprecated.

In order to migrate, you will need to update any references to id to id.is (or id.value). Any references to _id.is will need to be updated to id.is.

Fields

All standard Record Fields are supported. There is also support for Mongo specific types; ObjectId, UUID, Pattern, List, and Map.

Optional fields

Record Fields have separate Optional implementations. They were added for squeryl-record integration, so Mongo specific fields don’t need them. To make field optional you can override optional_? field:

class MainDoc private() extends MongoRecord[MainDoc] with ObjectIdPk[MainDoc] {
  ...
  object optional extends BsonRecordField(this, EmbedDoc) {
    override def optional_? = true
  }
  ...
}
...
val doc = MainDoc.find(...)
val optional = doc.optional.valueBox

You should use valueBox to access optional value. Accessing value field you’ll get back the value in the Box or the defaultValue:

trait TypedField[ThisType] extends BaseField {
...
def value: MyType = valueBox openOr defaultValue
...

Querying

Rogue can be used for querying and updating data:

Rogue is a type-safe internal Scala DSL for constructing and executing find and modify commands against MongoDB in the Lift web framework. It is fully expressive with respect to the basic options provided by MongoDB’s native query language, but in a type-safe manner, building on the record types specified in your Lift models.

Your other option is to use the built-in methods. All of the built-in methods that require a query (find, findAll, count, update) take either a DBObject or JObject as the query. This means you can either use the standard mongo-java-driver apis, or use the BsonDSL (extension of JsonDSL that supports BSON types) to construct your queries.

BsonDSL example:

import net.liftweb.mongodb.BsonDSL._

Person.findAll(("name" -> "joe") ~ ("age" -> 27))

val someObjectId: ObjectId ...

Person.find(("personId" -> someObjectId))

QueryBuilder example:

import com.mongodb.BsonDSL._

val qry = QueryBuilder.start("name").is("joe")
  .put("age").is(27)
  .get

Person.findAll(qry)

More examples:

class ArticleDoc private() extends MongoRecord[ArticleDoc] with ObjectIdPk[ArticleDoc] {
  def meta = ArticleDoc
  object name extends StringField(this, 20)
  object price extends DoubleField(this)
}
object ArticleDoc extends ArticleDoc with MongoMetaRecord[ArticleDoc] {
  override def collectionName = "articles"
}

ArticleDoc.createRecord.name("milk").price(25).save
ArticleDoc.createRecord.name("milk").price(24).save
ArticleDoc.createRecord.name("milk").price(23).save
ArticleDoc.createRecord.name("butter").price(28).save

import com.mongodb.BsonDSL._

ArticleDoc.findAll("name" -> "milk") // 3 objects
ArticleDoc.findAll("$where" -> "function() { return this.name=='milk'}") // 3 objects. Won't use indexes!
ArticleDoc.findAll("price" -> ("$gte"->25)) // 2 objects
ArticleDoc.findAll(("name" -> "milk")~("price" -> ("$gte"->25))) // 1 object (~==and)
ArticleDoc.findAll(("name" -> "milk")~("price" -> ("$gte"->23)~("price" -> ("$lte"->24)))) // 2 objects

More Info:
Mongo advanced queries

Updating

Mongo supports a number of modifier operations including $inc and $set.
To increment two fields using the BasicDBObjectBuilder that is part of the java driver:

import com.mongodb.{BasicDBObject, BasicDBObjectBuilder, DBObject}

val dbo = BasicDBObjectBuilder.start
  .append("$inc", BasicDBObjectBuilder.start
    .append("cnt", 1)
    .append("magic", 42).get).get

MainDoc.update(("name" -> "md1"), dbo)

Or using the BsonDSL:

import import com.mongodb.BsonDSL._

MainDoc.update(("name" -> "md1"), ("$inc" -> ("cnt" -> 1) ~ ("magic" -> 42))

More Info:
BasicDBObjectBuilder

Mongo update