All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wellfactored.restless.play.json.Selection.scala Maven / Gradle / Ivy

package com.wellfactored.restless.play.json

import com.wellfactored.restless.query.QueryAST.{Path, Query}
import play.api.libs.json._

import scala.language.implicitConversions

object Selection {

  implicit class SelectionOps[T: Writes](xs: Iterable[T]) {
    def limit(l: Option[Int]): Iterable[T] = l match {
      case Some(i) => xs.take(i)
      case None => xs
    }

    def where(qo: Option[Query]): Iterable[T] = qo match {
      case None => xs
      case Some(q) => xs.filter { x =>
        Json.toJson(x) match {
          case doc: JsObject => JsonQuerying.query(q)(doc)
          case _ => false
        }
      }
    }

    def reverse(ro: Option[Boolean]): Iterable[T] = ro match {
      case Some(true) => xs.toSeq.reverse
      case _ => xs
    }
  }

  implicit def filterFn[T: Writes](qo: Option[Query]): T => Boolean = qo match {
    case None => _ => true
    case Some(q) => filterFn(q)
  }

  def filterFn[T: Writes](q: Query): T => Boolean = { x =>
    Json.toJson(x) match {
      case doc: JsObject => JsonQuerying.query(q)(doc)
      case _ => false
    }
  }

  def isEmpty(jv: JsValue): Boolean = jv match {
    case JsObject(e) if e.isEmpty => true
    case JsNull => true
    case _ => false
  }

  def nonEmpty(jv: JsValue): Boolean = !isEmpty(jv)

  def projection[T: Writes](po: Option[List[Path]], t: T): JsValue = po.map {
    paths => new JsonProjector[T](paths).project(t)
  }.getOrElse(new JsonIdentity[T].project(t))


  implicit def jsSortFn(po: Option[Path]): (JsValue, JsValue) => Boolean = po match {
    case None => (a, b) => false
    case Some(p) => jsSortFn(p)
  }

  def jsSortFn(p: Path): (JsValue, JsValue) => Boolean = { (o1, o2) =>
    import play.api.libs.json._
    import JsonProjector.project

    (project(o1, p), project(o2, p)) match {
      case (n1: JsNumber, n2: JsNumber) => n1.value < n2.value
      case (s1: JsString, s2: JsString) => s1.value < s2.value
      case (b1: JsBoolean, b2: JsBoolean) => b1.value < b2.value
      case (JsNull, JsNull) => false // neither object has the key so keep original order
      case (_, JsNull) => true // sort objects without the key after those with the keys
      case _ => false //  JsObjects, JsArrays and mixed types keep original order
    }
  }

  def selectT[T: Writes, B](ts: Iterable[T], qo: Option[Query], po: Option[List[Path]], maxResults: Option[Int], rev: Option[Boolean])(sortKey: (T) => B)(implicit ordering: Ordering[B]): Iterable[JsValue] = {
    ts.where(qo).toSeq
      .sortBy(sortKey)
      .map(projection(po, _))
      .filter(nonEmpty)
      .distinct
      .limit(maxResults)
      .reverse(rev)
  }

  def selectFromJson(js: Seq[JsValue], qo: Option[Query], po: Option[List[Path]], maxResults: Option[Int], sortKey: Option[Path], rev: Option[Boolean]): Iterable[JsValue] = {
    js.where(qo).toSeq
      .sortWith(sortKey)
      .map(projection(po, _))
      .filter(nonEmpty)
      .distinct
      .limit(maxResults)
      .reverse(rev)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy