Search code examples
mongodbmongodb-queryaggregation-frameworkmongodb-lookup

Use fields that start with $ in MongoDB aggregation lookup


I have a MongoDB database that is populated by a Spring application using Spring Data. I want to perform a manual query to join two collections and extract some statistics from this data.

The first collection is named emailCampaign and contains this information (simplified):

{
    "_id" : ObjectId("5db85687307b0a0d184448db"),
    "name" : "Welcome email",
    "subject" : "¡Welcome {{ user.name }}!",
    "status" : "Sent",
    "_class" : "com.mycompany.EmailCampaign"
}

The second collection is named campaignDelivery and contains this information (simplified):

/* 1 */
{
    "_id" : ObjectId("5db183fb307b0aef3113361f"),
    "campaign" : {
        "$ref" : "emailCampaign",
        "$id" : ObjectId("5db85687307b0a0d184448db")
    },
    "deliveries" : 3,
    "_class" : "com.mycompany.CampaignDelivery"
}

/* 2 */
{
    "_id" : ObjectId("5db85f2c307b0a0d184448e1"),
    "campaign" : {
        "$ref" : "emailCampaign",
        "$id" : ObjectId("5db85687307b0a0d184448db")
    },
    "deliveries" : 5,
    "_class" : "com.mycompany.CampaignDelivery"
}

Ultimately I want to obtain the sum of both deliveries field, but by now I'm stuck with the basic JOIN:

db.emailCampaign.aggregate([
{
    $lookup: {
        from: 'campaignDelivery',
        localField: '_id',
        foreignField: 'campaign.$id',
        as: 'deliveries'
    }
}
])

Throws the following error:

FieldPath field names may not start with '$'.

Escaping the dollar had no impact whatsoever, and I can't any examples of fields that start with dollars.


Solution

  • You can workaround it by using uncorrelated $lookup with $objectToArray in the sub-query to access campaign.$id:

    db.emailCampaign.aggregate([
      { $lookup: {
        from: "campaignDelivery",
        let: { id: "$_id" },
        pipeline: [
          { $addFields: {
            refId: { $arrayElemAt: [
              { $filter: {
                input: { $objectToArray: "$campaign" },
                cond: { $eq: [ "$$this.k", { $literal: "$id" } ] }
              } }
              , 0
            ] }
          } },
          { $match: {
            $expr: { $eq: [
              "$refId.v",
              "$$id"
            ] }
          } },
          { $project: {
            refId: 0
          } }
        ],
        as: "deliveries"
      } }
    ])