Skip to content

Instantly share code, notes, and snippets.

@lemonxah
Last active September 29, 2015 17:50
Show Gist options
  • Save lemonxah/e8eeccf3e0bd74be9bed to your computer and use it in GitHub Desktop.
Save lemonxah/e8eeccf3e0bd74be9bed to your computer and use it in GitHub Desktop.
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