'mongodb lookup on nested documents

I have documents like these :

Merchant:

{
        "_id" : ObjectId("628728cf9f5c99d8dedbe759"),
        "name" : "agriTales",
        "information" : "Farmers social",
        "categories" : [
                ObjectId("6287220c52497922d6f4a121")
        ],
        "website" : "www.agritales.com",
        "active" : true       
}

LuckyDraw:

{
        "_id" : ObjectId("62873487e0e5eedc24748a55"),
        "business" : ObjectId("628728cf9f5c99d8dedbe759"),       
        "title" : "May Lucky draw 2022",
        "date_start" : ISODate("2022-04-26T00:00:00Z"),
        "max_participants" : 5,
        "active" : true,
        "date_of_draw" : ISODate("2022-07-30T00:00:00Z"),
        "prizes_winners" : [
                {
                        "prize_name" : "1st Prize",
                        "coupon_voucher" : ObjectId("62872f809f5c99d8dedbe764")
                },
                {
                        "prize_name" : "2nd Prize",
                        "coupon_voucher" : ObjectId("62872f8d9f5c99d8dedbe765")
                }
        ],
        "result_declared" : true
}

CouponVoucher:

{
        "_id" : ObjectId("62872f809f5c99d8dedbe764"),
        "business" : ObjectId("628728cf9f5c99d8dedbe759"),
        "type" : "voucher",
        "title" : "8farmers Coupons 2001",
        "minimum_order_value" : 0,
        "voucher_face_value" : 2001,
        "active" : true,
        "status" : "NEW",
        "date_expiry" : ISODate("2022-12-31T00:00:00Z"),
}
{
        "_id" : ObjectId("62872f8d9f5c99d8dedbe765")
        "type" : "voucher",
        "title" : "8farmers Coupons 1501",
        "minimum_order_value" : 0,
        "voucher_face_value" : 1501,
        "active" : true,
        "status" : "NEW",
        "date_expiry" : ISODate("2022-12-31T00:00:00Z")
}

    class LuckyDraws(Document):
        business = ReferenceField(Merchants, required=True)
        branches = ListField(ReferenceField(MerchantBranches))
        title = StringField(required=True)
        date_start = DateTimeField(required=True)  # the date when luck draw will be available to participate
        max_participants = IntField()  # max number of participants
        active = BooleanField(default=True)
        date_of_draw = DateTimeField(required=True)  # the date when luck draw will be declared
    
        filter_conditions = EmbeddedDocumentField(LuckyDrawFilterConditions)
        participants = ListField(ReferenceField(Users), required=False)
    
        prizes_winners = ListField(EmbeddedDocumentField(LuckyDrawPrizesWinners), required=True)
        
    
    class LuckyDrawPrizesWinners(EmbeddedDocument):
        prize_name = StringField(required=True)
        coupon_voucher = ReferenceField(CouponVoucher, required=True)
        winner = ReferenceField(Users, blank=True, null=True, default=None)
        
    
    class CouponVoucher(Document):
        business = ReferenceField(Merchants, required=True)
        title = StringField(required=True, unique=True)
        discount_type = StringField(required=False)  # amount / percentage
        coupon_discount = DecimalField(required=False)
        max_count = IntField(required=False)
        minimum_order_value = DecimalField(required=False)

I am looking for a result where I get details of the lucky draw and its corresponding prizes and couponvoucher details used below pipeline

