summaryrefslogtreecommitdiff
path: root/jstests/core/hashed_index_collation.js
blob: ead8b69bbfc3e7a9227a97c710180d0ed96b1da4 (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
/**
 * Tests to verify that hashed indexes obey collation rules.
 *
 * @tags: [
 *   assumes_unsharded_collection,
 * ]
 */
(function() {
"use strict";

load("jstests/aggregation/extras/utils.js");  // For arrayEq().
load("jstests/libs/analyze_plan.js");         // For assertStagesForExplainOfCommand().

const coll = db.hashed_index_collation;
coll.drop();
const collation = {
    locale: "en_US",
    strength: 1
};

/**
 * Runs find command with the 'filter' and 'projection' provided in the input, then validates
 * that the output returned matches 'expectedOutput'. Also runs explain() command on the same find
 * command, validates that all the 'expectedStages' are present in the plan returned and all the
 * 'stagesNotExpected' are not present in the plan.
 */
function validateFindCmdOutputAndPlan(
    {filter, projection = {}, expectedOutput, expectedStages, stagesNotExpected}) {
    const cmdObj =
        {find: coll.getName(), filter: filter, projection: projection, collation: collation};
    const res = assert.commandWorked(coll.runCommand(cmdObj));
    const ouputArray = new DBCommandCursor(coll.getDB(), res).toArray();

    // We ignore the order since hashed index order is not predictable.
    assert(arrayEq(expectedOutput, ouputArray), ouputArray);

    assertStagesForExplainOfCommand({
        coll: coll,
        cmdObj: cmdObj,
        expectedStages: expectedStages,
        stagesNotExpected: stagesNotExpected
    });
}

// Verify that index creation works for compound hashed index with collation, when hashed field is a
// prefix.
assert.commandWorked(
    coll.createIndex({"a.b": "hashed", "a.c": 1, "a.e": -1}, {collation: collation}));

// Insert a series of documents whose fieldnames and values differ only by case.
assert.commandWorked(coll.insert({_id: 0, a: {b: "string", c: "STRING", e: 5}}));
assert.commandWorked(coll.insert({_id: 1, a: {b: "STRING", c: "string", e: 5}}));
assert.commandWorked(coll.insert({_id: 2, A: {B: "string", C: "STRING", E: 5}}));
assert.commandWorked(coll.insert({_id: 3, A: {B: "StrinG", C: "sTRINg", E: 5}}));

// Verify that hashed field can use index in the presence of collation. Also verify that only
// the document's values, not the field names, adhere to the case-insensitive collation.
validateFindCmdOutputAndPlan({
    filter: {"a.b": "string", "a.c": "string"},
    expectedStages: ["IXSCAN", "FETCH"],
    expectedOutput: [
        {_id: 0, a: {b: "string", c: "STRING", e: 5}},
        {_id: 1, a: {b: "STRING", c: "string", e: 5}}
    ]
});

// Verify that the field names doesn't adhere to the case-insensitive collation and uses collection
// scan if the case doesn't match.
validateFindCmdOutputAndPlan({
    filter: {"A.B": "string"},
    expectedStages: ["COLLSCAN"],
    expectedOutput: [
        {_id: 2, A: {B: "string", C: "STRING", E: 5}},
        {_id: 3, A: {B: "StrinG", C: "sTRINg", E: 5}}
    ]
});

// Verify that $or query with collation returns correct results.
validateFindCmdOutputAndPlan({
    filter: {$or: [{"a.b": "string_1"}, {"a.b": "string", "a.c": "string"}]},
    expectedStages: ["OR"],
    stagesNotExpected: ["COLLSCAN"],  // Verify that both the OR stages use index scan.
    expectedOutput: [
        {_id: 0, a: {b: "string", c: "STRING", e: 5}},
        {_id: 1, a: {b: "STRING", c: "string", e: 5}}
    ]
});

/**
 * When hashed field is not a prefix.
 */
assert.commandWorked(coll.dropIndexes());
assert.commandWorked(
    coll.createIndex({"a.b": 1, "a.c": "hashed", "a.e": -1}, {collation: collation}));

// Hashed indexes with collation can be covered, if the query predicate restrict strings from being
// returned.
validateFindCmdOutputAndPlan({
    filter: {"a.b": "string", "a.e": {$type: "number"}},
    projection: {"a.e": 1, _id: 0},
    expectedStages: ["IXSCAN"],
    stagesNotExpected: ["FETCH"],
    expectedOutput: [{a: {e: 5}}, {a: {e: 5}}]
});

// Hashed indexes with collation cannot be covered, if the query predicate doesn't restrict strings
// from being returneds.
validateFindCmdOutputAndPlan({
    filter: {"a.b": "string"},
    projection: {"a.e": 1, _id: 0},
    expectedStages: ["IXSCAN", "FETCH"],
    expectedOutput: [{a: {e: 5}}, {a: {e: 5}}]
});
})();