Skip to content

Commit

Permalink
Feature/select json (#532)
Browse files Browse the repository at this point in the history
* Adding JSON selection.

* Spacing tokens and adding support for JSON selection.

* Adding the ability to extract JSON columns.

* Adding tests for selecting JSON.

* Adding the right versions.

* Removing Cassandra lift parsers.

* Adding more fine-grained version checking.

* Oops

* Fixing test errors.

* Fixing post fix syntax.

* Fixing final match.
  • Loading branch information
alexflav23 authored Aug 7, 2016
1 parent 9a6c012 commit d809265
Show file tree
Hide file tree
Showing 18 changed files with 325 additions and 91 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ We publish phantom in 2 formats, stable releases and bleeding edge.
The latest versions are available here. The badges automatically update when a new version is released.

- Latest stable version: [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.websudos/phantom-dsl_2.11/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.websudos/phantom-dsl_2.11) (Maven Central)
- Bleeding edge: [![Bintray](https://api.bintray.com/packages/websudos/oss-releases/phantom/images/download.svg) ](https://bintray.com/websudos/oss-releases/phantom/_latestVersion) (Websudos OSS releases on Bintray)
- Bleeding edge: [![Bintray](https://api.bintray.com/packages/websudos/oss-releases/phantom-dsl/images/download.svg)](https://bintray.com/websudos/oss-releases/phantom-dsl/_latestVersion) (OSS releases on Bintray)

<a id="learning-phantom">Tutorials on phantom and Cassandra</a>
======================================================================
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ lazy val defaultCredentials: Seq[Credentials] = {

val sharedSettings: Seq[Def.Setting[_]] = Defaults.coreDefaultSettings ++ Seq(
organization := "com.websudos",
scalaVersion := "2.10.6",
scalaVersion := "2.11.8",
credentials ++= defaultCredentials,
crossScalaVersions := Seq("2.10.6", "2.11.8"),
resolvers ++= Seq(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ trait SelectTable[T <: CassandraTable[T, R], R] {
RootSelectBlock[T, (A, B, C)](t, List(c1.col.name, c2.col.name, c3.col.name), r => (c1(r), c2(r), c3(r)))
}

def select[A, B, C, D](
f1: T =>SelectColumn[A],
def select[A, B, C, D](f1: T =>SelectColumn[A],
f2: T => SelectColumn[B],
f3: T => SelectColumn[C],
f4: T => SelectColumn[D]): RootSelectBlock[T, (A, B, C, D)] = {
Expand All @@ -89,7 +88,7 @@ trait SelectTable[T <: CassandraTable[T, R], R] {
}

def select[A, B, C, D, E, F](
f1: T =>SelectColumn[A],
f1: T => SelectColumn[A],
f2: T => SelectColumn[B],
f3: T => SelectColumn[C],
f4: T => SelectColumn[D],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ sealed class CqlFunction extends SessionAugmenterImplicits

sealed class UnixTimestampOfCqlFunction extends CqlFunction {

def apply(pf: TimeUUIDColumn[_, _])(implicit ev: Primitive[Long], session: Session): TypedClause.Condition[Option[Long]] = {
def apply(pf: TimeUUIDColumn[_, _])(
implicit ev: Primitive[Long],
session: Session
): TypedClause.Condition[Option[Long]] = {
new TypedClause.Condition(QueryBuilder.Select.unixTimestampOf(pf.name), row => {
if (session.v3orNewer) {
if (row.getColumnDefinitions.contains(s"system.unixtimestampof(${pf.name})")) {
ev.fromRow(s"system.unixtimestampof(${pf.name})", row).toOption
} else {
ev.fromRow(s"unixtimestampof(${pf.name})", row).toOption
Expand All @@ -68,22 +71,27 @@ sealed class TTLOfFunction extends CqlFunction {

sealed class DateOfCqlFunction extends CqlFunction {

def apply(pf: TimeUUIDColumn[_, _])(implicit ev: Primitive[DateTime], session: Session): TypedClause.Condition[Option[DateTime]] = {
def apply(pf: TimeUUIDColumn[_, _])(
implicit ev: Primitive[DateTime],
session: Session
): TypedClause.Condition[Option[DateTime]] = {
new TypedClause.Condition(QueryBuilder.Select.dateOf(pf.name), row => {
if (session.v3orNewer) {

if (row.getColumnDefinitions.contains(s"system.dateof(${pf.name})")) {
ev.fromRow(s"system.dateof(${pf.name})", row).toOption
} else {
ev.fromRow(s"dateof(${pf.name})", row).toOption
}
})
}

def apply(op: OperatorClause.Condition)(implicit ev: Primitive[DateTime], session: Session): TypedClause.Condition[Option[DateTime]] = {
def apply(op: OperatorClause.Condition)(
implicit ev: Primitive[DateTime],
session: Session
): TypedClause.Condition[Option[DateTime]] = {
val pf = op.qb.queryString

new TypedClause.Condition(QueryBuilder.Select.dateOf(pf), row => {
if (session.v3orNewer) {
if (row.getColumnDefinitions.contains(s"system.dateof(${pf})")) {
ev.fromRow(s"system.dateof($pf)", row).toOption
} else {
ev.fromRow(s"dateof($pf)", row).toOption
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ package com.websudos.phantom.builder.query

import com.datastax.driver.core.{ConsistencyLevel, Row, Session}
import com.websudos.phantom.CassandraTable
import com.websudos.phantom.builder._
import com.websudos.phantom.builder.{ConsistencyBound, LimitBound, OrderBound, WhereBound, _}
import com.websudos.phantom.builder.clauses._
import com.websudos.phantom.builder.query.prepared.PreparedSelectBlock
import com.websudos.phantom.builder.syntax.CQLSyntax
import com.websudos.phantom.connectors.KeySpace
import shapeless.ops.hlist.Reverse
import shapeless.{::, =:!=, HList, HNil}
Expand Down Expand Up @@ -345,8 +346,7 @@ private[phantom] class RootSelectBlock[
](table: T, val rowFunc: Row => R, columns: List[String], clause: Option[CQLQuery] = None) {

@implicitNotFound("You haven't provided a KeySpace in scope. Use a Connector to automatically inject one.")
private[phantom] def all()(implicit keySpace: KeySpace): SelectQuery.Default[T, R] = {

def all()(implicit keySpace: KeySpace): SelectQuery.Default[T, R] = {
clause match {
case Some(opt) => {
new SelectQuery(
Expand Down Expand Up @@ -379,6 +379,30 @@ private[phantom] class RootSelectBlock[
Try(r.getLong("writetime")).getOrElse(0L)
}

def json()(implicit keySpace: KeySpace): SelectQuery.Default[T, String] = {

val jsonParser: (Row) => String = row => {
row.getString(CQLSyntax.JSON_EXTRACTOR)
}

clause match {
case Some(opt) => {
new SelectQuery(
table,
jsonParser,
QueryBuilder.Select.selectJson(table.tableName, keySpace.name)
)
}
case None => {
new SelectQuery(
table,
jsonParser,
QueryBuilder.Select.selectJson(table.tableName, keySpace.name, columns: _*)
)
}
}
}

def function[RR](f1: T => TypedClause.Condition[RR])(implicit keySpace: KeySpace): SelectQuery.Default[T, RR] = {
new SelectQuery(
table,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import org.joda.time.DateTime
import shapeless.HList
import shapeless.ops.hlist.Tupler

import scala.annotation.implicitNotFound
import scala.collection.JavaConverters._
import scala.concurrent.{ExecutionContextExecutor, blocking, Future => ScalaFuture}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ private[builder] class IndexModifiers extends BaseModifiers {
modifier(column, CQLSyntax.Operators.notEqs, value)
}

def ==(column: String, value: String): CQLQuery = {
modifier(column, CQLSyntax.Operators.eqs, value)
}

def lt(column: String, value: String): CQLQuery = {
modifier(column, CQLSyntax.Operators.lt, value)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,52 @@ private[builder] class SelectQueryBuilder {
.pad.append(QueryBuilder.keyspace(keyspace, tableName))
}

/**
* Creates a select JSON query builder from a table name, a keyspace, and an arbitrary clause.
* This is used to serialise SELECT functions, such as WRITETIME or other valid expressions.
* Will return a query in the following format:
*
* {{{
* SELECT JSON $clause FROM $keyspace.$tableName
* }}}
* @param tableName The name of the table.
* @param keyspace The name of the keyspace.
* @param clause The CQL clause to use as the select list value.
* @return
*/
def selectJson(tableName: String, keyspace: String, clause: CQLQuery): CQLQuery = {
CQLQuery(CQLSyntax.select)
.forcePad.append(CQLSyntax.json)
.pad.append(clause)
.pad.append(CQLSyntax.from)
.pad.append(QueryBuilder.keyspace(keyspace, tableName))
}

/**
* Selects an arbitrary number of columns given a table name and a keyspace.
* Return all the columns as JSON.
* Will return a query in the following format:
*
* {{{
* SELECT JSON ($name1, $name2, ..) FROM $keyspace.$tableName
* }}}
*
* @param tableName The name of the table.
* @param keyspace The name of the keyspace.
* @param names The names of the columns to include in the select.
* @return A CQLQuery matching the described pattern.
*/
def selectJson(tableName: String, keyspace: String, names: String*): CQLQuery = {
val cols = if (names.nonEmpty) CQLQuery(names) else CQLQuery(CQLSyntax.Symbols.`*`)

CQLQuery(CQLSyntax.select)
.forcePad.append(CQLSyntax.json)
.pad.append(cols)
.forcePad.append(CQLSyntax.from)
.forcePad.append(QueryBuilder.keyspace(keyspace, tableName))
}


def allowFiltering(): CQLQuery = {
CQLQuery(CQLSyntax.allowFiltering)
}
Expand Down Expand Up @@ -242,6 +288,4 @@ private[builder] class SelectQueryBuilder {
def blobAsText(column: String): CQLQuery = {
CQLQuery(CQLSyntax.Selection.BlobAsText).wrapn(column)
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ package com.websudos.phantom.builder.syntax

object CQLSyntax {
val Select = "SELECT"
val json = "JSON"
val JSON_EXTRACTOR = "[json]"
val Where = "WHERE"
val And = "AND"
val Or = "OR"
Expand Down
33 changes: 17 additions & 16 deletions phantom-dsl/src/main/scala/com/websudos/phantom/dsl/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.websudos.phantom.builder.ops._
import com.websudos.phantom.builder.primitives.{DefaultPrimitives, Primitive}
import com.websudos.phantom.builder.query.{CQLQuery, CreateImplicits, DeleteImplicits, SelectImplicits}
import com.websudos.phantom.builder.syntax.CQLSyntax
import com.websudos.phantom.column.AbstractColumn
import shapeless.{::, HNil}

import scala.concurrent.ExecutionContextExecutor
Expand Down Expand Up @@ -183,49 +184,49 @@ package object dsl extends ImplicitMechanism with CreateImplicits

implicit lazy val context: ExecutionContextExecutor = Manager.scalaExecutor

implicit class PartitionTokenHelper[T](val p: Column[_, _, T] with PartitionKey[T]) extends AnyVal {
implicit class PartitionTokenHelper[T](val col: AbstractColumn[T] with PartitionKey[T]) extends AnyVal {

def ltToken (value: T): WhereClause.Condition = {
def ltToken(value: T): WhereClause.Condition = {
new WhereClause.Condition(
QueryBuilder.Where.lt(
QueryBuilder.Where.token(p.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, p.asCql(value)).queryString
QueryBuilder.Where.token(col.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, col.asCql(value)).queryString
)
)
}

def lteToken (value: T): WhereClause.Condition = {
def lteToken(value: T): WhereClause.Condition = {
new WhereClause.Condition(
QueryBuilder.Where.lte(
QueryBuilder.Where.token(p.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, p.asCql(value)).queryString
QueryBuilder.Where.token(col.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, col.asCql(value)).queryString
)
)
}

def gtToken (value: T): WhereClause.Condition = {
def gtToken(value: T): WhereClause.Condition = {
new WhereClause.Condition(
QueryBuilder.Where.gt(
QueryBuilder.Where.token(p.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, p.asCql(value)).queryString
QueryBuilder.Where.token(col.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, col.asCql(value)).queryString
)
)
}

def gteToken (value: T): WhereClause.Condition = {
def gteToken(value: T): WhereClause.Condition = {
new WhereClause.Condition(
QueryBuilder.Where.gte(
QueryBuilder.Where.token(p.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, p.asCql(value)).queryString
QueryBuilder.Where.token(col.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, col.asCql(value)).queryString
)
)
}

def eqsToken (value: T): WhereClause.Condition = {
def eqsToken(value: T): WhereClause.Condition = {
new WhereClause.Condition(
QueryBuilder.Where.eqs(
QueryBuilder.Where.token(p.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, p.asCql(value)).queryString
QueryBuilder.Where.token(col.name).queryString,
QueryBuilder.Where.fcall(CQLSyntax.token, col.asCql(value)).queryString
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ package com.websudos.phantom

import java.util.concurrent.TimeUnit

import com.websudos.phantom.connectors.RootConnector
import com.websudos.phantom.connectors.{RootConnector, VersionNumber}
import com.websudos.phantom.tables.TestDatabase
import com.outworkers.util.lift.{DateTimeSerializer, UUIDSerializer}
import org.scalatest._
Expand Down Expand Up @@ -64,6 +64,8 @@ trait PhantomBaseSuite extends Suite with Matchers

trait PhantomSuite extends FlatSpec with PhantomBaseSuite with TestDatabase.connector.Connector {
val database = TestDatabase

def requireVersion[T](v: VersionNumber)(fn: => T): Unit = if (cassandraVersion.value.compareTo(v) >= 0) fn else ()
}


Expand Down
Loading

0 comments on commit d809265

Please sign in to comment.