Skip to content

Commit

Permalink
Feature/token aware support (#816)
Browse files Browse the repository at this point in the history
* Bumping versions

* Adding type params

* Removing Query as its useless.

* Updating more of SelectQury.

* More removal work

* More cleanup work

* Removing more deps and fixing more HLists for token queries

* More and more cleanup

* Adding more details in real time.

* Adding normal lists

* Lazy evaluating tokenizers.

* Pass through all tokens

* Propagating tokens through executable queries.

* Automatically setting token aware policy routing

* Isolating routing key setting

* Removing type param where not needed.

* Removing unused type param

* Finalising removal

* Using the operator

* Removing more of the implementation

* Fixing Scala 2.10 compilation

* Adding prepare mark

* Deferring primitive evaluation.

* Adding tests for tuple partiion tables.

* Fixing edge case
  • Loading branch information
alexflav23 authored Mar 25, 2018
1 parent 0c03b1f commit ee739e9
Show file tree
Hide file tree
Showing 51 changed files with 642 additions and 353 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ before_cache:
- find $HOME/.sbt -name "*.lock" -delete
env:
global:
- TARGET_SCALA_VERSION: 2.12.4
- TARGET_SCALA_VERSION: 2.12.5
- GH_REF: github.com/outworkers/phantom.git
- secure: WwLG0fkVV1DS5P6GeLOHjPJ2z+GP9UaLzX27DuHBEXRxNPPw27y7jag0gbbf6E80kYwGB4zfBx9YjcJFVFdGCCcAjDJHMP8kLQirUak1jzuoWZFUEtTkF7ev/OVs0PmMHrOGuysT4W/UaL9MZD/mYO5lO9oavycTQ0kbOwZJwUg=
- secure: Qtr5ULJ90s5pfLBaXRKFLMPBcDSYBQfUVz+abgjlG/um8iWE/OJswYn2n3zylAqnI4bSKhPobl6bBYzt8f6k9kzMqpR4t/4gWFV1LJUas0eAsTnrjV1He4nbPvO9RVEkvuQcTCqmus2AgYEnwdKhGWwktok5PAoJ22ycy4HK8C8=
Expand All @@ -37,7 +37,7 @@ jdk:
- oraclejdk8
matrix:
include:
- scala: 2.12.4
- scala: 2.12.5
jdk: oraclejdk8
addons:
apt:
Expand Down
8 changes: 3 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ scalacOptions in ThisBuild ++= ScalacOptions ++ YWarnOptions

lazy val Versions = new {
val logback = "1.2.3"
val util = "0.38.0"
val util = "0.40.0"
val json4s = "3.5.1"
val datastax = "3.4.0"
val scalatest = "3.0.4"
Expand All @@ -108,7 +108,7 @@ lazy val Versions = new {

val scala210 = "2.10.6"
val scala211 = "2.11.11"
val scala212 = "2.12.4"
val scala212 = "2.12.5"
val scalaAll = Seq(scala210, scala211, scala212)

val scala = new {
Expand Down Expand Up @@ -329,7 +329,6 @@ lazy val phantomFinagle = (project in file("phantom-finagle"))
compilerPlugin("org.scalamacros" % "paradise" % Versions.macroParadise cross CrossVersion.full),
"com.twitter" %% "util-core" % Versions.twitterUtil(scalaVersion.value),
"com.outworkers" %% "util-testing" % Versions.util % Test,
"com.outworkers" %% "util-testing-twitter" % Versions.util % Test,
"com.storm-enroute" %% "scalameter" % Versions.scalameter % Test
)
).dependsOn(
Expand All @@ -350,8 +349,7 @@ lazy val phantomThrift = (project in file("phantom-thrift"))
"org.apache.thrift" % "libthrift" % Versions.thrift,
"com.twitter" %% "scrooge-core" % Versions.scrooge(scalaVersion.value),
"com.twitter" %% "scrooge-serializer" % Versions.scrooge(scalaVersion.value),
"com.outworkers" %% "util-testing" % Versions.util % Test,
"com.outworkers" %% "util-testing-twitter" % Versions.util % Test
"com.outworkers" %% "util-testing" % Versions.util % Test
),
coverageExcludedPackages := "com.outworkers.phantom.thrift.models.*"
).settings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ abstract class CassandraTable[T <: CassandraTable[T, R], R](
QueryBuilder.Create.sasiIndexName(tableName, index.name),
index.name,
index.analyzer.qb
))
), QueryOptions.empty, Nil)
}
new QueryCollection[Seq](queries)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,29 @@
*/
package com.outworkers.phantom.builder.clauses

