129 lines
5.3 KiB
JavaScript
129 lines
5.3 KiB
JavaScript
// Cannot implicitly shard accessed collections because the explain output from a mongod when run
|
|
// against a sharded collection is wrapped in a "shards" object with keys for each shard.
|
|
// @tags: [
|
|
// assumes_unsharded_collection,
|
|
// does_not_support_stepdowns,
|
|
// requires_fcv_47,
|
|
// ]
|
|
|
|
// Read ops tests for partial indexes.
|
|
|
|
// Include helpers for analyzing explain output.
|
|
load("jstests/libs/analyze_plan.js");
|
|
|
|
(function() {
|
|
"use strict";
|
|
let explain;
|
|
const coll = db.index_partial_read_ops;
|
|
coll.drop();
|
|
|
|
assert.commandWorked(coll.createIndex({x: 1}, {partialFilterExpression: {a: {$lte: 1.5}}}));
|
|
assert.commandWorked(coll.insert({x: 5, a: 2})); // Not in index.
|
|
assert.commandWorked(coll.insert({x: 6, a: 1})); // In index.
|
|
|
|
//
|
|
// Verify basic functionality with find().
|
|
//
|
|
|
|
// find() operations that should use index.
|
|
explain = coll.explain('executionStats').find({x: 6, a: 1}).finish();
|
|
assert.eq(1, explain.executionStats.nReturned);
|
|
assert(isIxscan(db, getWinningPlan(explain.queryPlanner)));
|
|
explain = coll.explain('executionStats').find({x: {$gt: 1}, a: 1}).finish();
|
|
assert.eq(1, explain.executionStats.nReturned);
|
|
assert(isIxscan(db, getWinningPlan(explain.queryPlanner)));
|
|
explain = coll.explain('executionStats').find({x: 6, a: {$lte: 1}}).finish();
|
|
assert.eq(1, explain.executionStats.nReturned);
|
|
assert(isIxscan(db, getWinningPlan(explain.queryPlanner)));
|
|
|
|
// find() operations that should not use index.
|
|
explain = coll.explain('executionStats').find({x: 6, a: {$lt: 1.6}}).finish();
|
|
assert.eq(1, explain.executionStats.nReturned);
|
|
assert(isCollscan(db, getWinningPlan(explain.queryPlanner)));
|
|
explain = coll.explain('executionStats').find({x: 6}).finish();
|
|
assert.eq(1, explain.executionStats.nReturned);
|
|
assert(isCollscan(db, getWinningPlan(explain.queryPlanner)));
|
|
|
|
//
|
|
// Verify basic functionality with the count command.
|
|
//
|
|
|
|
// Count operation that should use index.
|
|
explain = coll.explain('executionStats').count({x: {$gt: 1}, a: 1});
|
|
assert(isIxscan(db, getWinningPlan(explain.queryPlanner)));
|
|
|
|
// Count operation that should not use index.
|
|
explain = coll.explain('executionStats').count({x: {$gt: 1}, a: 2});
|
|
assert(isCollscan(db, getWinningPlan(explain.queryPlanner)));
|
|
|
|
//
|
|
// Verify basic functionality with the aggregate command.
|
|
//
|
|
|
|
// Aggregate operation that should use index.
|
|
explain = coll.aggregate([{$match: {x: {$gt: 1}, a: 1}}], {explain: true});
|
|
assert(isIxscan(db, getWinningPlan(explain.queryPlanner)));
|
|
|
|
// Aggregate operation that should not use index.
|
|
explain = coll.aggregate([{$match: {x: {$gt: 1}, a: 2}}], {explain: true});
|
|
assert(isCollscan(db, getWinningPlan(explain.queryPlanner)));
|
|
|
|
//
|
|
// Verify basic functionality with the findAndModify command.
|
|
//
|
|
|
|
// findAndModify operation that should use index.
|
|
explain = coll.explain('executionStats')
|
|
.findAndModify({query: {x: {$gt: 1}, a: 1}, update: {$inc: {x: 1}}});
|
|
assert.eq(1, explain.executionStats.nReturned);
|
|
assert(isIxscan(db, getWinningPlan(explain.queryPlanner)));
|
|
|
|
// findAndModify operation that should not use index.
|
|
explain = coll.explain('executionStats')
|
|
.findAndModify({query: {x: {$gt: 1}, a: 2}, update: {$inc: {x: 1}}});
|
|
assert.eq(1, explain.executionStats.nReturned);
|
|
assert(isCollscan(db, getWinningPlan(explain.queryPlanner)));
|
|
|
|
//
|
|
// Verify functionality with multiple overlapping partial indexes on the same key pattern.
|
|
//
|
|
|
|
// Remove existing indexes and documents.
|
|
assert.commandWorked(coll.dropIndexes());
|
|
assert.commandWorked(coll.remove({}));
|
|
|
|
assert.commandWorked(
|
|
coll.createIndex({a: 1}, {name: "index1", partialFilterExpression: {a: {$gte: 0}}}));
|
|
assert.commandWorked(
|
|
coll.createIndex({a: 1}, {name: "index2", partialFilterExpression: {a: {$gte: 10}}}));
|
|
assert.commandWorked(
|
|
coll.createIndex({a: 1}, {name: "index3", partialFilterExpression: {a: {$gte: 100}}}));
|
|
|
|
assert.commandWorked(coll.insert([{a: 1}, {a: 2}, {a: 3}]));
|
|
assert.commandWorked(coll.insert([{a: 11}, {a: 12}, {a: 13}]));
|
|
assert.commandWorked(coll.insert([{a: 101}, {a: 102}, {a: 103}]));
|
|
|
|
// Function which verifies that the given query is indexed, that it produces the same output as a
|
|
// COLLSCAN and the given 'expectedResults' array, and that 'numAlternativePlans' were generated.
|
|
function assertIndexedQueryAndResults(query, numAlternativePlans, expectedResults) {
|
|
const explainOut = coll.explain().find(query).finish();
|
|
const results = coll.find(query).toArray();
|
|
assert(isIxscan(db, explainOut), tojson(explainOut));
|
|
assert.eq(getRejectedPlans(explainOut).length, numAlternativePlans, tojson(explainOut));
|
|
assert.sameMembers(results, coll.find(query).hint({$natural: 1}).toArray());
|
|
assert.sameMembers(results.map(doc => (delete doc._id && doc)), expectedResults);
|
|
}
|
|
|
|
// Queries which fall within the covered ranges generate plans for all applicable partial indexes.
|
|
assertIndexedQueryAndResults({a: {$gt: 0, $lt: 10}}, 0, [{a: 1}, {a: 2}, {a: 3}]);
|
|
assertIndexedQueryAndResults({a: {$gt: 10, $lt: 100}}, 1, [{a: 11}, {a: 12}, {a: 13}]);
|
|
assertIndexedQueryAndResults({a: {$gt: 100, $lt: 1000}}, 2, [{a: 101}, {a: 102}, {a: 103}]);
|
|
assertIndexedQueryAndResults(
|
|
{a: {$gt: 0}},
|
|
0,
|
|
[{a: 1}, {a: 2}, {a: 3}, {a: 11}, {a: 12}, {a: 13}, {a: 101}, {a: 102}, {a: 103}]);
|
|
|
|
// Queries which fall outside the range of any partial indexes produce a COLLSCAN.
|
|
assert(isCollscan(db, coll.explain().find({a: {$lt: 0}}).finish()));
|
|
})();
|