summaryrefslogtreecommitdiff
path: root/jstests/aggregation/bugs/server7781.js
blob: 19700cc22024d44c38ec3cfca7695cc38852248c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// SERVER-7781 $geoNear pipeline stage
// @tags: [
//   requires_sharding,
//   requires_spawning_own_processes,
// ]
(function() {
'use strict';

load('jstests/libs/geo_near_random.js');
load('jstests/aggregation/extras/utils.js');

var coll = 'server7781';

db[coll].drop();
db[coll].insert({loc: [0, 0]});

// $geoNear is only allowed as the first stage in a pipeline, nowhere else.
assert.throws(
    () => db[coll].aggregate(
        [{$match: {x: 1}}, {$geoNear: {near: [1, 1], spherical: true, distanceField: 'dis'}}]));

const kDistanceField = "dis";
const kIncludeLocsField = "loc";

/**
 * Tests the output of the $geoNear command. This function expects a document with the following
 * fields:
 *   - 'geoNearSpec' is the specification for a $geoNear aggregation stage.
 *   - 'limit' is an integer limiting the number of pipeline results.
 *   - 'batchSize', if specified, is the batchSize to use for the aggregation.
 */
function testGeoNearStageOutput({geoNearSpec, limit, batchSize}) {
    const aggOptions = batchSize ? {batchSize: batchSize} : {};
    const result =
        db[coll].aggregate([{$geoNear: geoNearSpec}, {$limit: limit}], aggOptions).toArray();
    const errmsg = () => tojson(result);

    // Verify that we got the expected number of results.
    assert.eq(result.length, limit, errmsg);

    // Run though the array, checking for proper sort order and sane computed distances.
    result.reduce((lastDist, curDoc) => {
        const curDist = curDoc[kDistanceField];

        // Verify that distances are in increasing order.
        assert.lte(lastDist, curDist, errmsg);

        // Verify that the computed distance is correct.
        const computed = Geo.sphereDistance(geoNearSpec["near"], curDoc[kIncludeLocsField]);
        assert.close(computed, curDist, errmsg);
        return curDist;
    }, 0);
}

// We use this to generate points. Using a single global to avoid reseting RNG in each pass.
var pointMaker = new GeoNearRandomTest(coll);

function test(db, sharded, indexType) {
    db[coll].drop();

    if (sharded) {  // sharded setup
        var shards = [];
        var config = db.getSiblingDB("config");
        config.shards.find().forEach(function(shard) {
            shards.push(shard._id);
        });

        assert.commandWorked(
            db.adminCommand({shardCollection: db[coll].getFullName(), key: {rand: 1}}));
        for (var i = 1; i < 10; i++) {
            // split at 0.1, 0.2, ... 0.9
            assert.commandWorked(
                db.adminCommand({split: db[coll].getFullName(), middle: {rand: i / 10}}));
            db.adminCommand({
                moveChunk: db[coll].getFullName(),
                find: {rand: i / 10},
                to: shards[i % shards.length]
            });
        }

        assert.eq(config.chunks.count({'ns': db[coll].getFullName()}), 10);
    }

    // insert points
    var numPts = 10 * 1000;
    var bulk = db[coll].initializeUnorderedBulkOp();
    for (var i = 0; i < numPts; i++) {
        bulk.insert({rand: Math.random(), loc: pointMaker.mkPt()});
    }
    assert.writeOK(bulk.execute());

    assert.eq(db[coll].count(), numPts);

    db[coll].ensureIndex({loc: indexType});

    // Test $geoNear with spherical coordinates.
    testGeoNearStageOutput({
        geoNearSpec: {
            near: pointMaker.mkPt(0.25),
            distanceField: kDistanceField,
            includeLocs: kIncludeLocsField,
            spherical: true,
        },
        limit: 100
    });

    // Test $geoNear with an initial batchSize of 1.
    testGeoNearStageOutput({
        geoNearSpec: {
            near: pointMaker.mkPt(0.25),
            distanceField: kDistanceField,
            includeLocs: kIncludeLocsField,
            spherical: true,
        },
        limit: 70,
        batchSize: 1
    });
}

test(db, false, '2d');
test(db, false, '2dsphere');

var sharded = new ShardingTest({shards: 3, mongos: 1});
assert.commandWorked(sharded.s0.adminCommand({enablesharding: "test"}));
sharded.ensurePrimaryShard('test', sharded.shard1.shardName);

test(sharded.getDB('test'), true, '2d');
test(sharded.getDB('test'), true, '2dsphere');

sharded.stop();
})();