mirror of https://github.com/mongodb/mongo
239 lines
8.3 KiB
JavaScript
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);
|