summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/hybrid_multikey.js
blob: ba0154708f4b98d6aeb62d30dc5855562cca07cf (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
132
133
134
135
136
137
138
/**
 * Tests that hybrid index builds result in a consistent state and correct multikey behavior across
 * various index types.
 */
load("jstests/noPassthrough/libs/index_build.js");
load("jstests/libs/analyze_plan.js");  // For getWinningPlan to analyze explain() output.

(function() {
"use strict";

const conn = MongoRunner.runMongod();
const dbName = 'test';
const collName = 'foo';
const testDB = conn.getDB(dbName);
const testColl = testDB[collName];

const runTest = (config) => {
    // Populate the collection.
    const nDocs = 10;
    testColl.drop();
    for (let i = 0; i < nDocs; i++) {
        assert.commandWorked(testColl.insert({x: i}));
    }

    jsTestLog("Testing: " + tojson(config));

    // Prevent the index build from completing.
    IndexBuildTest.pauseIndexBuilds(conn);

    // Start the index build and wait for it to start.
    const indexName = "test_index";
    let indexOptions = config.indexOptions || {};
    indexOptions.name = indexName;
    const awaitIndex = IndexBuildTest.startIndexBuild(
        conn, testColl.getFullName(), config.indexSpec, indexOptions);
    IndexBuildTest.waitForIndexBuildToStart(testDB, collName, indexName);

    // Perform writes while the index build is in progress.
    assert.commandWorked(testColl.insert({x: nDocs + 1}));
    assert.commandWorked(testColl.update({x: 0}, {x: -1}));
    assert.commandWorked(testColl.remove({x: -1}));

    let extraDocs = config.extraDocs || [];
    extraDocs.forEach((doc) => {
        assert.commandWorked(testColl.insert(doc));
    });

    // Allow the index build to complete.
    IndexBuildTest.resumeIndexBuilds(conn);
    awaitIndex();

    // Ensure the index is usable and has the expected multikey state.
    let explain = testColl.find({x: 1}).hint(indexName).explain();
    let plan = getWinningPlan(explain.queryPlanner);
    assert.eq("FETCH", plan.stage, explain);
    assert.eq("IXSCAN", plan.inputStage.stage, explain);
    assert.eq(
        config.expectMultikey,
        plan.inputStage.isMultiKey,
        `Index multikey state "${plan.inputStage.isMultiKey}" was not "${config.expectMultikey}"`);

    const validateRes = assert.commandWorked(testColl.validate());
    assert(validateRes.valid, validateRes);
};

// Hashed indexes should never be multikey.
runTest({
    indexSpec: {x: 'hashed'},
    expectMultikey: false,
});

// Wildcard indexes are not multikey unless they have multikey documents.
runTest({
    indexSpec: {'$**': 1},
    expectMultikey: false,
});
runTest({
    indexSpec: {'$**': 1},
    expectMultikey: true,
    extraDocs: [{x: [1, 2, 3]}],
});

// '2dsphere' indexes are not multikey unless they have multikey documents.
runTest({
    indexSpec: {x: 1, b: '2dsphere'},
    expectMultikey: false,
});

// '2dsphere' indexes are automatically sparse. If we insert a document where 'x' is multikey, even
// though 'b' is omitted, the index is still considered multikey. See SERVER-39705.
runTest({
    indexSpec: {x: 1, b: '2dsphere'},
    extraDocs: [
        {x: [1, 2]},
    ],
    expectMultikey: true,
});

// Test that a partial index is not multikey when a multikey document is not indexed.
runTest({
    indexSpec: {x: 1},
    indexOptions: {partialFilterExpression: {a: {$gt: 0}}},
    extraDocs: [
        {x: [0, 1, 2], a: 0},
    ],
    expectMultikey: false,
});

// Test that a partial index is multikey when a multikey document is indexed.
runTest({
    indexSpec: {x: 1},
    indexOptions: {partialFilterExpression: {a: {$gt: 0}}},
    extraDocs: [
        {x: [0, 1, 2], a: 1},
    ],
    expectMultikey: true,
});

// Text indexes are not multikey unless documents make them multikey.
runTest({
    indexSpec: {x: 'text'},
    extraDocs: [
        {x: 'hello'},
    ],
    expectMultikey: false,
});

// Text indexes can be multikey if a field has multiple words.
runTest({
    indexSpec: {x: 'text'},
    extraDocs: [
        {x: 'hello world'},
    ],
    expectMultikey: true,
});

MongoRunner.stopMongod(conn);
})();