skip to Main Content

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


  1. You can use array.flatMap() once you receive aggregation result:

    const property = [
        [
            {
                "_id": "Davenport",
                "country": "United States",
                "totalProperties": 1,
                "cityPhoto": "https://"
            }
        ],
        [
            {
                "_id": "Destin",
                "country": "United States",
                "totalProperties": 1,
                "cityPhoto": "https://"
            }
        ],
    ] ; // your aggregation output
    
    const response = property.flatMap(x => x);
    console.log(response);

    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.

    Login or Signup to reply.
  2. 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 in citiesArr), you then issue an individual aggregate() request to the database for each entry in that array (via the map where each city is now an item).

    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

                        {
                            $match: { city: item, propertyType: type },
                        }
    

    To something like

                        {
                            $match: { city: { $in: citiesArr }, propertyType: type },
                        }
    

    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:

    1. The $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.
    2. With this approach I’d still suggest checking that 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.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search