Last active
September 29, 2015 17:50
-
-
Save lemonxah/e8eeccf3e0bd74be9bed 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
package com.fullfacing.dao | |
import com.fullfacing.ticketing.macros.Mappable | |
import scala.language.higherKinds | |
/** | |
* Project: dbabspro | |
* Created on 2015/05/19. | |
* ryno aka lemonxah - | |
* https://github.com/lemonxah | |
* http://stackoverflow.com/users/2919672/lemon-xah | |
*/ | |
trait Model { def id: Option[String] } | |
trait DAO[A <: Model, B, C, Q] { | |
def interpreter(q: Query, empty: Q): Option[Q] | |
def insert(a: A)(implicit m: Mappable[A, B, C]) | |
def list(implicit m: Mappable[A, B, C]): Vector[A] | |
def filter(query: Query)(implicit m: Mappable[A, B, C]): Vector[A] | |
def headOption(query: Query)(implicit m: Mappable[A, B, C]): Option[A] | |
def update(a: A)(implicit m: Mappable[A, B, C]) | |
def delete(a: A)(implicit m: Mappable[A, B, C]) | |
def delete(query: Query)(implicit m: Mappable[A, B, C]) | |
// SHORTHAND NOTATION | |
def -=(a: A)(implicit m: Mappable[A, B, C]) = delete(a)(m) | |
def +=(a: A)(implicit m: Mappable[A, B, C]) = insert(a)(m) | |
def :=(a: A)(implicit m: Mappable[A, B, C]) = update(a)(m) | |
} | |
trait Writer[A, B] { def write(a: A): B } | |
trait Cleaner[A, B] { def clean(a: A): B } | |
case class Field[A](name: String) { | |
def ===(value: A): Query = Equals(this, value) | |
def !==(value: A): Query = NotEquals(this, value) | |
def <(value: A): Query = LessThan(this, value) | |
def >(value: A): Query = GreaterThan(this, value) | |
def >=(value: A): Query = GreaterOrEqual(this, value) | |
def <=(value: A): Query = LessOrEqual(this, value) | |
} | |
sealed trait Query { self => | |
def &&(t: Query): Query = and(t) | |
def and(t: Query): Query = And(self, t) | |
def ||(t: Query): Query = or(t) | |
def or(t: Query): Query = Or(self, t) | |
} | |
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 | |
sealed trait Operand[+A] extends Query { def field: Field[_]; def value: A } | |
case class GreaterOrEqual[A](field: Field[A], value: A) extends Operand[A] | |
case class GreaterThan[A](field: Field[A], value: A) extends Operand[A] | |
case class LessOrEqual[A](field: Field[A], value: A) extends Operand[A] | |
case class LessThan[A](field: Field[A], value: A) extends Operand[A] | |
case class Equals[A](field: Field[A], value: A) extends Operand[A] | |
case class NotEquals[A](field: Field[A], value: A) extends Operand[A] | |
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" | |
} | |
implicit val MongoOperandWriter = new Writer[Operand[_], DBObject] { | |
override def write(o: Operand[_]): DBObject = o match { | |
case Equals(f, v) => new BasicDBObject(f.name, v) | |
case op: Operand[_] => new BasicDBObject(op.field.name, new BasicDBObject(opmap(op), op.value)) | |
} | |
} | |
implicit val MongoOperatorWriter = new Writer[Operator, DBObject] { | |
override def write(o: Operator): DBObject = (o match | |
{ case _: Or => $or; case _: And => $and})(SqlQueryWriter.write(o.left),SqlQueryWriter.write(o.right)) | |
} | |
implicit val SqlQueryWriter: 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) | |
} | |
} | |
} | |
class MongoDAO[A <: Model](coll: MongoCollection) extends DAO[A, DBObject, DBObject, DBObject] { | |
import MongoInterpreter._ | |
def interpreter(q: Query, empty: DBObject = MongoDBObject()): Option[DBObject] = { | |
try { Some(implicitly[Writer[Query, DBObject]].write(q)) } catch { case e: Exception => None } | |
} | |
override def list(implicit m: Mappable[A, DBObject, DBObject]): Vector[A] = { | |
coll.find().toVector.map(m.fromDBType) | |
} | |
override def filter(query: Query)(implicit m: Mappable[A, DBObject, DBObject]): Vector[A] = { | |
interpreter(query) match { | |
case Some(q) => coll.find(q).toVector.map(m.fromDBType) | |
case None => Vector() | |
} | |
} | |
override def headOption(query: Query)(implicit m: Mappable[A, DBObject, DBObject]): Option[A] = { | |
interpreter(query) match { | |
case Some(q) => coll.find(q).toVector.map(m.fromDBType).headOption | |
case None => None | |
} | |
} | |
override def insert(a: A)(implicit m: Mappable[A, DBObject, DBObject]) { | |
coll.insert(m.toDBType(a)) | |
} | |
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 => | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment