1

I'm performing a query on my user model in MongoDB and want to order the search results first by their distance in kilometres (using the $geoNear operator), and then by their last login time. The problem is, the distance attribute is returned as metres in a float variable, which is too accurate.

This means that my results are ordered like this:

+----------------+------------------------+
|    distance    |  lastLoggedInTime      |
+----------------+------------------------+
|    1700        |  16 hours ago          |
|    1700.1      |  4 hours ago           |
|    2220        |  3 months ago          |
|    2220.5      |  1 day ago             |
+----------------+------------------------+

Those same values, where km = Math.ceil(distance / 1000) would order like this:

+----------------+------------------------+
|    km          |  lastLoggedInTime      |
+----------------+------------------------+
|    2           |  4 hours ago           |
|    2           |  16 hours ago          |
|    3           |  1 day ago             |
|    3           |  3 months ago          |
+----------------+------------------------+
Which is what I want. I've made the values more readable to highlight the issue I'm facing. The actual values would look more like this:
{
    distance: 2234.3715776698273,
    lastLoggedInTime: '2019-07-13T02:14:30.173Z'
}

I've tried changing the distance attribute myself:

      user.distance = Math.ceil(user.travel.distance / 1000);

But since distance is added by the $geoNear inside the search query itself, I can only access it after the query runs. So the results are already sorted undesirably by the time I have access to it.

There is also the distanceMultiplier attribute, which I can attach to my query like this:

      $geoNear: {
        spherical: true,
        includeLocs: 'loc',
        distanceField: 'distance',
        distanceMultiplier: 0.001,        
        near: {
          type: 'Point',
          coordinates: [lng, lat]
        },
        ...
      }

But the result is still returned as a float, (2234.3715776698273 becomes 2.2343715776698273), which has the same problem. What are my other options?

Sam
  • 2,172
  • 3
  • 24
  • 43

1 Answers1

0

Try converting the operation to an aggregation and then using $project and $sort to create and sort by a truncated distance field. The $project stage would use $trunc to provide the simplified distance field.

For example:

db.coll.aggregate([
  { "$geoNear" : {
    {
        spherical: true,
        includeLocs: 'loc',
        distanceField: 'distance',
        near: {
          type: 'Point',
          coordinates: [lng, lat]
        },
        ...
     }
  },
  {
    "$project": {
      "truncated_distance" : { "trunc" : [ "$distance", 0 ] }
    }
  },
  {
    "$sort" : {
      "truncated_distance" : 1, 
      "lastLoggedInTime": 1
    }
  }
])
Adam Harrison
  • 3,323
  • 2
  • 17
  • 25