lila/modules/db/src/main/dsl.scala

396 lines
13 KiB
Scala
Raw Normal View History

2016-04-01 05:41:57 -06:00
// Copyright (C) 2014 Fehmi Can Saglam (@fehmicans) and contributors.
// See the LICENCE.txt file distributed with this work for additional
// information regarding copyright ownership.
//
// 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 lila.db
import ornicar.scalalib.Zero
2020-07-19 07:11:06 -06:00
2019-11-29 07:40:28 -07:00
import reactivemongo.api.bson._
2016-04-01 05:41:57 -06:00
2019-11-29 07:40:28 -07:00
trait dsl {
2016-04-01 05:41:57 -06:00
2019-11-29 07:40:28 -07:00
type Coll = reactivemongo.api.bson.collection.BSONCollection
2016-04-01 10:43:50 -06:00
type Bdoc = BSONDocument
2016-04-02 00:11:09 -06:00
type Barr = BSONArray
2016-04-01 05:41:57 -06:00
//**********************************************************************************************//
// Helpers
2019-12-20 08:54:30 -07:00
val $empty: Bdoc = document.asStrict
2016-04-01 05:41:57 -06:00
2019-12-20 08:54:30 -07:00
def $doc(elements: ElementProducer*): Bdoc = BSONDocument.strict(elements: _*)
2016-04-01 05:41:57 -06:00
2019-12-20 08:54:30 -07:00
def $doc(elements: Iterable[(String, BSONValue)]): Bdoc = BSONDocument.strict(elements)
2016-04-01 05:41:57 -06:00
2021-03-11 09:19:36 -07:00
def $arr(elements: Producer[BSONValue]*): Barr = BSONArray(elements: _*)
2019-11-29 19:16:11 -07:00
2019-11-29 15:17:35 -07:00
def $id[T: BSONWriter](id: T): Bdoc = $doc("_id" -> id)
2016-04-01 05:41:57 -06:00
2019-11-29 15:17:35 -07:00
def $inIds[T: BSONWriter](ids: Iterable[T]): Bdoc =
2016-04-02 05:35:06 -06:00
$id($doc("$in" -> ids))
2016-04-01 05:41:57 -06:00
def $boolean(b: Boolean) = BSONBoolean(b)
2019-12-13 07:30:20 -07:00
def $string(s: String) = BSONString(s)
def $int(i: Int) = BSONInteger(i)
2016-04-01 05:41:57 -06:00
// End of Helpers
//**********************************************************************************************//
implicit val LilaBSONDocumentZero: Zero[Bdoc] = Zero.instance($empty)
2016-04-01 05:41:57 -06:00
//**********************************************************************************************//
// Top Level Logical Operators
2019-11-29 15:17:35 -07:00
def $or(expressions: Bdoc*): Bdoc = {
2016-04-01 10:43:50 -06:00
$doc("$or" -> expressions)
2016-04-01 05:41:57 -06:00
}
2019-11-29 15:17:35 -07:00
def $and(expressions: Bdoc*): Bdoc = {
2016-04-01 10:43:50 -06:00
$doc("$and" -> expressions)
2016-04-01 05:41:57 -06:00
}
2019-11-29 15:17:35 -07:00
def $nor(expressions: Bdoc*): Bdoc = {
2016-04-01 10:43:50 -06:00
$doc("$nor" -> expressions)
2016-04-01 05:41:57 -06:00
}
2021-11-13 14:20:22 -07:00
def $not(expression: Bdoc): Bdoc = {
$doc("$not" -> expression)
}
2016-04-01 05:41:57 -06:00
// End of Top Level Logical Operators
//**********************************************************************************************//
//**********************************************************************************************//
// Top Level Evaluation Operators
2019-12-08 01:02:12 -07:00
def $text(term: String): Bdoc = {
$doc("$text" -> $doc("$search" -> term))
2016-04-01 05:41:57 -06:00
}
2019-12-08 01:02:12 -07:00
def $text(term: String, lang: String): Bdoc = {
$doc("$text" -> $doc("$search" -> term, f"$$language" -> lang))
2016-04-01 05:41:57 -06:00
}
2019-12-08 01:02:12 -07:00
def $where(expr: String): Bdoc = {
$doc("$where" -> expr)
2016-04-01 05:41:57 -06:00
}
// End of Top Level Evaluation Operators
//**********************************************************************************************//
//**********************************************************************************************//
// Top Level Field Update Operators
2019-11-29 15:17:35 -07:00
def $inc(item: ElementProducer, items: ElementProducer*): Bdoc = {
2016-04-01 10:43:50 -06:00
$doc("$inc" -> $doc((Seq(item) ++ items): _*))
2016-04-01 05:41:57 -06:00
}
2019-11-29 19:16:11 -07:00
def $inc(doc: Bdoc): Bdoc =
$doc("$inc" -> doc)
2016-04-01 05:41:57 -06:00
2019-11-29 15:17:35 -07:00
def $mul(item: ElementProducer): Bdoc = {
2016-04-01 10:43:50 -06:00
$doc("$mul" -> $doc(item))
2016-04-01 05:41:57 -06:00
}
2019-11-29 15:17:35 -07:00
def $setOnInsert(item: ElementProducer, items: ElementProducer*): Bdoc = {
2016-04-01 10:43:50 -06:00
$doc("$setOnInsert" -> $doc((Seq(item) ++ items): _*))
2016-04-01 05:41:57 -06:00
}
2019-11-29 15:17:35 -07:00
def $set(item: ElementProducer, items: ElementProducer*): Bdoc = {
2016-04-01 10:43:50 -06:00
$doc("$set" -> $doc((Seq(item) ++ items): _*))
2016-04-01 05:41:57 -06:00
}
2019-11-29 15:17:35 -07:00
def $unset(field: String, fields: String*): Bdoc = {
$doc("$unset" -> $doc((Seq(field) ++ fields).map(k => (k, BSONString("")))))
2016-04-01 05:41:57 -06:00
}
2020-05-26 21:21:30 -06:00
def $unset(fields: Seq[String]): Bdoc =
fields.nonEmpty ?? {
$doc("$unset" -> $doc(fields.map(k => (k, BSONString("")))))
}
2019-11-29 15:17:35 -07:00
def $setBoolOrUnset(field: String, value: Boolean): Bdoc = {
if (value) $set(field -> true) else $unset(field)
}
2019-11-29 15:17:35 -07:00
def $min(item: ElementProducer): Bdoc = {
2016-04-01 10:43:50 -06:00
$doc("$min" -> $doc(item))
2016-04-01 05:41:57 -06:00
}
2019-11-29 15:17:35 -07:00
def $max(item: ElementProducer): Bdoc = {
2016-04-01 10:43:50 -06:00
$doc("$max" -> $doc(item))
2016-04-01 05:41:57 -06:00
}
// Helpers
2019-11-29 15:11:03 -07:00
def $eq[T: BSONWriter](value: T) = $doc("$eq" -> value)
2016-04-01 05:41:57 -06:00
2019-11-29 15:11:03 -07:00
def $gt[T: BSONWriter](value: T) = $doc("$gt" -> value)
2016-04-01 05:41:57 -06:00
/** Matches values that are greater than or equal to the value specified in the query. */
2019-11-29 15:11:03 -07:00
def $gte[T: BSONWriter](value: T) = $doc("$gte" -> value)
2016-04-01 05:41:57 -06:00
2020-06-24 03:36:35 -06:00
/** Matches any of the values that exist in an array specified in the query. */
2019-11-29 15:11:03 -07:00
def $in[T: BSONWriter](values: T*) = $doc("$in" -> values)
2016-04-01 05:41:57 -06:00
/** Matches values that are less than the value specified in the query. */
2019-11-29 15:11:03 -07:00
def $lt[T: BSONWriter](value: T) = $doc("$lt" -> value)
2016-04-01 05:41:57 -06:00
/** Matches values that are less than or equal to the value specified in the query. */
2019-11-29 15:11:03 -07:00
def $lte[T: BSONWriter](value: T) = $doc("$lte" -> value)
2016-04-01 05:41:57 -06:00
/** Matches all values that are not equal to the value specified in the query. */
2019-11-29 15:11:03 -07:00
def $ne[T: BSONWriter](value: T) = $doc("$ne" -> value)
2016-04-01 05:41:57 -06:00
/** Matches values that do not exist in an array specified to the query. */
2019-11-29 15:11:03 -07:00
def $nin[T: BSONWriter](values: T*) = $doc("$nin" -> values)
2016-04-01 05:41:57 -06:00
2016-04-01 10:43:50 -06:00
def $exists(value: Boolean) = $doc("$exists" -> value)
2016-04-01 05:41:57 -06:00
trait CurrentDateValueProducer[T] {
def produce: BSONValue
}
2019-12-13 07:30:20 -07:00
implicit final class BooleanCurrentDateValueProducer(value: Boolean)
extends CurrentDateValueProducer[Boolean] {
2016-04-01 05:41:57 -06:00
def produce: BSONValue = BSONBoolean(value)
}
2019-12-13 07:30:20 -07:00
implicit final class StringCurrentDateValueProducer(value: String)
extends CurrentDateValueProducer[String] {
2016-04-01 05:41:57 -06:00
def isValid: Boolean = Seq("date", "timestamp") contains value
def produce: BSONValue = {
if (!isValid)
throw new IllegalArgumentException(value)
2016-04-01 10:43:50 -06:00
$doc("$type" -> value)
2016-04-01 05:41:57 -06:00
}
}
// End of Top Level Field Update Operators
//**********************************************************************************************//
//**********************************************************************************************//
// Top Level Array Update Operators
def $addToSet(item: ElementProducer, items: ElementProducer*): Bdoc =
2016-04-01 10:43:50 -06:00
$doc("$addToSet" -> $doc((Seq(item) ++ items): _*))
2016-04-01 05:41:57 -06:00
2019-11-29 15:17:35 -07:00
def $pop(item: (String, Int)): Bdoc = {
2016-04-01 05:41:57 -06:00
if (item._2 != -1 && item._2 != 1)
throw new IllegalArgumentException(s"${item._2} is not equal to: -1 | 1")
2016-04-01 10:43:50 -06:00
$doc("$pop" -> $doc(item))
2016-04-01 05:41:57 -06:00
}
def $push(item: ElementProducer): Bdoc =
2016-04-01 10:43:50 -06:00
$doc("$push" -> $doc(item))
2016-04-01 05:41:57 -06:00
def $pushEach[T: BSONWriter](field: String, values: T*): Bdoc =
2016-04-01 10:43:50 -06:00
$doc(
"$push" -> $doc(
field -> $doc(
2016-04-01 05:41:57 -06:00
"$each" -> values
)
)
)
def $pull(item: ElementProducer): Bdoc =
2016-04-01 10:43:50 -06:00
$doc("$pull" -> $doc(item))
2019-12-31 10:57:04 -07:00
def $addOrPull[T: BSONWriter](key: String, value: T, add: Boolean): Bdoc =
$doc((if (add) "$addToSet" else "$pull") -> $doc(key -> value))
2016-04-01 05:41:57 -06:00
// End ofTop Level Array Update Operators
//**********************************************************************************************//
2020-10-10 03:08:23 -06:00
/** Represents the initial state of the expression which has only the name of the field.
2019-12-13 07:30:20 -07:00
* It does not know the value of the expression.
*/
2016-04-01 05:41:57 -06:00
trait ElementBuilder {
def field: String
2019-11-29 15:17:35 -07:00
def append(value: Bdoc): Bdoc = value
2016-04-01 05:41:57 -06:00
}
/** Represents the state of an expression which has a field and a value */
2019-11-29 15:11:03 -07:00
trait Expression[V] extends ElementBuilder {
2016-04-01 05:41:57 -06:00
def value: V
2019-11-29 15:11:03 -07:00
def toBdoc(implicit writer: BSONWriter[V]) = toBSONDocument(this)
2016-04-01 05:41:57 -06:00
}
/*
2019-12-13 07:30:20 -07:00
* This type of expressions cannot be cascaded. Examples:
2016-04-01 05:41:57 -06:00
*
* {{{
2019-12-13 07:30:20 -07:00
* "price" $eq 10
* "price" $ne 1000
* "size" $in ("S", "M", "L")
* "size" $nin ("S", "XXL")
2016-04-01 05:41:57 -06:00
* }}}
*
*/
2019-12-13 07:30:20 -07:00
case class SimpleExpression[V <: BSONValue](field: String, value: V) extends Expression[V]
2020-10-10 03:08:23 -06:00
/** Expressions of this type can be cascaded. Examples:
2019-12-13 07:30:20 -07:00
*
* {{{
* "age" $gt 50 $lt 60
* "age" $gte 50 $lte 60
* }}}
*/
2019-11-29 15:17:35 -07:00
case class CompositeExpression(field: String, value: Bdoc)
2019-12-13 07:30:20 -07:00
extends Expression[Bdoc]
with ComparisonOperators {
2019-11-29 15:17:35 -07:00
override def append(value: Bdoc): Bdoc = {
2016-04-01 05:41:57 -06:00
this.value ++ value
}
}
/** MongoDB comparison operators. */
trait ComparisonOperators { self: ElementBuilder =>
2019-11-29 15:11:03 -07:00
def $eq[T: BSONWriter](value: T): SimpleExpression[BSONValue] = {
SimpleExpression(field, implicitly[BSONWriter[T]].writeTry(value).get)
2016-04-01 05:41:57 -06:00
}
/** Matches values that are greater than the value specified in the query. */
2019-11-29 15:11:03 -07:00
def $gt[T: BSONWriter](value: T): CompositeExpression = {
2016-04-01 10:43:50 -06:00
CompositeExpression(field, append($doc("$gt" -> value)))
2016-04-01 05:41:57 -06:00
}
/** Matches values that are greater than or equal to the value specified in the query. */
2019-11-29 15:11:03 -07:00
def $gte[T: BSONWriter](value: T): CompositeExpression = {
2016-04-01 10:43:50 -06:00
CompositeExpression(field, append($doc("$gte" -> value)))
2016-04-01 05:41:57 -06:00
}
2020-06-24 03:36:35 -06:00
/** Matches any of the values that exist in an array specified in the query. */
2019-11-29 15:17:35 -07:00
def $in[T: BSONWriter](values: Iterable[T]): SimpleExpression[Bdoc] = {
2016-04-01 10:43:50 -06:00
SimpleExpression(field, $doc("$in" -> values))
2016-04-01 05:41:57 -06:00
}
/** Matches values that are less than the value specified in the query. */
2019-11-29 15:11:03 -07:00
def $lt[T: BSONWriter](value: T): CompositeExpression = {
2016-04-01 10:43:50 -06:00
CompositeExpression(field, append($doc("$lt" -> value)))
2016-04-01 05:41:57 -06:00
}
/** Matches values that are less than or equal to the value specified in the query. */
2019-11-29 15:11:03 -07:00
def $lte[T: BSONWriter](value: T): CompositeExpression = {
2016-04-01 10:43:50 -06:00
CompositeExpression(field, append($doc("$lte" -> value)))
2016-04-01 05:41:57 -06:00
}
/** Matches all values that are not equal to the value specified in the query. */
2019-11-29 15:17:35 -07:00
def $ne[T: BSONWriter](value: T): SimpleExpression[Bdoc] = {
2016-04-01 10:43:50 -06:00
SimpleExpression(field, $doc("$ne" -> value))
2016-04-01 05:41:57 -06:00
}
/** Matches values that do not exist in an array specified to the query. */
2019-11-29 15:17:35 -07:00
def $nin[T: BSONWriter](values: Iterable[T]): SimpleExpression[Bdoc] = {
2016-04-01 10:43:50 -06:00
SimpleExpression(field, $doc("$nin" -> values))
2016-04-01 05:41:57 -06:00
}
}
trait ElementOperators { self: ElementBuilder =>
2019-12-08 01:02:12 -07:00
def $exists(v: Boolean): SimpleExpression[Bdoc] = {
SimpleExpression(field, $doc("$exists" -> v))
2016-04-01 05:41:57 -06:00
}
}
trait EvaluationOperators { self: ElementBuilder =>
2019-11-29 15:17:35 -07:00
def $mod(divisor: Int, remainder: Int): SimpleExpression[Bdoc] =
2016-04-01 10:43:50 -06:00
SimpleExpression(field, $doc("$mod" -> BSONArray(divisor, remainder)))
2016-04-01 05:41:57 -06:00
2018-04-05 14:21:47 -06:00
def $regex(value: String, options: String = ""): SimpleExpression[BSONRegex] =
2016-04-01 05:41:57 -06:00
SimpleExpression(field, BSONRegex(value, options))
2018-04-05 14:21:47 -06:00
2019-10-02 10:50:09 -06:00
def $startsWith(value: String, options: String = ""): SimpleExpression[BSONRegex] =
$regex(s"^$value", options)
2016-04-01 05:41:57 -06:00
}
trait ArrayOperators { self: ElementBuilder =>
2019-11-29 15:17:35 -07:00
def $all[T: BSONWriter](values: Seq[T]): SimpleExpression[Bdoc] = {
2016-04-01 10:43:50 -06:00
SimpleExpression(field, $doc("$all" -> values))
2016-04-01 05:41:57 -06:00
}
2019-11-29 15:17:35 -07:00
def $elemMatch(query: ElementProducer*): SimpleExpression[Bdoc] = {
2016-04-01 10:43:50 -06:00
SimpleExpression(field, $doc("$elemMatch" -> $doc(query: _*)))
2016-04-01 05:41:57 -06:00
}
2019-12-08 01:02:12 -07:00
def $size(s: Int): SimpleExpression[Bdoc] = {
SimpleExpression(field, $doc("$size" -> s))
2016-04-01 05:41:57 -06:00
}
}
object $sort {
2019-12-13 07:30:20 -07:00
def asc(field: String) = $doc(field -> 1)
2016-04-01 05:41:57 -06:00
def desc(field: String) = $doc(field -> -1)
2019-12-13 07:30:20 -07:00
val naturalAsc = asc("$natural")
val naturalDesc = desc("$natural")
2016-04-01 05:41:57 -06:00
val naturalOrder = naturalDesc
2019-12-13 07:30:20 -07:00
val createdAsc = asc("createdAt")
2016-04-01 05:41:57 -06:00
val createdDesc = desc("createdAt")
}
2021-04-23 00:50:15 -06:00
object $lookup {
def simple(from: String, as: String, local: String, foreign: String): Bdoc = $doc(
"$lookup" -> $doc(
"from" -> from,
"as" -> as,
"localField" -> local,
"foreignField" -> foreign
)
)
def simple(from: Coll, as: String, local: String, foreign: String): Bdoc =
simple(from.name, as, local, foreign)
def simple(from: AsyncColl, as: String, local: String, foreign: String): Bdoc =
simple(from.name.value, as, local, foreign)
2021-10-14 00:28:37 -06:00
def pipeline(from: String, as: String, local: String, foreign: String, pipe: List[Bdoc]): Bdoc =
$doc(
"$lookup" -> $doc(
2021-10-14 00:28:37 -06:00
"from" -> from,
"as" -> as,
"let" -> $doc("local" -> s"$$$local"),
"pipeline" -> {
$doc(
"$match" -> $doc(
"$expr" -> $doc($doc("$eq" -> $arr(s"$$$foreign", "$$local")))
)
2021-10-14 00:28:37 -06:00
) :: pipe
}
)
)
2021-10-14 00:28:37 -06:00
def pipeline(from: Coll, as: String, local: String, foreign: String, pipe: List[Bdoc]): Bdoc =
pipeline(from.name, as, local, foreign, pipe)
def pipeline(from: AsyncColl, as: String, local: String, foreign: String, pipe: List[Bdoc]): Bdoc =
pipeline(from.name.value, as, local, foreign, pipe)
2021-04-23 00:50:15 -06:00
}
2016-04-01 05:41:57 -06:00
implicit class ElementBuilderLike(val field: String)
2019-12-13 07:30:20 -07:00
extends ElementBuilder
with ComparisonOperators
with ElementOperators
with EvaluationOperators
with ArrayOperators
2016-04-01 05:41:57 -06:00
2019-11-29 15:17:35 -07:00
implicit def toBSONDocument[V: BSONWriter](expression: Expression[V]): Bdoc =
2019-11-29 15:11:03 -07:00
$doc(expression.field -> expression.value)
2016-04-01 05:41:57 -06:00
}
2019-11-29 07:40:28 -07:00
// sealed trait LowPriorityDsl { self: dsl =>
// // Priority lower than toBSONDocument
2019-11-29 15:11:03 -07:00
// implicit def toBSONElement[V <: BSONValue](expression: Expression[V])(implicit writer: BSONWriter[V, _ <: BSONValue]): ElementProducer = {
2019-11-29 07:40:28 -07:00
// BSONElement(expression.field, expression.value)
// }
// }
object dsl extends dsl with CollExt with QueryBuilderExt with CursorExt with Handlers