如何在 ReactiveMongo 聚合框架中使用 $lookup 指定多个连接条件?

How do I specify multiple Join Conditions with $lookup in ReactiveMongo aggregation framework?

如何在 ReactiveMongo (0.17.1) 中使用 $lookup 指定多个连接条件并牢记以下几点? - 根据 the mongoDB documentation.

用户collection

[
    {
      "name": "dogfrey",
      "field": "cowboy"
    },
    {
      "name": "catsville",
      "field": "spaceman"
    }
]

角色collection

[
    {
      "id": 0,
      "userType": "cowboy",
      "spaceman": "fiver",
      "num": 2
    },
    {
      "id": 1,
      "userType": "joker",
      "spaceman": "tenner",
      "num": 3
    },
    {
      "id": 2,
      "userType": "cowboy",
      "spaceman": "tenner",
      "num": 1
    }
]

MongoDb查询

db.users.aggregate([
  {
    "$match": {
      "name": "dogfrey"
    }
  },
  {
    "$lookup": {
      "from": "roles",
      let: {
        "users_field": "$field"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$userType",
                "$$users_field"
              ]
            },
            
          }
        },
        {
          $project: {
            _id: 0
          }
        }
      ],
      "as": "dogs"
    }
  }
])

See here for the MongoPlayground example

ReactiveMongo documentation 不包含任何此类示例,表明这可能是不可能的。任何帮助表示赞赏!


另外补充一下我试过的(不成功的):

  def getResults(aColl: JSONCollection, bColl: JSONCollection)
                (id: BSONObjectID)(implicit request: Request[AnyContent]) = aColl.aggregateWith[JsObject]() {
    framework => import framework.{Match, Lookup, AddFields, Project, Sort, Ascending, Descending, Filter, Limit, Group, Sum, Push, Slice}

    ...

    val lookupJso = Lookup(
      from = bColl.name,
      let = Json.obj("fromDate" -> "$varData_e.plan_e.when_e.fromDate", "toDate" -> "$varData_e.plan_e.when_e.toDate"),
      pipeline = Json.arr(
        "$match" -> Json.obj("$expr" ->
            Json.obj("$and" -> Json.arr(
              Json.obj("$gte" -> Json.arr("$varData_e.dateTime", f"$$fromDate")),
              Json.obj("$lt" -> Json.arr("$varData_e.dateTime", f"$$toDate"))
            )),
         ),
        "$project" -> Json.obj("_id" -> 0)
      ),
      "temp_e.lookupTest1_e"
    )

    ...

  }.collect[List](Int.MaxValue, Cursor.FailOnError[List[JsObject]]())

我可以看到 Lookup case class 看起来像这样:

  case class Lookup(
    from: String,
    localField: String,
    foreignField: String,
    as: String) extends PipelineOperator {
    import builder.{ document, elementProducer => element, string }
    val makePipe: pack.Document = document(Seq(
      element(f"$$lookup", document(Seq(
        element("from", string(from)),
        element("localField", string(localField)),
        element("foreignField", string(foreignField)),
        element("as", string(as)))))))
  }

Release 0.17.1 is more than one year old (latest version being the major 1.0).

您可以在 documentation 中看到,可以为未在 API 中提供方便工厂的阶段定义原始聚合运算符。

import scala.concurrent.ExecutionContext

import reactivemongo.bson._
import reactivemongo.api.collections.bson.BSONCollection

def customAgg(coll: BSONCollection)(implicit ec: ExecutionContext) =
  coll.aggregateWith[BSONDocument]() { framework =>
    import framework.{ Match, PipelineOperator, Project }

    val lookup = PipelineOperator(BSONDocument(f"$$lookup" -> BSONDocument(
      "from" -> "roles",
      "let" -> BSONDocument("users_field" -> f"$$field"),
      "pipeline" -> Seq(
        Match(BSONDocument(f"$$expr" ->
          BSONDocument(f"$$eq" -> Seq(f"$$userType", f"$$$$users_field")))).
          makePipe,
        Project(BSONDocument("_id" -> 0)).makePipe),
      "as" -> "dogs")))

    Match(BSONDocument("name" -> "dogfrey")) -> List(lookup)
  }