summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJudah Schvimer <judah@mongodb.com>2016-09-15 10:20:59 -0400
committerJudah Schvimer <judah@mongodb.com>2016-09-15 10:20:59 -0400
commit4dc41d135737dbda0a9ecc70af75687dd5df9099 (patch)
tree381dcdf024db4da65d20051a430a4f7515c122d6
parent97dd4a8eebfb2a2dc2e3a5e0907c8fc6e859c0ac (diff)
downloadmongo-4dc41d135737dbda0a9ecc70af75687dd5df9099.tar.gz
SERVER-26033 Allow simple range to exclude start key
-rw-r--r--jstests/concurrency/fsm_workloads/yield_and_hashed.js2
-rw-r--r--jstests/concurrency/fsm_workloads/yield_and_sorted.js2
-rw-r--r--jstests/core/min_max_bounds.js59
-rw-r--r--jstests/core/server9547.js5
-rw-r--r--jstests/core/stages_and_hash.js2
-rw-r--r--jstests/core/stages_and_sorted.js3
-rw-r--r--jstests/core/stages_fetch.js2
-rw-r--r--jstests/core/stages_ixscan.js21
-rw-r--r--jstests/core/stages_limit_skip.js1
-rw-r--r--jstests/core/stages_mergesort.js2
-rw-r--r--jstests/core/stages_or.js2
-rw-r--r--jstests/core/stages_sort.js1
-rw-r--r--src/mongo/db/commands/dbcommands.cpp2
-rw-r--r--src/mongo/db/commands/dbhash.cpp2
-rw-r--r--src/mongo/db/dbhelpers.cpp6
-rw-r--r--src/mongo/db/dbhelpers.h2
-rw-r--r--src/mongo/db/exec/geo_near.cpp3
-rw-r--r--src/mongo/db/exec/index_scan.cpp22
-rw-r--r--src/mongo/db/exec/index_scan.h4
-rw-r--r--src/mongo/db/exec/stagedebug_cmd.cpp3
-rw-r--r--src/mongo/db/exec/text.cpp2
-rw-r--r--src/mongo/db/index/haystack_access_method.cpp15
-rw-r--r--src/mongo/db/query/expression_index.cpp11
-rw-r--r--src/mongo/db/query/index_bounds.cpp39
-rw-r--r--src/mongo/db/query/index_bounds.h31
-rw-r--r--src/mongo/db/query/index_bounds_builder.cpp48
-rw-r--r--src/mongo/db/query/index_bounds_builder.h7
-rw-r--r--src/mongo/db/query/index_bounds_test.cpp18
-rw-r--r--src/mongo/db/query/internal_plans.cpp12
-rw-r--r--src/mongo/db/query/internal_plans.h6
-rw-r--r--src/mongo/db/query/planner_access.cpp2
-rw-r--r--src/mongo/db/query/query_planner_common.cpp16
-rw-r--r--src/mongo/db/query/query_solution.cpp21
-rw-r--r--src/mongo/db/query/query_solution_test.cpp201
-rw-r--r--src/mongo/db/range_deleter_db_env.cpp2
-rw-r--r--src/mongo/db/repl/storage_interface_impl.cpp5
-rw-r--r--src/mongo/db/s/check_sharding_index_command.cpp17
-rw-r--r--src/mongo/db/s/collection_range_deleter.cpp19
-rw-r--r--src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp15
-rw-r--r--src/mongo/db/s/split_chunk_command.cpp2
-rw-r--r--src/mongo/db/s/split_vector_command.cpp19
-rw-r--r--src/mongo/db/ttl.cpp3
-rw-r--r--src/mongo/dbtests/dbhelper_tests.cpp3
-rw-r--r--src/mongo/dbtests/query_plan_executor.cpp2
-rw-r--r--src/mongo/dbtests/query_stage_and.cpp64
-rw-r--r--src/mongo/dbtests/query_stage_count.cpp2
-rw-r--r--src/mongo/dbtests/query_stage_ixscan.cpp2
-rw-r--r--src/mongo/dbtests/query_stage_merge_sort.cpp22
-rw-r--r--src/mongo/dbtests/query_stage_multiplan.cpp2
-rw-r--r--src/mongo/dbtests/query_stage_tests.cpp10
50 files changed, 508 insertions, 256 deletions
diff --git a/jstests/concurrency/fsm_workloads/yield_and_hashed.js b/jstests/concurrency/fsm_workloads/yield_and_hashed.js
index f9267b9c020..60d73670a1f 100644
--- a/jstests/concurrency/fsm_workloads/yield_and_hashed.js
+++ b/jstests/concurrency/fsm_workloads/yield_and_hashed.js
@@ -27,6 +27,7 @@ var $config = extendWorkload($config, function($config, $super) {
keyPattern: {c: 1},
startKey: {'': nMatches},
endKey: {},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: -1
}
@@ -41,6 +42,7 @@ var $config = extendWorkload($config, function($config, $super) {
keyPattern: {d: 1},
startKey: {'': this.nDocs - nMatches},
endKey: {},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
diff --git a/jstests/concurrency/fsm_workloads/yield_and_sorted.js b/jstests/concurrency/fsm_workloads/yield_and_sorted.js
index aed15988b10..ea077aeed12 100644
--- a/jstests/concurrency/fsm_workloads/yield_and_sorted.js
+++ b/jstests/concurrency/fsm_workloads/yield_and_sorted.js
@@ -28,6 +28,7 @@ var $config = extendWorkload($config, function($config, $super) {
keyPattern: {c: 1},
startKey: {'': 0},
endKey: {'': 0},
+ startKeyInclusive: true,
endKeyInclusive: false,
direction: 1
}
@@ -41,6 +42,7 @@ var $config = extendWorkload($config, function($config, $super) {
keyPattern: {d: 1},
startKey: {'': this.nDocs},
endKey: {'': this.nDocs},
+ startKeyInclusive: true,
endKeyInclusive: false,
direction: -1
}
diff --git a/jstests/core/min_max_bounds.js b/jstests/core/min_max_bounds.js
new file mode 100644
index 00000000000..0139ec39851
--- /dev/null
+++ b/jstests/core/min_max_bounds.js
@@ -0,0 +1,59 @@
+/**
+ * This test ensures that queries using simple ranges handle bound inclusion properly.
+ */
+(function() {
+ 'use strict';
+ var coll = db.query_bound_inclusion;
+ coll.drop();
+ assert.writeOK(coll.insert({a: 1, b: 1}));
+ assert.writeOK(coll.insert({a: 2, b: 2}));
+ assert.writeOK(coll.insert({a: 3, b: 3}));
+
+ assert.commandWorked(coll.createIndex({a: 1}));
+
+ var res = coll.find().sort({a: 1}).toArray();
+ assert.eq(res.length, 3);
+ assert.eq(res[0].a, 1);
+ assert.eq(res[1].a, 2);
+ assert.eq(res[2].a, 3);
+
+ res = coll.find().sort({a: -1}).toArray();
+ assert.eq(res.length, 3);
+ assert.eq(res[0].a, 3);
+ assert.eq(res[1].a, 2);
+ assert.eq(res[2].a, 1);
+
+ res = coll.find().min({a: 1}).max({a: 3}).toArray();
+ assert.eq(res.length, 2);
+ assert.eq(res[0].a, 1);
+ assert.eq(res[1].a, 2);
+
+ res = coll.find().min({a: 1}).max({a: 3}).sort({a: -1}).toArray();
+ assert.eq(res.length, 2);
+ assert.eq(res[0].a, 2);
+ assert.eq(res[1].a, 1);
+
+ assert.commandWorked(coll.createIndex({b: -1}));
+
+ res = coll.find().sort({b: -1}).toArray();
+ assert.eq(res.length, 3);
+ assert.eq(res[0].b, 3);
+ assert.eq(res[1].b, 2);
+ assert.eq(res[2].b, 1);
+
+ res = coll.find().sort({b: 1}).toArray();
+ assert.eq(res.length, 3);
+ assert.eq(res[0].b, 1);
+ assert.eq(res[1].b, 2);
+ assert.eq(res[2].b, 3);
+
+ res = coll.find().min({b: 3}).max({b: 1}).toArray();
+ assert.eq(res.length, 2);
+ assert.eq(res[0].b, 3);
+ assert.eq(res[1].b, 2);
+
+ res = coll.find().min({b: 3}).max({b: 1}).sort({b: 1}).toArray();
+ assert.eq(res.length, 2);
+ assert.eq(res[0].b, 2);
+ assert.eq(res[1].b, 3);
+})(); \ No newline at end of file
diff --git a/jstests/core/server9547.js b/jstests/core/server9547.js
index 9717893cbfb..56f059d82b3 100644
--- a/jstests/core/server9547.js
+++ b/jstests/core/server9547.js
@@ -16,6 +16,5 @@ assert.eq(4, t.find({}).max({a: 4}).toArray().length, "no order");
// Ascending order is fine.
assert.eq(4, t.find({}).max({a: 4}).sort({a: 1}).toArray().length, "ascending");
-// Descending order is still broken.
-// This should really return the same # of results but doesn't.
-assert.eq(5, t.find({}).max({a: 4}).sort({a: -1}).toArray().length, "descending");
+// Descending order is fine.
+assert.eq(4, t.find({}).max({a: 4}).sort({a: -1}).toArray().length, "descending");
diff --git a/jstests/core/stages_and_hash.js b/jstests/core/stages_and_hash.js
index 8dcc8cf1345..14b00a46e73 100644
--- a/jstests/core/stages_and_hash.js
+++ b/jstests/core/stages_and_hash.js
@@ -20,6 +20,7 @@ ixscan1 = {
keyPattern: {foo: 1},
startKey: {"": 20},
endKey: {},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: -1
}
@@ -34,6 +35,7 @@ ixscan2 = {
keyPattern: {bar: 1},
startKey: {"": 40},
endKey: {},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
diff --git a/jstests/core/stages_and_sorted.js b/jstests/core/stages_and_sorted.js
index c29db7ce2eb..50e79ce9ea8 100644
--- a/jstests/core/stages_and_sorted.js
+++ b/jstests/core/stages_and_sorted.js
@@ -30,6 +30,7 @@ ixscan1 = {
keyPattern: {foo: 1},
startKey: {"": 1},
endKey: {"": 1},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
@@ -44,6 +45,7 @@ ixscan2 = {
keyPattern: {bar: 1},
startKey: {"": 1},
endKey: {"": 1},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
@@ -58,6 +60,7 @@ ixscan3 = {
keyPattern: {baz: 1},
startKey: {"": 12},
endKey: {"": 12},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
diff --git a/jstests/core/stages_fetch.js b/jstests/core/stages_fetch.js
index 7adc52c67c5..02dffa929f9 100644
--- a/jstests/core/stages_fetch.js
+++ b/jstests/core/stages_fetch.js
@@ -18,6 +18,7 @@ ixscan1 = {
keyPattern: {foo: 1},
startKey: {"": 20},
endKey: {"": 30},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
},
@@ -34,6 +35,7 @@ ixscan2 = {
keyPattern: {foo: 1},
startKey: {"": 20},
endKey: {"": 30},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
diff --git a/jstests/core/stages_ixscan.js b/jstests/core/stages_ixscan.js
index fb7e8fd54c9..09c5480bd67 100644
--- a/jstests/core/stages_ixscan.js
+++ b/jstests/core/stages_ixscan.js
@@ -16,8 +16,17 @@ assert.commandFailed(db.runCommand({
stageDebug: {
collection: collname,
plan: {
- ixscan:
- {args: {startKey: {"": 20, endKey: {}, endKeyInclusive: true, direction: -1}}}
+ ixscan: {
+ args: {
+ startKey: {
+ "": 20,
+ endKey: {},
+ startKeyInclusive: true,
+ endKeyInclusive: true,
+ direction: -1
+ }
+ }
+ }
}
}
}));
@@ -29,6 +38,7 @@ ixscan1 = {
keyPattern: {foo: 1},
startKey: {"": 20},
endKey: {},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: -1
}
@@ -45,6 +55,7 @@ ixscan1 = {
keyPattern: {foo: 1},
startKey: {"": 20},
endKey: {"": 30},
+ startKeyInclusive: true,
endKeyInclusive: false,
direction: 1
}
@@ -61,6 +72,7 @@ ixscan1 = {
keyPattern: {foo: 1},
startKey: {"": 20},
endKey: {"": 30},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
@@ -78,6 +90,7 @@ ixscan1 = {
keyPattern: {foo: 1},
startKey: {"": 20},
endKey: {"": 30},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
},
@@ -96,6 +109,7 @@ ixscan1 = {
keyPattern: {foo: 1, baz: 1},
startKey: {foo: 20, baz: MinKey},
endKey: {foo: 30, baz: MaxKey},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
},
@@ -114,6 +128,7 @@ ixscan1 = {
keyPattern: {foo: 1, baz: 1},
startKey: {foo: 20, baz: MinKey},
endKey: {foo: 30, baz: MaxKey},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
},
@@ -137,6 +152,7 @@ var ixscanAmbiguous = {
keyPattern: {a: 1},
startKey: {a: 1},
endKey: {a: 2},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
},
@@ -153,6 +169,7 @@ var ixscanName = {
name: "numeric",
startKey: {a: ""},
endKey: {a: {}}, // All strings
+ startKeyInclusive: true,
endKeyInclusive: false,
direction: 1
},
diff --git a/jstests/core/stages_limit_skip.js b/jstests/core/stages_limit_skip.js
index c582cb6b1e4..7cd98c1c931 100644
--- a/jstests/core/stages_limit_skip.js
+++ b/jstests/core/stages_limit_skip.js
@@ -18,6 +18,7 @@ ixscan1 = {
keyPattern: {foo: 1},
startKey: {"": 20},
endKey: {},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: -1
}
diff --git a/jstests/core/stages_mergesort.js b/jstests/core/stages_mergesort.js
index 5156743078c..4ebda2547c4 100644
--- a/jstests/core/stages_mergesort.js
+++ b/jstests/core/stages_mergesort.js
@@ -20,6 +20,7 @@ ixscan1 = {
keyPattern: {foo: 1, bar: 1},
startKey: {foo: 1, bar: 0},
endKey: {foo: 1, bar: 100000},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
@@ -32,6 +33,7 @@ ixscan2 = {
keyPattern: {baz: 1, bar: 1},
startKey: {baz: 1, bar: 0},
endKey: {baz: 1, bar: 100000},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
diff --git a/jstests/core/stages_or.js b/jstests/core/stages_or.js
index 6ea73efd1ed..e8616fcbcaa 100644
--- a/jstests/core/stages_or.js
+++ b/jstests/core/stages_or.js
@@ -19,6 +19,7 @@ ixscan1 = {
keyPattern: {baz: 1},
startKey: {"": 40},
endKey: {},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
@@ -31,6 +32,7 @@ ixscan2 = {
keyPattern: {foo: 1},
startKey: {"": 40},
endKey: {},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: 1
}
diff --git a/jstests/core/stages_sort.js b/jstests/core/stages_sort.js
index 6d25279fa27..46b81662100 100644
--- a/jstests/core/stages_sort.js
+++ b/jstests/core/stages_sort.js
@@ -18,6 +18,7 @@ if (false) {
keyPattern: {foo: 1},
startKey: {"": 20},
endKey: {},
+ startKeyInclusive: true,
endKeyInclusive: true,
direction: -1
}
diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp
index 3e8ce384741..9720775b0cf 100644
--- a/src/mongo/db/commands/dbcommands.cpp
+++ b/src/mongo/db/commands/dbcommands.cpp
@@ -834,7 +834,7 @@ public:
idx,
min,
max,
- false, // endKeyInclusive
+ BoundInclusion::kIncludeStartKeyOnly,
PlanExecutor::YIELD_MANUAL);
}
diff --git a/src/mongo/db/commands/dbhash.cpp b/src/mongo/db/commands/dbhash.cpp
index 045376922a8..acfa7435c72 100644
--- a/src/mongo/db/commands/dbhash.cpp
+++ b/src/mongo/db/commands/dbhash.cpp
@@ -224,7 +224,7 @@ private:
desc,
BSONObj(),
BSONObj(),
- false, // endKeyInclusive
+ BoundInclusion::kIncludeStartKeyOnly,
PlanExecutor::YIELD_MANUAL,
InternalPlanner::FORWARD,
InternalPlanner::IXSCAN_FETCH);
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index 53e73c11119..82fb612ddf8 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -295,7 +295,7 @@ BSONObj Helpers::inferKeyPattern(const BSONObj& o) {
long long Helpers::removeRange(OperationContext* txn,
const KeyRange& range,
- bool maxInclusive,
+ BoundInclusion boundInclusion,
const WriteConcernOptions& writeConcern,
RemoveSaver* callback,
bool fromMigrate,
@@ -337,10 +337,12 @@ long long Helpers::removeRange(OperationContext* txn,
// Extend bounds to match the index we found
+ invariant(IndexBounds::isStartIncludedInBound(boundInclusion));
// Extend min to get (min, MinKey, MinKey, ....)
min = Helpers::toKeyFormat(indexKeyPattern.extendRangeBound(range.minKey, false));
// If upper bound is included, extend max to get (max, MaxKey, MaxKey, ...)
// If not included, extend max to get (max, MinKey, MinKey, ....)
+ const bool maxInclusive = IndexBounds::isEndIncludedInBound(boundInclusion);
max = Helpers::toKeyFormat(indexKeyPattern.extendRangeBound(range.maxKey, maxInclusive));
}
@@ -369,7 +371,7 @@ long long Helpers::removeRange(OperationContext* txn,
desc,
min,
max,
- maxInclusive,
+ boundInclusion,
PlanExecutor::YIELD_MANUAL,
InternalPlanner::FORWARD,
InternalPlanner::IXSCAN_FETCH));
diff --git a/src/mongo/db/dbhelpers.h b/src/mongo/db/dbhelpers.h
index f3acb9f190c..bb303a681c4 100644
--- a/src/mongo/db/dbhelpers.h
+++ b/src/mongo/db/dbhelpers.h
@@ -173,7 +173,7 @@ struct Helpers {
*/
static long long removeRange(OperationContext* txn,
const KeyRange& range,
- bool maxInclusive,
+ BoundInclusion boundInclusion,
const WriteConcernOptions& secondaryThrottle,
RemoveSaver* callback = NULL,
bool fromMigrate = false,
diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp
index 7e7d00fc8ec..56b10994c3c 100644
--- a/src/mongo/db/exec/geo_near.cpp
+++ b/src/mongo/db/exec/geo_near.cpp
@@ -331,7 +331,8 @@ void GeoNear2DStage::DensityEstimator::buildIndexScan(OperationContext* txn,
mongo::BSONObjBuilder builder;
it->appendHashMin(&builder, "");
it->appendHashMax(&builder, "");
- oil.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(builder.obj(), true, true));
+ oil.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ builder.obj(), BoundInclusion::kIncludeBothStartAndEndKeys));
}
invariant(oil.isValidFor(1));
diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp
index 7e0cf847dc3..767539e65b4 100644
--- a/src/mongo/db/exec/index_scan.cpp
+++ b/src/mongo/db/exec/index_scan.cpp
@@ -71,7 +71,8 @@ IndexScan::IndexScan(OperationContext* txn,
_shouldDedup(true),
_forward(params.direction == 1),
_params(params),
- _endKeyInclusive(false) {
+ _startKeyInclusive(IndexBounds::isStartIncludedInBound(params.bounds.boundInclusion)),
+ _endKeyInclusive(IndexBounds::isEndIncludedInBound(params.bounds.boundInclusion)) {
// We can't always access the descriptor in the call to getStats() so we pull
// any info we need for stats reporting out here.
_specificStats.keyPattern = _keyPattern;
@@ -104,20 +105,18 @@ boost::optional<IndexKeyEntry> IndexScan::initIndexScan() {
if (_params.bounds.isSimpleRange) {
// Start at one key, end at another.
+ _startKey = _params.bounds.startKey;
_endKey = _params.bounds.endKey;
- _endKeyInclusive = _params.bounds.endKeyInclusive;
_indexCursor->setEndPosition(_endKey, _endKeyInclusive);
- return _indexCursor->seek(_params.bounds.startKey, /*inclusive*/ true);
+ return _indexCursor->seek(_startKey, _startKeyInclusive);
} else {
// For single intervals, we can use an optimized scan which checks against the position
// of an end cursor. For all other index scans, we fall back on using
// IndexBoundsChecker to determine when we've finished the scan.
- BSONObj startKey;
- bool startKeyInclusive;
if (IndexBoundsBuilder::isSingleInterval(
- _params.bounds, &startKey, &startKeyInclusive, &_endKey, &_endKeyInclusive)) {
+ _params.bounds, &_startKey, &_startKeyInclusive, &_endKey, &_endKeyInclusive)) {
_indexCursor->setEndPosition(_endKey, _endKeyInclusive);
- return _indexCursor->seek(startKey, startKeyInclusive);
+ return _indexCursor->seek(_startKey, _startKeyInclusive);
} else {
_checker.reset(new IndexBoundsChecker(&_params.bounds, _keyPattern, _params.direction));
@@ -154,6 +153,15 @@ PlanStage::StageState IndexScan::doWork(WorkingSetID* out) {
if (kv) {
// In debug mode, check that the cursor isn't lying to us.
+ if (kDebugBuild && !_startKey.isEmpty()) {
+ int cmp = kv->key.woCompare(_startKey,
+ Ordering::make(_params.descriptor->keyPattern()),
+ /*compareFieldNames*/ false);
+ if (cmp == 0)
+ dassert(_startKeyInclusive);
+ dassert(_forward ? cmp >= 0 : cmp <= 0);
+ }
+
if (kDebugBuild && !_endKey.isEmpty()) {
int cmp = kv->key.woCompare(_endKey,
Ordering::make(_params.descriptor->keyPattern()),
diff --git a/src/mongo/db/exec/index_scan.h b/src/mongo/db/exec/index_scan.h
index c6f43992d47..1cf2780f24b 100644
--- a/src/mongo/db/exec/index_scan.h
+++ b/src/mongo/db/exec/index_scan.h
@@ -165,9 +165,13 @@ private:
// BSON compares against scanned keys. In this case _checker will be NULL.
//
+ // The key that the index cursor should start on/after.
+ BSONObj _startKey;
// The key that the index cursor should stop on/after.
BSONObj _endKey;
+ // Is the start key included in the range?
+ bool _startKeyInclusive;
// Is the end key included in the range?
bool _endKeyInclusive;
};
diff --git a/src/mongo/db/exec/stagedebug_cmd.cpp b/src/mongo/db/exec/stagedebug_cmd.cpp
index 13da0300c94..926cca4c485 100644
--- a/src/mongo/db/exec/stagedebug_cmd.cpp
+++ b/src/mongo/db/exec/stagedebug_cmd.cpp
@@ -306,7 +306,8 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = stripFieldNames(nodeArgs["startKey"].Obj());
params.bounds.endKey = stripFieldNames(nodeArgs["endKey"].Obj());
- params.bounds.endKeyInclusive = nodeArgs["endKeyInclusive"].Bool();
+ params.bounds.boundInclusion = IndexBounds::makeBoundInclusionFromBoundBools(
+ nodeArgs["startKeyInclusive"].Bool(), nodeArgs["endKeyInclusive"].Bool());
params.direction = nodeArgs["direction"].numberInt();
return new IndexScan(txn, params, workingSet, matcher);
diff --git a/src/mongo/db/exec/text.cpp b/src/mongo/db/exec/text.cpp
index ddbcc1d9a46..a290b8f3095 100644
--- a/src/mongo/db/exec/text.cpp
+++ b/src/mongo/db/exec/text.cpp
@@ -105,7 +105,7 @@ unique_ptr<PlanStage> TextStage::buildTextTree(OperationContext* txn,
MAX_WEIGHT, term, _params.indexPrefix, _params.spec.getTextIndexVersion());
ixparams.bounds.endKey = FTSIndexFormat::getIndexKey(
0, term, _params.indexPrefix, _params.spec.getTextIndexVersion());
- ixparams.bounds.endKeyInclusive = true;
+ ixparams.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
ixparams.bounds.isSimpleRange = true;
ixparams.descriptor = _params.index;
ixparams.direction = -1;
diff --git a/src/mongo/db/index/haystack_access_method.cpp b/src/mongo/db/index/haystack_access_method.cpp
index 95822b21dc8..3322a6f0aa9 100644
--- a/src/mongo/db/index/haystack_access_method.cpp
+++ b/src/mongo/db/index/haystack_access_method.cpp
@@ -110,13 +110,14 @@ void HaystackAccessMethod::searchCommand(OperationContext* txn,
unordered_set<RecordId, RecordId::Hasher> thisPass;
- unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn,
- collection,
- _descriptor,
- key,
- key,
- true, // endKeyInclusive
- PlanExecutor::YIELD_MANUAL));
+ unique_ptr<PlanExecutor> exec(
+ InternalPlanner::indexScan(txn,
+ collection,
+ _descriptor,
+ key,
+ key,
+ BoundInclusion::kIncludeBothStartAndEndKeys,
+ PlanExecutor::YIELD_MANUAL));
PlanExecutor::ExecState state;
BSONObj obj;
RecordId loc;
diff --git a/src/mongo/db/query/expression_index.cpp b/src/mongo/db/query/expression_index.cpp
index 45617f35a89..f2e1439610a 100644
--- a/src/mongo/db/query/expression_index.cpp
+++ b/src/mongo/db/query/expression_index.cpp
@@ -95,8 +95,8 @@ void ExpressionMapping::GeoHashsToIntervalsWithParents(
geoHash.appendHashMin(&builder, "");
geoHash.appendHashMax(&builder, "");
- oilOut->intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(builder.obj(), true, true));
+ oilOut->intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ builder.obj(), BoundInclusion::kIncludeBothStartAndEndKeys));
}
}
@@ -152,7 +152,8 @@ void S2CellIdsToIntervalsUnsorted(const std::vector<S2CellId>& intervalSet,
b.append("start", start);
b.append("end", end);
invariant(start <= end);
- oilOut->intervals.push_back(IndexBoundsBuilder::makeRangeInterval(b.obj(), true, true));
+ oilOut->intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ b.obj(), BoundInclusion::kIncludeBothStartAndEndKeys));
} else {
// for backwards compatibility, use strings
std::string start = interval.toString();
@@ -160,8 +161,8 @@ void S2CellIdsToIntervalsUnsorted(const std::vector<S2CellId>& intervalSet,
end[start.size() - 1]++;
b.append("start", start);
b.append("end", end);
- oilOut->intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(b.obj(), true, false));
+ oilOut->intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ b.obj(), BoundInclusion::kIncludeStartKeyOnly));
}
}
}
diff --git a/src/mongo/db/query/index_bounds.cpp b/src/mongo/db/query/index_bounds.cpp
index 065d53ba510..ef8eeb6d8b8 100644
--- a/src/mongo/db/query/index_bounds.cpp
+++ b/src/mongo/db/query/index_bounds.cpp
@@ -104,7 +104,7 @@ bool IndexBounds::operator==(const IndexBounds& other) const {
if (this->isSimpleRange) {
return SimpleBSONObjComparator::kInstance.evaluate(this->startKey == other.startKey) &&
SimpleBSONObjComparator::kInstance.evaluate(this->endKey == other.endKey) &&
- (this->endKeyInclusive == other.endKeyInclusive);
+ (this->boundInclusion == other.boundInclusion);
}
if (this->fields.size() != other.fields.size()) {
@@ -136,6 +136,34 @@ string OrderedIntervalList::toString() const {
return ss;
}
+bool IndexBounds::isStartIncludedInBound(BoundInclusion boundInclusion) {
+ return boundInclusion == BoundInclusion::kIncludeBothStartAndEndKeys ||
+ boundInclusion == BoundInclusion::kIncludeStartKeyOnly;
+}
+
+bool IndexBounds::isEndIncludedInBound(BoundInclusion boundInclusion) {
+ return boundInclusion == BoundInclusion::kIncludeBothStartAndEndKeys ||
+ boundInclusion == BoundInclusion::kIncludeEndKeyOnly;
+}
+
+BoundInclusion IndexBounds::makeBoundInclusionFromBoundBools(bool startKeyInclusive,
+ bool endKeyInclusive) {
+ if (startKeyInclusive) {
+ if (endKeyInclusive) {
+ return BoundInclusion::kIncludeBothStartAndEndKeys;
+ } else {
+ return BoundInclusion::kIncludeStartKeyOnly;
+ }
+ } else {
+ if (endKeyInclusive) {
+ return BoundInclusion::kIncludeEndKeyOnly;
+ } else {
+ return BoundInclusion::kExcludeBothStartAndEndKeys;
+ }
+ }
+}
+
+
bool OrderedIntervalList::operator==(const OrderedIntervalList& other) const {
if (this->name != other.name) {
return false;
@@ -216,12 +244,17 @@ void OrderedIntervalList::complement() {
string IndexBounds::toString() const {
mongoutils::str::stream ss;
if (isSimpleRange) {
- ss << "[" << startKey.toString() << ", ";
+ if (IndexBounds::isStartIncludedInBound(boundInclusion)) {
+ ss << "[";
+ } else {
+ ss << "(";
+ }
+ ss << startKey.toString() << ", ";
if (endKey.isEmpty()) {
ss << "]";
} else {
ss << endKey.toString();
- if (endKeyInclusive) {
+ if (IndexBounds::isEndIncludedInBound(boundInclusion)) {
ss << "]";
} else {
ss << ")";
diff --git a/src/mongo/db/query/index_bounds.h b/src/mongo/db/query/index_bounds.h
index e3fabde17b7..2dfcc122b26 100644
--- a/src/mongo/db/query/index_bounds.h
+++ b/src/mongo/db/query/index_bounds.h
@@ -37,6 +37,13 @@
namespace mongo {
+enum class BoundInclusion {
+ kExcludeBothStartAndEndKeys,
+ kIncludeStartKeyOnly,
+ kIncludeEndKeyOnly,
+ kIncludeBothStartAndEndKeys
+};
+
/**
* An ordered list of intervals for one field.
*/
@@ -74,7 +81,7 @@ struct OrderedIntervalList {
* interpret. Previously known as FieldRangeVector.
*/
struct IndexBounds {
- IndexBounds() : isSimpleRange(false), endKeyInclusive(false) {}
+ IndexBounds() : isSimpleRange(false), boundInclusion(BoundInclusion::kIncludeStartKeyOnly) {}
// For each indexed field, the values that the field is allowed to take on.
std::vector<OrderedIntervalList> fields;
@@ -101,6 +108,26 @@ struct IndexBounds {
bool operator!=(const IndexBounds& other) const;
/**
+ * Returns if the start key should be included in the bounds specified by the given
+ * BoundInclusion.
+ */
+ static bool isStartIncludedInBound(BoundInclusion boundInclusion);
+
+ /**
+ * Returns if the end key should be included in the bounds specified by the given
+ * BoundInclusion.
+ */
+ static bool isEndIncludedInBound(BoundInclusion boundInclusion);
+
+ /**
+ * Returns a BoundInclusion given two booleans of whether to included the start key and the end
+ * key.
+ */
+ static BoundInclusion makeBoundInclusionFromBoundBools(bool startKeyInclusive,
+ bool endKeyInclusive);
+
+
+ /**
* BSON format for explain. The format is an array of strings for each field.
* Each string represents an interval. The strings use "[" and "]" if the interval
* bounds are inclusive, and "(" / ")" if exclusive.
@@ -114,7 +141,7 @@ struct IndexBounds {
bool isSimpleRange;
BSONObj startKey;
BSONObj endKey;
- bool endKeyInclusive;
+ BoundInclusion boundInclusion;
};
/**
diff --git a/src/mongo/db/query/index_bounds_builder.cpp b/src/mongo/db/query/index_bounds_builder.cpp
index 21e56b4f42b..2268e3ef30c 100644
--- a/src/mongo/db/query/index_bounds_builder.cpp
+++ b/src/mongo/db/query/index_bounds_builder.cpp
@@ -186,14 +186,15 @@ void IndexBoundsBuilder::allValuesForField(const BSONElement& elt, OrderedInterv
bob.appendMinKey("");
bob.appendMaxKey("");
out->name = elt.fieldName();
- out->intervals.push_back(makeRangeInterval(bob.obj(), true, true));
+ out->intervals.push_back(
+ makeRangeInterval(bob.obj(), BoundInclusion::kIncludeBothStartAndEndKeys));
}
Interval IndexBoundsBuilder::allValues() {
BSONObjBuilder bob;
bob.appendMinKey("");
bob.appendMaxKey("");
- return makeRangeInterval(bob.obj(), true, true);
+ return makeRangeInterval(bob.obj(), BoundInclusion::kIncludeBothStartAndEndKeys);
}
bool IntervalComparison(const Interval& lhs, const Interval& rhs) {
@@ -310,7 +311,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
bob.appendNull("");
bob.appendNull("");
BSONObj dataObj = bob.obj();
- oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true));
+ oilOut->intervals.push_back(
+ makeRangeInterval(dataObj, BoundInclusion::kIncludeBothStartAndEndKeys));
*tightnessOut = IndexBoundsBuilder::INEXACT_FETCH;
return;
@@ -393,7 +395,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
CollationIndexKey::collationAwareIndexKeyAppend(dataElt, index.collator, &bob);
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
- oilOut->intervals.push_back(makeRangeInterval(dataObj, typeMatch(dataObj), true));
+ oilOut->intervals.push_back(makeRangeInterval(
+ dataObj, IndexBounds::makeBoundInclusionFromBoundBools(typeMatch(dataObj), true)));
*tightnessOut = getInequalityPredicateTightness(dataElt, index);
} else if (MatchExpression::LT == expr->matchType()) {
@@ -424,7 +427,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
CollationIndexKey::collationAwareIndexKeyAppend(dataElt, index.collator, &bob);
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
- Interval interval = makeRangeInterval(dataObj, typeMatch(dataObj), false);
+ Interval interval = makeRangeInterval(
+ dataObj, IndexBounds::makeBoundInclusionFromBoundBools(typeMatch(dataObj), false));
// If the operand to LT is equal to the lower bound X, the interval [X, X) is invalid
// and should not be added to the bounds.
@@ -460,7 +464,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
}
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
- Interval interval = makeRangeInterval(dataObj, false, typeMatch(dataObj));
+ Interval interval = makeRangeInterval(
+ dataObj, IndexBounds::makeBoundInclusionFromBoundBools(false, typeMatch(dataObj)));
// If the operand to GT is equal to the upper bound X, the interval (X, X] is invalid
// and should not be added to the bounds.
@@ -499,7 +504,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
- oilOut->intervals.push_back(makeRangeInterval(dataObj, true, typeMatch(dataObj)));
+ oilOut->intervals.push_back(makeRangeInterval(
+ dataObj, IndexBounds::makeBoundInclusionFromBoundBools(true, typeMatch(dataObj))));
*tightnessOut = getInequalityPredicateTightness(dataElt, index);
} else if (MatchExpression::REGEX == expr->matchType()) {
@@ -511,7 +517,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
bob.appendMaxForType("", NumberDouble);
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
- oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true));
+ oilOut->intervals.push_back(
+ makeRangeInterval(dataObj, BoundInclusion::kIncludeBothStartAndEndKeys));
*tightnessOut = IndexBoundsBuilder::INEXACT_COVERED;
} else if (MatchExpression::TYPE_OPERATOR == expr->matchType()) {
const TypeMatchExpression* tme = static_cast<const TypeMatchExpression*>(expr);
@@ -524,7 +531,8 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
bob.appendMaxForType("", type);
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
- oilOut->intervals.push_back(makeRangeInterval(dataObj, true, true));
+ oilOut->intervals.push_back(
+ makeRangeInterval(dataObj, BoundInclusion::kIncludeBothStartAndEndKeys));
*tightnessOut = tme->matchesAllNumbers() ? IndexBoundsBuilder::EXACT
: IndexBoundsBuilder::INEXACT_FETCH;
@@ -597,13 +605,11 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr,
}
// static
-Interval IndexBoundsBuilder::makeRangeInterval(const BSONObj& obj,
- bool startInclusive,
- bool endInclusive) {
+Interval IndexBoundsBuilder::makeRangeInterval(const BSONObj& obj, BoundInclusion boundInclusion) {
Interval ret;
ret._intervalData = obj;
- ret.startInclusive = startInclusive;
- ret.endInclusive = endInclusive;
+ ret.startInclusive = IndexBounds::isStartIncludedInBound(boundInclusion);
+ ret.endInclusive = IndexBounds::isEndIncludedInBound(boundInclusion);
BSONObjIterator it(obj);
verify(it.more());
ret.start = it.next();
@@ -705,7 +711,8 @@ void IndexBoundsBuilder::unionize(OrderedIntervalList* oilOut) {
bool endInclusive = iv[i + 1].endInclusive;
iv.erase(iv.begin() + i);
// iv[i] is now the former iv[i + 1]
- iv[i] = makeRangeInterval(data, startInclusive, endInclusive);
+ iv[i] = makeRangeInterval(
+ data, IndexBounds::makeBoundInclusionFromBoundBools(startInclusive, endInclusive));
// Don't increment 'i'.
}
}
@@ -714,12 +721,11 @@ void IndexBoundsBuilder::unionize(OrderedIntervalList* oilOut) {
// static
Interval IndexBoundsBuilder::makeRangeInterval(const string& start,
const string& end,
- bool startInclusive,
- bool endInclusive) {
+ BoundInclusion boundInclusion) {
BSONObjBuilder bob;
bob.append("", start);
bob.append("", end);
- return makeRangeInterval(bob.obj(), startInclusive, endInclusive);
+ return makeRangeInterval(bob.obj(), boundInclusion);
}
// static
@@ -776,14 +782,16 @@ void IndexBoundsBuilder::translateRegex(const RegexMatchExpression* rme,
if (!start.empty()) {
string end = start;
end[end.size() - 1]++;
- oilOut->intervals.push_back(makeRangeInterval(start, end, true, false));
+ oilOut->intervals.push_back(
+ makeRangeInterval(start, end, BoundInclusion::kIncludeStartKeyOnly));
} else {
BSONObjBuilder bob;
bob.appendMinForType("", String);
bob.appendMaxForType("", String);
BSONObj dataObj = bob.obj();
verify(dataObj.isOwned());
- oilOut->intervals.push_back(makeRangeInterval(dataObj, true, false));
+ oilOut->intervals.push_back(
+ makeRangeInterval(dataObj, BoundInclusion::kIncludeStartKeyOnly));
}
// Regexes are after strings.
diff --git a/src/mongo/db/query/index_bounds_builder.h b/src/mongo/db/query/index_bounds_builder.h
index 5d3c02e029f..b397f81b295 100644
--- a/src/mongo/db/query/index_bounds_builder.h
+++ b/src/mongo/db/query/index_bounds_builder.h
@@ -108,15 +108,14 @@ public:
* Make a range interval from the provided object.
* The object must have exactly two fields. The first field is the start, the second the
* end.
- * The two inclusive flags indicate whether or not the start/end fields are included in the
+ * The BoundInclusion indicates whether or not the start/end fields are included in the
* interval (closed interval if included, open if not).
*/
- static Interval makeRangeInterval(const BSONObj& obj, bool startInclusive, bool endInclusive);
+ static Interval makeRangeInterval(const BSONObj& obj, BoundInclusion boundInclusion);
static Interval makeRangeInterval(const std::string& start,
const std::string& end,
- bool startInclusive,
- bool endInclusive);
+ BoundInclusion boundInclusion);
/**
* Make a point interval from the provided object.
diff --git a/src/mongo/db/query/index_bounds_test.cpp b/src/mongo/db/query/index_bounds_test.cpp
index 250563b54ce..1fca089e584 100644
--- a/src/mongo/db/query/index_bounds_test.cpp
+++ b/src/mongo/db/query/index_bounds_test.cpp
@@ -298,13 +298,13 @@ TEST(IndexBoundsTest, SimpleRangeBoundsEqual) {
bounds1.isSimpleRange = true;
bounds1.startKey = BSON("" << 1 << "" << 3);
bounds1.endKey = BSON("" << 2 << "" << 4);
- bounds1.endKeyInclusive = false;
+ bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
IndexBounds bounds2;
bounds2.isSimpleRange = true;
bounds2.startKey = BSON("" << 1 << "" << 3);
bounds2.endKey = BSON("" << 2 << "" << 4);
- bounds2.endKeyInclusive = false;
+ bounds2.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
ASSERT_TRUE(bounds1 == bounds2);
ASSERT_FALSE(bounds1 != bounds2);
@@ -457,7 +457,7 @@ TEST(IndexBoundsTest, SimpleRangeBoundsNotEqualToRegularBounds) {
bounds1.isSimpleRange = true;
bounds1.startKey = BSON("" << 1 << "" << 3);
bounds1.endKey = BSON("" << 2 << "" << 4);
- bounds1.endKeyInclusive = false;
+ bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
IndexBounds bounds2;
OrderedIntervalList oil;
@@ -473,13 +473,13 @@ TEST(IndexBoundsTest, SimpleRangeBoundsNotEqualDifferentStartKey) {
bounds1.isSimpleRange = true;
bounds1.startKey = BSON("" << 1 << "" << 3);
bounds1.endKey = BSON("" << 2 << "" << 4);
- bounds1.endKeyInclusive = false;
+ bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
IndexBounds bounds2;
bounds1.isSimpleRange = true;
bounds1.startKey = BSON("" << 1 << "" << 1);
bounds1.endKey = BSON("" << 2 << "" << 4);
- bounds1.endKeyInclusive = false;
+ bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
ASSERT_FALSE(bounds1 == bounds2);
ASSERT_TRUE(bounds1 != bounds2);
@@ -490,13 +490,13 @@ TEST(IndexBoundsTest, SimpleRangeBoundsNotEqualDifferentEndKey) {
bounds1.isSimpleRange = true;
bounds1.startKey = BSON("" << 1 << "" << 3);
bounds1.endKey = BSON("" << 2 << "" << 4);
- bounds1.endKeyInclusive = false;
+ bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
IndexBounds bounds2;
bounds1.isSimpleRange = true;
bounds1.startKey = BSON("" << 1 << "" << 3);
bounds1.endKey = BSON("" << 2 << "" << 99);
- bounds1.endKeyInclusive = false;
+ bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
ASSERT_FALSE(bounds1 == bounds2);
ASSERT_TRUE(bounds1 != bounds2);
@@ -507,13 +507,13 @@ TEST(IndexBoundsTest, SimpleRangeBoundsNotEqualDifferentEndKeyInclusive) {
bounds1.isSimpleRange = true;
bounds1.startKey = BSON("" << 1 << "" << 3);
bounds1.endKey = BSON("" << 2 << "" << 4);
- bounds1.endKeyInclusive = false;
+ bounds1.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
IndexBounds bounds2;
bounds1.isSimpleRange = true;
bounds1.startKey = BSON("" << 1 << "" << 3);
bounds1.endKey = BSON("" << 2 << "" << 4);
- bounds1.endKeyInclusive = true;
+ bounds1.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
ASSERT_FALSE(bounds1 == bounds2);
ASSERT_TRUE(bounds1 != bounds2);
diff --git a/src/mongo/db/query/internal_plans.cpp b/src/mongo/db/query/internal_plans.cpp
index b0ebdcd1096..3889e82031b 100644
--- a/src/mongo/db/query/internal_plans.cpp
+++ b/src/mongo/db/query/internal_plans.cpp
@@ -94,7 +94,7 @@ std::unique_ptr<PlanExecutor> InternalPlanner::indexScan(OperationContext* txn,
const IndexDescriptor* descriptor,
const BSONObj& startKey,
const BSONObj& endKey,
- bool endKeyInclusive,
+ BoundInclusion boundInclusion,
PlanExecutor::YieldPolicy yieldPolicy,
Direction direction,
int options) {
@@ -106,7 +106,7 @@ std::unique_ptr<PlanExecutor> InternalPlanner::indexScan(OperationContext* txn,
descriptor,
startKey,
endKey,
- endKeyInclusive,
+ boundInclusion,
direction,
options);
@@ -123,7 +123,7 @@ std::unique_ptr<PlanExecutor> InternalPlanner::deleteWithIndexScan(
const IndexDescriptor* descriptor,
const BSONObj& startKey,
const BSONObj& endKey,
- bool endKeyInclusive,
+ BoundInclusion boundInclusion,
PlanExecutor::YieldPolicy yieldPolicy,
Direction direction) {
auto ws = stdx::make_unique<WorkingSet>();
@@ -134,7 +134,7 @@ std::unique_ptr<PlanExecutor> InternalPlanner::deleteWithIndexScan(
descriptor,
startKey,
endKey,
- endKeyInclusive,
+ boundInclusion,
direction,
InternalPlanner::IXSCAN_FETCH);
@@ -172,7 +172,7 @@ std::unique_ptr<PlanStage> InternalPlanner::_indexScan(OperationContext* txn,
const IndexDescriptor* descriptor,
const BSONObj& startKey,
const BSONObj& endKey,
- bool endKeyInclusive,
+ BoundInclusion boundInclusion,
Direction direction,
int options) {
invariant(collection);
@@ -184,7 +184,7 @@ std::unique_ptr<PlanStage> InternalPlanner::_indexScan(OperationContext* txn,
params.bounds.isSimpleRange = true;
params.bounds.startKey = startKey;
params.bounds.endKey = endKey;
- params.bounds.endKeyInclusive = endKeyInclusive;
+ params.bounds.boundInclusion = boundInclusion;
std::unique_ptr<PlanStage> root = stdx::make_unique<IndexScan>(txn, params, ws, nullptr);
diff --git a/src/mongo/db/query/internal_plans.h b/src/mongo/db/query/internal_plans.h
index 7c98df487aa..40b42f75df1 100644
--- a/src/mongo/db/query/internal_plans.h
+++ b/src/mongo/db/query/internal_plans.h
@@ -92,7 +92,7 @@ public:
const IndexDescriptor* descriptor,
const BSONObj& startKey,
const BSONObj& endKey,
- bool endKeyInclusive,
+ BoundInclusion boundInclusion,
PlanExecutor::YieldPolicy yieldPolicy,
Direction direction = FORWARD,
int options = IXSCAN_DEFAULT);
@@ -106,7 +106,7 @@ public:
const IndexDescriptor* descriptor,
const BSONObj& startKey,
const BSONObj& endKey,
- bool endKeyInclusive,
+ BoundInclusion boundInclusion,
PlanExecutor::YieldPolicy yieldPolicy,
Direction direction = FORWARD);
@@ -133,7 +133,7 @@ private:
const IndexDescriptor* descriptor,
const BSONObj& startKey,
const BSONObj& endKey,
- bool endKeyInclusive,
+ BoundInclusion boundInclusion,
Direction direction = FORWARD,
int options = IXSCAN_DEFAULT);
};
diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp
index b221210c035..7867b7c0161 100644
--- a/src/mongo/db/query/planner_access.cpp
+++ b/src/mongo/db/query/planner_access.cpp
@@ -1384,7 +1384,7 @@ QuerySolutionNode* QueryPlannerAccess::makeIndexScan(const IndexEntry& index,
isn->bounds.isSimpleRange = true;
isn->bounds.startKey = startKey;
isn->bounds.endKey = endKey;
- isn->bounds.endKeyInclusive = false;
+ isn->bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
isn->queryCollator = query.getCollator();
unique_ptr<MatchExpression> filter = query.root()->shallowClone();
diff --git a/src/mongo/db/query/query_planner_common.cpp b/src/mongo/db/query/query_planner_common.cpp
index 8f129e54e5b..33eb72100cc 100644
--- a/src/mongo/db/query/query_planner_common.cpp
+++ b/src/mongo/db/query/query_planner_common.cpp
@@ -45,9 +45,19 @@ void QueryPlannerCommon::reverseScans(QuerySolutionNode* node) {
if (isn->bounds.isSimpleRange) {
std::swap(isn->bounds.startKey, isn->bounds.endKey);
- // XXX: Not having a startKeyInclusive means that if we reverse a max/min query
- // we have different results with and without the reverse...
- isn->bounds.endKeyInclusive = true;
+ // If only one bound is included, swap which one is included.
+ switch (isn->bounds.boundInclusion) {
+ case BoundInclusion::kIncludeStartKeyOnly:
+ isn->bounds.boundInclusion = BoundInclusion::kIncludeEndKeyOnly;
+ break;
+ case BoundInclusion::kIncludeEndKeyOnly:
+ isn->bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
+ break;
+ case BoundInclusion::kIncludeBothStartAndEndKeys:
+ case BoundInclusion::kExcludeBothStartAndEndKeys:
+ // These are both symmetric so no change needed.
+ break;
+ }
} else {
for (size_t i = 0; i < isn->bounds.fields.size(); ++i) {
std::vector<Interval>& iv = isn->bounds.fields[i].intervals;
diff --git a/src/mongo/db/query/query_solution.cpp b/src/mongo/db/query/query_solution.cpp
index ed9ab70b4cc..aa2426954c4 100644
--- a/src/mongo/db/query/query_solution.cpp
+++ b/src/mongo/db/query/query_solution.cpp
@@ -52,31 +52,33 @@ OrderedIntervalList buildStringBoundsOil(const std::string& keyName) {
BSONObjBuilder strBob;
strBob.appendMinForType("", BSONType::String);
strBob.appendMaxForType("", BSONType::String);
- ret.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(strBob.obj(), true, false));
+ ret.intervals.push_back(
+ IndexBoundsBuilder::makeRangeInterval(strBob.obj(), BoundInclusion::kIncludeStartKeyOnly));
BSONObjBuilder objBob;
objBob.appendMinForType("", BSONType::Object);
objBob.appendMaxForType("", BSONType::Object);
- ret.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(objBob.obj(), true, false));
+ ret.intervals.push_back(
+ IndexBoundsBuilder::makeRangeInterval(objBob.obj(), BoundInclusion::kIncludeStartKeyOnly));
BSONObjBuilder arrBob;
arrBob.appendMinForType("", BSONType::Array);
arrBob.appendMaxForType("", BSONType::Array);
- ret.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(arrBob.obj(), true, false));
+ ret.intervals.push_back(
+ IndexBoundsBuilder::makeRangeInterval(arrBob.obj(), BoundInclusion::kIncludeStartKeyOnly));
return ret;
}
bool rangeCanContainString(const BSONElement& startKey,
const BSONElement& endKey,
- bool endKeyInclusive) {
+ BoundInclusion boundInclusion) {
OrderedIntervalList stringBoundsOil = buildStringBoundsOil("");
OrderedIntervalList rangeOil;
BSONObjBuilder bob;
bob.appendAs(startKey, "");
bob.appendAs(endKey, "");
- rangeOil.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(bob.obj(), true, endKeyInclusive));
+ rangeOil.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(bob.obj(), boundInclusion));
IndexBoundsBuilder::intersectize(rangeOil, &stringBoundsOil);
return !stringBoundsOil.intervals.empty();
@@ -606,8 +608,11 @@ std::set<StringData> IndexScanNode::getFieldsWithStringBounds(const IndexBounds&
BSONElement endKey = endKeyIterator.next();
if (SimpleBSONElementComparator::kInstance.evaluate(startKey != endKey) ||
CollationIndexKey::isCollatableType(startKey.type())) {
- if (!rangeCanContainString(
- startKey, endKey, (startKeyIterator.more() || bounds.endKeyInclusive))) {
+ BoundInclusion boundInclusion = bounds.boundInclusion;
+ if (startKeyIterator.more()) {
+ boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
+ }
+ if (!rangeCanContainString(startKey, endKey, boundInclusion)) {
// If the first non-point range cannot contain strings, we don't need to
// add it to the return set.
keyPatternIterator.next();
diff --git a/src/mongo/db/query/query_solution_test.cpp b/src/mongo/db/query/query_solution_test.cpp
index a116d497661..b1f8bc18e4b 100644
--- a/src/mongo/db/query/query_solution_test.cpp
+++ b/src/mongo/db/query/query_solution_test.cpp
@@ -160,32 +160,32 @@ TEST(QuerySolutionTest, IntervalListNoPoints) {
OrderedIntervalList a{};
a.name = "a";
- a.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ a.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(a);
OrderedIntervalList b{};
b.name = "b";
- b.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ b.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(b);
OrderedIntervalList c{};
c.name = "c";
- c.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ c.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(c);
OrderedIntervalList d{};
d.name = "d";
- d.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ d.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(d);
OrderedIntervalList e{};
e.name = "e";
- e.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ e.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(e);
node.computeProperties();
@@ -216,20 +216,20 @@ TEST(QuerySolutionTest, IntervalListSomePoints) {
OrderedIntervalList c{};
c.name = "c";
- c.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ c.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(c);
OrderedIntervalList d{};
d.name = "d";
- d.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ d.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(d);
OrderedIntervalList e{};
e.name = "e";
- e.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ e.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(e);
node.computeProperties();
@@ -253,36 +253,38 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesFieldsContainingStrin
OrderedIntervalList oilA{};
oilA.name = "a";
- oilA.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 1), true, true));
+ oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 1), BoundInclusion::kIncludeBothStartAndEndKeys));
bounds.fields.push_back(oilA);
OrderedIntervalList oilB{};
oilB.name = "b";
- oilB.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << false << "" << true), true, true));
+ oilB.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << false << "" << true), BoundInclusion::kIncludeBothStartAndEndKeys));
bounds.fields.push_back(oilB);
OrderedIntervalList oilC{};
oilC.name = "c";
- oilC.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(BSON(""
- << "a"
- << ""
- << "b"),
- true,
- true));
+ oilC.intervals.push_back(
+ IndexBoundsBuilder::makeRangeInterval(BSON(""
+ << "a"
+ << ""
+ << "b"),
+ BoundInclusion::kIncludeBothStartAndEndKeys));
bounds.fields.push_back(oilC);
OrderedIntervalList oilD{};
oilD.name = "d";
oilD.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
- BSON("" << BSON("foo" << 1) << "" << BSON("foo" << 2)), true, true));
+ BSON("" << BSON("foo" << 1) << "" << BSON("foo" << 2)),
+ BoundInclusion::kIncludeBothStartAndEndKeys));
bounds.fields.push_back(oilD);
OrderedIntervalList oilE{};
oilE.name = "e";
oilE.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
- BSON("" << BSON_ARRAY(1 << 2 << 3) << "" << BSON_ARRAY(2 << 3 << 4)), true, true));
+ BSON("" << BSON_ARRAY(1 << 2 << 3) << "" << BSON_ARRAY(2 << 3 << 4)),
+ BoundInclusion::kIncludeBothStartAndEndKeys));
bounds.fields.push_back(oilE);
auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern);
@@ -300,7 +302,7 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromNonPointBo
bounds.isSimpleRange = true;
bounds.startKey = BSON("a" << 1 << "b" << 2 << "c" << 3 << "d" << 4 << "e" << 5);
bounds.endKey = BSON("a" << 1 << "b" << 2 << "c" << 3 << "d" << 5 << "e" << 5);
- bounds.endKeyInclusive = true;
+ bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern);
ASSERT_EQUALS(fields.size(), 1U);
@@ -317,7 +319,7 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromStringType
bounds.isSimpleRange = true;
bounds.startKey = fromjson("{'a': 1, 'b': 'a', 'c': 3, 'd': 4, 'e': 5}");
bounds.endKey = bounds.startKey;
- bounds.endKeyInclusive = true;
+ bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern);
ASSERT_EQUALS(fields.size(), 4U);
@@ -334,7 +336,7 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromArrayTypeB
bounds.isSimpleRange = true;
bounds.startKey = fromjson("{'a': 1, 'b': [1,2], 'c': 3, 'd': 4, 'e': 5}");
bounds.endKey = bounds.startKey;
- bounds.endKeyInclusive = true;
+ bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern);
ASSERT_EQUALS(fields.size(), 4U);
@@ -351,7 +353,7 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromObjectType
bounds.isSimpleRange = true;
bounds.startKey = fromjson("{'a': 1, 'b': {'foo': 2}, 'c': 3, 'd': 4, 'e': 5}");
bounds.endKey = bounds.startKey;
- bounds.endKeyInclusive = true;
+ bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern);
ASSERT_EQUALS(fields.size(), 4U);
@@ -362,6 +364,59 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromObjectType
ASSERT_TRUE(fields.count("e"));
}
+TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsWithExclusiveBounds) {
+ IndexBounds bounds;
+ BSONObj keyPattern = BSON("a" << 1 << "b" << 1);
+ bounds.isSimpleRange = true;
+ bounds.startKey = fromjson("{'a': 1, 'b': 1}");
+ bounds.endKey = fromjson("{'a': 2, 'b': 2}");
+ bounds.boundInclusion = BoundInclusion::kExcludeBothStartAndEndKeys;
+
+ auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern);
+ ASSERT_EQUALS(fields.size(), 1U);
+ ASSERT_FALSE(fields.count("a"));
+ ASSERT_TRUE(fields.count("b"));
+}
+
+TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsWithExclusiveBoundsOnBoundary) {
+ IndexBounds bounds;
+ BSONObj keyPattern = BSON("a" << 1 << "b" << 1);
+ bounds.isSimpleRange = true;
+ bounds.startKey = fromjson("{'a': 1, 'b': 1}");
+ bounds.endKey = fromjson("{'a': '', 'b': 1}");
+ bounds.boundInclusion = BoundInclusion::kExcludeBothStartAndEndKeys;
+
+ auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern);
+ ASSERT_EQUALS(fields.size(), 2U);
+ ASSERT_TRUE(fields.count("a"));
+ ASSERT_TRUE(fields.count("b"));
+}
+
+TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesNoStringsWithEmptyExclusiveBounds) {
+ IndexBounds bounds;
+ BSONObj keyPattern = BSON("a" << 1);
+ bounds.isSimpleRange = true;
+ bounds.startKey = fromjson("{'a': 1}");
+ bounds.endKey = fromjson("{'a': ''}");
+ bounds.boundInclusion = BoundInclusion::kExcludeBothStartAndEndKeys;
+
+ auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern);
+ ASSERT_EQUALS(fields.size(), 0U);
+}
+
+TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsWithInclusiveBounds) {
+ IndexBounds bounds;
+ BSONObj keyPattern = BSON("a" << 1);
+ bounds.isSimpleRange = true;
+ bounds.startKey = fromjson("{'a': 1}");
+ bounds.endKey = fromjson("{'a': ''}");
+ bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
+
+ auto fields = IndexScanNode::getFieldsWithStringBounds(bounds, keyPattern);
+ ASSERT_EQUALS(fields.size(), 1U);
+ ASSERT_TRUE(fields.count("a"));
+}
+
TEST(QuerySolutionTest, IndexScanNodeRemovesNonMatchingCollatedFieldsFromSortsOnSimpleBounds) {
IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1))};
CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString);
@@ -370,7 +425,7 @@ TEST(QuerySolutionTest, IndexScanNodeRemovesNonMatchingCollatedFieldsFromSortsOn
node.bounds.isSimpleRange = true;
node.bounds.startKey = BSON("a" << 1 << "b" << 1);
node.bounds.endKey = BSON("a" << 2 << "b" << 1);
- node.bounds.endKeyInclusive = true;
+ node.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
node.computeProperties();
@@ -388,7 +443,7 @@ TEST(QuerySolutionTest, IndexScanNodeGetFieldsWithStringBoundsCorrectlyHandlesEn
node.bounds.startKey = BSON("a" << 1 << "b" << 1);
node.bounds.endKey = BSON("a" << 1 << "b"
<< "");
- node.bounds.endKeyInclusive = false;
+ node.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
node.computeProperties();
@@ -398,7 +453,7 @@ TEST(QuerySolutionTest, IndexScanNodeGetFieldsWithStringBoundsCorrectlyHandlesEn
ASSERT_TRUE(sorts.count(BSON("a" << 1 << "b" << 1)));
ASSERT_TRUE(sorts.count(BSON("b" << 1)));
- node.bounds.endKeyInclusive = true;
+ node.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
node.computeProperties();
@@ -416,8 +471,8 @@ TEST(QuerySolutionTest, IndexScanNodeRemovesCollatedFieldsFromSortsIfCollationDi
OrderedIntervalList oilA{};
oilA.name = "a";
- oilA.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << MINKEY << "" << MAXKEY), true, true));
+ oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << MINKEY << "" << MAXKEY), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(oilA);
node.computeProperties();
@@ -432,8 +487,8 @@ TEST(QuerySolutionTest, IndexScanNodeDoesNotRemoveCollatedFieldsFromSortsIfColla
OrderedIntervalList oilA{};
oilA.name = "a";
- oilA.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << MINKEY << "" << MAXKEY), true, true));
+ oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << MINKEY << "" << MAXKEY), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(oilA);
node.computeProperties();
@@ -464,20 +519,20 @@ TEST(QuerySolutionTest, CompoundIndexWithNonMatchingCollationFiltersAllSortsWith
OrderedIntervalList c{};
c.name = "c";
- c.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << MINKEY << "" << MAXKEY), true, true));
+ c.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << MINKEY << "" << MAXKEY), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(c);
OrderedIntervalList d{};
d.name = "d";
- d.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ d.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(d);
OrderedIntervalList e{};
e.name = "e";
- e.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ e.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(e);
node.computeProperties();
@@ -498,7 +553,8 @@ TEST(QuerySolutionTest, IndexScanNodeWithNonMatchingCollationFiltersObjectField)
OrderedIntervalList oilA{};
oilA.name = "a";
oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
- BSON("" << BSON("foo" << 1) << "" << BSON("foo" << 2)), true, true));
+ BSON("" << BSON("foo" << 1) << "" << BSON("foo" << 2)),
+ BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(oilA);
node.computeProperties();
@@ -516,8 +572,9 @@ TEST(QuerySolutionTest, IndexScanNodeWithNonMatchingCollationFiltersArrayField)
OrderedIntervalList oilA{};
oilA.name = "a";
- oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
- BSON("" << BSON_ARRAY(1) << "" << BSON_ARRAY(2)), true, true));
+ oilA.intervals.push_back(
+ IndexBoundsBuilder::makeRangeInterval(BSON("" << BSON_ARRAY(1) << "" << BSON_ARRAY(2)),
+ BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(oilA);
node.computeProperties();
@@ -533,18 +590,18 @@ TEST(QuerySolutionTest, WithNonMatchingCollatorAndNoEqualityPrefixSortsAreNotDup
OrderedIntervalList oilA{};
oilA.name = "a";
- oilA.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(oilA);
OrderedIntervalList oilB{};
oilB.name = "b";
- oilB.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(BSON(""
- << "a"
- << ""
- << "b"),
- true,
- true));
+ oilB.intervals.push_back(
+ IndexBoundsBuilder::makeRangeInterval(BSON(""
+ << "a"
+ << ""
+ << "b"),
+ BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(oilB);
node.computeProperties();
@@ -559,12 +616,12 @@ TEST(QuerySolutionTest, IndexScanNodeHasFieldIncludesStringFieldWhenNoCollator)
OrderedIntervalList oilA{};
oilA.name = "a";
- oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(BSON(""
- << "str"
- << ""
- << "str"),
- true,
- true));
+ oilA.intervals.push_back(
+ IndexBoundsBuilder::makeRangeInterval(BSON(""
+ << "str"
+ << ""
+ << "str"),
+ BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(oilA);
OrderedIntervalList oilB{};
@@ -582,7 +639,7 @@ TEST(QuerySolutionTest, IndexScanNodeHasFieldIncludesSimpleBoundsStringFieldWhen
node.bounds.isSimpleRange = true;
node.bounds.startKey = BSON("a" << 1 << "b" << 2);
node.bounds.endKey = BSON("a" << 2 << "b" << 1);
- node.bounds.endKeyInclusive = false;
+ node.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
ASSERT_TRUE(node.hasField("a"));
ASSERT_TRUE(node.hasField("b"));
@@ -595,18 +652,18 @@ TEST(QuerySolutionTest, IndexScanNodeHasFieldExcludesStringFieldWhenIndexHasColl
OrderedIntervalList oilA{};
oilA.name = "a";
- oilA.intervals.push_back(
- IndexBoundsBuilder::makeRangeInterval(BSON("" << 1 << "" << 2), true, true));
+ oilA.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(
+ BSON("" << 1 << "" << 2), BoundInclusion::kIncludeBothStartAndEndKeys));
node.bounds.fields.push_back(oilA);
OrderedIntervalList oilB{};
oilB.name = "b";
- oilB.intervals.push_back(IndexBoundsBuilder::makeRangeInterval(BSON(""
- << "bar"
- << ""
- << "foo"),
- true,
- false));
+ oilB.intervals.push_back(
+ IndexBoundsBuilder::makeRangeInterval(BSON(""
+ << "bar"
+ << ""
+ << "foo"),
+ BoundInclusion::kIncludeStartKeyOnly));
node.bounds.fields.push_back(oilB);
ASSERT_TRUE(node.hasField("a"));
@@ -621,7 +678,7 @@ TEST(QuerySolutionTest, IndexScanNodeHasFieldExcludesSimpleBoundsStringFieldWhen
node.bounds.isSimpleRange = true;
node.bounds.startKey = BSON("a" << 1 << "b" << 2);
node.bounds.endKey = BSON("a" << 2 << "b" << 1);
- node.bounds.endKeyInclusive = false;
+ node.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
ASSERT_TRUE(node.hasField("a"));
ASSERT_FALSE(node.hasField("b"));
diff --git a/src/mongo/db/range_deleter_db_env.cpp b/src/mongo/db/range_deleter_db_env.cpp
index e064de33599..2f692111c0a 100644
--- a/src/mongo/db/range_deleter_db_env.cpp
+++ b/src/mongo/db/range_deleter_db_env.cpp
@@ -90,7 +90,7 @@ bool RangeDeleterDBEnv::deleteRange(OperationContext* txn,
*deletedDocs =
Helpers::removeRange(txn,
KeyRange(ns, inclusiveLower, exclusiveUpper, keyPattern),
- false, /*maxInclusive*/
+ BoundInclusion::kIncludeStartKeyOnly,
writeConcern,
removeSaverPtr,
fromMigrate,
diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp
index 3a3c280fa7a..acabfe69eb9 100644
--- a/src/mongo/db/repl/storage_interface_impl.cpp
+++ b/src/mongo/db/repl/storage_interface_impl.cpp
@@ -506,7 +506,6 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn,
auto maxKey = Helpers::toKeyFormat(keyPattern.extendRangeBound({}, true));
auto bounds =
isForward ? std::make_pair(minKey, maxKey) : std::make_pair(maxKey, minKey);
- bool endKeyInclusive = false;
planExecutor = isFind
? InternalPlanner::indexScan(txn,
@@ -514,7 +513,7 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn,
indexDescriptor,
bounds.first,
bounds.second,
- endKeyInclusive,
+ BoundInclusion::kIncludeStartKeyOnly,
PlanExecutor::YIELD_MANUAL,
direction,
InternalPlanner::IXSCAN_FETCH)
@@ -524,7 +523,7 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn,
indexDescriptor,
bounds.first,
bounds.second,
- endKeyInclusive,
+ BoundInclusion::kIncludeStartKeyOnly,
PlanExecutor::YIELD_MANUAL,
direction);
}
diff --git a/src/mongo/db/s/check_sharding_index_command.cpp b/src/mongo/db/s/check_sharding_index_command.cpp
index 311ba971233..0d7ef33de31 100644
--- a/src/mongo/db/s/check_sharding_index_command.cpp
+++ b/src/mongo/db/s/check_sharding_index_command.cpp
@@ -135,14 +135,15 @@ public:
max = Helpers::toKeyFormat(kp.extendRangeBound(max, false));
}
- unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn,
- collection,
- idx,
- min,
- max,
- false, // endKeyInclusive
- PlanExecutor::YIELD_MANUAL,
- InternalPlanner::FORWARD));
+ unique_ptr<PlanExecutor> exec(
+ InternalPlanner::indexScan(txn,
+ collection,
+ idx,
+ min,
+ max,
+ BoundInclusion::kIncludeStartKeyOnly,
+ PlanExecutor::YIELD_MANUAL,
+ InternalPlanner::FORWARD));
exec->setYieldPolicy(PlanExecutor::YIELD_AUTO, collection);
// Find the 'missingField' value used to represent a missing document field in a key of
diff --git a/src/mongo/db/s/collection_range_deleter.cpp b/src/mongo/db/s/collection_range_deleter.cpp
index 37f1899b8af..4feb104f748 100644
--- a/src/mongo/db/s/collection_range_deleter.cpp
+++ b/src/mongo/db/s/collection_range_deleter.cpp
@@ -169,15 +169,16 @@ int CollectionRangeDeleter::_doDeletion(OperationContext* txn,
return -1;
}
- std::unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn,
- collection,
- desc,
- min,
- max,
- false,
- PlanExecutor::YIELD_MANUAL,
- InternalPlanner::FORWARD,
- InternalPlanner::IXSCAN_FETCH));
+ std::unique_ptr<PlanExecutor> exec(
+ InternalPlanner::indexScan(txn,
+ collection,
+ desc,
+ min,
+ max,
+ BoundInclusion::kIncludeStartKeyOnly,
+ PlanExecutor::YIELD_MANUAL,
+ InternalPlanner::FORWARD,
+ InternalPlanner::IXSCAN_FETCH));
int numDeleted = 0;
const int maxItersBeforeYield = std::max(static_cast<int>(internalQueryExecYieldIterations), 1);
diff --git a/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp b/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp
index d73e58cbfe4..a6f40042f1c 100644
--- a/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp
+++ b/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp
@@ -569,13 +569,14 @@ Status MigrationChunkClonerSourceLegacy::_storeCurrentLocs(OperationContext* txn
BSONObj min = Helpers::toKeyFormat(kp.extendRangeBound(_args.getMinKey(), false));
BSONObj max = Helpers::toKeyFormat(kp.extendRangeBound(_args.getMaxKey(), false));
- std::unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn,
- collection,
- idx,
- min,
- max,
- false, // endKeyInclusive
- PlanExecutor::YIELD_MANUAL));
+ std::unique_ptr<PlanExecutor> exec(
+ InternalPlanner::indexScan(txn,
+ collection,
+ idx,
+ min,
+ max,
+ BoundInclusion::kIncludeStartKeyOnly,
+ PlanExecutor::YIELD_MANUAL));
// We can afford to yield here because any change to the base data that we might miss is already
// being queued and will migrate in the 'transferMods' stage.
diff --git a/src/mongo/db/s/split_chunk_command.cpp b/src/mongo/db/s/split_chunk_command.cpp
index 3be198a377d..c5719f9c7e1 100644
--- a/src/mongo/db/s/split_chunk_command.cpp
+++ b/src/mongo/db/s/split_chunk_command.cpp
@@ -78,7 +78,7 @@ bool checkIfSingleDoc(OperationContext* txn,
idx,
newmin,
newmax,
- false, // endKeyInclusive
+ BoundInclusion::kIncludeStartKeyOnly,
PlanExecutor::YIELD_MANUAL));
// check if exactly one document found
PlanExecutor::ExecState state;
diff --git a/src/mongo/db/s/split_vector_command.cpp b/src/mongo/db/s/split_vector_command.cpp
index 77503dda5f5..9b013430f3b 100644
--- a/src/mongo/db/s/split_vector_command.cpp
+++ b/src/mongo/db/s/split_vector_command.cpp
@@ -253,14 +253,15 @@ public:
long long currCount = 0;
long long numChunks = 0;
- unique_ptr<PlanExecutor> exec(InternalPlanner::indexScan(txn,
- collection,
- idx,
- min,
- max,
- false, // endKeyInclusive
- PlanExecutor::YIELD_MANUAL,
- InternalPlanner::FORWARD));
+ unique_ptr<PlanExecutor> exec(
+ InternalPlanner::indexScan(txn,
+ collection,
+ idx,
+ min,
+ max,
+ BoundInclusion::kIncludeStartKeyOnly,
+ PlanExecutor::YIELD_MANUAL,
+ InternalPlanner::FORWARD));
BSONObj currKey;
PlanExecutor::ExecState state = exec->getNext(&currKey, NULL);
@@ -334,7 +335,7 @@ public:
idx,
min,
max,
- false, // endKeyInclusive
+ BoundInclusion::kIncludeStartKeyOnly,
PlanExecutor::YIELD_MANUAL,
InternalPlanner::FORWARD);
diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp
index 09aea33b069..c2ee5ba1561 100644
--- a/src/mongo/db/ttl.cpp
+++ b/src/mongo/db/ttl.cpp
@@ -221,7 +221,6 @@ private:
const Date_t expirationTime = Date_t::now() - Seconds(secondsExpireElt.numberLong());
const BSONObj startKey = BSON("" << kDawnOfTime);
const BSONObj endKey = BSON("" << expirationTime);
- const bool endKeyInclusive = true;
// The canonical check as to whether a key pattern element is "ascending" or
// "descending" is (elt.number() >= 0). This is defined by the Ordering class.
const InternalPlanner::Direction direction = (key.firstElement().number() >= 0)
@@ -251,7 +250,7 @@ private:
desc,
startKey,
endKey,
- endKeyInclusive,
+ BoundInclusion::kIncludeBothStartAndEndKeys,
PlanExecutor::YIELD_AUTO,
direction);
diff --git a/src/mongo/dbtests/dbhelper_tests.cpp b/src/mongo/dbtests/dbhelper_tests.cpp
index f7027bb8f91..1ed948092c7 100644
--- a/src/mongo/dbtests/dbhelper_tests.cpp
+++ b/src/mongo/dbtests/dbhelper_tests.cpp
@@ -74,7 +74,8 @@ public:
KeyRange range(ns, BSON("_id" << _min), BSON("_id" << _max), BSON("_id" << 1));
mongo::WriteConcernOptions dummyWriteConcern;
- Helpers::removeRange(&txn, range, false, dummyWriteConcern);
+ Helpers::removeRange(
+ &txn, range, BoundInclusion::kIncludeStartKeyOnly, dummyWriteConcern);
}
// Check that the expected documents remain.
diff --git a/src/mongo/dbtests/query_plan_executor.cpp b/src/mongo/dbtests/query_plan_executor.cpp
index 9cd8c8f4428..e1319cecf3f 100644
--- a/src/mongo/dbtests/query_plan_executor.cpp
+++ b/src/mongo/dbtests/query_plan_executor.cpp
@@ -141,7 +141,7 @@ public:
ixparams.bounds.isSimpleRange = true;
ixparams.bounds.startKey = BSON("" << start);
ixparams.bounds.endKey = BSON("" << end);
- ixparams.bounds.endKeyInclusive = true;
+ ixparams.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
ixparams.direction = 1;
const Collection* coll = db->getCollection(nss.ns());
diff --git a/src/mongo/dbtests/query_stage_and.cpp b/src/mongo/dbtests/query_stage_and.cpp
index 1939ce4c969..340101f66fa 100644
--- a/src/mongo/dbtests/query_stage_and.cpp
+++ b/src/mongo/dbtests/query_stage_and.cpp
@@ -193,7 +193,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -201,7 +201,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1), coll);
params.bounds.startKey = BSON("" << 10);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -299,7 +299,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -387,7 +387,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -395,7 +395,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1), coll);
params.bounds.startKey = BSON("" << 10);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -442,7 +442,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20 << "" << big);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -450,7 +450,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1), coll);
params.bounds.startKey = BSON("" << 10);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -495,7 +495,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -503,7 +503,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1 << "big" << 1), coll);
params.bounds.startKey = BSON("" << 10 << "" << big);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -543,7 +543,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -551,7 +551,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1), coll);
params.bounds.startKey = BSON("" << 10);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -559,7 +559,7 @@ public:
params.descriptor = getIndex(BSON("baz" << 1), coll);
params.bounds.startKey = BSON("" << 5);
params.bounds.endKey = BSON("" << 15);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -610,7 +610,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -618,7 +618,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1 << "big" << 1), coll);
params.bounds.startKey = BSON("" << 10 << "" << big);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -626,7 +626,7 @@ public:
params.descriptor = getIndex(BSON("baz" << 1), coll);
params.bounds.startKey = BSON("" << 5);
params.bounds.endKey = BSON("" << 15);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -664,7 +664,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -672,7 +672,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1), coll);
params.bounds.startKey = BSON("" << 5);
params.bounds.endKey = BSON("" << 5);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -727,7 +727,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 100);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -739,7 +739,7 @@ public:
// want to include that in our scan.
params.bounds.endKey = BSON(""
<< "");
- params.bounds.endKeyInclusive = false;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -779,7 +779,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
IndexScan* firstScan = new IndexScan(&_txn, params, &ws, NULL);
@@ -792,7 +792,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1), coll);
params.bounds.startKey = BSON("" << 10);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -838,7 +838,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -846,7 +846,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1), coll);
params.bounds.startKey = BSON("" << 10);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
IndexScan* secondScan = new IndexScan(&_txn, params, &ws, NULL);
@@ -1032,7 +1032,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 1);
params.bounds.endKey = BSON("" << 1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -1165,7 +1165,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 1);
params.bounds.endKey = BSON("" << 1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -1210,7 +1210,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 7);
params.bounds.endKey = BSON("" << 7);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -1218,7 +1218,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1), coll);
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSON("" << 20);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -1259,7 +1259,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 7);
params.bounds.endKey = BSON("" << 7);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -1267,7 +1267,7 @@ public:
params.descriptor = getIndex(BSON("bar" << 1), coll);
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSON("" << 20);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -1304,7 +1304,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 1);
params.bounds.endKey = BSON("" << 1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ah->addChild(new IndexScan(&_txn, params, &ws, NULL));
@@ -1370,7 +1370,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 1);
params.bounds.endKey = BSON("" << 1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
IndexScan* firstScan = new IndexScan(&_txn, params, &ws, NULL);
@@ -1424,7 +1424,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 1);
params.bounds.endKey = BSON("" << 1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
as->addChild(new IndexScan(&_txn, params, &ws, NULL));
diff --git a/src/mongo/dbtests/query_stage_count.cpp b/src/mongo/dbtests/query_stage_count.cpp
index 4a642fb26d8..ccc08cab5a5 100644
--- a/src/mongo/dbtests/query_stage_count.cpp
+++ b/src/mongo/dbtests/query_stage_count.cpp
@@ -212,7 +212,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 0);
params.bounds.endKey = BSON("" << kDocuments + 1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
// This child stage gets owned and freed by its parent CountStage
diff --git a/src/mongo/dbtests/query_stage_ixscan.cpp b/src/mongo/dbtests/query_stage_ixscan.cpp
index 7ad4e1a0b84..4f39d27588e 100644
--- a/src/mongo/dbtests/query_stage_ixscan.cpp
+++ b/src/mongo/dbtests/query_stage_ixscan.cpp
@@ -108,7 +108,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = startKey;
params.bounds.endKey = endKey;
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
// This child stage gets owned and freed by the caller.
diff --git a/src/mongo/dbtests/query_stage_merge_sort.cpp b/src/mongo/dbtests/query_stage_merge_sort.cpp
index d2a9139b2c6..e69c0e9d18b 100644
--- a/src/mongo/dbtests/query_stage_merge_sort.cpp
+++ b/src/mongo/dbtests/query_stage_merge_sort.cpp
@@ -154,7 +154,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = objWithMinKey(1);
params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
@@ -225,7 +225,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = objWithMinKey(1);
params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
@@ -295,7 +295,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = objWithMinKey(1);
params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
@@ -367,7 +367,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = objWithMaxKey(1);
params.bounds.endKey = objWithMinKey(1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
// This is the direction along the index.
params.direction = 1;
ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
@@ -438,7 +438,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = objWithMinKey(1);
params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
@@ -492,7 +492,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = objWithMinKey(1);
params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
int numIndices = 20;
@@ -551,7 +551,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = objWithMinKey(1);
params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
// Index 'a'+i has foo equal to 'i'.
@@ -681,7 +681,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 5);
params.bounds.endKey = BSON("" << 10);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
auto fetchStage = stdx::make_unique<FetchStage>(
&_txn, &ws, new IndexScan(&_txn, params, &ws, nullptr), nullptr, coll);
@@ -695,7 +695,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 4);
params.bounds.endKey = BSON("" << 10);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
auto fetchStage = stdx::make_unique<FetchStage>(
&_txn, &ws, new IndexScan(&_txn, params, &ws, nullptr), nullptr, coll);
@@ -782,7 +782,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = objWithMinKey(1);
params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
@@ -856,7 +856,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = objWithMinKey(1);
params.bounds.endKey = objWithMaxKey(1);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ms->addChild(new IndexScan(&_txn, params, ws.get(), NULL));
diff --git a/src/mongo/dbtests/query_stage_multiplan.cpp b/src/mongo/dbtests/query_stage_multiplan.cpp
index 67f43043a46..2f1ea56d50e 100644
--- a/src/mongo/dbtests/query_stage_multiplan.cpp
+++ b/src/mongo/dbtests/query_stage_multiplan.cpp
@@ -147,7 +147,7 @@ public:
ixparams.bounds.isSimpleRange = true;
ixparams.bounds.startKey = BSON("" << 7);
ixparams.bounds.endKey = BSON("" << 7);
- ixparams.bounds.endKeyInclusive = true;
+ ixparams.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
ixparams.direction = 1;
unique_ptr<WorkingSet> sharedWs(new WorkingSet());
diff --git a/src/mongo/dbtests/query_stage_tests.cpp b/src/mongo/dbtests/query_stage_tests.cpp
index 70957466b2d..93ebb697d12 100644
--- a/src/mongo/dbtests/query_stage_tests.cpp
+++ b/src/mongo/dbtests/query_stage_tests.cpp
@@ -149,7 +149,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSONObj();
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = -1;
ASSERT_EQUALS(countResults(params), 21);
@@ -167,7 +167,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSON("" << 30);
- params.bounds.endKeyInclusive = false;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeStartKeyOnly;
params.direction = 1;
ASSERT_EQUALS(countResults(params), 10);
@@ -185,7 +185,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSON("" << 30);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ASSERT_EQUALS(countResults(params), 11);
@@ -204,7 +204,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSON("" << 30);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ASSERT_EQUALS(countResults(params, BSON("foo" << 25)), 1);
@@ -223,7 +223,7 @@ public:
params.bounds.isSimpleRange = true;
params.bounds.startKey = BSON("" << 20);
params.bounds.endKey = BSON("" << 30);
- params.bounds.endKeyInclusive = true;
+ params.bounds.boundInclusion = BoundInclusion::kIncludeBothStartAndEndKeys;
params.direction = 1;
ASSERT_THROWS(countResults(params, BSON("baz" << 25)), MsgAssertionException);