Skip to content

Commit d564cb8

Browse files
desavitskydenis_savitsky
and
denis_savitsky
authored
Add support for $pull (#45)
Co-authored-by: denis_savitsky <d.e.savitskiy@gmail.com>
1 parent 690e3be commit d564cb8

File tree

7 files changed

+63
-8
lines changed

7 files changed

+63
-8
lines changed

oolong-core/src/main/scala/oolong/AstParser.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,10 @@ private[oolong] class DefaultAstParser(using quotes: Quotes) extends AstParser {
321321
case '{($updater: Updater[Doc]).popLast($selectProp)} =>
322322
val prop = parsePropSelector(selectProp)
323323
parseUpdater(updater, FieldUpdateExpr.Pop(UExpr.Prop(prop), FieldUpdateExpr.Pop.Remove.Last) :: acc)
324+
325+
case '{type t; ($updater: Updater[Doc]).pull[`t`,`t`]($selectProp, $input)} =>
326+
val prop = parsePropSelector(selectProp)
327+
parseUpdater(updater, FieldUpdateExpr.Pull(UExpr.Prop(prop), parseQExpr[`t`](input)) :: acc)
324328

325329
case '{ $updater: Updater[Doc] } =>
326330
updater match {

oolong-core/src/main/scala/oolong/UExpr.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ private[oolong] object UExpr {
5151
}
5252
}
5353

54+
case class Pull(prop: Prop, cond: QExpr) extends FieldUpdateExpr(prop)
55+
5456
}
5557

5658
}

oolong-core/src/main/scala/oolong/dsl/Dsl.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,8 @@ sealed trait Updater[DocT] {
5858

5959
def popHead(selectProp: DocT => Iterable[?]): Updater[DocT] = useWithinMacro("popHead")
6060
def popLast(selectProp: DocT => Iterable[?]): Updater[DocT] = useWithinMacro("popLast")
61+
62+
def pull[PropT, ValueT](selectProp: DocT => Iterable[PropT], input: PropT => Boolean)(using
63+
PropT =:= ValueT
64+
): Updater[DocT] = useWithinMacro("pull")
6165
}

oolong-mongo/src/main/scala/oolong/mongo/MongoUpdateCompiler.scala

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
5454
case FieldUpdateExpr.Pop.Remove.First => Remove.First
5555
case FieldUpdateExpr.Pop.Remove.Last => Remove.Last
5656
MU.MongoUpdateOp.Pop(MU.Prop(renames.getOrElse(prop.path, prop.path)), muRemove)
57+
case FieldUpdateExpr.Pull(prop, query) =>
58+
val optimized = MongoQueryCompiler.optimize(MongoQueryCompiler.opt(LogicalOptimizer.optimize(query)))
59+
MU.MongoUpdateOp.Pull(
60+
MU.Prop(renames.getOrElse(prop.path, prop.path)),
61+
MU.QueryWrapper(optimized)
62+
)
63+
5764
})
5865
case UExpr.ScalaCode(code) => MU.ScalaCode(code)
5966
case UExpr.Constant(t) => MU.Constant(t)
@@ -100,7 +107,10 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
100107
)("$addToSet"),
101108
renderOps(
102109
ops.collect { case s: MU.MongoUpdateOp.Pop => s }.map(op => render(op.prop) + ": " + render(op.value))
103-
)("$pop")
110+
)("$pop"),
111+
renderOps(
112+
ops.collect { case s: MU.MongoUpdateOp.Pull => s }.map(op => render(op.prop) + ": " + render(op.value))
113+
)("$pull")
104114
).flatten
105115
.mkString("{\n", ",\n", "\n}")
106116

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

134+
case MongoUpdateNode.QueryWrapper(query) =>
135+
MongoQueryCompiler.render(query)
136+
124137
case _ => report.errorAndAbort(s"Wrong term: $query")
125138
}
126139