import com.outworkers.phantom.Row
import com.outworkers.phantom.builder.QueryBuilder
import com.outworkers.phantom.builder.ops.TokenizerKey
import com.outworkers.phantom.builder.query.engine.CQLQuery
import com.outworkers.phantom.builder.query.prepared.PrepareMark
import com.outworkers.phantom.builder.syntax.CQLSyntax
import com.outworkers.phantom.column.AbstractColumn
import com.outworkers.phantom.Row
import com.outworkers.phantom.builder.query.prepared.PrepareMark
import shapeless.{::, HList, HNil}

abstract class QueryCondition[T <: HList](val qb: CQLQuery)
abstract class QueryCondition[
PS <: HList
](
val qb: CQLQuery,
val tokens: List[TokenizerKey]
)

/**
* A query that can be used inside "WHERE", "AND", and conditional compare-and-set type queries.
*/
sealed trait Clause

class PreparedCondition[RR] extends QueryCondition[RR :: HNil](PrepareMark.?.qb)
class ValueCondition[RR](val obj: RR) extends QueryCondition[HNil](CQLQuery.empty)
class PreparedCondition[RR] extends QueryCondition[RR :: HNil](PrepareMark.?.qb, Nil)
class ValueCondition[RR](val obj: RR) extends QueryCondition[HNil](CQLQuery.empty, Nil)

class WhereClause extends Clause {

Expand All @@ -48,15 +54,24 @@ class WhereClause extends Clause {
*
* @param qb The underlying query builder of the condition.
*/
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb)
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb, Nil)

class PartitionCondition(
override val qb: CQLQuery,
tokenCreator: TokenizerKey
) extends QueryCondition[HNil](qb, tokenCreator :: Nil)

/**
*
* @tparam T Type of argument
*/
class ParametricCondition[T](override val qb: CQLQuery) extends QueryCondition[T :: HNil](qb)
class ParametricCondition[T](
override val qb: CQLQuery
) extends QueryCondition[T :: HNil](qb, Nil)

class HListCondition[HL <: HList](override val qb: CQLQuery) extends QueryCondition[HL](qb)
class HListCondition[HL <: HList](
override val qb: CQLQuery
) extends QueryCondition[HL](qb, Nil)
}

object WhereClause extends WhereClause
Expand All @@ -82,42 +97,53 @@ object CompareAndSetClause extends Clause {
*
* @param qb The underlying builder.
*/
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb)
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb, Nil)
}

object OrderingClause extends Clause {
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb)
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb, Nil)
}
object UsingClause extends Clause {
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb)
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb, Nil)
}

object UpdateClause extends Clause {
class Condition[HL <: HList](override val qb: CQLQuery, val skipped: Boolean = false) extends QueryCondition[HL](qb)
class Condition[HL <: HList](
override val qb: CQLQuery,
val skipped: Boolean = false
) extends QueryCondition[HL](qb, Nil)

type Default = Condition[HNil]

type Prepared[RR] = Condition[RR :: HNil]
}

object OperatorClause extends Clause {
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb)
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb, Nil)
}

object TypedClause extends Clause {
class Condition[RR](override val qb: CQLQuery, val extractor: Row => RR) extends QueryCondition(qb)
class Condition[RR](override val qb: CQLQuery, val extractor: Row => RR) extends QueryCondition(qb, Nil)
}

object DeleteClause extends Clause {
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb)
class Condition(override val qb: CQLQuery) extends QueryCondition[HNil](qb, Nil)
}

