amir zamli
amir zamli

Reputation: 65

Mongodb geoNear and group aggregation

I have been searching the web for something related to this but cant.

I have this aggreation

 Place.aggregate(
    [
        { "$geoNear": {
            "near": {
                "type": "Point",
                "coordinates": [longitude, latitude]
            },
            "spherical": true,
            "distanceField": "distance"
        }},
        { $group:
        {   _id: "$_id",
            name: { '$first': '$name' },
            distance: { $first: "$distance" }
        }
        },
        { $project : {
            name: 1,
            distance: 1,
        }}
    ],
    function(error, places) {
        if (error) return callback(error, null);
        callback(null, places)
    }
);

It works, but the geoNear sorting is lost!

But this gives me properly sorted documents:

    Place.aggregate(
    [
        { "$geoNear": {
            "near": {
                "type": "Point",
                "coordinates": [longitude, latitude]
            },
            "spherical": true,
            "distanceField": "distance"
        }}
    ],
    function(error, places) {
        if (error) return callback(error, null);
        callback(null, places)
    }
);

Any ideas?

To give you an idea of what im trying to do here is the full query im using

    Place.aggregate(
    [
        { "$geoNear": {
            "near": {
                "type": "Point",
                "coordinates": [longitude, latitude]
            },
            "spherical": true,
            "distanceField": "distance"
        }},
        {"$unwind": "$participants" } ,
        { $group:
        {   _id: "$_id",
            name: { '$first': '$name' },
            distance: { $first: "$distance" },
            person: { $sum: 1 },
            sumof:{ $sum: "$participants.age"}
        }
        },
        { $project : {
            name: name,
            distance: 1,
            person: 1,
            meanAge:{ $divide: [ "$sumof", "$person" ]}
        }}
    ],
    function(error, places) {
        if (error) return callback(error, null);
        callback(null, places)
    }
);

Upvotes: 2

Views: 3058

Answers (1)

Blakes Seven
Blakes Seven

Reputation: 50416

In a nutshell when you use an operator such as $group there is no guarantee of the order of results returned. Documents will be processed in the order that proceeded the "input" to the group pipline in order to honour things such as $first, but the output does not necessarily come out in the same order that it went in.

Directly from the documentation:

$group does not order its output documents.

In fact your likely to find that the order is by the grouping key but in reverse on most occasions.

If you want to have a specific order of output then use $sort and for final "output" that should be your last pipeline stage so nothing else changes that order.

Place.aggregate(
    [
        { "$geoNear": {
            "near": {
                "type": "Point",
                "coordinates": [longitude, latitude]
            },
            "spherical": true,
            "distanceField": "distance"
        }},
        { "$unwind": "$participants" } ,
        { "$group": {   
            "_id": "$_id",
             "name": { "$first": "$name" },
             "distance": { "$first": "$distance" },
             "person": { "$sum": 1 },
             "sumof":{ "$sum": "$participants.age" }
        }},
        { "$project" : {
            "name": 1,
            "distance": 1,
            "person": 1,
            "meanAge": { "$divide": [ "$sumof", "$person" ]}
        }},
        { "$sort": { "distance": 1 } }
    ],
    callback
 );

Upvotes: 4

Related Questions