2

I have the following schema:

const MenuSchema = new mongoose.Schema({
  name: String,
  type: String,
  children: [{ type: ObjectId, ref: 'Menu' }],
});

And the following query:

const res = await Menu.aggregate([
   { $graphLookup: { from: "menus", startWith: "$children", connectToField: "children", connectFromField: "_id", as: "menus" }}
]);

As you can see, the menu schema is a self-referential data structure, children stores references to other instances of the same entity, with a 'type' field to differentiate the levels. I'm attempting to find and populate documents for each array of children (which are just BSON IDs) and return the results.

The above example seems to get most of the way there, however when I access one of the populated menus, it has a list of all the populated children in a flattened array, it doesn't retain the relationship structure.

For example, if I print out res, I get:

[  {
    _id: 5f1212d053e5494bb45f18f3,
    children: [ 5f1212d053e5494bb45f18f1 ],
    name: 'Vodka',
    type: 'Item',
    __v: 0,
    menus: [ [Object], [Object], [Object], [Object] ]
  },
  {
    _id: 5f1212d053e5494bb45f18f4,
    children: [ 5f1212d053e5494bb45f18f3, 5f1212d053e5494bb45f18f2 ],
    name: 'Drinks',
    type: 'Category',
    __v: 0,
    menus: [ [Object], [Object] ]
  },
  {
    _id: 5f1212d053e5494bb45f18f5,
    children: [ 5f1212d053e5494bb45f18f4 ],
    name: 'Main Menu',
    type: 'Menu',
    __v: 0,
    menus: [ [Object] ]
  }
]

But when I print out res[1].menus, I get:

[
  {
    _id: 5f1212d053e5494bb45f18f3,
    children: [ 5f1212d053e5494bb45f18f1 ],
    name: 'Vodka',
    type: 'Item',
    __v: 0
  },
  {
    _id: 5f1212d053e5494bb45f18f1,
    children: [ 5f1212d053e5494bb45f18f0 ],
    name: 'Double',
    type: 'Variant',
    __v: 0
  },
  {
    _id: 5f1212d053e5494bb45f18f4,
    children: [ 5f1212d053e5494bb45f18f3, 5f1212d053e5494bb45f18f2 ],
    name: 'Drinks',
    type: 'Category',
    __v: 0
  }
]

Which is all of the children in a flat array.

Is $graphLookup the correct approach, or am I just using it wrong?

Ewan Valentine
  • 3,741
  • 7
  • 43
  • 68

1 Answers1

0

I don't know if you are still looking for the answer for this, but if you use mongoose you can take advantage of the populate feature and use it as a middleware

Here's an example: Let's say I want a list of people and their friends, and their friends-friends, etc. The result should look like this:

[
    {
        _id: "abc123",
        name: "John Doe",
        friends: [
            {
                _id: "efg456",
                name: "Foo bar",
                friends: [
                    {
                        _id: "hij789",
                        name: "Jane Doe",
                        friends: [more friends...]
                    }
                ]
            }
        ]
]

In the db they are stored like this

{_id: "abc123", name: "John Doe", friends: ["efg456"]}
{_id: "efg456", name: "Foo bar", friends: ["hij789"]}
{_id: "hij789", name: "Jane Doe", friends: [more friends...]}

Your schema and middleware would be:

const Person = new Schema<Folder>({
    name: {type: String, required: true},
    friends: [{type: Schema.Types.ObjectId, ref: "person"}],
}, {timestamps: true})

Person.pre("find", function(next) {
    this.populate("friends")
    next()
})

Adding the function as a middleware to find will make it run for every person found. That includes the children in the friends array.

Charles-Eugene Loubao
  • 1,080
  • 2
  • 12
  • 22