Mongoose: Get multiple distinct lists of values for multiple array fields

Viewed 287

I need to get three distinct lists of values for three different fields, but I would like to avoid querying the database more than once. I am sure the answer it to use the mongodb aggregation framework, however, I am not sure how to use it to select a distinct list of strings?

tags: [{ type: String }],
categories: [{ type: String }],
hashtags: [{ type: String }],
...
let tags = await Model.distinct('tags');
let categories = await Model.distinct('categories');
let hashtags = await Model.distinct('hashtags');
2 Answers

You can $group by constant value to get an array of arrays from all the documents and then run $reduce along with $setUnion to get unique values:

await Model.aggregate([
    {
        $group: {
            _id: null,
            tags: { $push: "$tags" },
            categories: { $push: "$categories" },
            hashtags: { $push: "$hashtags" }
        }
    },
    {
        $project: {
            tags: {
                $reduce: {
                    input: "$tags",
                    initialValue: [],
                    in: { $setUnion: [ "$$value", "$$this" ] }
                }
            },
            categories: {
                $reduce: {
                    input: "$categories",
                    initialValue: [],
                    in: { $setUnion: [ "$$value", "$$this" ] }
                }
            },
            hashtags: {
                $reduce: {
                    input: "$hashtags",
                    initialValue: [],
                    in: { $setUnion: [ "$$value", "$$this" ] }
                }
            }
        }
    }
])

You can simplify using just $addToSet

await Model.aggregate([
    {
        $group: {
            _id: null,
            tags: { $addToSet: "$tags" },
            categories: { $addToSet: "$categories" },
            hashtags: { $addToSet: "$hashtags" }
        }
    }])
Related