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
|
/**
* Tests to verify that hashed indexes obey collation rules.
*
* The tags below are necessary because collation requires that we use read/write commands rather
* than legacy operations.
* @tags: [
* assumes_unsharded_collection,
* requires_find_command,
* sbe_incompatible,
* ]
*/
(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}}]
});
})();
|