Skip to content

Commit

Permalink
Add support for $pullAll
Browse files Browse the repository at this point in the history
  • Loading branch information
denis_savitsky committed Apr 3, 2024
1 parent d564cb8 commit 71b23b6
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 8 deletions.
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,13 +340,13 @@ II Array update operators
case class Student(id: Int, courses: List[Int])

val q = update[Student](_.addToSet(_.courses, 55))
// q us {"$addToSet": {"courses": 55}}
// q is {"$addToSet": {"courses": 55}}
```
In order to append multiple values to array `addToSetAll` should be used:

```scala
val q = update[Student](_.addToSetAll(_.courses, List(42, 44, 53)))
// q us {"$addToSet": {"courses": {$each: [42, 44, 53] }}}
// q is {"$addToSet": {"courses": {$each: [42, 44, 53] }}}
```

2. $pop
Expand All @@ -355,10 +355,26 @@ val q = update[Student](_.addToSetAll(_.courses, List(42, 44, 53)))
case class Student(id: Int, courses: List[Int])

val q = update[Student](_.popHead(_.courses)) // removes the first element
// q us {"$pop": {"courses": -1}}
// q is {"$pop": {"courses": -1}}

val q1 = update[Student](_.popLast(_.courses)) // removes the last element
// q1 us {"$pop": {"courses": 1}}
// q1 is {"$pop": {"courses": 1}}
```

3. $pull
```scala
case class Student(id: Int, courses: List[Int])

val q = update[Student](_.pull(_.courses, _ >= 42))
// q is {"$pull": {"courses": {"$gte": 42}}}
```
4. $pullAll

```scala
case class Student(id: Int, courses: List[Int])

val q = update[Student](_.pullAll(_.courses, List(5, 10, 42)))
// q is {"$pullAll": {"courses": [5, 10, 42]}}
```

#### Projection
Expand Down
8 changes: 7 additions & 1 deletion oolong-core/src/main/scala/oolong/AstParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,13 @@ private[oolong] class DefaultAstParser(using quotes: Quotes) extends AstParser {
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 '{type t; ($updater: Updater[Doc]).pullAll[`t`,`t`]($selectProp, $input: Iterable[`t`])} =>
val prop = parsePropSelector(selectProp)
val value = getValueOrIterable(input)
parseUpdater(updater, FieldUpdateExpr.PullAll(UExpr.Prop(prop), value) :: acc)


case '{ $updater: Updater[Doc] } =>
updater match {
case AsTerm(Ident(name)) if name == paramName =>
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 @@ -53,6 +53,8 @@ private[oolong] object UExpr {

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

case class PullAll(prop: Prop, expr: UExpr) 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 @@ -62,4 +62,8 @@ sealed trait Updater[DocT] {
def pull[PropT, ValueT](selectProp: DocT => Iterable[PropT], input: PropT => Boolean)(using
PropT =:= ValueT
): Updater[DocT] = useWithinMacro("pull")

def pullAll[PropT, ValueT](selectProp: DocT => Iterable[PropT], input: Iterable[ValueT])(using
PropT =:= ValueT
): Updater[DocT] = useWithinMacro("pullAll")
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import com.dimafeng.testcontainers.MongoDBContainer
import com.mongodb.client.model.FindOneAndUpdateOptions
import com.mongodb.client.model.ReturnDocument
import concurrent.duration.DurationInt
import oolong.bson.BsonDecoder
import oolong.bson.*
import oolong.bson.given
import oolong.dsl.*
import org.mongodb.scala.MongoClient
import org.mongodb.scala.bson.BsonDocument
Expand All @@ -33,7 +34,8 @@ class OolongMongoUpdateSpec extends AsyncFlatSpec with ForAllTestContainer with
TestClass("4", 13, InnerClass("sdf", 1), List(1), None, List.empty),
TestClass("12345", 12, InnerClass("sdf", 11), Nil, None, List.empty),
TestClass("popHead", 1, InnerClass("popHead", 1), List(1, 2, 3), None, List.empty),
TestClass("popTail", 1, InnerClass("popTail", 1), List(1, 2, 3), None, List.empty)
TestClass("popTail", 1, InnerClass("popTail", 1), List(1, 2, 3), None, List.empty),
TestClass("pullAll", 1, InnerClass("pullAll", 1), List(1, 2, 3), None, List(InnerClass("1", 1), InnerClass("2", 2))),
)

implicit val ec = ExecutionContext.global
Expand Down Expand Up @@ -204,4 +206,41 @@ class OolongMongoUpdateSpec extends AsyncFlatSpec with ForAllTestContainer with
)
}

it should "$pulAll primitive elements" in {
for {
upd <- collection
.findOneAndUpdate(
query[TestClass](_.field1 == "pullAll"),
update[TestClass](
_.pullAll(_.field4, List(1, 100))
),
new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
)
.head()
.map(BsonDecoder[TestClass].fromBson(_).get)
_ = pprint.log(upd)

} yield assert(
upd.field4 == List(2, 3)
)
}

it should "$pulAll documents" in {
for {
upd <- collection
.findOneAndUpdate(
query[TestClass](_.field1 == "pullAll"),
update[TestClass](
_.pullAll(_.field6, lift(List(InnerClass("1", 1)))) // TODO: fix lift for update
),
new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER)
)
.head()
.map(BsonDecoder[TestClass].fromBson(_).get)
_ = pprint.log(upd)
} yield assert(
upd.field6 == List(InnerClass("2", 2))
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
MU.Prop(renames.getOrElse(prop.path, prop.path)),
MU.QueryWrapper(optimized)
)
case FieldUpdateExpr.PullAll(prop, expr) =>
MU.MongoUpdateOp.PullAll(MU.Prop(renames.getOrElse(prop.path, prop.path)), rec(expr))

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

Expand Down Expand Up @@ -179,6 +184,7 @@ object MongoUpdateCompiler extends Backend[UExpr, MU, BsonDocument] {
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 })
val tPullAlls = targetOps(ops.collect { case s: MU.MongoUpdateOp.PullAll => s })

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

'{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,7 @@ case object MongoUpdateNode {
}

case class Pull(override val prop: Prop, fieldQuery: QueryWrapper) extends MongoUpdateOp(prop, fieldQuery)

case class PullAll(override val prop: Prop, override val value: MU) extends MongoUpdateOp(prop, value)
}
}
14 changes: 14 additions & 0 deletions oolong-mongo/src/test/scala/oolong/mongo/UpdateSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,20 @@ class UpdateSpec extends AnyFunSuite {
)
}

test("$pullAll #1") {
val q = update[TestClass](_.pullAll(_.listField, Vector(1, 2, 3)))
val repr = renderUpdate[TestClass](_.pullAll(_.listField, Vector(1, 2, 3)))
test(
q,
repr,
BsonDocument(
"$pullAll" -> BsonDocument(
"listField" -> BsonArray(1, 2, 3)
)
),
)
}

test("several update operators combined") {
val q = update[TestClass](
_.unset(_.dateField)
Expand Down

0 comments on commit 71b23b6

Please sign in to comment.