I have a Property controller :
//Get Properties By Type
const getPropertiesByType = async (req: Request, res: Response) => {
const { cities, type } = req.query
const citiesArr = typeof cities === 'string' ? cities.split(',') : []
try {
const property = await Promise.all(
citiesArr?.map((item) => {
return PropertyModel.aggregate([
{
$match: { city: item, propertyType: type },
},
{
$project: {
_id: 0,
city: 1,
country: 1,
cityPhoto: 1,
},
},
{
$group: {
_id: '$city',
country: { $first: '$country' },
totalProperties: { $sum: 1 },
cityPhoto: { $first: '$cityPhoto' },
},
},
])
})
)
res.status(201).json(property)
} catch (error) {
console.log(error)
res.status(400).json(error)
}
}
in Postman i am getting this reponse :
[
[
{
"_id": "Davenport",
"country": "United States",
"totalProperties": 1,
"cityPhoto": "https://"
}
],
[
{
"_id": "Destin",
"country": "United States",
"totalProperties": 1,
"cityPhoto": "https://"
}
],
]
Problem is, that i get all objects in arrays, but i want instead get in response all objects in one main array. Also, can i do that without aggregation pipeline?
2
Answers
You can use array.flatMap() once you receive aggregation result:
The reason you can’t do it directly in aggregation pipeline is you are using
Promise.all
which returns an array and each.aggregate()
also returns an array so you will get an array of arrays anyway.Looks like @mickl provided an answer as to how to the structure of the response via
flatMap()
after the database finishes its processing. Seems like a perfectly acceptable approach. But to the question in the comments about doing it via aggregation, I think there is an alternative that I explore below.If I am reading the code correctly, you are taking a look at the
cities
query parameter and expecting a string that contains a comma delimited list of values. Something like"A,B,C"
. After splitting that into an array of individual values (contained incitiesArr
), you then issue an individualaggregate()
request to the database for each entry in that array (via themap
where each city is now anitem
).I think you can, and probably should, collapse this into a single call to the database. In terms of the aggregation itself, it would require changing
To something like
If you similarly change the structure of the code surrounding this database call (e.g. no need for the
.all()
and.map()
) then I think the direct response from the database will be more along the lines of what you are looking for.Two additional things come to mind:
$project
stage that you have in the middle isn’t valuable. The database will figure out what stages you need for the$group
and retrieve just those automatically. I’d recommend removing the$project
entirely to simplify things.citiesArr
is nonempty before issuing the aggregation. There is no need for a roundtrip to the database if the array is empty and therefore no results can be returned.