summaryrefslogtreecommitdiff
path: root/jstests/core/timeseries/timeseries_min_max.js
blob: 71887d4f00b396303d11dde57da68141bd31969a (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
/**
 * Tests that a time-series bucket's control.min and control.max accurately reflect the minimum and
 * maximum values inserted into the bucket.
 *
 * @tags: [
 *   does_not_support_stepdowns,
 *   does_not_support_transactions,
 * ]
 */
(function() {
"use strict";

load("jstests/core/timeseries/libs/timeseries.js");  // For 'TimeseriesTest'.

TimeseriesTest.run((insert) => {
    const collNamePrefix = 'timeseries_min_max_';

    const timeFieldName = 'time';
    const metaFieldName = 'meta';

    let collCount = 0;
    let coll;
    let bucketsColl;

    const clearColl = function() {
        coll = db.getCollection(collNamePrefix + collCount++);
        bucketsColl = db.getCollection('system.buckets.' + coll.getName());

        coll.drop();

        const timeFieldName = 'time';
        assert.commandWorked(db.createCollection(
            coll.getName(), {timeseries: {timeField: timeFieldName, metaField: metaFieldName}}));
        assert.contains(bucketsColl.getName(), db.getCollectionNames());
    };
    clearColl();

    const runTest = function(doc, expectedMin, expectedMax) {
        doc[timeFieldName] = ISODate();
        assert.commandWorked(insert(coll, doc));

        // Find the _id value of the measurement just inserted.
        const matchingMeasurements = coll.find(doc).toArray();
        assert.eq(matchingMeasurements.length, 1);
        const measurementId = matchingMeasurements[0]._id;

        // Find the bucket the measurement belongs to.
        const bucketDocs = bucketsColl
                               .find({
                                   $and: [
                                       {"control.min._id": {$lte: measurementId}},
                                       {"control.max._id": {$gte: measurementId}}
                                   ]
                               },
                                     {
                                         'control.min._id': 0,
                                         'control.max._id': 0,
                                         ['control.min.' + timeFieldName]: 0,
                                         ['control.max.' + timeFieldName]: 0
                                     })
                               .toArray();
        assert.eq(bucketDocs.length, 1);

        const bucketDoc = bucketDocs[0];
        jsTestLog('Bucket collection document: ' + tojson(bucketDoc));

        assert.docEq(
            expectedMin, bucketDoc.control.min, 'invalid min in bucket: ' + tojson(bucketDoc));
        assert.docEq(
            expectedMax, bucketDoc.control.max, 'invalid max in bucket: ' + tojson(bucketDoc));
    };

    // Empty objects are considered.
    runTest({a: {}}, {a: {}}, {a: {}});
    runTest({a: {x: {}}}, {a: {x: {}}}, {a: {x: {}}});
    runTest({a: {x: {y: 1}}}, {a: {x: {y: 1}}}, {a: {x: {y: 1}}});
    runTest({a: {x: {}}}, {a: {x: {y: 1}}}, {a: {x: {y: 1}}});
    clearColl();

    // The metadata field is not considered.
    runTest({meta: 1}, {}, {});
    clearColl();

    // Objects and arrays are updated element-wise.
    runTest(
        {a: {x: 1, y: 2}, b: [1, 2]}, {a: {x: 1, y: 2}, b: [1, 2]}, {a: {x: 1, y: 2}, b: [1, 2]});
    runTest(
        {a: {x: 2, y: 1}, b: [2, 1]}, {a: {x: 1, y: 1}, b: [1, 1]}, {a: {x: 2, y: 2}, b: [2, 2]});

    // Schema change, new bucket.
    runTest({a: {x: {z: [3, 4]}}, b: [{x: 3, y: 4}]},
            {a: {x: {z: [3, 4]}}, b: [{x: 3, y: 4}]},
            {a: {x: {z: [3, 4]}}, b: [{x: 3, y: 4}]});
    // Sub-objects and arrays also updated element-wise
    runTest({a: {x: {z: [4, 3]}}, b: [{x: 4, y: 3}, 3, 1]},
            {a: {x: {z: [3, 3]}}, b: [{x: 3, y: 3}, 3, 1]},
            {a: {x: {z: [4, 4]}}, b: [{x: 4, y: 4}, 3, 1]});
    clearColl();

    // If the two types being compared are not both objects or both arrays, we'll detect a schema
    // change and open a new bucket.
    runTest({a: 1}, {a: 1}, {a: 1});
    runTest({a: {b: 1}}, {a: {b: 1}}, {a: {b: 1}});
    runTest({a: []}, {a: []}, {a: []});
    runTest({a: [5]}, {a: [5]}, {a: [5]});
    clearColl();

    // We correctly detect schema changes from Array to Object and back
    runTest({a: []}, {a: []}, {a: []});
    runTest({a: {b: 1}}, {a: {b: 1}}, {a: {b: 1}});
    runTest({a: [5]}, {a: [5]}, {a: [5]});
    clearColl();

    // Sparse measurements only affect the min/max for the fields present.
    runTest({a: 1, c: 1}, {a: 1, c: 1}, {a: 1, c: 1});
    runTest({b: 2}, {a: 1, b: 2, c: 1}, {a: 1, b: 2, c: 1});
    runTest({c: 3, d: 3}, {a: 1, b: 2, c: 1, d: 3}, {a: 1, b: 2, c: 3, d: 3});
    clearColl();

    // We correctly handle canonical type
    runTest({a: Number(1.5)}, {a: Number(1.5)}, {a: Number(1.5)});
    runTest({a: NumberLong(2)}, {a: Number(1.5)}, {a: NumberLong(2)});
    runTest({a: NumberInt(1)}, {a: NumberInt(1)}, {a: NumberLong(2)});
    runTest({a: NumberDecimal(2.5)}, {a: NumberInt(1)}, {a: NumberDecimal(2.5)});
    runTest({a: Number(0.5)}, {a: Number(0.5)}, {a: NumberDecimal(2.5)});
});
})();