LuckyDraws.objects.aggregate([{
            '$lookup':
                {
                    "from": "merchants",
                    "localField": "business",
                    "foreignField": "_id",
                    "as": "business",
                    "pipeline": [
                        {"$project": {"name": 1, "information": 1, "categories": 1}}
                    ]
                }
        },
        
    {"$project":
        {
            "business": 1, "title": 1, "date_start": 1, "active": 1, "date_of_draw": 1,
            "prizes_winners.prize_name": 1, "prizes_winners.coupon_voucher": 1,
            "result_declared": 1, "vouchers": 1
        }
    },
    {
        '$lookup':
            {
                "from": "coupon_voucher",
                "localField": "prizes_winners.coupon_voucher",
                "foreignField": "_id",
                "pipeline": [
                    {"$project": {"title": 1, "minimum_order_value": 1, "voucher_face_value": 1,
                                  "active": 1, "date_expiry": 1}}

                ],
                "as": "prizes_winners"

            }
    },
    {
        "$sort": {
            "date_of_draw": -1
        }
    }
]

and getting below results but its missing prize_name under prizes_winners, please let me know what I am missing and how t oadd prize_name under prizes_winners. the result i am getting :

[
    {
        "_id": {
            "$oid": "62873487e0e5eedc24748a55"
        },
        "business": [
            {
                "_id": {
                    "$oid": "628728cf9f5c99d8dedbe759"
                },
                "name": "agriTales",
                "information": "Farmers social",
                "categories": [
                    {
                        "$oid": "6287220c52497922d6f4a121"
                    }
                ]
            }
        ],
        "title": "May Lucky draw 2022",
        "date_start": {
            "$date": "2022-04-26T00:00:00Z"
        },
        "active": true,
        "date_of_draw": {
            "$date": "2022-07-30T00:00:00Z"
        },
        "prizes_winners": [
            {
                "_id": {
                    "$oid": "62872f809f5c99d8dedbe764"
                },
                "title": "8farmers Coupons 2001",
                "minimum_order_value": 0.0,
                "voucher_face_value": 2001.0,
                "active": true,
                "date_expiry": {
                    "$date": "2022-12-31T00:00:00Z"
                }
            },
            {
                "_id": {
                    "$oid": "62872f8d9f5c99d8dedbe765"
                },
                "title": "8farmers Coupons 1501",
                "minimum_order_value": 0.0,
                "voucher_face_value": 1501.0,
                "active": true,
                "date_expiry": {
                    "$date": "2022-12-31T00:00:00Z"
                }
            }
        ],
        "result_declared": true
    }
]

I want the result should be :

[
    {
        "_id": {
            "$oid": "62873487e0e5eedc24748a55"
        },
        "business": [
            {
                "_id": {
                    "$oid": "628728cf9f5c99d8dedbe759"
                },
                "name": "agriTales",
                "information": "Farmers social",
                "categories": [
                    {
                        "$oid": "6287220c52497922d6f4a121"
                    }
                ]
            }
        ],
        "title": "May Lucky draw 2022",
        "date_start": {
            "$date": "2022-04-26T00:00:00Z"
        },
        "active": true,
        "date_of_draw": {
            "$date": "2022-07-30T00:00:00Z"
        },
        "prizes_winners": [
            {
                **"prize_name":"1st prize",**
                "_id": {
                    "$oid": "62872f809f5c99d8dedbe764"
                },
                "title": "8farmers Coupons 2001",
                "minimum_order_value": 0.0,
                "voucher_face_value": 2001.0,
                "active": true,
                "date_expiry": {
                    "$date": "2022-12-31T00:00:00Z"
                }
            },
            {
                **"prize_name":"2nd prize",**
                "_id": {
                    "$oid": "62872f8d9f5c99d8dedbe765"
                },
                "title": "8farmers Coupons 1501",
                "minimum_order_value": 0.0,
                "voucher_face_value": 1501.0,
                "active": true,
                "date_expiry": {
                    "$date": "2022-12-31T00:00:00Z"
                }
            }
        ],
        "result_declared": true
    }
]


Solution 1:[1]

There are lots of ways, and possibly more efficient ways, to do this. Here's one way.

db.LuckyDraw.aggregate([
  {
    "$lookup": {
      "from": "Merchant",
      "localField": "business",
      "foreignField": "_id",
      "pipeline": [ { "$unset": [ "active", "website" ] } ],
      "as": "business"
    }
  },
  {
    "$lookup": {
      "from": "CouponVoucher",
      "localField": "prizes_winners.coupon_voucher",
      "foreignField": "_id",
      "let": { "prizes_winners": "$prizes_winners" },
      "pipeline": [
        {
          "$set": {
            "prize_name": {
              "$reduce": {
                "input": "$$prizes_winners",
                "initialValue": "",
                "in": {
                  "$cond": [
                    { "$eq": [ "$$this.coupon_voucher", "$_id" ] },
                    "$$this.prize_name",
                    "$$value"
                  ]
                }
              }
            }
          }
        }
      ],
      "as": "prizes_winners"
    }
  }
])

Try it on mongoplayground.net.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 rickhg12hs