0

I'm working on product & category relation. Where category has parent-child relation.

i.e.

Category C1 has a child Category C2 and this can go to n-th layer

also,

a product has a category array and a reference to the last layer of category.

i.e. A product P1 belongs to the category C3

The problem arises, when the parent of Category C3 is changed to say Category 1 . As all the products assigned to Category C3 will have different category tree structure because of this change.

Here's my implementation so far:

GraphQl definition

type Product{
    Id : ID
    name : String
    slug : String
    category_direct : Category  // Reference of the last layered category
    category : [Category]  // reference of the category parents or category structure depending on the last layered category
}

When the parent of category is changed:

await db.collection('categories').bulkWrite([
    {
        // Remove child from parent
        updateOne: {
            "filter": {Id: doc.parent},
            "update": {
                $pull: {child: {$in: [doc.Id]}},
                $currentDate: {
                    "date_updated": {$type: "timestamp"}
                }
            }
        }
    }, {
        // Add child ID to the new parent
        // Add child details to parent category
        updateOne: {
            "filter": {Id: args.category.parent},
            "update": {
                $currentDate: {
                    "date_updated": {$type: "timestamp"}
                },
                $push: {"child": doc.Id}
            }
        }
    }
]);

I'm stuck with the following where I need to restructure the category array in the products

// TODO Restructure product category depending on the products belonging to the category
// Find all products with direct_category as doc.Id and recursively get all parent categories
await db.runCommand({
    update: "products",
    updates: [{
        q: { direct_category: doc.Id},
        u: [
            {
                $set: {
                    category: {
                      // Can I add the below function here??
                    }
                }
            }
        ],
        multi: true
    }]
});

In order to append all category parents, I need to add the following inside of the update query :

// If category exists - get all the parents -- including self
args.product.category = await db.collection('categories').aggregate([{$match: {"Id": data.Id}},
    {
        $graphLookup: {
            from: "categories",
            startWith: "$parent",
            connectFromField: "parent",
            connectToField: "Id",
            "as": "ancestors"
        }
    },
    {
        "$addFields": {
            "ancestors": {
                "$reverseArray": {
                    "$map": {
                        "input": "$ancestors",
                        "as": "t",
                        "in": {"Id": "$$t.Id"}
                    }
                }
            }
        }
    }
]).toArray().then(data => {
    if (data[0]) {
        if (!_.isNull(data[0].ancestors)) {
            data[0].ancestors.push({Id: data[0].Id})
            return data[0].ancestors.map(value => value.Id)
        } else return [{Id: data[0].Id}].map(value => value.Id)
    } else return []
});
silverFoxA
  • 4,549
  • 7
  • 33
  • 73
  • 1
    You cannot. [Read the documentation](https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-with-an-aggregation-pipeline) - *"Starting in MongoDB 4.2, the db.collection.update() method can accept an aggregation pipeline [ , , ... ] that specifies the modifications to perform. The pipeline can consist of the following stages: $addFields and its alias $set, $project and its alias $unset, $replaceRoot and its alias $replaceWith."*. That literally does mean **those stages and those stages only**. No other pipeline stage operators are allowed. – Neil Lunn Oct 02 '19 at 10:55
  • 1
    In short, your approach here is to either **A**. Iterate items and use discrete updates, preferably using `bulkWrite()`. **B**: Use the `$out` operator and create a new collection. Possibly renaming to the original. – Neil Lunn Oct 02 '19 at 10:58
  • @NeilLunn Not sure why you marked it as duplicate but I solved the problem with a different approach as in my case one product is linked to the last layered category thus only one category but will have reference to the parents of the category. – silverFoxA Oct 02 '19 at 11:03

0 Answers0