@@ -142,11 +155,13 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
142155
def targetOps(setters: List[MU.MongoUpdateOp]): List[Expr[(String, BsonValue)]] =
143156
setters.map { op =>
144157
val key = op.prop.path
145-
val valueExpr = handleValues(op.value)
158+
def valueExpr = handleValues(op.value)
146159
val finalValueExpr = op match
147160
case addToSet: MongoUpdateOp.AddToSet =>
148161
if addToSet.each then '{ BsonDocument("$each" -> $valueExpr) }
149162
else valueExpr
163+
case pull: MongoUpdateOp.Pull =>
164+
MongoQueryCompiler.target(pull.fieldQuery.query)
150165
case _ => valueExpr
151166
'{ ${ Expr(key) } -> $finalValueExpr }
152167
}
@@ -163,6 +178,7 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
163178
val tSetOnInserts = targetOps(ops.collect { case s: MU.MongoUpdateOp.SetOnInsert => s })
164179
val tAddToSets = targetOps(ops.collect { case s: MU.MongoUpdateOp.AddToSet => s })
165180
val tPops = targetOps(ops.collect { case s: MU.MongoUpdateOp.Pop => s })
181+
val tPulls = targetOps(ops.collect { case s: MU.MongoUpdateOp.Pull => s })
166182

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

189206
'{

oolong-mongo/src/main/scala/oolong/mongo/MongoUpdateNode.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ case object MongoUpdateNode {
1919

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

22+
case class QueryWrapper(query: MongoQueryNode) extends MU
23+
2224
sealed abstract class MongoUpdateOp(val prop: Prop, val value: MU) extends MU
2325
object MongoUpdateOp {
2426
case class Set(override val prop: Prop, override val value: MU) extends MongoUpdateOp(prop, value)
@@ -43,5 +45,7 @@ case object MongoUpdateNode {
4345
case Remove.Last => MU.Constant(1)
4446
}
4547
}
48+
49+
case class Pull(override val prop: Prop, fieldQuery: QueryWrapper) extends MongoUpdateOp(prop, fieldQuery)
4650
}
4751
}

oolong-mongo/src/test/scala/oolong/mongo/UpdateSpec.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,34 @@ class UpdateSpec extends AnyFunSuite {
246246
)
247247
}
248248

249+
test("$pull #1") {
250+
val q = update[TestClass](_.pull(_.listField, _ == 2))
251+
val repr = renderUpdate[TestClass](_.pull(_.listField, _ == 2))
252+
test(
253+
q,
254+
repr,
255+
BsonDocument(
256+
"$pull" -> BsonDocument(
257+
"listField" -> BsonDocument("$eq" -> BsonInt32(2)) // TODO: think about having here a value instead of a condition
258+
)
259+
),
260+
)
261+
}
262+
263+
test("$pull #2") {
264+
val q = update[TestClass](_.pull(_.classInnerClassField, _.fieldOne == "1"))
265+
val repr = renderUpdate[TestClass](_.pull(_.classInnerClassField, _.fieldOne == "1"))
266+
test(
267+
q,
268+
repr,
269+
BsonDocument(
270+
"$pull" -> BsonDocument(
271+
"classInnerClassField" -> BsonDocument("fieldOne" -> BsonString("1"))
272+
)
273+
),
274+
)
275+
}
276+
249277
test("several update operators combined") {
250278
val q = update[TestClass](
251279
_.unset(_.dateField)

project/Settings.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
import scalafix.sbt.ScalafixPlugin.autoImport.scalafixResolvers
2-
3-
import coursierapi.{MavenRepository => CoursierMvnRepo}
4-
import sbt.Keys._
5-
import sbt._
1+
import sbt.Keys.*
62

73
object Settings {
84
val common = Seq(
95
organization := "io.github.leviysoft",
10-
scalaVersion := "3.3.1",
6+
scalaVersion := "3.3.3",
117
scalacOptions ++= Seq(
128
// For reference: https://docs.scala-lang.org/scala3/guides/migration/options-lookup.html
139
"-encoding",

0 commit comments

Comments
 (0)