private[phantom] class OrderingColumn[RR](col: AbstractColumn[RR]) {

def asc: OrderingClause.Condition = new OrderingClause.Condition(QueryBuilder.Select.Ordering.ascending(col.name))
def ascending: OrderingClause.Condition = new OrderingClause.Condition(QueryBuilder.Select.Ordering.ascending(col.name))
def desc: OrderingClause.Condition = new OrderingClause.Condition(QueryBuilder.Select.Ordering.descending(col.name))
def descending: OrderingClause.Condition = new OrderingClause.Condition(QueryBuilder.Select.Ordering.descending(col.name))
def asc: OrderingClause.Condition = {
new OrderingClause.Condition(QueryBuilder.Select.Ordering.ascending(col.name))
}
def ascending: OrderingClause.Condition = {
new OrderingClause.Condition(QueryBuilder.Select.Ordering.ascending(col.name))
}
def desc: OrderingClause.Condition = {
new OrderingClause.Condition(QueryBuilder.Select.Ordering.descending(col.name))
}
def descending: OrderingClause.Condition = {
new OrderingClause.Condition(QueryBuilder.Select.Ordering.descending(col.name))
}
}

trait UsingClauseOperations {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.outworkers.phantom.builder.ops

import com.datastax.driver.core.Session
import com.outworkers.phantom.CassandraTable
import com.outworkers.phantom.builder.QueryBuilder
import com.outworkers.phantom.builder.clauses.{CompareAndSetClause, OrderingColumn, WhereClause}
Expand Down Expand Up @@ -131,7 +132,6 @@ sealed class MapConditionals[T <: CassandraTable[T, R], R, K, V](val col: Abstra
}
}


