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

com.outworkers.phantom.builder.serializers.CollectionModifiers.scala Maven / Gradle / Ivy

/*
 * Copyright 2013 - 2020 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.serializers

import com.outworkers.phantom.builder.QueryBuilder
import com.outworkers.phantom.builder.query.engine.CQLQuery
import com.outworkers.phantom.builder.syntax.CQLSyntax
import com.outworkers.phantom.builder.primitives.Primitive
import com.outworkers.phantom.builder.query.prepared.PrepareMark

/**
  * Query builder generators specific to List[E] columns, or list in Cassandra.
  */
trait ListModifiers extends BaseModifiers {

  def queryBuilder: QueryBuilder

  def setIdX(column: String, index: String, value: String): CQLQuery = {
    CQLQuery(column).append(CQLSyntax.Symbols.`[`)
      .append(index).append(CQLSyntax.Symbols.`]`)
      .forcePad.append(CQLSyntax.eqs)
      .forcePad.append(value)
  }

  def discard(column: String, values: String*): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        column,
        CQLSyntax.Symbols.-,
        queryBuilder.Collections.serialize(values).queryString
      )
    )
  }

  def discard(column: String, valueDef: String): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(column, CQLSyntax.Symbols.-, valueDef)
    )
  }

  final def prepend(column: String, mark: PrepareMark): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        mark.qb.queryString,
        CQLSyntax.Symbols.plus,
        column
      )
    )
  }

  def prepend(column: String, values: String*): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        queryBuilder.Collections.serialize(values).queryString,
        CQLSyntax.Symbols.plus,
        column
      )
    )
  }

  def prepend(column: String, valueDef: String): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(valueDef, CQLSyntax.Symbols.plus, column)
    )
  }

  def append(column: String, values: String*): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        column, CQLSyntax.Symbols.plus,
        queryBuilder.Collections.serialize(values).queryString
      )
    )
  }

  final def append(column: String, mark: PrepareMark): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(column, CQLSyntax.Symbols.plus, mark.qb.queryString)
    )
  }

  def append(column: String, valueDef: String): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(column, CQLSyntax.Symbols.plus, valueDef)
    )
  }
}

trait SetModifiers extends BaseModifiers {
  def queryBuilder: QueryBuilder

  def add(column: String, values: Set[String]): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        column,
        CQLSyntax.Symbols.plus,
        queryBuilder.Utils.set(values)
      )
    )
  }

  final def add(column: String, mark: PrepareMark): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        column,
        CQLSyntax.Symbols.plus,
        mark.qb
      )
    )
  }

  /**
    * Creates a set removal query, to remove the given values from the name set column.
    * Assumes values are already serialised to their CQL form and escaped.
    *
    * {{{
    *  setColumn = setColumn - {`test`, `test2`}
    * }}}
    *
    * @param column The name of the set column.
    * @param values The set of values, pre-serialized and escaped.
    * @return A CQLQuery set remove query as described above.
    */
  def remove(column: String, values: Set[String]): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        column,
        CQLSyntax.Symbols.-,
        queryBuilder.Utils.set(values)
      )
    )
  }

  /**
    * Creates a set removal query, to remove the given values from the name set column.
    * Assumes values are already serialised to their CQL form and escaped.
    *
    * {{{
    *  setColumn = setColumn - {`test`, `test2`}
    * }}}
    *
    * @param column The name of the set column.
    * @param value The set of values, pre-serialized and escaped.
    * @return A CQLQuery set remove query as described above.
    */
  final def removePrepared(column: String, value: PrepareMark): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        column,
        CQLSyntax.Symbols.-,
        value.qb
      )
    )
  }

  /**
    * Used to generate a query that allows removing one or more keys from a map.
    * Example: {{{
    *   UPDATE db.table WHERE a = b SET mapColumn -= {"a", "b", "c"}
    * }}}
    * @param column The name of the map column to remove from.
    * @param keys The keys to remove from the map column.
    * @return
    */
  def removeAll(column: String, keys: Seq[String]): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        column,
        CQLSyntax.Symbols.-,
        queryBuilder.Utils.set(keys.toSet[String])
      )
    )
  }

}

