summaryrefslogtreecommitdiff
path: root/jstests/core/indexes_multiple_commands.js
blob: ea57a0392befccdad84a3b2e4d93cb069b737a70 (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Cannot implicitly shard accessed collections because of collection existing when none
// expected.
// @tags: [
//   assumes_no_implicit_collection_creation_after_drop,
// ]

// Test that commands behave correctly under the presence of multiple indexes with the same key
// pattern.
(function() {
'use strict';

var coll = db.indexes_multiple_commands;
var usingWriteCommands = db.getMongo().writeMode() === "commands";

/**
 * Assert that the result of the index creation ('cmd') indicates that 'numIndexes' were
 * created.
 *
 * If omitted, 'numIndexes' defaults to 1.
 *
 * @param cmd {Function} A function to execute that attempts to create indexes.
 * @param numIndexes {Number} The expected number of indexes that cmd creates.
 */
function assertIndexesCreated(cmd, numIndexes) {
    var cmdResult;

    if (typeof numIndexes === "undefined") {
        numIndexes = 1;
    }

    if (usingWriteCommands) {
        cmdResult = cmd();
        if (numIndexes == 0) {
            assert.commandFailedWithCode(cmdResult, ErrorCodes.IndexOptionsConflict);
            return;
        }

        assert.commandWorked(cmdResult);
        var isShardedNS = cmdResult.hasOwnProperty('raw');
        if (isShardedNS) {
            cmdResult = cmdResult['raw'][Object.getOwnPropertyNames(cmdResult['raw'])[0]];
        }
        assert.eq(
            cmdResult.numIndexesAfter - cmdResult.numIndexesBefore, numIndexes, tojson(cmdResult));
    } else {
        var nIndexesBefore = coll.getIndexes().length;
        cmdResult = cmd();
        if (numIndexes == 0) {
            assert.commandFailedWithCode(cmdResult, ErrorCodes.IndexOptionsConflict);
            return;
        }

        assert.commandWorked(cmdResult);
        var nIndexesAfter = coll.getIndexes().length;
        assert.eq(nIndexesAfter - nIndexesBefore, numIndexes, tojson(coll.getIndexes()));
    }
}

/**
 * Assert that the result of the index create command indicates no indexes were created since
 * the indexes were the same (collation and key pattern matched).
 *
 * (Index creation succeeds if none are created, as long as no options conflict.)
 *
 * @param {Function} A function to execute that attempts to create indexes.
 */
function assertIndexNotCreated(cmd) {
    assertIndexesCreated(cmd, 0);
}

coll.drop();
assert.commandWorked(db.createCollection(coll.getName()));

// Test that multiple indexes with the same key pattern and different collation can be created.

assertIndexesCreated(() => coll.createIndex({a: 1}, {name: "a_1"}));
// The requested index already exists, but with a different name, so the index is not created.
assertIndexNotCreated(() => coll.createIndex({a: 1}, {name: "a_1:1"}));

// Indexes with different collations and the same key pattern are allowed if the names are
// not the same.
assertIndexesCreated(() => coll.createIndex({a: 1}, {name: "fr", collation: {locale: "fr"}}));
assertIndexesCreated(() => coll.createIndex({a: 1}, {name: "en_US", collation: {locale: "en_US"}}));

// The requested index doesn't yet exist, but the name is used, so this command fails.
assert.commandFailed(coll.createIndex({a: 1}, {name: "a_1", collation: {locale: "en_US"}}));

// The requested index already exists with a different name, so the index is not created.
assertIndexNotCreated(() => coll.createIndex({a: 1}, {name: "fr2", collation: {locale: "fr"}}));

// Options can differ on indexes with different collations.
assertIndexesCreated(
    () => coll.createIndex(
        {a: 1}, {name: "fr1_sparse", collation: {locale: "fr", strength: 1}, sparse: true}));

// The requested index already exists, but with different options, so the command fails.
assert.commandFailed(
    coll.createIndex({a: 1}, {name: "fr_sparse", collation: {locale: "fr"}, sparse: true}));

coll.drop();
assert.commandWorked(db.createCollection(coll.getName()));

// Multiple non-conflicting indexes can be created in one command.
var multipleCreate = () => db.runCommand({
    createIndexes: coll.getName(),
    indexes: [
        {key: {a: 1}, name: "en_US", collation: {locale: "en_US"}},
        {key: {a: 1}, name: "en_US_1", collation: {locale: "en_US", strength: 1}}
    ]
});
assertIndexesCreated(multipleCreate, 2);

// Cannot create another _id index.
assert.commandFailed(coll.createIndex({_id: 1}, {name: "other", collation: {locale: "fr"}}));

// Test that indexes must be dropped by name if the key pattern is ambiguous.
coll.drop();
assert.commandWorked(db.createCollection(coll.getName()));

// Create multiple indexes with the same key pattern and collation.
assertIndexesCreated(() => coll.createIndex({a: 1}, {name: "foo", collation: {locale: "en_US"}}));
assertIndexesCreated(
    () => coll.createIndex({a: 1}, {name: "bar", collation: {locale: "en_US", strength: 1}}));

// Indexes cannot be dropped by an ambiguous key pattern.
assert.commandFailed(coll.dropIndex({a: 1}));

// Indexes can be dropped by name.
assert.commandWorked(coll.dropIndex("foo"));
assert.commandWorked(coll.dropIndex("bar"));

// Test that hint behaves correctly in the presence of multiple indexes.
coll.drop();
assert.commandWorked(db.createCollection(coll.getName()));

assertIndexesCreated(() => coll.createIndex({a: 1}, {name: "sbc"}));
assertIndexesCreated(
    () => coll.createIndex({a: 1},
                           {name: "caseInsensitive", collation: {locale: "en_US", strength: 2}}));

assert.commandWorked(coll.insert([{a: "a"}, {a: "A"}, {a: 20}]));

// An ambiguous hint pattern fails.
assert.throws(() => coll.find({a: 1}).hint({a: 1}).itcount());
if (db.getMongo().useReadCommands()) {
    assert.throws(
        () => coll.find({a: 1}).collation({locale: "en_US", strength: 2}).hint({a: 1}).itcount());
}

// Index hint by name succeeds.
assert.eq(coll.find({a: "a"}).hint("sbc").itcount(), 1);
// A hint on an incompatible index does a whole index scan, and then filters using the query
// collation.
assert.eq(coll.find({a: "a"}).hint("caseInsensitive").itcount(), 1);
if (db.getMongo().useReadCommands()) {
    assert.eq(coll.find({a: "a"}).collation({locale: "en_US", strength: 2}).hint("sbc").itcount(),
              2);

    // A non-ambiguous index hint by key pattern is allowed, even if the collation doesn't
    // match.
    assertIndexesCreated(() => coll.createIndex({b: 1}, {collation: {locale: "fr"}}));
    assert.eq(coll.find({a: "a"}).collation({locale: "en_US"}).hint({b: 1}).itcount(), 1);
}
})();