summaryrefslogtreecommitdiff
path: root/jstests/core/update_hint.js
blob: 9412f84d71ff845da2cf41692dfc8c20985fe6a9 (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
/**
 * Tests passing hint to the update command:
 *   - A bad argument to the hint option should raise an error.
 *   - The hint option should support both the name of the index, and the object spec of the
 *     index.
 *
 * @tags: [assumes_unsharded_collection, requires_non_retryable_writes]
 */

(function() {
"use strict";

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

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

assert.commandWorked(coll.insert({x: 1, y: 1}));
assert.commandWorked(coll.insert({x: 1, y: 1}));

assert.commandWorked(coll.createIndex({x: 1}));
assert.commandWorked(coll.createIndex({y: -1}));

function assertCommandUsesIndex(command, expectedHintKeyPattern) {
    const out = assert.commandWorked(coll.runCommand({explain: command}));
    const planStage = getPlanStage(out, "IXSCAN");
    assert.neq(null, planStage);
    assert.eq(planStage.keyPattern, expectedHintKeyPattern, tojson(planStage));
}

const updateCmd = {
    update: 'jstests_update_hint',
};

const updates = [{q: {x: 1}, u: {$set: {y: 1}}, hint: {x: 1}}];

updateCmd.updates = updates;
// Hint using a key pattern.
assertCommandUsesIndex(updateCmd, {x: 1});

// Hint using an index name.
updates[0].hint = 'y_-1';
assertCommandUsesIndex(updateCmd, {y: -1});

// Passing a hint should not use the idhack fast-path.
updates[0].q = {
    _id: 1
};
assertCommandUsesIndex(updateCmd, {y: -1});

// Create a sparse index.
assert.commandWorked(coll.createIndex({s: 1}, {sparse: true}));

// Hint should be respected, even on incomplete indexes.
updates[0].hint = {
    s: 1
};
assertCommandUsesIndex(updateCmd, {s: 1});

// Command should fail with incorrectly formatted hints.
updates[0].hint = 1;
assert.commandFailedWithCode(coll.runCommand(updateCmd), ErrorCodes.FailedToParse);
updates[0].hint = true;
assert.commandFailedWithCode(coll.runCommand(updateCmd), ErrorCodes.FailedToParse);

// Command should fail with hints to non-existent indexes.
updates[0].hint = {
    badHint: 1
};
assert.commandFailedWithCode(coll.runCommand(updateCmd), ErrorCodes.BadValue);

// Insert document that will be in the sparse index.
assert.commandWorked(coll.insert({x: 1, s: 0}));

// Update hinting a sparse index updates only the document in the sparse index.
updates[0] = {
    q: {},
    u: {$set: {s: 1}},
    hint: {s: 1}
};
assert.commandWorked(coll.runCommand(updateCmd));
assert.eq(1, coll.count({s: 1}));

// Update hinting a sparse index with upsert option can result in an insert even if the correct
// behaviour would be to update an existing document.
assert.commandWorked(coll.insert({x: 2}));
updates[0] = {
    q: {x: 2},
    u: {$set: {s: 1}},
    hint: {s: 1},
    upsert: true
};
assert.commandWorked(coll.runCommand(updateCmd));
assert.eq(2, coll.count({x: 2}));
})();