Reputation: 111
in my project a user can create products. each user have a reference to all of its products and each product have a reference to its user.
both the user and the product have a 'name' field.
i need to get all of the users products array, and in that array i want to have the product name and the user name that created it (and only those fields and no others).
for example:
Users:
{ _id: 1, name: 'josh', productIds: [1,3]}
{ _id: 2, name: 'sheldon', productIds: [2]}
Products:
{ _id: 1, name: 'table', price: 45, userId: 1}
{ _id: 2, name: 'television', price: 25 userId: 2}
{ _id: 3, name: 'chair', price: 14 userId: 1}
i want to get the following result:
{ _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' }
}
}
i tried the following query that didn't fill the inner userId and left it with only the id (no name):
User.aggregate([
{
$lookup:
{
from: 'products',
localField: 'productIds',
foreignField: '_id',
as: 'products'
}
}
i also tried the following, which did the same as the first query except it only retried the first product for each user:
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"
}
}
Product:
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);
User:
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);
any help will be highly appreciated
Upvotes: 1
Views: 554
Reputation: 49945
It looks like a perfect scenario for $lookup with custom pipeline and another nested $lookup
. The inner one allows you to handle product-> user
relationship while the outer one handles user -> product
one:
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"
}
}
])
Upvotes: 2