Skip to content

Commit

Permalink
Add support for $pull
Browse files Browse the repository at this point in the history
  • Loading branch information
denis_savitsky committed Apr 1, 2024
1 parent 690e3be commit a3fc28b
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 8 deletions.
4 changes: 4 additions & 0 deletions oolong-core/src/main/scala/oolong/AstParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ private[oolong] class DefaultAstParser(using quotes: Quotes) extends AstParser {
case '{($updater: Updater[Doc]).popLast($selectProp)} =>
val prop = parsePropSelector(selectProp)
parseUpdater(updater, FieldUpdateExpr.Pop(UExpr.Prop(prop), FieldUpdateExpr.Pop.Remove.Last) :: acc)

case '{type t; ($updater: Updater[Doc]).pull[`t`,`t`]($selectProp, $input)} =>
val prop = parsePropSelector(selectProp)
parseUpdater(updater, FieldUpdateExpr.Pull(UExpr.Prop(prop), parseQExpr[`t`](input)) :: acc)

case '{ $updater: Updater[Doc] } =>
updater match {
Expand Down
2 changes: 2 additions & 0 deletions oolong-core/src/main/scala/oolong/UExpr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ private[oolong] object UExpr {
}
}

case class Pull(prop: Prop, cond: QExpr) extends FieldUpdateExpr(prop)

}

}
4 changes: 4 additions & 0 deletions oolong-core/src/main/scala/oolong/dsl/Dsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ sealed trait Updater[DocT] {

def popHead(selectProp: DocT => Iterable[?]): Updater[DocT] = useWithinMacro("popHead")
def popLast(selectProp: DocT => Iterable[?]): Updater[DocT] = useWithinMacro("popLast")

def pull[PropT, ValueT](selectProp: DocT => Iterable[PropT], input: PropT => Boolean)(using
PropT =:= ValueT
): Updater[DocT] = useWithinMacro("pull")
}
21 changes: 19 additions & 2 deletions oolong-mongo/src/main/scala/oolong/mongo/MongoUpdateCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
case FieldUpdateExpr.Pop.Remove.First => Remove.First
case FieldUpdateExpr.Pop.Remove.Last => Remove.Last
MU.MongoUpdateOp.Pop(MU.Prop(renames.getOrElse(prop.path, prop.path)), muRemove)
case FieldUpdateExpr.Pull(prop, query) =>
val optimized = MongoQueryCompiler.optimize(MongoQueryCompiler.opt(LogicalOptimizer.optimize(query)))
MU.MongoUpdateOp.Pull(
MU.Prop(renames.getOrElse(prop.path, prop.path)),
MU.QueryWrapper(optimized)
)

})
case UExpr.ScalaCode(code) => MU.ScalaCode(code)
case UExpr.Constant(t) => MU.Constant(t)
Expand Down Expand Up @@ -100,7 +107,10 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
)("$addToSet"),
renderOps(
ops.collect { case s: MU.MongoUpdateOp.Pop => s }.map(op => render(op.prop) + ": " + render(op.value))
)("$pop")
)("$pop"),
renderOps(
ops.collect { case s: MU.MongoUpdateOp.Pull => s }.map(op => render(op.prop) + ": " + render(op.value))
)("$pull")
).flatten
.mkString("{\n", ",\n", "\n}")

Expand All @@ -121,6 +131,9 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
case MU.UIterable(iterable) => iterable.map(render).mkString("[", ",", "]")
case MU.ScalaCodeIterable(_) => "[ ? ]"

case MongoUpdateNode.QueryWrapper(query) =>
MongoQueryCompiler.render(query)

case _ => report.errorAndAbort(s"Wrong term: $query")
}

Expand All @@ -142,11 +155,13 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
def targetOps(setters: List[MU.MongoUpdateOp]): List[Expr[(String, BsonValue)]] =
setters.map { op =>
val key = op.prop.path
val valueExpr = handleValues(op.value)
def valueExpr = handleValues(op.value)
val finalValueExpr = op match
case addToSet: MongoUpdateOp.AddToSet =>
if addToSet.each then '{ BsonDocument("$each" -> $valueExpr) }
else valueExpr
case pull: MongoUpdateOp.Pull =>
MongoQueryCompiler.target(pull.fieldQuery.query)
case _ => valueExpr
'{ ${ Expr(key) } -> $finalValueExpr }
}
Expand All @@ -163,6 +178,7 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
val tSetOnInserts = targetOps(ops.collect { case s: MU.MongoUpdateOp.SetOnInsert => s })
val tAddToSets = targetOps(ops.collect { case s: MU.MongoUpdateOp.AddToSet => s })
val tPops = targetOps(ops.collect { case s: MU.MongoUpdateOp.Pop => s })
val tPulls = targetOps(ops.collect { case s: MU.MongoUpdateOp.Pull => s })

// format: off
def updaterGroup(groupName: String, updaters: List[Expr[(String, BsonValue)]]): Option[Expr[(String, BsonDocument)]] =
Expand All @@ -184,6 +200,7 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
updaterGroup("$setOnInsert", tSetOnInserts),
updaterGroup("$addToSet", tAddToSets),
updaterGroup("$pop", tPops),
updaterGroup("$pull", tPulls),
).flatten

'{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ case object MongoUpdateNode {

case class ScalaCodeIterable(code: Expr[Iterable[Any]]) extends MU

case class QueryWrapper(query: MongoQueryNode) extends MU

sealed abstract class MongoUpdateOp(val prop: Prop, val value: MU) extends MU
object MongoUpdateOp {
case class Set(override val prop: Prop, override val value: MU) extends MongoUpdateOp(prop, value)
Expand All @@ -43,5 +45,7 @@ case object MongoUpdateNode {
case Remove.Last => MU.Constant(1)
}
}

case class Pull(override val prop: Prop, fieldQuery: QueryWrapper) extends MongoUpdateOp(prop, fieldQuery)
}
}
28 changes: 28 additions & 0 deletions oolong-mongo/src/test/scala/oolong/mongo/UpdateSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,34 @@ class UpdateSpec extends AnyFunSuite {
)
}

test("$pull #1") {
val q = update[TestClass](_.pull(_.listField, _ == 2))
val repr = renderUpdate[TestClass](_.pull(_.listField, _ == 2))
test(
q,
repr,
BsonDocument(
"$pull" -> BsonDocument(
"listField" -> BsonDocument("$eq" -> BsonInt32(2)) // TODO: think about having here a value instead of a condition
)
),
)
}

test("$pull #2") {
val q = update[TestClass](_.pull(_.classInnerClassField, _.fieldOne == "1"))
val repr = renderUpdate[TestClass](_.pull(_.classInnerClassField, _.fieldOne == "1"))
test(
q,
repr,
BsonDocument(
"$pull" -> BsonDocument(
"classInnerClassField" -> BsonDocument("fieldOne" -> BsonString("1"))
)
),
)
}

test("several update operators combined") {
val q = update[TestClass](
_.unset(_.dateField)
Expand Down
8 changes: 2 additions & 6 deletions project/Settings.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import scalafix.sbt.ScalafixPlugin.autoImport.scalafixResolvers

import coursierapi.{MavenRepository => CoursierMvnRepo}
import sbt.Keys._
import sbt._
import sbt.Keys.*

object Settings {
val common = Seq(
organization := "io.github.leviysoft",
scalaVersion := "3.3.1",
scalaVersion := "3.3.3",
scalacOptions ++= Seq(
// For reference: https://docs.scala-lang.org/scala3/guides/migration/options-lookup.html
"-encoding",
Expand Down

0 comments on commit a3fc28b

Please sign in to comment.