private[builder] abstract class CollectionModifiers(
  val queryBuilder: QueryBuilder
) extends BaseModifiers with ListModifiers with SetModifiers {

  def tupled(tuples: String*): CQLQuery = {
    queryBuilder.Utils.join(tuples)
  }

  def tuple(name: String, tuples: String*): CQLQuery = {
    CQLQuery(name).forcePad.append(CQLSyntax.Collections.tuple)
      .wrap(queryBuilder.Utils.join(tuples))
      .append(CQLSyntax.Symbols.`>`)
  }

  def frozen(column: String, definition: String): CQLQuery = {
    frozen(column, CQLQuery(definition))
  }

  /**
   * This will pre-fix and post-fix the given value with the "<>" diamond syntax.
   * It is used to define the collection type of a column.
   *
   * Sample outputs would be:
   * {{{
   *   dimond("list", "int") = list
   *   dimond("set", "varchar") = set
   * }}}
   *
   * @param collection The name of the collection in use.
   * @param value The value, usually the type of the CQL collection.
   * @return A CQL query serialising the CQL collection column definition syntax.
   */
  def diamond(collection: String, value: String): CQLQuery = {
    CQLQuery(collection)
      .append(CQLSyntax.Symbols.`<`)
      .append(value).
      append(CQLSyntax.Symbols.`>`)
  }


  def mapSet(column: String, key: String, value: String): CQLQuery = {
    CQLQuery(column).append(CQLSyntax.Symbols.`[`)
      .append(key).append(CQLSyntax.Symbols.`]`)
      .forcePad.append(CQLSyntax.eqs)
      .forcePad.append(value)
  }

  def put(column: String, mark: PrepareMark): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        column,
        CQLSyntax.Symbols.plus,
        mark.qb.queryString
      )
    )
  }

  def put(column: String, pairs: (String, String)*): CQLQuery = {
    CQLQuery(column).forcePad.append(CQLSyntax.Symbols.eqs).forcePad.append(
      collectionModifier(
        column,
        CQLSyntax.Symbols.plus,
        queryBuilder.Utils.map(pairs)
      )
    )
  }

  def serialize(list: Seq[String]): CQLQuery = {
    CQLQuery(CQLSyntax.Symbols.`[`)
      .append(list.mkString(", "))
      .append(CQLSyntax.Symbols.`]`)
  }

  def serialize(set: Set[String]): CQLQuery = {
    CQLQuery(CQLSyntax.Symbols.`{`)
      .forcePad.append(CQLQuery(set))
      .forcePad.append(CQLSyntax.Symbols.`}`)
  }

  def serialize(col: Map[String, String] ): CQLQuery = {
    CQLQuery(CQLSyntax.Symbols.`{`).forcePad
      .append(CQLQuery(col.map { case (key, value) => s"$key : $value" }))
      .forcePad.append(CQLSyntax.Symbols.`}`)
  }

  def mapType[K, V](key: Primitive[K], value: Primitive[V]): CQLQuery = {
    diamond(CQLSyntax.Collections.map, CQLQuery(
      List(frozen(key).queryString, frozen(value).queryString)
    ).queryString)
  }

  def listType(valueType: String): CQLQuery = {
    diamond(CQLSyntax.Collections.list, valueType)
  }

  def setType(valueType: String): CQLQuery = {
    diamond(CQLSyntax.Collections.set, valueType)
  }

  def mapColumnType(column: String, key: String): CQLQuery = {
    CQLQuery(column).append(CQLSyntax.Symbols.`[`)
      .append(key).append(CQLSyntax.Symbols.`]`)
  }

  def tupleType(types: String*): CQLQuery = {
    CQLQuery(CQLSyntax.Collections.tuple)
    .append(CQLSyntax.Symbols.`<`)
    .append(types)
    .append(CQLSyntax.Symbols.`>`)
  }

  def frozen[V](p: Primitive[V]): CQLQuery = frozen(p.dataType, p.shouldFreeze)

  def frozen(cassandraType: String, shouldFreeze: Boolean): CQLQuery = {
    if (shouldFreeze) {
      diamond(CQLSyntax.Collections.frozen, cassandraType)
    } else {
      CQLQuery(cassandraType)
    }
  }

  def frozen(cassandraType: String): CQLQuery = {
    diamond(CQLSyntax.Collections.frozen, cassandraType)
  }

  def collectionType(
    colType: String,
    cassandraType: String,
    shouldFreeze: Boolean,
    freezeInner: Boolean,
    static: Boolean
  ): CQLQuery = {
    val root = (shouldFreeze, freezeInner) match {
      case (true, true) =>
        // frozen>>
        frozen(diamond(colType, frozen(cassandraType).queryString))
      case (true, false) =>
        // frozen>
        frozen(diamond(colType, cassandraType))
        // list>
      case (false, true) => diamond(colType, frozen(cassandraType).queryString)
        // list
      case (false, false) => diamond(colType, cassandraType)
    }

    if (static) {
      root.pad.append("static")
    } else {
      root
    }
  }

  def frozen(cassandraType: CQLQuery): CQLQuery = {
    frozen(cassandraType.queryString)
  }

  def frozen(name: String, cassandraType: CQLQuery): CQLQuery = {
    CQLQuery(name).forcePad.append(
      diamond(CQLSyntax.Collections.frozen, cassandraType.queryString)
    )
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy