Created
November 30, 2015 09:15
-
-
Save lemonxah/299962210cbc48cadc74 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Writer[A, B] { def write(a: A): B } | |
trait Cleaner[A, B] { def clean(a: A): B } | |
trait Value[+A] { def get: A } | |
case class SingleValue[A](a: A) extends Value[A] { override def get: A = a } | |
case class ListValue[+A](a: List[A]) extends Value[List[A]] { override def get: List[A] = a } | |
case class Field[A](name: String) { | |
def asc: OrderField = OrderField(this, 1) | |
def dsc: OrderField = OrderField(this, -1) | |
def ===(value: A): Query = Equals(this, SingleValue(value)) | |
def !==(value: A): Query = NotEquals(this, SingleValue(value)) | |
def <(value: A): Query = LessThan(this, SingleValue(value)) | |
def >(value: A): Query = GreaterThan(this, SingleValue(value)) | |
def >=(value: A): Query = GreaterOrEqual(this, SingleValue(value)) | |
def <=(value: A): Query = LessOrEqual(this, SingleValue(value)) | |
def =!=(value: List[A]): Query = In(this, ListValue(value)) | |
def =!!=(value: A): Query = ElemMatchList(this, SingleValue(value)) | |
} | |
object FieldExtentions { | |
implicit class stringField(field: Field[String]) { | |
def <%>(value: String): Query = Like(field, SingleValue(value)) | |
def <*>(value: String): Query = Regex(field, SingleValue(value)) | |
} | |
} | |
sealed trait Query { self => | |
def &&(t: Query): Query = And(self, t) | |
def ||(t: Query): Query = Or(self, t) | |
def orderBy(orderings: List[OrderField]): OrderBy = OrderBy(self, orderings) | |
} | |
case class OrderField(field: Field[_], order: Int) | |
sealed trait Operator extends Query { def left: Query; def right: Query } | |
case class Or(left: Query, right: Query) extends Operator | |
case class And(left: Query, right: Query) extends Operator | |
case class OrderBy(left: Query, field: List[OrderField]) extends Query | |
sealed trait Operand extends Query { def field: Field[_]; def value: Value[_] } | |
case class GreaterOrEqual[A](field: Field[A], value: SingleValue[A]) extends Operand | |
case class GreaterThan[A](field: Field[A], value: SingleValue[A]) extends Operand | |
case class LessOrEqual[A](field: Field[A], value: SingleValue[A]) extends Operand | |
case class LessThan[A](field: Field[A], value: SingleValue[A]) extends Operand | |
case class Equals[A](field: Field[A], value: SingleValue[A]) extends Operand | |
case class NotEquals[A](field: Field[A], value: SingleValue[A]) extends Operand | |
case class In[A](field: Field[A], value: ListValue[A]) extends Operand | |
case class ElemMatchList[A](field: Field[A], value: SingleValue[A]) extends Operand | |
case class Like[A](field: Field[A], value: SingleValue[A]) extends Operand | |
case class Regex[A](field: Field[A], value: SingleValue[A]) extends Operand | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
object MongoInterpreter { | |
// Interpreter typeclasses | |
def opmap: PartialFunction[Operand, String] = { | |
case _: Equals[_] => "$eq" | |
case _: GreaterThan[_] => "$gt" | |
case _: GreaterOrEqual[_] => "$gte" | |
case _: LessThan[_] => "$lt" | |
case _: LessOrEqual[_] => "$lte" | |
case _: NotEquals[_] => "$ne" | |
case _: In[_] => "$in" | |
case _: Regex[_] => "$regex" | |
} | |
implicit val MongoOperandWriter = new Writer[Operand, DBObject] { | |
override def write(o: Operand): DBObject = o match { | |
case Equals(f, v) => new BasicDBObject(f.name, v.get) | |
case ElemMatchList(f, v) => new BasicDBObject(f.name, new BasicDBObject("$elemMatch", new BasicDBObject("$in", v.get))) | |
case Like(f, v) => new BasicDBObject(f.name, new BasicDBObject("$regex", s".*${v.get}.*")) | |
case op: Operand => new BasicDBObject(op.field.name, new BasicDBObject(opmap(op), op.value.get)) | |
} | |
} | |
implicit val MongoOperatorWriter = new Writer[Operator, DBObject] { | |
override def write(o: Operator): DBObject = (o match | |
{ case _: Or => $or; case _: And => $and})(MongoQueryWriter.write(o.left),MongoQueryWriter.write(o.right)) | |
} | |
implicit val MongoOrderByWriter = new Writer[OrderBy, DBObject] { | |
override def write(order: OrderBy): DBObject = | |
new BasicDBObject("$query", MongoQueryWriter.write(order.left)) | |
.append("$orderby", order.field.foldLeft(new BasicDBObject()) { case (a, o) => a.append(o.field.name, o.order) }) | |
} | |
implicit val MongoQueryWriter: Writer[Query, DBObject] = new Writer[Query, DBObject] { | |
override def write(a: Query): DBObject = a match { | |
case op: Operand => MongoOperandWriter.write(op) | |
case op: Operator => MongoOperatorWriter.write(op) | |
case op: OrderBy => MongoOrderByWriter.write(op) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MongoDAO[A <: Model](coll: MongoCollection) extends DAO[A, DBObject, DBObject, DBObject] { | |
import MongoInterpreter._ | |
def interpreter(q: Query, empty: DBObject = new MongoDBObject()): Option[DBObject] = { | |
try { Some(implicitly[Writer[Query, DBObject]].write(q)) } catch { case e: Exception => None } | |
} | |
override def list(limit: Int = 0)(implicit m: Mappable[A, DBObject, DBObject]): Vector[A] = { | |
coll.find().limit(limit).toVector.map(m.fromDBType) | |
} | |
override def filter(query: Query, limit: Int = 0)(implicit m: Mappable[A, DBObject, DBObject]): Vector[A] = { | |
interpreter(query) match { | |
case Some(q) => coll.find(q).limit(limit).toVector.map(m.fromDBType) | |
case None => Vector() | |
} | |
} | |
override def headOption(query: Query, limit: Int = 0)(implicit m: Mappable[A, DBObject, DBObject]): Option[A] = { | |
interpreter(query) match { | |
case Some(q) => coll.find(q).limit(limit).toVector.map(m.fromDBType).headOption | |
case None => None | |
} | |
} | |
override def insert(a: A)(implicit m: Mappable[A, DBObject, DBObject], ev: A => ModelWithId[A]): A = { | |
val aa = a.copy(id = UUID.randomUUID().toString.some) | |
coll.insert(m.toDBType(aa)) | |
aa | |
} | |
override def update(a: A)(implicit m: Mappable[A, DBObject, DBObject]) { | |
coll.update("id" $eq a.id.get, m.toDBType(a)) | |
} | |
override def delete(a: A)(implicit m: Mappable[A, DBObject, DBObject]) { | |
coll.findAndRemove("id" $eq a.id.get) | |
} | |
override def delete(query: Query)(implicit m: Mappable[A, DBObject, DBObject]): Unit = { | |
interpreter(query) match { | |
case Some(q) => coll.findAndRemove(q) | |
case None => | |
} | |
} | |
/** | |
* | |
* @deprecated This will be removed when sub-object query has been implemented | |
*/ | |
@Deprecated | |
override def customQuery(q: DBObject, limit: Int)(implicit m: Mappable[A, DBObject, DBObject]): Vector[A] = { | |
coll.find(q).limit(limit).toVector.map(m.fromDBType) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment