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

com.websudos.phantom.builder.ops.ImplicitMechanism.scala Maven / Gradle / Ivy

There is a newer version: 1.29.6
Show newest version
/*
 * Copyright 2013-2015 Websudos, Limited.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Explicit consent must be obtained from the copyright owner, Outworkers Limited before any redistribution is made.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package com.websudos.phantom.builder.ops

import com.websudos.phantom.builder.QueryBuilder
import com.websudos.phantom.builder.clauses.{WhereClause, OrderingColumn, CompareAndSetClause}
import com.websudos.phantom.builder.primitives.Primitive
import com.websudos.phantom.column._
import com.websudos.phantom.dsl._
import com.websudos.phantom.keys.{Undroppable, Indexed}
import shapeless.<:!<

import scala.annotation.implicitNotFound

sealed class DropColumn[RR](val column: AbstractColumn[RR])

sealed class CasConditionalOperators[RR](col: AbstractColumn[RR]) {

  /**
   * DSL method used to chain "is" clauses in Compare-And-Set operations.
   * Using a call to {{is}}, a column is only updated if the conditional clause of the compare-and-set is met.
   *
   * Example:
   *
   * {{{
   *   Recipes.update.where(_.url eqs recipe.url)
   *    .modify(_.description setTo updated)
   *    .onlyIf(_.description is recipe.description)
   *    .future()
   * }}}
   *
   * @param value The value to compare against in the match clause.
   * @return A compare and set clause usable in an "onlyIf" condition.
   */
  final def is(value: RR): CompareAndSetClause.Condition = {
    new CompareAndSetClause.Condition(QueryBuilder.Where.eqs(col.name, col.asCql(value)))
  }

  final def isNot(value: RR): CompareAndSetClause.Condition = {
    new CompareAndSetClause.Condition(QueryBuilder.Where.notEqs(col.name, col.asCql(value)))
  }

  final def isGt(value: RR): CompareAndSetClause.Condition = {
    new CompareAndSetClause.Condition(QueryBuilder.Where.gt(col.name, col.asCql(value)))
  }

  final def isGte(value: RR): CompareAndSetClause.Condition = {
    new CompareAndSetClause.Condition(QueryBuilder.Where.gte(col.name, col.asCql(value)))
  }

  final def isLt(value: RR): CompareAndSetClause.Condition = {
    new CompareAndSetClause.Condition(QueryBuilder.Where.lt(col.name, col.asCql(value)))
  }

  final def isLte(value: RR): CompareAndSetClause.Condition = {
    new CompareAndSetClause.Condition(QueryBuilder.Where.lte(col.name, col.asCql(value)))
  }
}

sealed class SetConditionals[T <: CassandraTable[T, R], R, RR](val col: AbstractSetColumn[T, R, RR]) {

  /**
   * Generates a Set CONTAINS clause that can be used inside a CQL Where condition.
   * @param elem The element to check for in the contains clause.
   * @return A Where clause.
   */
  final def contains(elem: RR): WhereClause.Condition = {
    new WhereClause.Condition(
      QueryBuilder.Where.contains(col.name, col.valueAsCql(elem))
    )
  }
}

sealed class MapEntriesConditionals[K : Primitive, V : Primitive](val col: MapKeyUpdateClause[K, V]) {

  /**
    * Generates a Map CONTAINS ENTRY clause that can be used inside a CQL Where condition.
    * This allows users to lookup records by their full entry inside a map column of a table.
    *
    * Key support is not yet enabled in phantom because index generation has to be done differently.
    * Otherwise, there is no support for simultaneous indexing on both KEYS and VALUES of a MAP column.
    * This limitation will be lifted in the future.
    *
    * @param entry The map entry to look for.
    * @return A Where clause.
    */
  final def eqs(entry: V): WhereClause.Condition = {
    new WhereClause.Condition(
      QueryBuilder.Where.containsEntry(col.column, col.keyName, Primitive[V].asCql(entry))
    )
  }
}

sealed class MapKeyConditionals[T <: CassandraTable[T, R], R, K, V](val col: AbstractMapColumn[T, R, K, V]) {

