summaryrefslogtreecommitdiff
path: root/jstests/aggregation/expressions/percentile_expression_syntax.js
blob: a156b599eec7e5f1798e9e01a75856bb312a9b75 (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
/**
 * Tests for the $percentile expression syntax.
 * @tags: [
 *   requires_fcv_70,
 *   featureFlagApproxPercentiles
 * ]
 */
(function() {
"use strict";

load("jstests/aggregation/extras/utils.js");

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

assert.commandWorked(coll.insert([{_id: 0, k1: 3, k2: 2, k3: "hi", k4: [1, 2, 3]}]));

/**
 * Tests to check that invalid $percentile specifications are rejected.
 */
function assertInvalidSyntax(percentileSpec, msg) {
    assert.commandFailed(
        coll.runCommand("aggregate", {pipeline: [{$project: {p: percentileSpec}}], cursor: {}}),
        msg);
}

assertInvalidSyntax({$percentile: 0.5}, "Should fail if $percentile is not an object");

assertInvalidSyntax({$percentile: {input: ["$k1", "$k2"], method: "approximate"}},
                    "Should fail if $percentile is missing 'p' field");

assertInvalidSyntax({$percentile: {p: [0.5], method: "approximate"}},
                    "Should fail if $percentile is missing 'input' field");

assertInvalidSyntax({$percentile: {p: [0.5], input: "$k1"}},
                    "Should fail if $percentile is missing 'method' field");

assertInvalidSyntax(
    {$percentile: {p: [0.5], input: ["$k1", "$k2"], method: "approximate", extras: 42}},
    "Should fail if $percentile contains an unexpected field");

assertInvalidSyntax({$percentile: {p: 0.5, input: ["$k1", "$k2"], method: "approximate"}},
                    "Should fail if 'p' field in $percentile isn't array");

assertInvalidSyntax({$percentile: {p: [], input: ["$k1", "$k2"], method: "approximate"}},
                    "Should fail if 'p' field in $percentile is an empty array");

assertInvalidSyntax({$percentile: {p: [0.5], input: [], method: "approximate"}},
                    "Should fail if 'input' field in $percentile is an empty array");

assertInvalidSyntax(
    {$percentile: {p: [0.5, "foo"], input: ["$k1", "$k2"], method: "approximate"}},
    "Should fail if 'p' field in $percentile is an array with a non-numeric element");

assertInvalidSyntax(
    {$percentile: {p: [0.5, 10], input: ["$k1", "$k2"], method: "approximate"}},
    "Should fail if 'p' field in $percentile is an array with any value outside of [0, 1] range");

assertInvalidSyntax({$percentile: {p: [0.5, 0.7], input: ["$k1", "$k2"], method: 42}},
                    "Should fail if 'method' field isn't a string");

assertInvalidSyntax({$percentile: {p: [0.5, 0.7], input: ["$k1", "$k2"], method: "fancy"}},
                    "Should fail if 'method' isn't one of _predefined_ strings");

/**
 * Tests for $median. $median desugars to $percentile with the field p:[0.5] added, and therefore
 * has similar syntax to $percentile.
 */

assertInvalidSyntax({$median: {p: [0.5], input: "$k4", method: "approximate"}},
                    "Should fail if 'p' is defined");

assertInvalidSyntax({$median: {method: "approximate"}},
                    "Should fail if $median is missing 'input' field");

assertInvalidSyntax({$median: {input: [], method: "approximate"}},
                    "Should fail if $median has an empty array as its 'input' field");

assertInvalidSyntax({$median: {input: ["$k1", "$k2"]}},
                    "Should fail if $median is missing 'method' field");

assertInvalidSyntax({$median: {input: "$x", method: "approximate", extras: 42}},
                    "Should fail if $median contains an unexpected field");

/**
 * Test that valid $percentile specifications are accepted. The results, i.e. semantics, are
 * tested elsewhere and would cover all of the cases below, we are providing them here
 * nonetheless for completeness.
 */
function assertValidSyntax(percentileSpec, msg) {
    assert.commandWorked(
        coll.runCommand("aggregate", {pipeline: [{$project: {p: percentileSpec}}], cursor: {}}),
        msg);
}

assertValidSyntax(
    {$percentile: {p: [0.0, 0.0001, 0.5, 0.995, 1.0], input: ["$k1"], method: "approximate"}},
    "Should be able to specify an array of percentiles");

assertValidSyntax({$percentile: {p: [0.5, 0.9], input: ["k3"], method: "approximate"}},
                  "Non-numeric expressions in input array should be gracefully ignored");

assertValidSyntax({$percentile: {p: [0.5], input: "$k4", method: "approximate"}},
                  "Should work if 'input' field in $percentile is a single expression");

/**
 * Tests for $median. $median desugars to $percentile with the field p:[0.5] added.
 */

assertValidSyntax({$median: {input: "$k4", method: "approximate"}},
                  "Simple base case for $median with single expression input field");

assertValidSyntax({$median: {input: ["$k1", "$k2"], method: "approximate"}},
                  "Simple base case for $median with array input field");
})();