private[phantom] trait ImplicitMechanism extends ModifyMechanism {

// implicit lazy val context: ExecutionContextExecutor = Manager.scalaExecutor
Expand Down Expand Up @@ -160,6 +160,10 @@ private[phantom] trait ImplicitMechanism extends ModifyMechanism {
new MapEntriesConditionals[K, V](cond)
}

implicit def partitionColumnQueries[RR : Primitive](
col: AbstractColumn[RR] with PartitionKey
): PartitionQueryColumn[RR] = new PartitionQueryColumn[RR](col.name)

/**
* Definition used to cast an index map column with values indexed to a query-able definition.
* This will allow users to use "CONTAINS" clauses to search for matches based on map values.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright 2013 - 2017 Outworkers Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.outworkers.phantom.builder.ops

import com.datastax.driver.core.Session
import com.outworkers.phantom.builder.QueryBuilder
import com.outworkers.phantom.builder.clauses.{OperatorClause, WhereClause}
import com.outworkers.phantom.builder.primitives.Primitive
import com.outworkers.phantom.builder.query.engine.CQLQuery
import com.outworkers.phantom.builder.query.prepared.{ListValue, PrepareMark}
import com.outworkers.phantom.connectors.SessionAugmenterImplicits

/**
* A class enforcing columns used in where clauses to be indexed.
* Using an implicit mechanism, only columns that are indexed can be converted into Indexed columns.
* This enforces a Cassandra limitation at compile time.
* It prevents a user from querying and using where operators on a column without any index.
* @param name The name of the column.
* @tparam RR The type of the value the column holds.
*/
case class PartitionQueryColumn[RR](name: String)(
implicit p: Primitive[RR]
) extends SessionAugmenterImplicits {

protected[this] def operator[R : Primitive](
value: R
)(fn: (String, String) => CQLQuery)(implicit pp: Primitive[R]): WhereClause.PartitionCondition = {
new WhereClause.PartitionCondition(
fn(name, pp.asCql(value)), {
session: Session => pp.serialize(value, session.protocolVersion)
}
)
}

def eqs(value: RR): WhereClause.PartitionCondition = {
operator(value)(QueryBuilder.Where.eqs)
}

def eqs(value: OperatorClause.Condition): WhereClause.Condition = {
new WhereClause.Condition(QueryBuilder.Where.eqs(name, value.qb.queryString))
}

def lt(value: RR): WhereClause.PartitionCondition = {
operator(value)(QueryBuilder.Where.lt)
}

def <(value: RR): WhereClause.PartitionCondition = lt(value)

def lt(value: OperatorClause.Condition): WhereClause.Condition = {
new WhereClause.Condition(QueryBuilder.Where.lt(name, value.qb.queryString))
}

def <(value: OperatorClause.Condition): WhereClause.Condition = lt(value)

def lte(value: RR): WhereClause.PartitionCondition = {
operator(value)(QueryBuilder.Where.lte)
}

def <=(value: RR): WhereClause.PartitionCondition = lte(value)

def lte(value: OperatorClause.Condition): WhereClause.Condition = {
new WhereClause.Condition(QueryBuilder.Where.lte(name, value.qb.queryString))
}

def <=(value: OperatorClause.Condition): WhereClause.Condition = lte(value)

def gt(value: RR): WhereClause.PartitionCondition = {
operator(value)(QueryBuilder.Where.gt)
}

def >(value: RR): WhereClause.PartitionCondition = gt(value)

def gt(value: OperatorClause.Condition): WhereClause.Condition = {
new WhereClause.Condition(QueryBuilder.Where.gt(name, value.qb.queryString))
}

def >(value: OperatorClause.Condition): WhereClause.Condition = gt(value)

def gte(value: RR): WhereClause.PartitionCondition = {
operator(value)(QueryBuilder.Where.gte)
}

def >=(value: RR): WhereClause.PartitionCondition = gte(value)

def gte(value: OperatorClause.Condition): WhereClause.Condition = {
new WhereClause.Condition(QueryBuilder.Where.gte(name, value.qb.queryString))
}

def >=(value: OperatorClause.Condition): WhereClause.Condition = gte(value)

def in(values: List[RR])(
implicit ev: Primitive[ListValue[RR]]
): WhereClause.PartitionCondition = {
new WhereClause.PartitionCondition(
QueryBuilder.Where.in(name, values.map(p.asCql)), {
session: Session => ev.serialize(ListValue(values), session.protocolVersion)
}
)
}

final def in(value: PrepareMark): WhereClause.ParametricCondition[ListValue[RR]] = {
new WhereClause.ParametricCondition[ListValue[RR]](QueryBuilder.Where.in(name, value))
}

/**
* Equals clause defined for the prepared statement.
* When this prepared clause is applied, the value specified in the WHERE clause can be binded at a later stage.
*
* {{{
* Example usage:
*
* Table.select.where(_.id eqs ?)
*
* Will produce
*
* SELECT * FROM KEYSPACE.TABLE WHERE ID = ?
*
* }}}
*
*
* @param value The prepare mark value to use, the "?" singleton.
* @return A where clause with a parametric condition specified.
*/
final def eqs(value: PrepareMark): WhereClause.ParametricCondition[RR] = {
new WhereClause.ParametricCondition[RR](QueryBuilder.Where.eqs(name, value.symbol))
}

final def lt(value: PrepareMark): WhereClause.ParametricCondition[RR] = {
new WhereClause.ParametricCondition[RR](QueryBuilder.Where.lt(name, value.symbol))
}

final def <(value: PrepareMark): WhereClause.ParametricCondition[RR] = lt(value)

final def lte(value: PrepareMark): WhereClause.ParametricCondition[RR] = {
new WhereClause.ParametricCondition[RR](QueryBuilder.Where.lte(name, value.symbol))
}

final def <=(value: PrepareMark): WhereClause.ParametricCondition[RR] = lte(value)

final def gt(value: PrepareMark): WhereClause.ParametricCondition[RR] = {
new WhereClause.ParametricCondition[RR](QueryBuilder.Where.gt(name, value.symbol))
}

final def >(value: PrepareMark): WhereClause.ParametricCondition[RR] = gt(value)

final def gte(value: PrepareMark): WhereClause.ParametricCondition[RR] = {
new WhereClause.ParametricCondition[RR](QueryBuilder.Where.gte(name, value.symbol))
}

final def >=(value: PrepareMark): WhereClause.ParametricCondition[RR] = gte(value)
}
Loading

0 comments on commit ee739e9

Please sign in to comment.