summaryrefslogtreecommitdiff
path: root/jstests/core/geonear_key.js
blob: e86b6b3a69921d6e969ff33f0f07408b027c1c1d (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
/**
 * Tests for the 'key' field accepted by the $geoNear aggregation stage.
 */
(function() {
"use strict";

load("jstests/libs/analyze_plan.js");

const coll = db.jstests_geonear_key;
coll.drop();

assert.writeOK(coll.insert({_id: 0, a: [1, 1]}));
assert.writeOK(coll.insert({_id: 1, a: [1, 2]}));
assert.writeOK(coll.insert({_id: 2, b: {c: [1, 1]}}));
assert.writeOK(coll.insert({_id: 3, b: {c: [1, 2]}}));
assert.writeOK(coll.insert({_id: 4, b: {d: [1, 1]}}));
assert.writeOK(coll.insert({_id: 5, b: {d: [1, 2]}}));

/**
 * Runs an aggregation consisting of a single $geoNear stage described by 'nearParams', and
 * returns the raw command result object. 'nearParams' consists of the parameters to the
 * $geoNear stage, but is expected to omit 'distanceField'.
 */
function runNearAgg(nearParams) {
    let nearAggParams = Object.extend({distanceField: "dist"}, nearParams);
    let nearAggStage = {$geoNear: nearAggParams};
    let aggCmd = {aggregate: coll.getName(), pipeline: [nearAggStage], cursor: {}};
    return db.runCommand(aggCmd);
}

/**
 * Runs the near described by 'nearParams' as a $geoNear aggregation and verifies that the
 * operation fails with 'code'.
 */
function assertGeoNearFails(nearParams, code) {
    assert.commandFailedWithCode(runNearAgg(nearParams), code);
}

/**
 * Runs the near described by 'nearParams' as a $geoNear aggregation and verifies that the
 * operation returns the _id values in 'expectedIds', in order.
 */
function assertGeoNearSucceedsAndReturnsIds(nearParams, expectedIds) {
    let aggResult = assert.commandWorked(runNearAgg(nearParams));
    let res = aggResult.cursor.firstBatch;
    let errfn = () => `expected ids ${tojson(expectedIds)}, but these documents were ` +
        `returned: ${tojson(res)}`;

    assert.eq(expectedIds.length, res.length, errfn);
    for (let i = 0; i < expectedIds.length; i++) {
        assert.eq(expectedIds[i], aggResult.cursor.firstBatch[i]._id, errfn);
    }
}

// Verify that $geoNear fails when the key field is not a string.
assertGeoNearFails({near: [0, 0], key: 1}, ErrorCodes.TypeMismatch);

// Verify that $geoNear fails when the key field the empty string.
assertGeoNearFails({near: [0, 0], key: ""}, ErrorCodes.BadValue);

// Verify that $geoNear fails when there are no eligible indexes.
assertGeoNearFails({near: [0, 0]}, ErrorCodes.IndexNotFound);

// Verify that the query system raises an error when an index is specified that doesn't exist.
assertGeoNearFails({near: [0, 0], key: "a"}, ErrorCodes.NoQueryExecutionPlans);

// Create a number of 2d and 2dsphere indexes.
assert.commandWorked(coll.createIndex({a: "2d"}));
assert.commandWorked(coll.createIndex({a: "2dsphere"}));
assert.commandWorked(coll.createIndex({"b.c": "2d"}));
assert.commandWorked(coll.createIndex({"b.d": "2dsphere"}));

// Verify that $geoNear fails when the index to use is ambiguous because of the absence of the
// key field.
assertGeoNearFails({near: [0, 0]}, ErrorCodes.IndexNotFound);

// Verify that the key field can correctly identify the index to use, when there is only a
// single geo index on the relevant path.
assertGeoNearSucceedsAndReturnsIds({near: [0, 0], key: "b.c"}, [2, 3]);
assertGeoNearSucceedsAndReturnsIds({near: {type: "Point", coordinates: [0, 0]}, key: "b.d"},
                                   [4, 5]);

// Verify that when the key path has both a 2d or 2dsphere index, the command still succeeds.
assertGeoNearSucceedsAndReturnsIds({near: [0, 0], key: "a"}, [0, 1]);
assertGeoNearSucceedsAndReturnsIds({near: [0, 0], spherical: true, key: "a"}, [0, 1]);
assertGeoNearSucceedsAndReturnsIds({near: {type: "Point", coordinates: [0, 0]}, key: "a"}, [0, 1]);
assertGeoNearSucceedsAndReturnsIds(
    {near: {type: "Point", coordinates: [0, 0]}, spherical: true, key: "a"}, [0, 1]);

// Verify that $geoNear fails when a GeoJSON point is used with a 'key' path that only has a 2d
// index. GeoJSON points can only be used for spherical geometry.
assertGeoNearFails({near: {type: "Point", coordinates: [0, 0]}, key: "b.c"},
                   ErrorCodes.NoQueryExecutionPlans);

// Verify that $geoNear fails when:
//  -- The only index available over the 'key' path is 2dsphere.
//  -- spherical=false.
//  -- The search point is a legacy coordinate pair.
assertGeoNearFails({near: [0, 0], key: "b.d"}, ErrorCodes.NoQueryExecutionPlans);
assertGeoNearFails({near: [0, 0], key: "b.d", spherical: false}, ErrorCodes.NoQueryExecutionPlans);
}());