Browse files

Issue 926 - Add better support for embedded objects in mongodb-record

  • Loading branch information...
1 parent c16b707 commit 409e23e4320e9b20eb3acc62e71bb036c75d5de3 @eltimn eltimn committed Feb 25, 2011
View
130 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/BsonRecord.scala
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2011 WorldWide Conferencing, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.liftweb
+package mongodb
+package record
+
+import common._
+
+import scala.collection.JavaConversions._
+
+import net.liftweb.record.{Field, MetaRecord, Record}
+import net.liftweb.record.field._
+
+import com.mongodb._
+
+/** Specialized Record that can be encoded and decoded from BSON (DBObject) */
+trait BsonRecord[MyType <: BsonRecord[MyType]] extends Record[MyType] {
+ self: MyType =>
+
+ /** Refines meta to require a BsonMetaRecord */
+ def meta: BsonMetaRecord[MyType]
+
+ /**
+ * Encode a record instance into a DBObject
+ */
+ def asDBObject: DBObject = meta.asDBObject(this)
+
+ /**
+ * Set the fields of this record from the given DBObject
+ */
+ def setFieldsFromDBObject(dbo: DBObject): Unit = meta.setFieldsFromDBObject(this, dbo)
+
+ override def toString = {
+ val fieldList = this.fields.map(f => "%s=%s" format (f.name,
+ f.valueBox match {
+ case Full(c: java.util.Calendar) => c.getTime().toString()
+ case Full(null) => ""
+ case Full(v) => v.toString
+ case _ => ""
+ }))
+
+ "%s={%s}" format (this.getClass.toString, fieldList.mkString(", "))
+ }
+}
+
+/** Specialized MetaRecord that deals with BsonRecords */
+trait BsonMetaRecord[BaseRecord <: BsonRecord[BaseRecord]] extends MetaRecord[BaseRecord] with JsonFormats {
+ self: BaseRecord =>
+
+ /**
+ * Create a BasicDBObject from the field names and values.
+ * - MongoFieldFlavor types (List) are converted to DBObjects
+ * using asDBObject
+ */
+ def asDBObject(inst: BaseRecord): DBObject = {
+
+ import Meta.Reflection._
+ import field.MongoFieldFlavor
+
+ val dbo = BasicDBObjectBuilder.start // use this so regex patterns can be stored.
+
+ for (f <- fields(inst)) {
+ f match {
+ case field if (field.optional_? && field.valueBox.isEmpty) => // don't add to DBObject
+ case field: EnumTypedField[Enumeration] =>
+ field.asInstanceOf[EnumTypedField[Enumeration]].valueBox foreach {
+ v => dbo.add(f.name, v.id)
+ }
+ case field: EnumNameTypedField[Enumeration] =>
+ field.asInstanceOf[EnumNameTypedField[Enumeration]].valueBox foreach {
+ v => dbo.add(f.name, v.toString)
+ }
+ case field: MongoFieldFlavor[Any] =>
+ dbo.add(f.name, field.asInstanceOf[MongoFieldFlavor[Any]].asDBObject)
+ case field => field.valueBox foreach (_.asInstanceOf[AnyRef] match {
+ case null => dbo.add(f.name, null)
+ case x if primitive_?(x.getClass) => dbo.add(f.name, x)
+ case x if mongotype_?(x.getClass) => dbo.add(f.name, x)
+ case x if datetype_?(x.getClass) => dbo.add(f.name, datetype2dbovalue(x))
+ case x: BsonRecord[_] => dbo.add(f.name, x.asDBObject)
+ case o => dbo.add(f.name, o.toString)
+ })
+ }
+ }
+ dbo.get
+ }
+
+ /**
+ * Creates a new record, then sets the fields with the given DBObject.
+ *
+ * @param dbo - the DBObject
+ * @return Box[BaseRecord]
+ */
+ def fromDBObject(dbo: DBObject): BaseRecord = {
+ val inst: BaseRecord = createRecord
+ setFieldsFromDBObject(inst, dbo)
+ inst
+ }
+
+ /**
+ * Populate the inst's fields with the values from a DBObject. Values are set
+ * using setFromAny passing it the DBObject returned from Mongo.
+ *
+ * @param inst - the record that will be populated
+ * @param obj - The DBObject
+ * @return Box[BaseRecord]
+ */
+ def setFieldsFromDBObject(inst: BaseRecord, dbo: DBObject): Unit = {
+ for (k <- dbo.keySet; field <- inst.fieldByName(k.toString)) {
+ field.setFromAny(dbo.get(k.toString))
+ }
+ inst.runSafe {
+ inst.fields.foreach(_.resetDirty)
+ }
+ }
+}
View
75 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/MongoMetaRecord.scala
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package net.liftweb
-package mongodb
-package record
+package net.liftweb
+package mongodb
+package record
import java.util.{Calendar, UUID}
import java.util.regex.Pattern
@@ -37,7 +37,7 @@ import com.mongodb.util.JSON
import org.bson.types.ObjectId
trait MongoMetaRecord[BaseRecord <: MongoRecord[BaseRecord]]
- extends MetaRecord[BaseRecord] with MongoMeta[BaseRecord] {
+ extends BsonMetaRecord[BaseRecord] with MongoMeta[BaseRecord] {
self: BaseRecord =>
@@ -298,71 +298,4 @@ trait MongoMetaRecord[BaseRecord <: MongoRecord[BaseRecord]]
.get)
this.update(query, update)
}
-
- /**
- * Create a BasicDBObject from the field names and values.
- * - MongoFieldFlavor types (List) are converted to DBObjects
- * using asDBObject
- */
- def asDBObject(inst: BaseRecord): DBObject = {
-
- import Meta.Reflection._
- import field.MongoFieldFlavor
-
- val dbo = BasicDBObjectBuilder.start // use this so regex patterns can be stored.
-
- for (f <- fields(inst)) {
- f match {
- case field if (field.optional_? && field.valueBox.isEmpty) => // don't add to DBObject
- case field: EnumTypedField[Enumeration] =>
- field.asInstanceOf[EnumTypedField[Enumeration]].valueBox foreach {
- v => dbo.add(f.name, v.id)
- }
- case field: EnumNameTypedField[Enumeration] =>
- field.asInstanceOf[EnumNameTypedField[Enumeration]].valueBox foreach {
- v => dbo.add(f.name, v.toString)
- }
- case field: MongoFieldFlavor[Any] =>
- dbo.add(f.name, field.asInstanceOf[MongoFieldFlavor[Any]].asDBObject)
- case field => field.valueBox foreach (_.asInstanceOf[AnyRef] match {
- case null => dbo.add(f.name, null)
- case x if primitive_?(x.getClass) => dbo.add(f.name, x)
- case x if mongotype_?(x.getClass) => dbo.add(f.name, x)
- case x if datetype_?(x.getClass) => dbo.add(f.name, datetype2dbovalue(x))
- case o => dbo.add(f.name, o.toString)
- })
- }
- }
- dbo.get
- }
-
- /**
- * Creates a new record, then sets the fields with the given DBObject.
- *
- * @param dbo - the DBObject
- * @return Box[BaseRecord]
- */
- def fromDBObject(dbo: DBObject): BaseRecord = {
- val inst: BaseRecord = createRecord
- setFieldsFromDBObject(inst, dbo)
- inst
- }
-
- /**
- * Populate the inst's fields with the values from a DBObject. Values are set
- * using setFromAny passing it the DBObject returned from Mongo.
- *
- * @param inst - the record that will be populated
- * @param obj - The DBObject
- * @return Box[BaseRecord]
- */
- def setFieldsFromDBObject(inst: BaseRecord, dbo: DBObject): Unit = {
- for (k <- dbo.keySet; field <- inst.fieldByName(k.toString)) {
- field.setFromAny(dbo.get(k.toString))
- }
- inst.runSafe {
- inst.fields.foreach(_.resetDirty)
- }
- }
}
-
View
51 persistence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/MongoRecord.scala
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package net.liftweb
-package mongodb
-package record
+package net.liftweb
+package mongodb
+package record
import net.liftweb.common.{Box, Full}
import net.liftweb.record.{MetaRecord, Record}
@@ -25,7 +25,7 @@ import com.mongodb.{BasicDBObject, DBObject, DBRef, WriteConcern}
import org.bson.types.ObjectId
-trait MongoRecord[MyType <: MongoRecord[MyType]] extends Record[MyType] {
+trait MongoRecord[MyType <: MongoRecord[MyType]] extends BsonRecord[MyType] {
self: MyType =>
/*
@@ -71,48 +71,6 @@ trait MongoRecord[MyType <: MongoRecord[MyType]] extends Record[MyType] {
meta.delete_!(this)
}
}
-
-/* Set mongoIdentifier in meta object. No need to support calcDbId for sharding
- private var dbMongoIdentifier: Option[MongoIdentifier] = None
-
- def mongoIdentifier = dbMongoIdentifier getOrElse calcDbId
-
- def dbCalculateMongoIdentifier: PartialFunction[MyType, MongoIdentifier] = Map.empty
-
- private def calcDbId = if (dbCalculateMongoIdentifier.isDefinedAt(this)) dbCalculateMongoIdentifier(this)
- else meta.dbDefaultMongoIdentifier
-*/
-
- /**
- * Append a function to perform after the commit happens
- * @param func - the function to perform after the commit happens
-
- def doPostCommit(func: () => Unit) {
- //DB.appendPostFunc(connectionIdentifier, func)
- }
-*/
-
- /**
- * Encode a record instance into a DBObject
- */
- def asDBObject: DBObject = meta.asDBObject(this)
-
- /**
- * Set the fields of this record from the given DBObject
- */
- def setFieldsFromDBObject(dbo: DBObject): Unit = meta.setFieldsFromDBObject(this, dbo)
-
- override def toString = {
- val fieldList = this.fields.map(f => "%s=%s" format (f.name,
- f.valueBox match {
- case Full(c: java.util.Calendar) => c.getTime().toString()
- case Full(null) => ""
- case Full(v) => v.toString
- case _ => ""
- }))
-
- "%s={%s}" format (this.getClass.toString, fieldList.mkString(", "))
- }
}
/**
@@ -137,4 +95,3 @@ trait MongoId[OwnerType <: MongoRecord[OwnerType]] {
)
}
}
-
View
104 ...ence/mongodb-record/src/main/scala/net/liftweb/mongodb/record/field/BsonRecordField.scala
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2011 WorldWide Conferencing, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.liftweb
+package mongodb
+package record
+package field
+
+import common._
+import http.js.JsExp
+import http.js.JE.JsNull
+import json.JsonAST._
+import json.Printer
+
+import net.liftweb.record._
+import com.mongodb._
+
+import scala.xml._
+
+/** Field that contains an entire record represented as an inline object value. Inspired by JSONSubRecordField */
+class BsonRecordField[OwnerType <: MongoRecord[OwnerType], SubRecordType <: BsonRecord[SubRecordType]]
+ (rec: OwnerType, valueMeta: BsonMetaRecord[SubRecordType])(implicit subRecordType: Manifest[SubRecordType])
+ extends Field[SubRecordType, OwnerType]
+ with MandatoryTypedField[SubRecordType]
+{
+ def this(rec: OwnerType, valueMeta: BsonMetaRecord[SubRecordType], value: SubRecordType)
+ (implicit subRecordType: Manifest[SubRecordType]) = {
+ this(rec, value.meta)
+ set(value)
+ }
+
+ def this(rec: OwnerType, valueMeta: BsonMetaRecord[SubRecordType], value: Box[SubRecordType])
+ (implicit subRecordType: Manifest[SubRecordType]) = {
+ this(rec, valueMeta)
+ setBox(value)
+ }
+
+ def owner = rec
+ def asJs = asJValue match {
+ case JNothing => JsNull
+ case jv => new JsExp {
+ lazy val toJsCmd = Printer.compact(render(jv))
+ }
+ }
+ def toForm: Box[NodeSeq] = Empty
+ def defaultValue = valueMeta.createRecord
+
+ def setFromString(s: String): Box[SubRecordType] = valueMeta.fromJsonString(s)
+
+ def setFromAny(in: Any): Box[SubRecordType] = in match {
+ case dbo: DBObject => setBox(Full(valueMeta.fromDBObject(dbo)))
+ case _ => genericSetFromAny(in)
+ }
+
+ def asJValue: JValue = valueBox.map(_.asJValue) openOr (JNothing: JValue)
+ def setFromJValue(jvalue: JValue): Box[SubRecordType] = jvalue match {
+ case JNothing|JNull if optional_? => setBox(Empty)
+ case _ => setBox(valueMeta.fromJValue(jvalue))
+ }
+}
+
+/*
+ * List of BsonRecords
+ */
+class BsonRecordListField[OwnerType <: MongoRecord[OwnerType], SubRecordType <: BsonRecord[SubRecordType]]
+ (rec: OwnerType, valueMeta: BsonMetaRecord[SubRecordType])
+ extends MongoListField[OwnerType, SubRecordType](rec: OwnerType) {
+
+ import scala.collection.JavaConversions._
+
+ override def asDBObject: DBObject = {
+ val dbl = new BasicDBList
+ value.foreach { v => dbl.add(v.asDBObject) }
+ dbl
+ }
+
+ override def setFromDBObject(dbo: DBObject): Box[List[SubRecordType]] =
+ setBox(Full(dbo.keySet.toList.map(k => {
+ valueMeta.fromDBObject(dbo.get(k.toString).asInstanceOf[DBObject])
+ })))
+
+ override def asJValue = JArray(value.map(_.asJValue))
+
+ override def setFromJValue(jvalue: JValue) = jvalue match {
+ case JNothing|JNull if optional_? => setBox(Empty)
+ case JArray(arr) => setBox(Full(arr.map( jv => {
+ valueMeta.fromJValue(jv) openOr valueMeta.createRecord
+ })))
+ case other => setBox(FieldHelpers.expectedA("JArray", other))
+ }
+}
View
50 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/Fixtures.scala
@@ -150,7 +150,7 @@ class FieldTypeTestRecord private () extends MongoRecord[FieldTypeTestRecord] wi
}
}
-case class MongoCaseClassTestObject(intField: Int, stringField: String)
+case class MongoCaseClassTestObject(intField: Int, stringField: String)
object FieldTypeTestRecord extends FieldTypeTestRecord with MongoMetaRecord[FieldTypeTestRecord]
@@ -232,7 +232,7 @@ class ListTestRecord private () extends MongoRecord[ListTestRecord] with MongoId
object mandatoryMongoJsonObjectListField extends MongoJsonObjectListField(this, TypeTestJsonObject)
object legacyOptionalMongoJsonObjectListField extends MongoJsonObjectListField(this, TypeTestJsonObject) { override def optional_? = true }
-
+
object mongoCaseClassListField extends MongoCaseClassListField[ListTestRecord, MongoCaseClassTestObject](this)
// TODO: More List types
@@ -291,6 +291,52 @@ object LifecycleTestRecord extends LifecycleTestRecord with MongoMetaRecord[Life
override def foreachCallback(inst: LifecycleTestRecord, f: LifecycleCallbacks => Any) = super.foreachCallback(inst, f)
}
+/*
+ * SubRecord fields
+ */
+class SubRecord extends BsonRecord[SubRecord] {
+ def meta = SubRecord
+
+ object name extends StringField(this, 12)
+
+ override def equals(other: Any): Boolean = other match {
+ case that:SubRecord =>
+ this.name.value == that.name.value
+ case _ => false
+ }
+}
+object SubRecord extends SubRecord with BsonMetaRecord[SubRecord] {
+ override def formats = allFormats
+}
+
+class SubRecordTestRecord extends MongoRecord[SubRecordTestRecord] with MongoId[SubRecordTestRecord] {
+ def meta = SubRecordTestRecord
+
+ object mandatoryBsonRecordField extends BsonRecordField(this, SubRecord)
+ object legacyOptionalBsonRecordField extends BsonRecordField(this, SubRecord) {
+ override def optional_? = true
+ }
+
+ object mandatoryBsonRecordListField extends BsonRecordListField(this, SubRecord)
+ object legacyOptionalBsonRecordListField extends BsonRecordListField(this, SubRecord) {
+ override def optional_? = true
+ }
+
+ override def equals(other: Any): Boolean = other match {
+ case that:SubRecordTestRecord =>
+ this.id == that.id &&
+ this.mandatoryBsonRecordField.value == that.mandatoryBsonRecordField.value &&
+ this.legacyOptionalBsonRecordField.valueBox == that.legacyOptionalBsonRecordField.valueBox &&
+ this.mandatoryBsonRecordListField.value == that.mandatoryBsonRecordListField.value &&
+ this.legacyOptionalBsonRecordListField.valueBox == that.legacyOptionalBsonRecordListField.valueBox
+ case _ => false
+ }
+
+}
+object SubRecordTestRecord extends SubRecordTestRecord with MongoMetaRecord[SubRecordTestRecord] {
+ override def formats = allFormats
+}
+
case class JsonObj(id: String, name: String) extends JsonObject[JsonObj] {
def meta = JsonObj
}
View
43 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoRecordSpec.scala
@@ -166,7 +166,12 @@ object MongoRecordSpec extends Specification("MongoRecord Specification") with M
"MongoRecord" should {
checkMongoIsRunning
-
+
+ val sr1 = SubRecord.createRecord
+ .name("SubRecord1")
+ val sr2 = SubRecord.createRecord
+ .name("SubRecord2")
+
val fttr = FieldTypeTestRecord.createRecord
//.mandatoryBinaryField()
.mandatoryBooleanField(false)
@@ -205,9 +210,24 @@ object MongoRecordSpec extends Specification("MongoRecord Specification") with M
.mandatoryStringMapField(Map("a" -> "abc", "b" -> "def", "c" -> "ghi"))
.mandatoryIntMapField(Map("a" -> 4, "b" -> 5, "c" -> 6))
- val json = "{\"mandatoryDateField\":{\"$dt\":\""+mfttr.meta.formats.dateFormat.format(mfttr.mandatoryDateField.value)+"\"},\"mandatoryJsonObjectField\":{\"intField\":1,\"stringField\":\"jsonobj1\"},\"mandatoryObjectIdField\":{\"$oid\":\""+mfttr.mandatoryObjectIdField.value.toString+"\"},\"mandatoryPatternField\":{\"$regex\":\"^Mo\",\"$flags\":2},\"mandatoryUUIDField\":{\"$uuid\":\""+mfttr.mandatoryUUIDField.value.toString+"\"},\"_id\":{\"$oid\":\""+mfttr.id.toString+"\"}}"
- val ljson = "{\"mandatoryStringListField\":[\"abc\",\"def\",\"ghi\"],\"legacyOptionalStringListField\":[],\"mandatoryIntListField\":[4,5,6],\"legacyOptionalIntListField\":[],\"mandatoryMongoJsonObjectListField\":[{\"intField\":1,\"stringField\":\"jsonobj1\"},{\"intField\":2,\"stringField\":\"jsonobj2\"}],\"legacyOptionalMongoJsonObjectListField\":[],\"_id\":{\"$oid\":\""+ltr.id.toString+"\"}}"
- val mjson = "{\"mandatoryStringMapField\":{\"a\":\"abc\",\"b\":\"def\",\"c\":\"ghi\"},\"legacyOptionalStringMapField\":{},\"mandatoryIntMapField\":{\"a\":4,\"b\":5,\"c\":6},\"legacyOptionalIntMapField\":{},\"_id\":{\"$oid\":\""+mtr.id.toString+"\"}}"
+ val srtr = SubRecordTestRecord.createRecord
+ .mandatoryBsonRecordField(sr1)
+ .mandatoryBsonRecordListField(List(sr1,sr2))
+
+ val json = Printer.compact(render(mfttr.asJValue))
+ val ljson = Printer.compact(render(ltr.asJValue))
+ val mjson = Printer.compact(render(mtr.asJValue))
+
+ val srtrJson = JObject(List(
+ JField("_id", JObject(List(JField("$oid", JString(srtr.id.toString))))),
+ JField("mandatoryBsonRecordField", JObject(List(JField("name", JString("SubRecord1"))))),
+ JField("legacyOptionalBsonRecordField", JNothing),
+ JField("mandatoryBsonRecordListField", JArray(List(
+ JObject(List(JField("name", JString("SubRecord1")))),
+ JObject(List(JField("name", JString("SubRecord2"))))
+ ))),
+ JField("legacyOptionalBsonRecordListField", JArray(List()))
+ ))
"save and retrieve 'standard' type fields" in {
checkMongoIsRunning
@@ -249,6 +269,14 @@ object MongoRecordSpec extends Specification("MongoRecord Specification") with M
mtrFromDb foreach { tr =>
tr mustEqual mtr
}
+
+ srtr.save
+
+ val srtrFromDb = SubRecordTestRecord.find(srtr.id)
+ srtrFromDb must notBeEmpty
+ srtrFromDb foreach { tr =>
+ tr mustEqual srtr
+ }
}
"convert Mongo type fields to JValue" in {
@@ -302,6 +330,13 @@ object MongoRecordSpec extends Specification("MongoRecord Specification") with M
))),
JField("legacyOptionalIntMapField", JObject(List()))
))
+
+ val srtrAsJValue = srtr.asJValue
+ srtrAsJValue \\ "_id" mustEqual srtrJson \\ "_id"
+ srtrAsJValue \\ "mandatoryBsonRecordField" mustEqual srtrJson \\ "mandatoryBsonRecordField"
+ srtrAsJValue \\ "legacyOptionalBsonRecordField" mustEqual srtrJson \\ "legacyOptionalBsonRecordField"
+ srtrAsJValue \\ "mandatoryBsonRecordListField" mustEqual srtrJson \\ "mandatoryBsonRecordListField"
+ srtrAsJValue \\ "legacyOptionalBsonRecordListField" mustEqual srtrJson \\ "legacyOptionalBsonRecordListField"
}
"convert Mongo type fields to JsExp" in {
View
26 persistence/mongodb/src/main/scala/net/liftweb/mongodb/MongoMeta.scala
@@ -11,20 +11,29 @@
* limitations under the License.
*/
-package net.liftweb
-package mongodb
+package net.liftweb
+package mongodb
import org.bson.types.ObjectId
-import net.liftweb.json.{DefaultFormats, Formats}
-import net.liftweb.json.JsonAST.JObject
+import json.{DefaultFormats, Formats}
+import json.JsonAST.JObject
import com.mongodb.{BasicDBObject, DB, DBObject}
+trait JsonFormats {
+ // override this for custom Formats
+ def formats: Formats = DefaultFormats.lossless
+
+ implicit lazy val _formats: Formats = formats
+
+ lazy val allFormats = DefaultFormats.lossless + new ObjectIdSerializer + new DateSerializer + new PatternSerializer + new UUIDSerializer
+}
+
/*
* This is used by both MongoDocumentMeta and MongoMetaRecord
*/
-trait MongoMeta[BaseDocument] {
+trait MongoMeta[BaseDocument] extends JsonFormats {
// class name has a $ at the end. because it's an object(?)
private lazy val _collectionName = {
@@ -55,13 +64,6 @@ trait MongoMeta[BaseDocument] {
// override this to specify a MongoIdentifier for this MongoDocument type
def mongoIdentifier: MongoIdentifier = DefaultMongoIdentifier
- // override this for custom Formats
- def formats: Formats = DefaultFormats.lossless
-
- implicit lazy val _formats: Formats = formats
-
- lazy val allFormats = DefaultFormats.lossless + new ObjectIdSerializer + new DateSerializer + new PatternSerializer + new UUIDSerializer
-
/*
* Count all documents
*/

0 comments on commit 409e23e

Please sign in to comment.