  /**
   * Generates a Map CONTAINS KEY clause that can be used inside a CQL Where condition.
   * This allows users to lookup records by a KEY inside a map column of a table.
   *
   * Key support is not yet enabled in phantom because index generation has to be done differently.
   * Otherwise, there is no support for simultaneous indexing on both KEYS and VALUES of a MAP column.
   * This limitation will be lifted in the future.
   *
   * @param elem The element to check for in the contains clause.
   * @return A Where clause.
   */
  final def containsKey(elem: K): WhereClause.Condition = {
    new WhereClause.Condition(
      QueryBuilder.Where.containsKey(col.name, col.keyAsCql(elem))
    )
  }
}

sealed class MapConditionals[T <: CassandraTable[T, R], R, K, V](val col: AbstractMapColumn[T, R, K, V]) {

  /**
   * Generates a Map CONTAINS clause that can be used inside a CQL Where condition.
   * This allows users to lookup records by a VALUE inside a map column of a table.
   *
   * @param elem The element to check for in the contains clause.
   * @return A Where clause.
   */
  final def contains(elem: K): WhereClause.Condition = {
    new WhereClause.Condition(
      QueryBuilder.Where.contains(col.name, col.keyAsCql(elem))
    )
  }
}


private[phantom] trait ImplicitMechanism extends ModifyMechanism {

  @implicitNotFound(msg = "Compare-and-set queries can only be applied to non indexed primitive columns.")
  implicit final def columnToCasCompareColumn[RR](col: AbstractColumn[RR])(implicit ev: col.type <:!< Indexed): CasConditionalOperators[RR] = {
    new CasConditionalOperators[RR](col)
  }

  @implicitNotFound(msg = "Index columns and counters cannot be dropped!")
  implicit final def columnToDropColumn[T](col: AbstractColumn[T])(implicit ev: col.type <:!< Undroppable): DropColumn[T] = new DropColumn[T](col)

  implicit def indexedToQueryColumn[T : Primitive](col: AbstractColumn[T] with Indexed): QueryColumn[T] = new QueryColumn(col)

  implicit def orderingColumn[RR](col: AbstractColumn[RR] with PrimaryKey[RR]): OrderingColumn[RR] = new OrderingColumn[RR](col)

  implicit def setColumnToQueryColumn[T <: CassandraTable[T, R], R, RR](col: AbstractSetColumn[T, R, RR] with Index[Set[RR]]): SetConditionals[T, R, RR] = {
    new SetConditionals(col)
  }

  /**
    * Definition used to cast a comparison clause to Map entry lookup based on a secondary index.
    * @param cond The column update clause generated from MapColumn.apply(keyValue)
    * @tparam K The type of the key inside the MapColumn.
    * @tparam V The type of the value held inside the MapColumn.
    * @return A MapEntriesConditionals query that allows secondary index operators on map entries.
    */
  implicit def mapColumnDefinitionToEntriesQueryColumn[
    K : Primitive,
    V : Primitive
  ](cond: MapKeyUpdateClause[K, V]): MapEntriesConditionals[K, V] = {
    new MapEntriesConditionals[K, V](cond)
  }

  /**
    * 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.
    *
    * @param col The map column to cast to a Map column secondary index query.
    * @tparam T The Cassandra table inner type.
    * @tparam R The record type of the table.
    * @tparam K The type of the key held in the map.
    * @tparam V The type of the value held in the map.
    * @return A MapConditionals class with CONTAINS support.
    */
  implicit def mapColumnToQueryColumn[T <: CassandraTable[T, R], R, K, V](
    col: AbstractMapColumn[T, R, K, V] with Index[Map[K, V]]
  )(implicit ev: col.type <:!< Keys): MapConditionals[T, R, K, V] = {
    new MapConditionals(col)
  }

  /**
    * Definition used to cast an index map column with keys indexed to a query-able definition.
    * This will allow users to use "CONTAINS KEY" clauses to search for matches based on map keys.
    *
    * @param col The map column to cast to a Map column secondary index query.
    * @tparam T The Cassandra table inner type.
    * @tparam R The record type of the table.
    * @tparam K The type of the key held in the map.
    * @tparam V The type of the value held in the map.
    * @return A MapConditionals class with CONTAINS KEY support.
    */
  implicit def mapKeysColumnToQueryColumn[T <: CassandraTable[T, R], R, K, V](
    col: AbstractMapColumn[T, R, K, V] with Index[Map[K, V]] with Keys): MapKeyConditionals[T, R, K, V] = {
    new MapKeyConditionals(col)
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy