Is it possible to reverse the traversing order of

2019-09-07 06:56发布

问题:

Collection c has hundred thousands of documents, and every document has two fields (x and y). Below are some examples.

{ "_id" : ObjectId("53fecab45ae4ec5280a0736f"), "x" : 1, "y" : 1 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07370"), "x" : 2, "y" : 1 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07371"), "x" : 3, "y" : 1 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07372"), "x" : 3, "y" : 2 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07373"), "x" : 3, "y" : 3 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07374"), "x" : 4, "y" : 2 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07376"), "x" : 5, "y" : 1 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07377"), "x" : 5, "y" : 3 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07378"), "x" : 5, "y" : 5 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07379"), "x" : 6, "y" : 2 }
{ "_id" : ObjectId("53fecab45ae4ec5280a0737a"), "x" : 6, "y" : 4 }
{ "_id" : ObjectId("53fecab45ae4ec5280a0737b"), "x" : 6, "y" : 4 }
{ "_id" : ObjectId("53fecab45ae4ec5280a0737c"), "x" : 7, "y" : 3 }
{ "_id" : ObjectId("53fecab45ae4ec5280a0737d"), "x" : 8, "y" : 8 }
{ "_id" : ObjectId("53fecab45ae4ec5280a0737e"), "x" : 9, "y" : 3 }
{ "_id" : ObjectId("53fecab45ae4ec5280a0737f"), "x" : 9, "y" : 1 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07380"), "x" : 10, "y" : 11 }

The values of x and y is number type, but random. Now I want to find no more than 3 documents whose values are least but larger than or equal to {x:5, y:3}. Regulation to compare two documents by function:

int compareDocument(doc1, doc2) {
    var result;
    if (doc1.x > doc2.x) {
        result = 1;
    } else if (doc1.x == doc2.x) {
        if (doc1.y > doc2.y) {
            result = 1;
        } else if (doc1.y == doc2.y) {
            result = 0;
        } else {
            result = -1;
        }
    } else {
        result = -1;
    }
    return result;
}

It must be executed in high performance. The method I have is:

> db.c.ensureIndex({x:1,y:1},{name:"asc"});
> db.c.find().hint("asc").min({x:5,y:3}).limit(3);
{ "_id" : ObjectId("53fecab45ae4ec5280a07377"), "x" : 5, "y" : 3 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07378"), "x" : 5, "y" : 5 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07379"), "x" : 6, "y" : 2 }
> db.c.find().hint("asc").min({x:5,y:3}).limit(3).explain();
{
        "cursor" : "BtreeCursor asc",
        "isMultiKey" : false,
        "n" : 3,
        "nscannedObjects" : 3,
        "nscanned" : 3,
        "nscannedObjectsAllPlans" : 3,
        "nscannedAllPlans" : 3,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {
                "start" : {
                        "x" : 5,
                        "y" : 3
                },
                "end" : {
                        "x" : {
                                "$maxElement" : 1
                        },
                        "y" : {
                                "$maxElement" : 1
                        }
                }
        },
        "server" : "Mars-PC:27017"
}

Above only 3 documents scanned.

Again, I want to find no more than 3 documents whose values are largest but less than or equal to {x:5, y:3}. Is it possible to do this by using above index asc, or something else without creating new index, but keep high perforance?

I can't find it. What I can do is to create another similar index to fultill it.

> db.c.ensureIndex({x:-1, y:-1},{name:"desc"});
> db.c.find().hint("desc").min({x:5, y:3}).limit(3);
{ "_id" : ObjectId("53fecab45ae4ec5280a07377"), "x" : 5, "y" : 3 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07376"), "x" : 5, "y" : 1 }
{ "_id" : ObjectId("53fecab45ae4ec5280a07374"), "x" : 4, "y" : 2 }
> db.c.find().hint("desc").min({x:5, y:3}).limit(3).explain();
{
        "cursor" : "BtreeCursor desc",
        "isMultiKey" : false,
        "n" : 3,
        "nscannedObjects" : 3,
        "nscanned" : 3,
        "nscannedObjectsAllPlans" : 3,
        "nscannedAllPlans" : 3,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {
                "start" : {
                        "x" : 5,
                        "y" : 3
                },
                "end" : {
                        "x" : {
                                "$minElement" : 1
                        },
                        "y" : {
                                "$minElement" : 1
                        }
                }
        },
        "server" : "Mars-PC:27017"
}