MongoDB pagination with location criteria

2020-04-30 02:23发布

问题:

I want get data sorted by field. For example

db.Users.find().limit(200).sort({'rating': -1}).skip(0)

It's work, I get sorted data. And can use pagination.

But, If add criteria .find({'location':{$near : [12,32], $maxDistance: 10}}) sorting doesn't work correctly.

Full the query:

db.Users.find({'location':{$near : [12,32], $maxDistance: 10}}).limit(200).sort({'rating': -1}).skip(0)

For example

Whithout criteria location:

offset 0

rating 100
rating 99
rating 98
rating 97
rating 96

offset 5

rating 95
rating 94
rating 93
rating 92
rating 91

offset 10

rating 90
rating 89
rating 88
rating 87
rating 86

With criteria location

offset 0

rating 100
rating 99
rating 98
rating 97
rating 96

offset 5

rating 90
rating 89
rating 88
rating 87
rating 86

offset 10

rating 95
rating 94
rating 93
rating 92
rating 91

What could be the problem? Can I use pagination with location criteria in MongoDB?

回答1:

The aggregation framework has a way to do this using the $geoNear pipeline stage. Basically it will "project" a "distance" field which you can then use in a combination sort:

db.collection.aggregate([
    { "$geoNear": {
        "near": [12,32],
        "distanceField": "distance",
        "maxDistance": 10
    }},
    { "$sort": { "distance": 1, "rating" -1 } }
    { "$skip": 0 },
    { "$limit": 25 }
])

Should be fine, but "skip" and "limit" are not really efficient over large skips. If you can get away without needing "page numbering" and just want to go forwards, then try a different technique.

The basic principle is to keep track of the last distance value found for the page and also the _id values of the documents from that page or a few previous, which can then be filtered out using the $nin operator:

db.collection.aggregate([
    { "$geoNear": {
        "near": [12,32],
        "distanceField": "distance",
        "maxDistance": 10,
        "minDistance": lastSeenDistanceValue,
        "query": { 
            "_id": { "$nin": seenIds },
            "rating": { "$lte": lastSeenRatingValue } 
        },
        "num": 25
    }},
    { "$sort": { "distance": 1, "rating": -1 }
])

Essentially that is going to be a lot better, but it won't help you with jumps to "page" 25 for example. Not without a lot more effort in working that out.