首页 > 解决方案 > 猫鼬递归嵌套

问题描述

在我的项目中,用户可以创建产品。每个用户都有对其所有产品的引用,每个产品都有对其用户的引用。

用户和产品都有一个“名称”字段。

我需要获取所有用户产品数组,并且在该数组中我想拥有产品名称和创建它的用户名(并且只有那些字段,没有其他字段)。

例如:

用户:

{ _id: 1, name: 'josh', productIds: [1,3]}
{ _id: 2, name: 'sheldon', productIds: [2]}

产品:

{ _id: 1, name: 'table', price: 45, userId: 1}
{ _id: 2, name: 'television', price: 25 userId: 2}
{ _id: 3, name: 'chair', price: 14 userId: 1}

我想得到以下结果:

{ _id: 1, name: 'josh', 
    products: {
        { _id: 1, name: 'table', user: { _id: 1, name: 'josh' },
        { _id: 3, name: 'chair', user: { _id: 1, name: 'josh' },
    }
}
{ _id: 2, name: 'sheldon', 
    products: {
        { _id: 2, name: 'television', userId: { _id: 2, name: 'sheldon' }
    }
}

我尝试了以下未填充内部 userId 的查询,只留下了 id(无名称):

User.aggregate([
  { 
    $lookup: 
      { 
        from: 'products',
        localField: 'productIds',
        foreignField: '_id',
        as: 'products' 
      }  
  }

我还尝试了以下操作,它与第一个查询相同,只是它只为每个用户重试了第一个产品:

User.aggregate([
  { 
    $lookup: 
      { 
        from: 'products',
        localField: 'productIds',
        foreignField: '_id',
        as: 'products' 
      }  
  },
  {
    $unwind: {
      path: "$products",
      preserveNullAndEmptyArrays: true
    }
  },
  {
    $lookup: {
      from: "user",
      localField: "products.userId",
      foreignField: "_id",
      as: "prodUsr",
    }
  },
  {
    $group: {
      _id : "$_id",
      products: { $push: "$products" },
      "doc": { "$first": "$$ROOT" }
    }
  },
  {
    "$replaceRoot": {
    "newRoot": "$doc"
    }
}

产品:

const schema = new Schema(
  {
    name: {
      type: String,
      required: true
    },
    price: {
      type: Number,
      required: true
    },
    userId: {
      type: Schema.Types.ObjectId, 
      ref: 'User',
      required: true
    },
  }
);

module.exports = mongoose.model('Product', schema);

用户:

const schema = new Schema(
  {
    name: {
      type: String,
      required: true,
      unique: true
    },
    productIds: [{
      type: Schema.Types.ObjectId, 
      ref: 'Product',
      require: false
    }],
  { timestamps: true }
);

module.exports = mongoose.model('User', schema);

任何帮助将不胜感激

标签: mongodbmongooseaggregation-frameworkaggregatelookup

解决方案


对于带有自定义管道和另一个嵌套的 $lookup 来说,这看起来像是一个完美的场景$lookup。内层允许您处理product-> user关系,而外层则处理关系user -> product

db.Users.aggregate([
    {
        $project: {
            productIds: 0
        }
    },
    {
        $lookup: {
            from: "Products",
            let: { user_id: "$_id" },
            pipeline: [
                {
                    $match: {
                        $expr: {
                            $eq: [ "$userId", "$$user_id" ]
                        }
                    }
                },
                {
                    $lookup: {
                        from: "Users",
                        localField: "userId",
                        foreignField: "_id",
                        as: "user"
                    }
                },
                {
                    $unwind: "$user"
                },
                {
                    $project: {
                        "user.productIds": 0,
                        "price": 0,
                        "userId": 0
                    }
                }
            ],
            as: "products"
        }
    }
])

蒙戈游乐场


推荐阅读