mongo/jstests/noPassthrough/query/regex_collations.js

239 lines
8.3 KiB
JavaScript

/**
* Tests how regex filters are satisfied with different index + query collation combinations
*/
import {assertDropAndRecreateCollection} from "jstests/libs/collection_drop_recreate.js";
import {getPlanStage, getWinningPlanFromExplain, isCollscan, isIxscan} from "jstests/libs/query/analyze_plan.js";
const conn = MongoRunner.runMongod();
const db = conn.getDB(jsTestName());
function assertIXScanTightBounds(explain) {
const winningPlan = getWinningPlanFromExplain(explain);
assert(isIxscan(db, winningPlan), explain);
const ixscan = getPlanStage(winningPlan, "IXSCAN");
assert.doesNotContain(
'["", {})',
ixscan.indexBounds.value,
`Unexpected full IXSCAN plan!\n${JSON.stringify(explain, null, 2)}`,
);
}
function assertFullIXScan(explain) {
const winningPlan = getWinningPlanFromExplain(explain);
assert(isIxscan(db, winningPlan), explain);
const ixscan = getPlanStage(winningPlan, "IXSCAN");
assert(
ixscan.indexBounds.value[0] == '["", {})' || ixscan.indexBounds.value[0] == "[CollationKey(0x), {})",
`Expected full IXSCAN plan!\n${JSON.stringify(explain, null, 2)}`,
);
}
const collName = jsTestName();
assertDropAndRecreateCollection(db, collName);
const collection = db[collName];
assert.commandWorked(collection.insertMany([{value: "c"}, {value: "d"}, {value: "ç"}])); // Simple collation sorting would produce c, d, ç, French would produce c, ç, d
{
jsTestLog("Testing regex filters with simple collation on index with simple collation too...");
assert.commandWorked(collection.createIndex({value: 1}));
jsTestLog("Testing that prefix regexes use a bounded index scan plan");
let results = collection.find({value: {$regex: "^c"}}, {_id: 0, value: 1}).toArray();
assert.sameMembers([{value: "c"}], results);
let explain = collection.find({value: {$regex: "^c"}}).explain();
assertIXScanTightBounds(explain);
jsTestLog("Testing that non prefix regexes use full index scan plan");
results = collection.find({value: {$regex: "c"}}, {_id: 0, value: 1}).toArray();
assert.sameMembers([{value: "c"}], results);
explain = collection.find({value: {$regex: "c"}}).explain();
assertFullIXScan(explain);
}
{
jsTestLog("Testing regex filters with not simple collation on index with simple collation...");
assert.commandWorked(collection.dropIndex({value: 1}));
assert.commandWorked(collection.createIndex({value: 1}));
jsTestLog("Testing that prefix regexes use a bounded index scan plan");
let results = collection
.find({value: {$regex: "^c"}}, {_id: 0, value: 1})
.collation({locale: "fr"})
.toArray();
assert.sameMembers([{value: "c"}], results);
let explain = collection
.find({value: {$regex: "^c"}})
.collation({locale: "fr"})
.explain();
assertIXScanTightBounds(explain);
jsTestLog("Testing that non prefix regexes use full index scan plan");
results = collection
.find({value: {$regex: "c"}}, {_id: 0, value: 1})
.collation({locale: "fr"})
.toArray();
assert.sameMembers([{value: "c"}], results);
explain = collection
.find({value: {$regex: "c"}})
.collation({locale: "fr"})
.explain();
assertFullIXScan(explain);
}
{
jsTestLog("Testing regex filters with simple collation on index with not simple collation...");
assert.commandWorked(collection.dropIndex({value: 1}));
assert.commandWorked(collection.createIndex({value: 1}, {collation: {locale: "fr"}}));
{
assert.commandWorked(
db.adminCommand({
setParameter: 1,
internalQueryPlannerIgnoreIndexWithCollationForRegex: 0,
}),
);
jsTestLog("Testing that prefix regexes use full index scan plan");
let results = collection.find({value: {$regex: "^c"}}, {_id: 0, value: 1}).toArray();
assert.sameMembers([{value: "c"}], results);
let explain = collection.find({value: {$regex: "^c"}}).explain();
assertFullIXScan(explain);
jsTestLog("Testing that non prefix regexes use full index scan plan");
results = collection.find({value: {$regex: "c"}}, {_id: 0, value: 1}).toArray();
assert.sameMembers([{value: "c"}], results);
explain = collection.find({value: {$regex: "c"}}).explain();
assertFullIXScan(explain);
}
{
jsTestLog("Ignoring now the index with collation for regexes...");
assert.commandWorked(
db.adminCommand({
setParameter: 1,
internalQueryPlannerIgnoreIndexWithCollationForRegex: 1,
}),
);
jsTestLog("Testing that prefix regexes use collscan");
let results = collection.find({value: {$regex: "^c"}}, {_id: 0, value: 1}).toArray();
assert.sameMembers([{value: "c"}], results);
let explain = collection.find({value: {$regex: "^c"}}).explain();
let winningPlan = getWinningPlanFromExplain(explain);
assert(isCollscan(db, winningPlan), `Expected COLLSCAN plan\n${JSON.stringify(explain, null, 2)}`);
jsTestLog("Testing that non prefix regexes use collscan");
results = collection.find({value: {$regex: "c"}}, {_id: 0, value: 1}).toArray();
assert.sameMembers([{value: "c"}], results);
explain = collection.find({value: {$regex: "c"}}).explain();
winningPlan = getWinningPlanFromExplain(explain);
assert(isCollscan(db, winningPlan), `Expected COLLSCAN plan\n${JSON.stringify(explain, null, 2)}`);
}
}
{
jsTestLog("Testing regex filters with not simple collation on index with not simple collation too...");
assert.commandWorked(collection.dropIndex({value: 1}));
assert.commandWorked(collection.createIndex({value: 1}, {collation: {locale: "fr"}}));
{
assert.commandWorked(
db.adminCommand({
setParameter: 1,
internalQueryPlannerIgnoreIndexWithCollationForRegex: 0,
}),
);
jsTestLog("Testing that prefix regexes use full index scan plan");
let results = collection
.find({value: {$regex: "^c"}}, {_id: 0, value: 1})
.collation({locale: "fr"})
.toArray();
assert.sameMembers([{value: "c"}], results);
let explain = collection
.find({value: {$regex: "^c"}})
.collation({locale: "fr"})
.explain();
assertFullIXScan(explain);
jsTestLog("Testing that non prefix regexes use full index scan plan");
results = collection
.find({value: {$regex: "c"}}, {_id: 0, value: 1})
.collation({locale: "fr"})
.toArray();
assert.sameMembers([{value: "c"}], results);
explain = collection
.find({value: {$regex: "c"}})
.collation({locale: "fr"})
.explain();
assertFullIXScan(explain);
}
{
jsTestLog("Ignoring now the index with collation for regexes...");
assert.commandWorked(
db.adminCommand({
setParameter: 1,
internalQueryPlannerIgnoreIndexWithCollationForRegex: 1,
}),
);
jsTestLog("Testing that prefix regexes use collscan");
let results = collection
.find({value: {$regex: "^c"}}, {_id: 0, value: 1})
.collation({locale: "fr"})
.toArray();
assert.sameMembers([{value: "c"}], results);
let explain = collection
.find({value: {$regex: "^c"}})
.collation({locale: "fr"})
.explain();
let winningPlan = getWinningPlanFromExplain(explain);
assert(isCollscan(db, winningPlan), `Expected COLLSCAN plan\n${JSON.stringify(explain, null, 2)}`);
jsTestLog("Testing that non prefix regexes use collscan");
results = collection
.find({value: {$regex: "c"}}, {_id: 0, value: 1})
.collation({locale: "fr"})
.toArray();
assert.sameMembers([{value: "c"}], results);
explain = collection
.find({value: {$regex: "c"}})
.collation({locale: "fr"})
.explain();
winningPlan = getWinningPlanFromExplain(explain);
assert(isCollscan(db, winningPlan), `Expected COLLSCAN plan\n${JSON.stringify(explain, null, 2)}`);
}
}
MongoRunner.stopMongod(conn);