summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorDavid Hatch <david.hatch@mongodb.com>2016-07-08 17:16:15 -0400
committerDavid Hatch <david.hatch@mongodb.com>2016-07-28 22:33:08 -0400
commit13448cde4947adb5c935bb034187480365cf692f (patch)
tree2cf01918467d33755976588fac7dedb530530c7a /src/mongo/db
parent3a9e531cd088b6c10bec4b1d9d6ea49b3db8ce7a (diff)
downloadmongo-13448cde4947adb5c935bb034187480365cf692f.tar.gz
SERVER-24239 Allow creation of indexes with the same key pattern.
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/auth/auth_index_d.cpp7
-rw-r--r--src/mongo/db/catalog/coll_mod.cpp23
-rw-r--r--src/mongo/db/catalog/drop_indexes.cpp20
-rw-r--r--src/mongo/db/catalog/index_catalog.cpp81
-rw-r--r--src/mongo/db/catalog/index_catalog.h31
-rw-r--r--src/mongo/db/commands/dbcommands.cpp3
-rw-r--r--src/mongo/db/dbhelpers.cpp78
-rw-r--r--src/mongo/db/exec/stagedebug_cmd.cpp30
-rw-r--r--src/mongo/db/exec/subplan.cpp4
-rw-r--r--src/mongo/db/exec/subplan.h3
-rw-r--r--src/mongo/db/query/get_executor.cpp13
-rw-r--r--src/mongo/db/query/index_entry.h9
-rw-r--r--src/mongo/db/query/plan_cache_test.cpp121
-rw-r--r--src/mongo/db/query/planner_access.cpp26
-rw-r--r--src/mongo/db/query/planner_analysis.cpp11
-rw-r--r--src/mongo/db/query/planner_analysis_test.cpp3
-rw-r--r--src/mongo/db/query/query_planner.cpp33
-rw-r--r--src/mongo/db/query/query_planner.h3
-rw-r--r--src/mongo/db/query/query_planner_common.cpp2
-rw-r--r--src/mongo/db/query/query_planner_test.cpp22
-rw-r--r--src/mongo/db/query/query_planner_test_fixture.cpp14
-rw-r--r--src/mongo/db/query/query_planner_test_fixture.h2
-rw-r--r--src/mongo/db/query/query_planner_test_lib.cpp26
-rw-r--r--src/mongo/db/query/query_solution.cpp67
-rw-r--r--src/mongo/db/query/query_solution.h32
-rw-r--r--src/mongo/db/query/query_solution_test.cpp65
-rw-r--r--src/mongo/db/query/stage_builder.cpp15
-rw-r--r--src/mongo/db/repl/oplog_buffer_collection.cpp6
-rw-r--r--src/mongo/db/repl/storage_interface.h10
-rw-r--r--src/mongo/db/repl/storage_interface_impl.cpp27
-rw-r--r--src/mongo/db/repl/storage_interface_impl.h5
-rw-r--r--src/mongo/db/repl/storage_interface_impl_test.cpp62
-rw-r--r--src/mongo/db/repl/storage_interface_mock.h17
-rw-r--r--src/mongo/db/ttl.cpp5
34 files changed, 509 insertions, 367 deletions
diff --git a/src/mongo/db/auth/auth_index_d.cpp b/src/mongo/db/auth/auth_index_d.cpp
index 8f45cfda0e3..427900c8377 100644
--- a/src/mongo/db/auth/auth_index_d.cpp
+++ b/src/mongo/db/auth/auth_index_d.cpp
@@ -97,10 +97,11 @@ Status verifySystemIndexes(OperationContext* txn) {
}
IndexCatalog* indexCatalog = collection->getIndexCatalog();
- IndexDescriptor* oldIndex = NULL;
+ std::vector<IndexDescriptor*> indexes;
+ indexCatalog->findIndexesByKeyPattern(txn, v1SystemUsersKeyPattern, false, &indexes);
- if (indexCatalog &&
- (oldIndex = indexCatalog->findIndexByKeyPattern(txn, v1SystemUsersKeyPattern))) {
+ if (indexCatalog && !indexes.empty()) {
+ fassert(ErrorCodes::AmbiguousIndexKeyPattern, indexes.size() == 1);
return Status(ErrorCodes::AuthSchemaIncompatible,
"Old 2.4 style user index identified. "
"The authentication schema needs to be updated by "
diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp
index 75769b5c752..8d142b25a12 100644
--- a/src/mongo/db/catalog/coll_mod.cpp
+++ b/src/mongo/db/catalog/coll_mod.cpp
@@ -118,14 +118,29 @@ Status collMod(OperationContext* txn,
continue;
}
- const IndexDescriptor* idx =
- coll->getIndexCatalog()->findIndexByKeyPattern(txn, keyPattern);
- if (idx == NULL) {
+ std::vector<IndexDescriptor*> indexes;
+ coll->getIndexCatalog()->findIndexesByKeyPattern(txn, keyPattern, false, &indexes);
+
+ if (indexes.size() > 1) {
+ errorStatus =
+ Status(ErrorCodes::AmbiguousIndexKeyPattern,
+ str::stream() << "index keyPattern " << keyPattern << " matches "
+ << indexes.size()
+ << " indexes,"
+ << " must use index name. "
+ << "Conflicting indexes:"
+ << indexes[0]->infoObj()
+ << ", "
+ << indexes[1]->infoObj());
+ continue;
+ } else if (indexes.empty()) {
errorStatus = Status(
- ErrorCodes::InvalidOptions,
+ ErrorCodes::IndexNotFound,
str::stream() << "cannot find index " << keyPattern << " for ns " << nss.ns());
continue;
}
+
+ const IndexDescriptor* idx = indexes[0];
BSONElement oldExpireSecs = idx->infoObj().getField("expireAfterSeconds");
if (oldExpireSecs.eoo()) {
errorStatus =
diff --git a/src/mongo/db/catalog/drop_indexes.cpp b/src/mongo/db/catalog/drop_indexes.cpp
index cab438315a9..3def51b36ed 100644
--- a/src/mongo/db/catalog/drop_indexes.cpp
+++ b/src/mongo/db/catalog/drop_indexes.cpp
@@ -103,14 +103,24 @@ Status wrappedRun(OperationContext* txn,
}
if (f.type() == Object) {
- IndexDescriptor* desc =
- collection->getIndexCatalog()->findIndexByKeyPattern(txn, f.embeddedObject());
- if (desc == NULL) {
+ std::vector<IndexDescriptor*> indexes;
+ collection->getIndexCatalog()->findIndexesByKeyPattern(
+ txn, f.embeddedObject(), false, &indexes);
+ if (indexes.empty()) {
return Status(ErrorCodes::IndexNotFound,
- str::stream() << "can't find index with key: "
- << f.embeddedObject().toString());
+ str::stream() << "can't find index with key: " << f.embeddedObject());
+ } else if (indexes.size() > 1) {
+ return Status(ErrorCodes::AmbiguousIndexKeyPattern,
+ str::stream() << indexes.size() << " indexes found for key: "
+ << f.embeddedObject()
+ << ", identify by name instead."
+ << " Conflicting indexes: "
+ << indexes[0]->infoObj()
+ << ", "
+ << indexes[1]->infoObj());
}
+ IndexDescriptor* desc = indexes[0];
if (desc->isIdIndex()) {
return Status(ErrorCodes::InvalidOptions, "cannot drop _id index");
}
diff --git a/src/mongo/db/catalog/index_catalog.cpp b/src/mongo/db/catalog/index_catalog.cpp
index 7d51b2b7d01..c6c6a9fa034 100644
--- a/src/mongo/db/catalog/index_catalog.cpp
+++ b/src/mongo/db/catalog/index_catalog.cpp
@@ -656,6 +656,7 @@ Status IndexCatalog::_doesSpecConflictWithExisting(OperationContext* txn,
invariant(name[0]);
const BSONObj key = spec.getObjectField("key");
+ const BSONObj collation = spec.getObjectField("collation");
{
// Check both existing and in-progress indexes (2nd param = true)
@@ -663,15 +664,29 @@ Status IndexCatalog::_doesSpecConflictWithExisting(OperationContext* txn,
if (desc) {
// index already exists with same name
- if (!desc->keyPattern().equal(key))
+ if (desc->keyPattern().equal(key) &&
+ desc->infoObj().getObjectField("collation") != collation) {
+ // key patterns are equal but collations differ.
+ return Status(ErrorCodes::IndexOptionsConflict,
+ str::stream()
+ << "An index with the same key pattern, but a different "
+ << "collation already exists with the same name. Try again with "
+ << "a unique name. "
+ << "Existing index: "
+ << desc->infoObj()
+ << " Requested index: "
+ << spec);
+ }
+
+ if (!desc->keyPattern().equal(key) ||
+ desc->infoObj().getObjectField("collation") != collation) {
return Status(ErrorCodes::IndexKeySpecsConflict,
- str::stream() << "Trying to create an index "
- << "with same name "
- << name
- << " with different key spec "
- << key
- << " vs existing spec "
- << desc->keyPattern());
+ str::stream() << "Index must have unique name."
+ << "The existing index: "
+ << desc->infoObj()
+ << " has the same name as the requested index: "
+ << spec);
+ }
IndexDescriptor temp(_collection, _getAccessMethodName(txn, key), spec);
if (!desc->areIndexOptionsEquivalent(&temp))
@@ -682,30 +697,34 @@ Status IndexCatalog::_doesSpecConflictWithExisting(OperationContext* txn,
// Index already exists with the same options, so no need to build a new
// one (not an error). Most likely requested by a client using ensureIndex.
return Status(ErrorCodes::IndexAlreadyExists,
- str::stream() << "index already exists: " << name);
+ str::stream() << "Identical index already exists: " << name);
}
}
{
- // Check both existing and in-progress indexes (2nd param = true)
- const IndexDescriptor* desc = findIndexByKeyPattern(txn, key, true);
+ // Check both existing and in-progress indexes.
+ const bool findInProgressIndexes = true;
+ const IndexDescriptor* desc =
+ findIndexByKeyPatternAndCollationSpec(txn, key, collation, findInProgressIndexes);
if (desc) {
- LOG(2) << "index already exists with diff name " << name << ' ' << key << endl;
+ LOG(2) << "index already exists with diff name " << name << " pattern: " << key
+ << " collation: " << collation << endl;
IndexDescriptor temp(_collection, _getAccessMethodName(txn, key), spec);
if (!desc->areIndexOptionsEquivalent(&temp))
return Status(ErrorCodes::IndexOptionsConflict,
- str::stream() << "Index with pattern: " << key
- << " already exists with different options");
+ str::stream() << "Index: " << spec
+ << " already exists with different options: "
+ << desc->infoObj());
return Status(ErrorCodes::IndexAlreadyExists,
- str::stream() << "index already exists: " << name);
+ str::stream() << "index already exists with different name: " << name);
}
}
if (numIndexesTotal(txn) >= _maxNumIndexesAllowed) {
string s = str::stream() << "add index fails, too many indexes for "
- << _collection->ns().ns() << " key:" << key.toString();
+ << _collection->ns().ns() << " key:" << key;
log() << s;
return Status(ErrorCodes::CannotCreateIndex, s);
}
@@ -1051,18 +1070,35 @@ IndexDescriptor* IndexCatalog::findIndexByName(OperationContext* txn,
return NULL;
}
-IndexDescriptor* IndexCatalog::findIndexByKeyPattern(OperationContext* txn,
- const BSONObj& key,
- bool includeUnfinishedIndexes) const {
+IndexDescriptor* IndexCatalog::findIndexByKeyPatternAndCollationSpec(
+ OperationContext* txn,
+ const BSONObj& key,
+ const BSONObj& collationSpec,
+ bool includeUnfinishedIndexes) const {
IndexIterator ii = getIndexIterator(txn, includeUnfinishedIndexes);
while (ii.more()) {
IndexDescriptor* desc = ii.next();
- if (desc->keyPattern() == key)
+ if (desc->keyPattern() == key &&
+ desc->infoObj().getObjectField("collation") == collationSpec)
return desc;
}
return NULL;
}
+void IndexCatalog::findIndexesByKeyPattern(OperationContext* txn,
+ const BSONObj& key,
+ bool includeUnfinishedIndexes,
+ std::vector<IndexDescriptor*>* matches) const {
+ invariant(matches);
+ IndexIterator ii = getIndexIterator(txn, includeUnfinishedIndexes);
+ while (ii.more()) {
+ IndexDescriptor* desc = ii.next();
+ if (desc->keyPattern() == key) {
+ matches->push_back(desc);
+ }
+ }
+}
+
IndexDescriptor* IndexCatalog::findShardKeyPrefixedIndex(OperationContext* txn,
const BSONObj& shardKey,
bool requireSingleKey) const {
@@ -1071,6 +1107,7 @@ IndexDescriptor* IndexCatalog::findShardKeyPrefixedIndex(OperationContext* txn,
IndexIterator ii = getIndexIterator(txn, false);
while (ii.more()) {
IndexDescriptor* desc = ii.next();
+ bool hasSimpleCollation = desc->infoObj().getObjectField("collation").isEmpty();
if (desc->isPartial())
continue;
@@ -1078,10 +1115,10 @@ IndexDescriptor* IndexCatalog::findShardKeyPrefixedIndex(OperationContext* txn,
if (!shardKey.isPrefixOf(desc->keyPattern()))
continue;
- if (!desc->isMultikey(txn))
+ if (!desc->isMultikey(txn) && hasSimpleCollation)
return desc;
- if (!requireSingleKey)
+ if (!requireSingleKey && hasSimpleCollation)
best = desc;
}
diff --git a/src/mongo/db/catalog/index_catalog.h b/src/mongo/db/catalog/index_catalog.h
index a982a6e976b..37ec3305615 100644
--- a/src/mongo/db/catalog/index_catalog.h
+++ b/src/mongo/db/catalog/index_catalog.h
@@ -86,6 +86,8 @@ public:
IndexDescriptor* findIdIndex(OperationContext* txn) const;
/**
+ * Find index by name. The index name uniquely identifies an index.
+ *
* @return null if cannot find
*/
IndexDescriptor* findIndexByName(OperationContext* txn,
@@ -93,11 +95,31 @@ public:
bool includeUnfinishedIndexes = false) const;
/**
- * @return null if cannot find
+ * Find index by matching key pattern and collation spec. The key pattern and collation spec
+ * uniquely identify an index.
+ *
+ * Collation is specified as a normalized collation spec as returned by
+ * CollationInterface::getSpec. An empty object indicates the simple collation.
+ *
+ * @return null if cannot find index, otherwise the index with a matching key pattern and
+ * collation.
+ */
+ IndexDescriptor* findIndexByKeyPatternAndCollationSpec(
+ OperationContext* txn,
+ const BSONObj& key,
+ const BSONObj& collationSpec,
+ bool includeUnfinishedIndexes = false) const;
+
+ /**
+ * Find indexes with a matching key pattern, putting them into the vector 'matches'. The key
+ * pattern alone does not uniquely identify an index.
+ *
+ * Consider using 'findIndexByName' if expecting to match one index.
*/
- IndexDescriptor* findIndexByKeyPattern(OperationContext* txn,
- const BSONObj& key,
- bool includeUnfinishedIndexes = false) const;
+ void findIndexesByKeyPattern(OperationContext* txn,
+ const BSONObj& key,
+ bool includeUnfinishedIndexes,
+ std::vector<IndexDescriptor*>* matches) const;
/**
* Returns an index suitable for shard key range scans.
@@ -105,6 +127,7 @@ public:
* This index:
* - must be prefixed by 'shardKey', and
* - must not be a partial index.
+ * - must have the simple collation.
*
* If the parameter 'requireSingleKey' is true, then this index additionally must not be
* multi-key.
diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp
index 6ddae87fa3b..d41678d5518 100644
--- a/src/mongo/db/commands/dbcommands.cpp
+++ b/src/mongo/db/commands/dbcommands.cpp
@@ -1009,7 +1009,8 @@ public:
virtual void help(stringstream& help) const {
help << "Sets collection options.\n"
"Example: { collMod: 'foo', usePowerOf2Sizes:true }\n"
- "Example: { collMod: 'foo', index: {keyPattern: {a: 1}, expireAfterSeconds: 600} }";
+ "Example: { collMod: 'foo', index: {keyPattern: {a: 1}, expireAfterSeconds: 600} "
+ "}\n";
}
virtual void addRequiredPrivileges(const std::string& dbname,
diff --git a/src/mongo/db/dbhelpers.cpp b/src/mongo/db/dbhelpers.cpp
index 1ae6e1f60da..65f8c801447 100644
--- a/src/mongo/db/dbhelpers.cpp
+++ b/src/mongo/db/dbhelpers.cpp
@@ -291,30 +291,6 @@ BSONObj Helpers::inferKeyPattern(const BSONObj& o) {
return kpBuilder.obj();
}
-static bool findShardKeyIndexPattern(OperationContext* txn,
- const string& ns,
- const BSONObj& shardKeyPattern,
- BSONObj* indexPattern) {
- AutoGetCollectionForRead ctx(txn, ns);
- Collection* collection = ctx.getCollection();
- if (!collection) {
- return false;
- }
-
- // Allow multiKey based on the invariant that shard keys must be single-valued.
- // Therefore, any multi-key index prefixed by shard key cannot be multikey over
- // the shard key fields.
- const IndexDescriptor* idx =
- collection->getIndexCatalog()->findShardKeyPrefixedIndex(txn,
- shardKeyPattern,
- false); // requireSingleKey
-
- if (idx == NULL)
- return false;
- *indexPattern = idx->keyPattern().getOwned();
- return true;
-}
-
long long Helpers::removeRange(OperationContext* txn,
const KeyRange& range,
bool maxInclusive,
@@ -327,24 +303,45 @@ long long Helpers::removeRange(OperationContext* txn,
// The IndexChunk has a keyPattern that may apply to more than one index - we need to
// select the index and get the full index keyPattern here.
- BSONObj indexKeyPatternDoc;
- if (!findShardKeyIndexPattern(txn, ns, range.keyPattern, &indexKeyPatternDoc)) {
- warning(LogComponent::kSharding) << "no index found to clean data over range of type "
- << range.keyPattern << " in " << ns << endl;
- return -1;
- }
+ std::string indexName;
+ BSONObj min;
+ BSONObj max;
+
+ {
+ AutoGetCollectionForRead ctx(txn, ns);
+ Collection* collection = ctx.getCollection();
+ if (!collection) {
+ warning(LogComponent::kSharding)
+ << "collection deleted before cleaning data over range of type " << range.keyPattern
+ << " in " << ns << endl;
+ return -1;
+ }
- KeyPattern indexKeyPattern(indexKeyPatternDoc);
+ // Allow multiKey based on the invariant that shard keys must be single-valued.
+ // Therefore, any multi-key index prefixed by shard key cannot be multikey over
+ // the shard key fields.
+ const IndexDescriptor* idx =
+ collection->getIndexCatalog()->findShardKeyPrefixedIndex(txn,
+ range.keyPattern,
+ false); // requireSingleKey
+ if (!idx) {
+ warning(LogComponent::kSharding) << "no index found to clean data over range of type "
+ << range.keyPattern << " in " << ns << endl;
+ return -1;
+ }
+
+ indexName = idx->indexName();
+ KeyPattern indexKeyPattern(idx->keyPattern());
- // Extend bounds to match the index we found
+ // Extend bounds to match the index we found
+
+ // 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, ....)
+ max = Helpers::toKeyFormat(indexKeyPattern.extendRangeBound(range.maxKey, maxInclusive));
+ }
- // Extend min to get (min, MinKey, MinKey, ....)
- const BSONObj& 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 BSONObj& max =
- Helpers::toKeyFormat(indexKeyPattern.extendRangeBound(range.maxKey, maxInclusive));
MONGO_LOG_COMPONENT(1, LogComponent::kSharding)
<< "begin removal of " << min << " to " << max << " in " << ns
@@ -362,8 +359,7 @@ long long Helpers::removeRange(OperationContext* txn,
if (!collection)
break;
- IndexDescriptor* desc =
- collection->getIndexCatalog()->findIndexByKeyPattern(txn, indexKeyPattern.toBSON());
+ IndexDescriptor* desc = collection->getIndexCatalog()->findIndexByName(txn, indexName);
unique_ptr<PlanExecutor> exec(
InternalPlanner::indexScan(txn,
diff --git a/src/mongo/db/exec/stagedebug_cmd.cpp b/src/mongo/db/exec/stagedebug_cmd.cpp
index 837300e3ace..d626595b87d 100644
--- a/src/mongo/db/exec/stagedebug_cmd.cpp
+++ b/src/mongo/db/exec/stagedebug_cmd.cpp
@@ -273,12 +273,30 @@ public:
string nodeName = firstElt.fieldName();
if ("ixscan" == nodeName) {
- // This'll throw if it's not an obj but that's OK.
- BSONObj keyPatternObj = nodeArgs["keyPattern"].Obj();
-
- IndexDescriptor* desc =
- collection->getIndexCatalog()->findIndexByKeyPattern(txn, keyPatternObj);
- uassert(16890, "Can't find index: " + keyPatternObj.toString(), desc);
+ IndexDescriptor* desc;
+ if (BSONElement keyPatternElement = nodeArgs["keyPattern"]) {
+ // This'll throw if it's not an obj but that's OK.
+ BSONObj keyPatternObj = keyPatternElement.Obj();
+ std::vector<IndexDescriptor*> indexes;
+ collection->getIndexCatalog()->findIndexesByKeyPattern(
+ txn, keyPatternObj, false, &indexes);
+ uassert(16890,
+ str::stream() << "Can't find index: " << keyPatternObj,
+ !indexes.empty());
+ uassert(ErrorCodes::AmbiguousIndexKeyPattern,
+ str::stream() << indexes.size() << " matching indexes for key pattern: "
+ << keyPatternObj
+ << ". Conflicting indexes: "
+ << indexes[0]->infoObj()
+ << ", "
+ << indexes[1]->infoObj(),
+ indexes.size() == 1);
+ desc = indexes[0];
+ } else {
+ StringData name = nodeArgs["name"].valueStringData();
+ desc = collection->getIndexCatalog()->findIndexByName(txn, name);
+ uassert(40223, str::stream() << "Can't find index: " << name.toString(), desc);
+ }
IndexScanParams params;
params.descriptor = desc;
diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp
index b909047b9ed..f98f088ca9c 100644
--- a/src/mongo/db/exec/subplan.cpp
+++ b/src/mongo/db/exec/subplan.cpp
@@ -172,7 +172,7 @@ Status SubplanStage::planSubqueries() {
for (size_t i = 0; i < _plannerParams.indices.size(); ++i) {
const IndexEntry& ie = _plannerParams.indices[i];
- _indexMap[ie.keyPattern] = i;
+ _indexMap[ie.name] = i;
LOG(5) << "Subplanner: index " << i << " is " << ie.toString();
}
@@ -249,7 +249,7 @@ namespace {
Status tagOrChildAccordingToCache(PlanCacheIndexTree* compositeCacheData,
SolutionCacheData* branchCacheData,
MatchExpression* orChild,
- const std::map<BSONObj, size_t>& indexMap) {
+ const std::map<StringData, size_t>& indexMap) {
invariant(compositeCacheData);
// We want a well-formed *indexed* solution.
diff --git a/src/mongo/db/exec/subplan.h b/src/mongo/db/exec/subplan.h
index 198be1d7d06..a9b356ff4a6 100644
--- a/src/mongo/db/exec/subplan.h
+++ b/src/mongo/db/exec/subplan.h
@@ -32,6 +32,7 @@
#include "mongo/base/owned_pointer_vector.h"
#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
#include "mongo/db/exec/plan_stage.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/plan_cache.h"
@@ -205,7 +206,7 @@ private:
OwnedPointerVector<BranchPlanningResult> _branchResults;
// We need this to extract cache-friendly index data from the index assignments.
- std::map<BSONObj, size_t> _indexMap;
+ std::map<StringData, size_t> _indexMap;
};
} // namespace mongo
diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp
index 8130ac57e4d..d45c6145abb 100644
--- a/src/mongo/db/query/get_executor.cpp
+++ b/src/mongo/db/query/get_executor.cpp
@@ -1095,8 +1095,7 @@ bool turnIxscanIntoCount(QuerySolution* soln) {
}
// Make the count node that we replace the fetch + ixscan with.
- CountScanNode* csn = new CountScanNode();
- csn->indexKeyPattern = isn->indexKeyPattern;
+ CountScanNode* csn = new CountScanNode(isn->index);
csn->startKey = startKey;
csn->startKeyInclusive = startKeyInclusive;
csn->endKey = endKey;
@@ -1330,8 +1329,7 @@ bool turnIxscanIntoDistinctIxscan(QuerySolution* soln, const string& field) {
}
// Make a new DistinctNode. We swap this for the ixscan in the provided solution.
- DistinctNode* dn = new DistinctNode();
- dn->indexKeyPattern = isn->indexKeyPattern;
+ DistinctNode* dn = new DistinctNode(isn->index);
dn->direction = isn->direction;
dn->bounds = isn->bounds;
@@ -1339,7 +1337,7 @@ bool turnIxscanIntoDistinctIxscan(QuerySolution* soln, const string& field) {
// try to distinct-hack when there is an index prefixed by the field we're distinct-ing
// over. Consider removing this code if we stick with that policy.
dn->fieldNo = 0;
- BSONObjIterator it(isn->indexKeyPattern);
+ BSONObjIterator it(isn->index.keyPattern);
while (it.more()) {
if (field == it.next().fieldName()) {
break;
@@ -1447,10 +1445,9 @@ StatusWith<unique_ptr<PlanExecutor>> getExecutorDistinct(OperationContext* txn,
parsedDistinct->getKey(),
cq->getCollator(),
&distinctNodeIndex)) {
- auto dn = stdx::make_unique<DistinctNode>();
- dn->indexKeyPattern = plannerParams.indices[distinctNodeIndex].keyPattern;
+ auto dn = stdx::make_unique<DistinctNode>(plannerParams.indices[distinctNodeIndex]);
dn->direction = 1;
- IndexBoundsBuilder::allValuesBounds(dn->indexKeyPattern, &dn->bounds);
+ IndexBoundsBuilder::allValuesBounds(dn->index.keyPattern, &dn->bounds);
dn->fieldNo = 0;
// An index with a non-simple collation requires a FETCH stage.
diff --git a/src/mongo/db/query/index_entry.h b/src/mongo/db/query/index_entry.h
index 92507461c4d..42e4d2281d2 100644
--- a/src/mongo/db/query/index_entry.h
+++ b/src/mongo/db/query/index_entry.h
@@ -92,17 +92,22 @@ struct IndexEntry {
/**
* For testing purposes only.
*/
- IndexEntry(const BSONObj& kp)
+ IndexEntry(const BSONObj& kp, const std::string& indexName = "test_foo")
: keyPattern(kp),
multikey(false),
sparse(false),
unique(false),
- name("test_foo"),
+ name(indexName),
filterExpr(nullptr),
infoObj(BSONObj()) {
type = IndexNames::nameToType(IndexNames::findPluginName(keyPattern));
}
+ bool operator==(const IndexEntry& rhs) const {
+ // Indexes are logically equal when names are equal.
+ return this->name == rhs.name;
+ }
+
std::string toString() const;
BSONObj keyPattern;
diff --git a/src/mongo/db/query/plan_cache_test.cpp b/src/mongo/db/query/plan_cache_test.cpp
index 0c13cb3300c..bac9704e0cf 100644
--- a/src/mongo/db/query/plan_cache_test.cpp
+++ b/src/mongo/db/query/plan_cache_test.cpp
@@ -449,7 +449,7 @@ class CachePlanSelectionTest : public mongo::unittest::Test {
protected:
void setUp() {
params.options = QueryPlannerParams::INCLUDE_COLLSCAN;
- addIndex(BSON("_id" << 1));
+ addIndex(BSON("_id" << 1), "_id_");
}
void tearDown() {
@@ -458,22 +458,21 @@ protected:
}
}
- void addIndex(BSONObj keyPattern, bool multikey = false) {
+ void addIndex(BSONObj keyPattern, const std::string& indexName, bool multikey = false) {
// The first false means not multikey.
// The second false means not sparse.
- // The third arg is the index name and I am egotistical.
// The NULL means no filter expression.
- params.indices.push_back(IndexEntry(
- keyPattern, multikey, false, false, "hari_king_of_the_stove", NULL, BSONObj()));
+ params.indices.push_back(
+ IndexEntry(keyPattern, multikey, false, false, indexName, NULL, BSONObj()));
}
- void addIndex(BSONObj keyPattern, bool multikey, bool sparse) {
- params.indices.push_back(IndexEntry(
- keyPattern, multikey, sparse, false, "note_to_self_dont_break_build", NULL, BSONObj()));
+ void addIndex(BSONObj keyPattern, const std::string& indexName, bool multikey, bool sparse) {
+ params.indices.push_back(
+ IndexEntry(keyPattern, multikey, sparse, false, indexName, NULL, BSONObj()));
}
- void addIndex(BSONObj keyPattern, CollatorInterface* collator) {
- IndexEntry entry(keyPattern, false, false, false, "index_with_collation", NULL, BSONObj());
+ void addIndex(BSONObj keyPattern, const std::string& indexName, CollatorInterface* collator) {
+ IndexEntry entry(keyPattern, false, false, false, indexName, NULL, BSONObj());
entry.collator = collator;
params.indices.push_back(entry);
}
@@ -782,7 +781,7 @@ const PlanCacheKey CachePlanSelectionTest::ck = "mock_cache_key";
//
TEST_F(CachePlanSelectionTest, EqualityIndexScan) {
- addIndex(BSON("x" << 1));
+ addIndex(BSON("x" << 1), "x_1");
runQuery(BSON("x" << 5));
assertPlanCacheRecoversSolution(BSON("x" << 5),
@@ -790,7 +789,7 @@ TEST_F(CachePlanSelectionTest, EqualityIndexScan) {
}
TEST_F(CachePlanSelectionTest, EqualityIndexScanWithTrailingFields) {
- addIndex(BSON("x" << 1 << "y" << 1));
+ addIndex(BSON("x" << 1 << "y" << 1), "x_1_y_1");
runQuery(BSON("x" << 5));
assertPlanCacheRecoversSolution(
@@ -803,7 +802,8 @@ TEST_F(CachePlanSelectionTest, EqualityIndexScanWithTrailingFields) {
TEST_F(CachePlanSelectionTest, Basic2DSphereNonNear) {
addIndex(BSON("a"
- << "2dsphere"));
+ << "2dsphere"),
+ "a_2dsphere");
BSONObj query;
query = fromjson(
@@ -819,7 +819,8 @@ TEST_F(CachePlanSelectionTest, Basic2DSphereNonNear) {
TEST_F(CachePlanSelectionTest, Basic2DSphereGeoNear) {
addIndex(BSON("a"
- << "2dsphere"));
+ << "2dsphere"),
+ "a_2dsphere");
BSONObj query;
query = fromjson("{a: {$nearSphere: [0,0], $maxDistance: 0.31 }}");
@@ -838,9 +839,10 @@ TEST_F(CachePlanSelectionTest, Basic2DSphereGeoNear) {
}
TEST_F(CachePlanSelectionTest, Basic2DSphereGeoNearReverseCompound) {
- addIndex(BSON("x" << 1));
+ addIndex(BSON("x" << 1), "x_1");
addIndex(BSON("x" << 1 << "a"
- << "2dsphere"));
+ << "2dsphere"),
+ "x_1_a_2dsphere");
BSONObj query = fromjson("{x:1, a: {$nearSphere: [0,0], $maxDistance: 0.31 }}");
runQuery(query);
assertPlanCacheRecoversSolution(
@@ -851,7 +853,8 @@ TEST_F(CachePlanSelectionTest, Basic2DSphereGeoNearReverseCompound) {
TEST_F(CachePlanSelectionTest, TwoDSphereNoGeoPred) {
addIndex(BSON("x" << 1 << "a"
- << "2dsphere"));
+ << "2dsphere"),
+ "x_1_a_2dsphere");
runQuery(BSON("x" << 1));
assertPlanCacheRecoversSolution(BSON("x" << 1),
"{fetch: {node: {ixscan: {pattern: {x: 1, a: '2dsphere'}}}}}");
@@ -859,9 +862,11 @@ TEST_F(CachePlanSelectionTest, TwoDSphereNoGeoPred) {
TEST_F(CachePlanSelectionTest, Or2DSphereNonNear) {
addIndex(BSON("a"
- << "2dsphere"));
+ << "2dsphere"),
+ "a_2dsphere");
addIndex(BSON("b"
- << "2dsphere"));
+ << "2dsphere"),
+ "b_2dsphere");
BSONObj query = fromjson(
"{$or: [ {a: {$geoIntersects: {$geometry: {type: 'Point', coordinates: [10.0, 10.0]}}}},"
" {b: {$geoWithin: { $centerSphere: [[ 10, 20 ], 0.01 ] } }} ]}");
@@ -879,7 +884,8 @@ TEST_F(CachePlanSelectionTest, AndWithinPolygonWithinCenterSphere) {
addIndex(BSON("a"
<< "2dsphere"
<< "b"
- << 1));
+ << 1),
+ "a_2dsphere_b_2dsphere");
BSONObj query = fromjson(
"{$and: [{b: 1}, {a: {$within: {$polygon: [[0, 0], [0, 0], [0, 0], [0, 0]]}}}, {a: "
@@ -895,7 +901,7 @@ TEST_F(CachePlanSelectionTest, AndWithinPolygonWithinCenterSphere) {
//
TEST_F(CachePlanSelectionTest, TwoPredicatesAnding) {
- addIndex(BSON("x" << 1));
+ addIndex(BSON("x" << 1), "x_1");
BSONObj query = fromjson("{$and: [ {x: {$gt: 1}}, {x: {$lt: 3}} ] }");
runQuery(query);
assertPlanCacheRecoversSolution(
@@ -903,7 +909,7 @@ TEST_F(CachePlanSelectionTest, TwoPredicatesAnding) {
}
TEST_F(CachePlanSelectionTest, SimpleOr) {
- addIndex(BSON("a" << 1));
+ addIndex(BSON("a" << 1), "a_1");
BSONObj query = fromjson("{$or: [{a: 20}, {a: 21}]}");
runQuery(query);
assertPlanCacheRecoversSolution(
@@ -911,7 +917,7 @@ TEST_F(CachePlanSelectionTest, SimpleOr) {
}
TEST_F(CachePlanSelectionTest, OrWithAndChild) {
- addIndex(BSON("a" << 1));
+ addIndex(BSON("a" << 1), "a_1");
BSONObj query = fromjson("{$or: [{a: 20}, {$and: [{a:1}, {b:7}]}]}");
runQuery(query);
assertPlanCacheRecoversSolution(query,
@@ -922,7 +928,7 @@ TEST_F(CachePlanSelectionTest, OrWithAndChild) {
}
TEST_F(CachePlanSelectionTest, AndWithUnindexedOrChild) {
- addIndex(BSON("a" << 1));
+ addIndex(BSON("a" << 1), "a_1");
BSONObj query = fromjson("{a:20, $or: [{b:1}, {c:7}]}");
runQuery(query);
assertPlanCacheRecoversSolution(query,
@@ -932,8 +938,8 @@ TEST_F(CachePlanSelectionTest, AndWithUnindexedOrChild) {
TEST_F(CachePlanSelectionTest, AndWithOrWithOneIndex) {
- addIndex(BSON("b" << 1));
- addIndex(BSON("a" << 1));
+ addIndex(BSON("b" << 1), "b_1");
+ addIndex(BSON("a" << 1), "a_1");
BSONObj query = fromjson("{$or: [{b:1}, {c:7}], a:20}");
runQuery(query);
assertPlanCacheRecoversSolution(query,
@@ -947,8 +953,8 @@ TEST_F(CachePlanSelectionTest, AndWithOrWithOneIndex) {
// SERVER-1205.
TEST_F(CachePlanSelectionTest, MergeSort) {
- addIndex(BSON("a" << 1 << "c" << 1));
- addIndex(BSON("b" << 1 << "c" << 1));
+ addIndex(BSON("a" << 1 << "c" << 1), "a_1_c_1");
+ addIndex(BSON("b" << 1 << "c" << 1), "b_1_c_1");
BSONObj query = fromjson("{$or: [{a:1}, {b:1}]}");
BSONObj sort = BSON("c" << 1);
@@ -965,8 +971,8 @@ TEST_F(CachePlanSelectionTest, MergeSort) {
// SERVER-1205 as well.
TEST_F(CachePlanSelectionTest, NoMergeSortIfNoSortWanted) {
- addIndex(BSON("a" << 1 << "c" << 1));
- addIndex(BSON("b" << 1 << "c" << 1));
+ addIndex(BSON("a" << 1 << "c" << 1), "a_1_c_1");
+ addIndex(BSON("b" << 1 << "c" << 1), "b_1_c_1");
BSONObj query = fromjson("{$or: [{a:1}, {b:1}]}");
runQuerySortProj(query, BSONObj(), BSONObj());
@@ -983,7 +989,7 @@ TEST_F(CachePlanSelectionTest, NoMergeSortIfNoSortWanted) {
// Disabled: SERVER-10801.
/*
TEST_F(CachePlanSelectionTest, SortOnGeoQuery) {
- addIndex(BSON("timestamp" << -1 << "position" << "2dsphere"));
+ addIndex(BSON("timestamp" << -1 << "position" << "2dsphere"), "timestamp_-1_position_2dsphere");
BSONObj query = fromjson("{position: {$geoWithin: {$geometry: {type: \"Polygon\", "
"coordinates: [[[1, 1], [1, 90], [180, 90], "
"[180, 1], [1, 1]]]}}}}");
@@ -998,7 +1004,8 @@ TEST_F(CachePlanSelectionTest, SortOnGeoQuery) {
// SERVER-9257
TEST_F(CachePlanSelectionTest, CompoundGeoNoGeoPredicate) {
addIndex(BSON("creationDate" << 1 << "foo.bar"
- << "2dsphere"));
+ << "2dsphere"),
+ "creationDate_1_foo.bar_2dsphere");
BSONObj query = fromjson("{creationDate: {$gt: 7}}");
BSONObj sort = fromjson("{creationDate: 1}");
runQuerySortProj(query, sort, BSONObj());
@@ -1012,7 +1019,7 @@ TEST_F(CachePlanSelectionTest, CompoundGeoNoGeoPredicate) {
}
TEST_F(CachePlanSelectionTest, ReverseScanForSort) {
- addIndex(BSON("_id" << 1));
+ addIndex(BSON("_id" << 1), "_id_1");
runQuerySortProj(BSONObj(), fromjson("{_id: -1}"), BSONObj());
assertPlanCacheRecoversSolution(
BSONObj(),
@@ -1027,22 +1034,22 @@ TEST_F(CachePlanSelectionTest, ReverseScanForSort) {
//
TEST_F(CachePlanSelectionTest, CollscanNoUsefulIndices) {
- addIndex(BSON("a" << 1 << "b" << 1));
- addIndex(BSON("c" << 1));
+ addIndex(BSON("a" << 1 << "b" << 1), "a_1_b_1");
+ addIndex(BSON("c" << 1), "c_1");
runQuery(BSON("b" << 4));
assertPlanCacheRecoversSolution(BSON("b" << 4), "{cscan: {filter: {b: 4}, dir: 1}}");
}
TEST_F(CachePlanSelectionTest, CollscanOrWithoutEnoughIndices) {
- addIndex(BSON("a" << 1));
+ addIndex(BSON("a" << 1), "a_1");
BSONObj query = fromjson("{$or: [{a: 20}, {b: 21}]}");
runQuery(query);
assertPlanCacheRecoversSolution(query, "{cscan: {filter: {$or:[{a:20},{b:21}]}, dir: 1}}");
}
TEST_F(CachePlanSelectionTest, CollscanMergeSort) {
- addIndex(BSON("a" << 1 << "c" << 1));
- addIndex(BSON("b" << 1 << "c" << 1));
+ addIndex(BSON("a" << 1 << "c" << 1), "a_1_c_1");
+ addIndex(BSON("b" << 1 << "c" << 1), "b_1_c_1");
BSONObj query = fromjson("{$or: [{a:1}, {b:1}]}");
BSONObj sort = BSON("c" << 1);
@@ -1064,7 +1071,7 @@ TEST_F(CachePlanSelectionTest, CachedPlanForCompoundMultikeyIndexCanCompoundBoun
params.options = QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::INDEX_INTERSECTION;
const bool multikey = true;
- addIndex(BSON("a" << 1 << "b" << 1), multikey);
+ addIndex(BSON("a" << 1 << "b" << 1), "a_1_b_1", multikey);
BSONObj query = fromjson("{a: 2, b: 3}");
runQuery(query);
@@ -1080,7 +1087,7 @@ TEST_F(CachePlanSelectionTest,
params.options = QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::INDEX_INTERSECTION;
const bool multikey = true;
- addIndex(BSON("a" << 1), multikey);
+ addIndex(BSON("a" << 1), "a_1", multikey);
BSONObj query = fromjson("{$and: [{a: 2}, {a: 3}]}");
runQuery(query);
@@ -1104,7 +1111,7 @@ TEST_F(CachePlanSelectionTest,
params.options = QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::INDEX_INTERSECTION;
const bool multikey = true;
- addIndex(BSON("a" << 1), multikey);
+ addIndex(BSON("a" << 1), "a_1", multikey);
BSONObj query = fromjson("{$and: [{a: {$gte: 2}}, {a: {$lt: 3}}]}");
runQuery(query);
@@ -1121,8 +1128,8 @@ TEST_F(CachePlanSelectionTest, CachedPlanForIntersectionOfMultikeyIndexesWhenUsi
params.options = QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::INDEX_INTERSECTION;
const bool multikey = true;
- addIndex(BSON("a.b" << 1), multikey);
- addIndex(BSON("a.c" << 1), multikey);
+ addIndex(BSON("a.b" << 1), "a.b_1", multikey);
+ addIndex(BSON("a.c" << 1), "a.c_1", multikey);
BSONObj query = fromjson("{a: {$elemMatch: {b: 2, c: 3}}}");
runQuery(query);
@@ -1145,8 +1152,8 @@ TEST_F(CachePlanSelectionTest, CachedPlanForIntersectionWithNonMultikeyIndexCanI
params.options = QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::INDEX_INTERSECTION;
const bool multikey = true;
- addIndex(BSON("a.b" << 1), multikey);
- addIndex(BSON("a.c" << 1), !multikey);
+ addIndex(BSON("a.b" << 1), "a.b_1", multikey);
+ addIndex(BSON("a.c" << 1), "a.c_1", !multikey);
BSONObj query = fromjson("{'a.b': 2, 'a.c': {$gte: 0, $lt: 10}}}}");
runQuery(query);
@@ -1164,13 +1171,14 @@ TEST_F(CachePlanSelectionTest, CachedPlanForIntersectionWithNonMultikeyIndexCanI
TEST_F(CachePlanSelectionTest, GeoNear2DNotCached) {
addIndex(BSON("a"
- << "2d"));
+ << "2d"),
+ "a_2d");
runQuery(fromjson("{a: {$near: [0,0], $maxDistance:0.3 }}"));
assertNotCached("{geoNear2d: {a: '2d'}}");
}
TEST_F(CachePlanSelectionTest, MinNotCached) {
- addIndex(BSON("a" << 1));
+ addIndex(BSON("a" << 1), "a_1");
runQueryHintMinMax(BSONObj(), BSONObj(), fromjson("{a: 1}"), BSONObj());
assertNotCached(
"{fetch: {filter: null, "
@@ -1178,7 +1186,7 @@ TEST_F(CachePlanSelectionTest, MinNotCached) {
}
TEST_F(CachePlanSelectionTest, MaxNotCached) {
- addIndex(BSON("a" << 1));
+ addIndex(BSON("a" << 1), "a_1");
runQueryHintMinMax(BSONObj(), BSONObj(), BSONObj(), fromjson("{a: 1}"));
assertNotCached(
"{fetch: {filter: null, "
@@ -1186,8 +1194,8 @@ TEST_F(CachePlanSelectionTest, MaxNotCached) {
}
TEST_F(CachePlanSelectionTest, NaturalHintNotCached) {
- addIndex(BSON("a" << 1));
- addIndex(BSON("b" << 1));
+ addIndex(BSON("a" << 1), "a_1");
+ addIndex(BSON("b" << 1), "b_1");
runQuerySortHint(BSON("a" << 1), BSON("b" << 1), BSON("$natural" << 1));
assertNotCached(
"{sort: {pattern: {b: 1}, limit: 0, node: {sortKeyGen: {node: "
@@ -1195,7 +1203,7 @@ TEST_F(CachePlanSelectionTest, NaturalHintNotCached) {
}
TEST_F(CachePlanSelectionTest, HintValidNotCached) {
- addIndex(BSON("a" << 1));
+ addIndex(BSON("a" << 1), "a_1");
runQueryHint(BSONObj(), fromjson("{a: 1}"));
assertNotCached(
"{fetch: {filter: null, "
@@ -1208,7 +1216,8 @@ TEST_F(CachePlanSelectionTest, HintValidNotCached) {
TEST_F(CachePlanSelectionTest, Basic2DNonNearNotCached) {
addIndex(BSON("a"
- << "2d"));
+ << "2d"),
+ "a_2d");
BSONObj query;
// Polygon
@@ -1234,9 +1243,11 @@ TEST_F(CachePlanSelectionTest, Basic2DNonNearNotCached) {
TEST_F(CachePlanSelectionTest, Or2DNonNearNotCached) {
addIndex(BSON("a"
- << "2d"));
+ << "2d"),
+ "a_2d");
addIndex(BSON("b"
- << "2d"));
+ << "2d"),
+ "b_2d");
BSONObj query = fromjson(
"{$or: [ {a : { $within : { $polygon : [[0,0], [2,0], [4,0]] } }},"
" {b : { $within : { $center : [[ 5, 5 ], 7 ] } }} ]}");
@@ -1253,7 +1264,7 @@ TEST_F(CachePlanSelectionTest, Or2DNonNearNotCached) {
TEST_F(CachePlanSelectionTest, MatchingCollation) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
- addIndex(BSON("x" << 1), &collator);
+ addIndex(BSON("x" << 1), "x_1", &collator);
runQueryAsCommand(fromjson(
"{find: 'testns', filter: {x: 'foo'}, collation: {locale: 'mock_reverse_string'}}"));
diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp
index 896e56d5253..fd53efbc3c0 100644
--- a/src/mongo/db/query/planner_access.cpp
+++ b/src/mongo/db/query/planner_access.cpp
@@ -174,8 +174,7 @@ QuerySolutionNode* QueryPlannerAccess::makeLeafNode(
bool indexIs2D = (String == elt.type() && "2d" == elt.String());
if (indexIs2D) {
- GeoNear2DNode* ret = new GeoNear2DNode();
- ret->indexKeyPattern = index.keyPattern;
+ GeoNear2DNode* ret = new GeoNear2DNode(index);
ret->nq = &nearExpr->getData();
ret->baseBounds.fields.resize(index.keyPattern.nFields());
if (NULL != query.getProj()) {
@@ -185,8 +184,7 @@ QuerySolutionNode* QueryPlannerAccess::makeLeafNode(
return ret;
} else {
- GeoNear2DSphereNode* ret = new GeoNear2DSphereNode();
- ret->indexKeyPattern = index.keyPattern;
+ GeoNear2DSphereNode* ret = new GeoNear2DSphereNode(index);
ret->nq = &nearExpr->getData();
ret->baseBounds.fields.resize(index.keyPattern.nFields());
if (NULL != query.getProj()) {
@@ -199,20 +197,16 @@ QuerySolutionNode* QueryPlannerAccess::makeLeafNode(
// We must not keep the expression node around.
*tightnessOut = IndexBoundsBuilder::EXACT;
TextMatchExpressionBase* textExpr = static_cast<TextMatchExpressionBase*>(expr);
- TextNode* ret = new TextNode();
- ret->indexKeyPattern = index.keyPattern;
+ TextNode* ret = new TextNode(index);
ret->ftsQuery = textExpr->getFTSQuery().clone();
return ret;
} else {
// Note that indexKeyPattern.firstElement().fieldName() may not equal expr->path()
// because expr might be inside an array operator that provides a path prefix.
- IndexScanNode* isn = new IndexScanNode();
- isn->indexKeyPattern = index.keyPattern;
- isn->indexIsMultiKey = index.multikey;
+ IndexScanNode* isn = new IndexScanNode(index);
isn->bounds.fields.resize(index.keyPattern.nFields());
isn->maxScan = query.getQueryRequest().getMaxScan();
isn->addKeyMetadata = query.getQueryRequest().returnKey();
- isn->indexCollator = index.collator;
isn->queryCollator = query.getCollator();
// Get the ixtag->pos-th element of the index key pattern.
@@ -402,7 +396,7 @@ void QueryPlannerAccess::finishTextNode(QuerySolutionNode* node, const IndexEntr
// For example, say keyPattern = { a: 1, _fts: "text", _ftsx: 1, b: 1 }
// prefixEnd should be 1.
size_t prefixEnd = 0;
- BSONObjIterator it(tn->indexKeyPattern);
+ BSONObjIterator it(tn->index.keyPattern);
// Count how many prefix terms we have.
while (it.more()) {
// We know that the only key pattern with a type of String is the _fts field
@@ -1246,12 +1240,9 @@ QuerySolutionNode* QueryPlannerAccess::scanWholeIndex(const IndexEntry& index,
QuerySolutionNode* solnRoot = NULL;
// Build an ixscan over the id index, use it, and return it.
- unique_ptr<IndexScanNode> isn = make_unique<IndexScanNode>();
- isn->indexKeyPattern = index.keyPattern;
- isn->indexIsMultiKey = index.multikey;
+ unique_ptr<IndexScanNode> isn = make_unique<IndexScanNode>(index);
isn->maxScan = query.getQueryRequest().getMaxScan();
isn->addKeyMetadata = query.getQueryRequest().returnKey();
- isn->indexCollator = index.collator;
isn->queryCollator = query.getCollator();
IndexBoundsBuilder::allValuesBounds(index.keyPattern, &isn->bounds);
@@ -1384,9 +1375,7 @@ QuerySolutionNode* QueryPlannerAccess::makeIndexScan(const IndexEntry& index,
QuerySolutionNode* solnRoot = NULL;
// Build an ixscan over the id index, use it, and return it.
- IndexScanNode* isn = new IndexScanNode();
- isn->indexKeyPattern = index.keyPattern;
- isn->indexIsMultiKey = index.multikey;
+ IndexScanNode* isn = new IndexScanNode(index);
isn->direction = 1;
isn->maxScan = query.getQueryRequest().getMaxScan();
isn->addKeyMetadata = query.getQueryRequest().returnKey();
@@ -1394,7 +1383,6 @@ QuerySolutionNode* QueryPlannerAccess::makeIndexScan(const IndexEntry& index,
isn->bounds.startKey = startKey;
isn->bounds.endKey = endKey;
isn->bounds.endKeyInclusive = false;
- isn->indexCollator = index.collator;
isn->queryCollator = query.getCollator();
unique_ptr<MatchExpression> filter = query.root()->shallowClone();
diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp
index a5ba08cb211..7298959764f 100644
--- a/src/mongo/db/query/planner_analysis.cpp
+++ b/src/mongo/db/query/planner_analysis.cpp
@@ -213,13 +213,10 @@ void explodeScan(const IndexScanNode* isn,
verify(prefix.size() == fieldsToExplode);
// Copy boring fields into new child.
- IndexScanNode* child = new IndexScanNode();
- child->indexKeyPattern = isn->indexKeyPattern;
+ IndexScanNode* child = new IndexScanNode(isn->index);
child->direction = isn->direction;
child->maxScan = isn->maxScan;
child->addKeyMetadata = isn->addKeyMetadata;
- child->indexIsMultiKey = isn->indexIsMultiKey;
- child->indexCollator = isn->indexCollator;
child->queryCollator = isn->queryCollator;
// Copy the filter, if there is one.
@@ -380,7 +377,7 @@ bool QueryPlannerAnalysis::explodeForSort(const CanonicalQuery& query,
// Skip every field that is a union of point intervals and build the resulting sort
// order from the remaining fields.
- BSONObjIterator kpIt(isn->indexKeyPattern);
+ BSONObjIterator kpIt(isn->index.keyPattern);
size_t boundsIdx = 0;
while (kpIt.more()) {
const OrderedIntervalList& oil = bounds.fields[boundsIdx];
@@ -773,12 +770,12 @@ QuerySolution* QueryPlannerAnalysis::analyzeDataAccess(const CanonicalQuery& que
if (STAGE_IXSCAN == leafNodes[0]->getType()) {
projType = ProjectionNode::COVERED_ONE_INDEX;
IndexScanNode* ixn = static_cast<IndexScanNode*>(leafNodes[0]);
- coveredKeyObj = ixn->indexKeyPattern;
+ coveredKeyObj = ixn->index.keyPattern;
LOG(5) << "PROJECTION: covered via IXSCAN, using COVERED fast path";
} else if (STAGE_DISTINCT_SCAN == leafNodes[0]->getType()) {
projType = ProjectionNode::COVERED_ONE_INDEX;
DistinctNode* dn = static_cast<DistinctNode*>(leafNodes[0]);
- coveredKeyObj = dn->indexKeyPattern;
+ coveredKeyObj = dn->index.keyPattern;
LOG(5) << "PROJECTION: covered via DISTINCT, using COVERED fast path";
}
}
diff --git a/src/mongo/db/query/planner_analysis_test.cpp b/src/mongo/db/query/planner_analysis_test.cpp
index c02c9c25cfb..a47e6780787 100644
--- a/src/mongo/db/query/planner_analysis_test.cpp
+++ b/src/mongo/db/query/planner_analysis_test.cpp
@@ -103,8 +103,7 @@ TEST(QueryPlannerAnalysis, GetSortPatternSpecialIndexTypes) {
// Test the generation of sort orders provided by an index scan done by
// IndexScanNode::computeProperties().
TEST(QueryPlannerAnalysis, IxscanSortOrdersBasic) {
- IndexScanNode ixscan;
- ixscan.indexKeyPattern = fromjson("{a: 1, b: 1, c: 1, d: 1, e: 1}");
+ IndexScanNode ixscan(IndexEntry(fromjson("{a: 1, b: 1, c: 1, d: 1, e: 1}")));
// Bounds are {a: [[1,1]], b: [[2,2]], c: [[3,3]], d: [[1,5]], e:[[1,1],[2,2]]},
// all inclusive.
diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp
index 83c7c500fc7..5665b333a56 100644
--- a/src/mongo/db/query/query_planner.cpp
+++ b/src/mongo/db/query/query_planner.cpp
@@ -32,8 +32,10 @@
#include "mongo/db/query/query_planner.h"
+#include <boost/optional.hpp>
#include <vector>
+#include "mongo/base/string_data.h"
#include "mongo/client/dbclientinterface.h" // For QueryOption_foobar
#include "mongo/db/bson/dotted_path_support.h"
#include "mongo/db/matcher/expression_algo.h"
@@ -292,7 +294,7 @@ Status QueryPlanner::cacheDataFromTaggedTree(const MatchExpression* const tagged
// static
Status QueryPlanner::tagAccordingToCache(MatchExpression* filter,
const PlanCacheIndexTree* const indexTree,
- const map<BSONObj, size_t>& indexMap) {
+ const map<StringData, size_t>& indexMap) {
if (NULL == filter) {
return Status(ErrorCodes::BadValue, "Cannot tag tree: filter is NULL.");
}
@@ -321,10 +323,10 @@ Status QueryPlanner::tagAccordingToCache(MatchExpression* filter,
}
if (NULL != indexTree->entry.get()) {
- map<BSONObj, size_t>::const_iterator got = indexMap.find(indexTree->entry->keyPattern);
+ map<StringData, size_t>::const_iterator got = indexMap.find(indexTree->entry->name);
if (got == indexMap.end()) {
mongoutils::str::stream ss;
- ss << "Did not find index with keyPattern: " << indexTree->entry->keyPattern.toString();
+ ss << "Did not find index with name: " << indexTree->entry->name;
return Status(ErrorCodes::BadValue, ss);
}
filter->setTag(
@@ -386,11 +388,11 @@ Status QueryPlanner::planFromCache(const CanonicalQuery& query,
// Map from index name to index number.
// TODO: can we assume that the index numbering has the same lifetime
// as the cache state?
- map<BSONObj, size_t> indexMap;
+ map<StringData, size_t> indexMap;
for (size_t i = 0; i < params.indices.size(); ++i) {
const IndexEntry& ie = params.indices[i];
- indexMap[ie.keyPattern] = i;
- LOG(5) << "Index " << i << ": " << ie.keyPattern.toString() << endl;
+ indexMap[ie.name] = i;
+ LOG(5) << "Index " << i << ": " << ie.name << endl;
}
Status s = tagAccordingToCache(clone.get(), winnerCacheData.tree.get(), indexMap);
@@ -531,7 +533,7 @@ Status QueryPlanner::plan(const CanonicalQuery& query,
}
}
- size_t hintIndexNumber = numeric_limits<size_t>::max();
+ boost::optional<size_t> hintIndexNumber;
if (hintIndex.isEmpty()) {
QueryPlannerIXSelect::findRelevantIndices(fields, params.indices, &relevantIndices);
@@ -558,13 +560,20 @@ Status QueryPlanner::plan(const CanonicalQuery& query,
relevantIndices.push_back(params.indices[i]);
LOG(5) << "Hint specified, restricting indices to " << hintIndex.toString()
<< endl;
+ if (hintIndexNumber) {
+ return Status(ErrorCodes::IndexNotFound,
+ str::stream() << "Hint matched multiple indexes, "
+ << "must hint by index name. Matched: "
+ << params.indices[i].toString()
+ << " and "
+ << params.indices[*hintIndexNumber].toString());
+ }
hintIndexNumber = i;
- break;
}
}
}
- if (hintIndexNumber == numeric_limits<size_t>::max()) {
+ if (!hintIndexNumber) {
return Status(ErrorCodes::BadValue, "bad hint");
}
}
@@ -598,7 +607,7 @@ Status QueryPlanner::plan(const CanonicalQuery& query,
return Status(ErrorCodes::BadValue, "hint provided does not work with max query");
}
- const BSONObj& kp = params.indices[hintIndexNumber].keyPattern;
+ const BSONObj& kp = params.indices[*hintIndexNumber].keyPattern;
finishedMinObj = finishMinObj(kp, minObj, maxObj);
finishedMaxObj = finishMaxObj(kp, minObj, maxObj);
@@ -609,7 +618,7 @@ Status QueryPlanner::plan(const CanonicalQuery& query,
"hint provided does not work with min/max query");
}
- idxNo = hintIndexNumber;
+ idxNo = *hintIndexNumber;
} else {
// No hinted index, look for one that is compatible (has same field names and
// ordering thereof).
@@ -807,7 +816,7 @@ Status QueryPlanner::plan(const CanonicalQuery& query,
// desired behavior when an index is hinted that is not relevant to the query.
if (!hintIndex.isEmpty()) {
if (0 == out->size()) {
- QuerySolution* soln = buildWholeIXSoln(params.indices[hintIndexNumber], query, params);
+ QuerySolution* soln = buildWholeIXSoln(params.indices[*hintIndexNumber], query, params);
verify(NULL != soln);
LOG(5) << "Planner: outputting soln that uses hinted index as scan." << endl;
out->push_back(soln);
diff --git a/src/mongo/db/query/query_planner.h b/src/mongo/db/query/query_planner.h
index 0b33b109b4e..da4e7225541 100644
--- a/src/mongo/db/query/query_planner.h
+++ b/src/mongo/db/query/query_planner.h
@@ -28,6 +28,7 @@
#pragma once
+#include "mongo/base/string_data.h"
#include "mongo/db/query/canonical_query.h"
#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/query/query_solution.h"
@@ -111,7 +112,7 @@ public:
*/
static Status tagAccordingToCache(MatchExpression* filter,
const PlanCacheIndexTree* const indexTree,
- const std::map<BSONObj, size_t>& indexMap);
+ const std::map<StringData, size_t>& indexMap);
};
} // namespace mongo
diff --git a/src/mongo/db/query/query_planner_common.cpp b/src/mongo/db/query/query_planner_common.cpp
index 2ae977a825e..d97b9f39345 100644
--- a/src/mongo/db/query/query_planner_common.cpp
+++ b/src/mongo/db/query/query_planner_common.cpp
@@ -60,7 +60,7 @@ void QueryPlannerCommon::reverseScans(QuerySolutionNode* node) {
}
}
- if (!isn->bounds.isValidFor(isn->indexKeyPattern, isn->direction)) {
+ if (!isn->bounds.isValidFor(isn->index.keyPattern, isn->direction)) {
LOG(5) << "Invalid bounds: " << isn->bounds.toString() << std::endl;
invariant(0);
}
diff --git a/src/mongo/db/query/query_planner_test.cpp b/src/mongo/db/query/query_planner_test.cpp
index 4fc0145d0dc..b063e12db0b 100644
--- a/src/mongo/db/query/query_planner_test.cpp
+++ b/src/mongo/db/query/query_planner_test.cpp
@@ -35,6 +35,7 @@
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/db/matcher/extensions_callback_disallow_extensions.h"
+#include "mongo/db/query/collation/collator_interface_mock.h"
#include "mongo/db/query/query_planner.h"
#include "mongo/db/query/query_planner_test_fixture.h"
@@ -4172,9 +4173,9 @@ TEST_F(QueryPlannerTest, TagAccordingToCacheFailsOnBadInput) {
std::unique_ptr<CanonicalQuery> scopedCq = std::move(statusWithCQ.getValue());
std::unique_ptr<PlanCacheIndexTree> indexTree(new PlanCacheIndexTree());
- indexTree->setIndexEntry(IndexEntry(BSON("a" << 1)));
+ indexTree->setIndexEntry(IndexEntry(BSON("a" << 1), "a_1"));
- std::map<BSONObj, size_t> indexMap;
+ std::map<StringData, size_t> indexMap;
// Null filter.
Status s = QueryPlanner::tagAccordingToCache(NULL, indexTree.get(), indexMap);
@@ -4189,7 +4190,7 @@ TEST_F(QueryPlannerTest, TagAccordingToCacheFailsOnBadInput) {
ASSERT_NOT_OK(s);
// Index found once added to the map.
- indexMap[BSON("a" << 1)] = 0;
+ indexMap["a_1"_sd] = 0;
s = QueryPlanner::tagAccordingToCache(scopedCq->root(), indexTree.get(), indexMap);
ASSERT_OK(s);
@@ -4248,4 +4249,19 @@ TEST_F(QueryPlannerTest, NorWithSingleChildCanUseIndexAfterComplementingBounds)
"{a: [['MinKey', -Infinity, true, false], [3, 'MaxKey', true, true]]}}}}}");
}
+// Multiple indexes
+TEST_F(QueryPlannerTest, PlansForMultipleIndexesOnTheSameKeyPatternAreGenerated) {
+ CollatorInterfaceMock reverseCollator(CollatorInterfaceMock::MockType::kReverseString);
+ CollatorInterfaceMock equalCollator(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ addIndex(BSON("a" << 1), &reverseCollator, "reverse"_sd);
+ addIndex(BSON("a" << 1), &equalCollator, "forward"_sd);
+
+ runQuery(BSON("a" << 1));
+
+ assertNumSolutions(3U);
+ assertSolutionExists("{fetch: {node: {ixscan: {name: 'reverse'}}}}");
+ assertSolutionExists("{fetch: {node: {ixscan: {name: 'forward'}}}}");
+ assertSolutionExists("{cscan: {dir: 1}}}}");
+}
+
} // namespace
diff --git a/src/mongo/db/query/query_planner_test_fixture.cpp b/src/mongo/db/query/query_planner_test_fixture.cpp
index 46762b3eb4a..d97856b849a 100644
--- a/src/mongo/db/query/query_planner_test_fixture.cpp
+++ b/src/mongo/db/query/query_planner_test_fixture.cpp
@@ -140,6 +140,20 @@ void QueryPlannerTest::addIndex(BSONObj keyPattern, const CollatorInterface* col
}
void QueryPlannerTest::addIndex(BSONObj keyPattern,
+ const CollatorInterface* collator,
+ StringData indexName) {
+ const bool sparse = false;
+ const bool unique = false;
+ const bool multikey = false;
+ const auto name = indexName.toString();
+ const MatchExpression* filterExpr = nullptr;
+ const BSONObj infoObj;
+ IndexEntry entry(keyPattern, multikey, sparse, unique, name, filterExpr, infoObj);
+ entry.collator = collator;
+ params.indices.push_back(entry);
+}
+
+void QueryPlannerTest::addIndex(BSONObj keyPattern,
MatchExpression* filterExpr,
const CollatorInterface* collator) {
const bool sparse = false;
diff --git a/src/mongo/db/query/query_planner_test_fixture.h b/src/mongo/db/query/query_planner_test_fixture.h
index 6c649702c74..c55182c226c 100644
--- a/src/mongo/db/query/query_planner_test_fixture.h
+++ b/src/mongo/db/query/query_planner_test_fixture.h
@@ -72,6 +72,8 @@ protected:
MatchExpression* filterExpr,
const CollatorInterface* collator);
+ void addIndex(BSONObj keyPattern, const CollatorInterface* collator, StringData indexName);
+
//
// Execute planner.
//
diff --git a/src/mongo/db/query/query_planner_test_lib.cpp b/src/mongo/db/query/query_planner_test_lib.cpp
index a52505acc23..9e71f1d0f8c 100644
--- a/src/mongo/db/query/query_planner_test_lib.cpp
+++ b/src/mongo/db/query/query_planner_test_lib.cpp
@@ -266,10 +266,26 @@ bool QueryPlannerTestLib::solutionMatches(const BSONObj& testSoln,
BSONObj ixscanObj = el.Obj();
BSONElement pattern = ixscanObj["pattern"];
- if (pattern.eoo() || !pattern.isABSONObj()) {
- return false;
+ if (!pattern.eoo()) {
+ if (!pattern.isABSONObj()) {
+ return false;
+ }
+ if (pattern.Obj() != ixn->index.keyPattern) {
+ return false;
+ }
}
- if (pattern.Obj() != ixn->indexKeyPattern) {
+
+ BSONElement name = ixscanObj["name"];
+ if (!name.eoo()) {
+ if (name.type() != BSONType::String) {
+ return false;
+ }
+ if (name.valueStringData() != ixn->index.name) {
+ return false;
+ }
+ }
+
+ if (name.eoo() && pattern.eoo()) {
return false;
}
@@ -314,7 +330,7 @@ bool QueryPlannerTestLib::solutionMatches(const BSONObj& testSoln,
return false;
}
BSONObj geoObj = el.Obj();
- return geoObj == node->indexKeyPattern;
+ return geoObj == node->index.keyPattern;
} else if (STAGE_GEO_NEAR_2DSPHERE == trueSoln->getType()) {
const GeoNear2DSphereNode* node = static_cast<const GeoNear2DSphereNode*>(trueSoln);
BSONElement el = testSoln["geoNear2dsphere"];
@@ -327,7 +343,7 @@ bool QueryPlannerTestLib::solutionMatches(const BSONObj& testSoln,
if (pattern.eoo() || !pattern.isABSONObj()) {
return false;
}
- if (pattern.Obj() != node->indexKeyPattern) {
+ if (pattern.Obj() != node->index.keyPattern) {
return false;
}
diff --git a/src/mongo/db/query/query_solution.cpp b/src/mongo/db/query/query_solution.cpp
index 51ff8aea132..60fd5cc84fc 100644
--- a/src/mongo/db/query/query_solution.cpp
+++ b/src/mongo/db/query/query_solution.cpp
@@ -182,7 +182,9 @@ void TextNode::appendToString(mongoutils::str::stream* ss, int indent) const {
addIndent(ss, indent);
*ss << "TEXT\n";
addIndent(ss, indent + 1);
- *ss << "keyPattern = " << indexKeyPattern.toString() << '\n';
+ *ss << "name = " << index.name << '\n';
+ addIndent(ss, indent + 1);
+ *ss << "keyPattern = " << index.keyPattern.toString() << '\n';
addIndent(ss, indent + 1);
*ss << "query = " << ftsQuery->getQuery() << '\n';
addIndent(ss, indent + 1);
@@ -201,11 +203,10 @@ void TextNode::appendToString(mongoutils::str::stream* ss, int indent) const {
}
QuerySolutionNode* TextNode::clone() const {
- TextNode* copy = new TextNode();
+ TextNode* copy = new TextNode(this->index);
cloneBaseData(copy);
copy->_sort = this->_sort;
- copy->indexKeyPattern = this->indexKeyPattern;
copy->ftsQuery = this->ftsQuery->clone();
copy->indexPrefix = this->indexPrefix;
@@ -503,19 +504,19 @@ QuerySolutionNode* FetchNode::clone() const {
// IndexScanNode
//
-IndexScanNode::IndexScanNode()
- : indexIsMultiKey(false),
+IndexScanNode::IndexScanNode(IndexEntry index)
+ : index(std::move(index)),
direction(1),
maxScan(0),
addKeyMetadata(false),
- indexCollator(nullptr),
queryCollator(nullptr) {}
void IndexScanNode::appendToString(mongoutils::str::stream* ss, int indent) const {
addIndent(ss, indent);
*ss << "IXSCAN\n";
addIndent(ss, indent + 1);
- *ss << "keyPattern = " << indexKeyPattern << '\n';
+ *ss << "indexName = " << index.name << '\n';
+ *ss << "keyPattern = " << index.keyPattern << '\n';
if (NULL != filter) {
addIndent(ss, indent + 1);
*ss << "filter = " << filter->toString();
@@ -530,17 +531,17 @@ void IndexScanNode::appendToString(mongoutils::str::stream* ss, int indent) cons
bool IndexScanNode::hasField(const string& field) const {
// There is no covering in a multikey index because you don't know whether or not the field
// in the key was extracted from an array in the original document.
- if (indexIsMultiKey) {
+ if (index.multikey) {
return false;
}
// Custom index access methods may return non-exact key data - this function is currently
// used for covering exact key data only.
- if (IndexNames::BTREE != IndexNames::findPluginName(indexKeyPattern)) {
+ if (IndexNames::BTREE != IndexNames::findPluginName(index.keyPattern)) {
return false;
}
- BSONObjIterator it(indexKeyPattern);
+ BSONObjIterator it(index.keyPattern);
while (it.more()) {
if (field == it.next().fieldName()) {
return true;
@@ -556,7 +557,7 @@ bool IndexScanNode::sortedByDiskLoc() const {
// If it's a simple range query, it's easy to determine if the range is a point.
if (bounds.isSimpleRange) {
- return 0 == bounds.startKey.woCompare(bounds.endKey, indexKeyPattern);
+ return 0 == bounds.startKey.woCompare(bounds.endKey, index.keyPattern);
}
// If it's a more complex bounds query, we make sure that each field is a point.
@@ -627,7 +628,7 @@ std::set<StringData> IndexScanNode::getFieldsWithStringBounds(const IndexBounds&
void IndexScanNode::computeProperties() {
_sorts.clear();
- BSONObj sortPattern = QueryPlannerAnalysis::getSortPattern(indexKeyPattern);
+ BSONObj sortPattern = QueryPlannerAnalysis::getSortPattern(index.keyPattern);
if (direction == -1) {
sortPattern = QueryPlannerCommon::reverseSortObj(sortPattern);
}
@@ -668,7 +669,7 @@ void IndexScanNode::computeProperties() {
equalityFields.insert(oil.name);
}
} else {
- BSONObjIterator keyIter(indexKeyPattern);
+ BSONObjIterator keyIter(index.keyPattern);
BSONObjIterator startIter(bounds.startKey);
BSONObjIterator endIter(bounds.endKey);
while (keyIter.more() && startIter.more() && endIter.more()) {
@@ -683,9 +684,9 @@ void IndexScanNode::computeProperties() {
addEqualityFieldSorts(sortPattern, equalityFields, &_sorts);
}
- if (!CollatorInterface::collatorsMatch(queryCollator, indexCollator)) {
+ if (!CollatorInterface::collatorsMatch(queryCollator, index.collator)) {
// Prune sorts containing fields that don't match the collation.
- std::set<StringData> collatedFields = getFieldsWithStringBounds(bounds, indexKeyPattern);
+ std::set<StringData> collatedFields = getFieldsWithStringBounds(bounds, index.keyPattern);
auto sortsIt = _sorts.begin();
while (sortsIt != _sorts.end()) {
bool matched = false;
@@ -705,17 +706,14 @@ void IndexScanNode::computeProperties() {
}
QuerySolutionNode* IndexScanNode::clone() const {
- IndexScanNode* copy = new IndexScanNode();
+ IndexScanNode* copy = new IndexScanNode(this->index);
cloneBaseData(copy);
copy->_sorts = this->_sorts;
- copy->indexKeyPattern = this->indexKeyPattern;
- copy->indexIsMultiKey = this->indexIsMultiKey;
copy->direction = this->direction;
copy->maxScan = this->maxScan;
copy->addKeyMetadata = this->addKeyMetadata;
copy->bounds = this->bounds;
- copy->indexCollator = this->indexCollator;
copy->queryCollator = this->queryCollator;
return copy;
@@ -736,8 +734,7 @@ bool filtersAreEquivalent(const MatchExpression* lhs, const MatchExpression* rhs
} // namespace
bool IndexScanNode::operator==(const IndexScanNode& other) const {
- return filtersAreEquivalent(filter.get(), other.filter.get()) &&
- indexKeyPattern == other.indexKeyPattern && indexIsMultiKey == other.indexIsMultiKey &&
+ return filtersAreEquivalent(filter.get(), other.filter.get()) && index == other.index &&
direction == other.direction && maxScan == other.maxScan &&
addKeyMetadata == other.addKeyMetadata && bounds == other.bounds;
}
@@ -916,7 +913,9 @@ void GeoNear2DNode::appendToString(mongoutils::str::stream* ss, int indent) cons
addIndent(ss, indent);
*ss << "GEO_NEAR_2D\n";
addIndent(ss, indent + 1);
- *ss << "keyPattern = " << indexKeyPattern.toString() << '\n';
+ *ss << "name = " << index.name << '\n';
+ addIndent(ss, indent + 1);
+ *ss << "keyPattern = " << index.keyPattern.toString() << '\n';
addCommon(ss, indent);
*ss << "nearQuery = " << nq->toString() << '\n';
if (NULL != filter) {
@@ -926,13 +925,12 @@ void GeoNear2DNode::appendToString(mongoutils::str::stream* ss, int indent) cons
}
QuerySolutionNode* GeoNear2DNode::clone() const {
- GeoNear2DNode* copy = new GeoNear2DNode();
+ GeoNear2DNode* copy = new GeoNear2DNode(this->index);
cloneBaseData(copy);
copy->_sorts = this->_sorts;
copy->nq = this->nq;
copy->baseBounds = this->baseBounds;
- copy->indexKeyPattern = this->indexKeyPattern;
copy->addPointMeta = this->addPointMeta;
copy->addDistMeta = this->addDistMeta;
@@ -947,7 +945,9 @@ void GeoNear2DSphereNode::appendToString(mongoutils::str::stream* ss, int indent
addIndent(ss, indent);
*ss << "GEO_NEAR_2DSPHERE\n";
addIndent(ss, indent + 1);
- *ss << "keyPattern = " << indexKeyPattern.toString() << '\n';
+ *ss << "name = " << index.name << '\n';
+ addIndent(ss, indent + 1);
+ *ss << "keyPattern = " << index.keyPattern.toString() << '\n';
addCommon(ss, indent);
*ss << "baseBounds = " << baseBounds.toString() << '\n';
addIndent(ss, indent + 1);
@@ -959,13 +959,12 @@ void GeoNear2DSphereNode::appendToString(mongoutils::str::stream* ss, int indent
}
QuerySolutionNode* GeoNear2DSphereNode::clone() const {
- GeoNear2DSphereNode* copy = new GeoNear2DSphereNode();
+ GeoNear2DSphereNode* copy = new GeoNear2DSphereNode(this->index);
cloneBaseData(copy);
copy->_sorts = this->_sorts;
copy->nq = this->nq;
copy->baseBounds = this->baseBounds;
- copy->indexKeyPattern = this->indexKeyPattern;
copy->addPointMeta = this->addPointMeta;
copy->addDistMeta = this->addDistMeta;
@@ -1035,7 +1034,9 @@ void DistinctNode::appendToString(mongoutils::str::stream* ss, int indent) const
addIndent(ss, indent);
*ss << "DISTINCT\n";
addIndent(ss, indent + 1);
- *ss << "keyPattern = " << indexKeyPattern << '\n';
+ *ss << "name = " << index.name << '\n';
+ addIndent(ss, indent + 1);
+ *ss << "keyPattern = " << index.keyPattern << '\n';
addIndent(ss, indent + 1);
*ss << "direction = " << direction << '\n';
addIndent(ss, indent + 1);
@@ -1043,11 +1044,10 @@ void DistinctNode::appendToString(mongoutils::str::stream* ss, int indent) const
}
QuerySolutionNode* DistinctNode::clone() const {
- DistinctNode* copy = new DistinctNode();
+ DistinctNode* copy = new DistinctNode(this->index);
cloneBaseData(copy);
copy->sorts = this->sorts;
- copy->indexKeyPattern = this->indexKeyPattern;
copy->direction = this->direction;
copy->bounds = this->bounds;
copy->fieldNo = this->fieldNo;
@@ -1063,7 +1063,9 @@ void CountScanNode::appendToString(mongoutils::str::stream* ss, int indent) cons
addIndent(ss, indent);
*ss << "COUNT\n";
addIndent(ss, indent + 1);
- *ss << "keyPattern = " << indexKeyPattern << '\n';
+ *ss << "name = " << index.name << '\n';
+ addIndent(ss, indent + 1);
+ *ss << "keyPattern = " << index.keyPattern << '\n';
addIndent(ss, indent + 1);
*ss << "startKey = " << startKey << '\n';
addIndent(ss, indent + 1);
@@ -1071,11 +1073,10 @@ void CountScanNode::appendToString(mongoutils::str::stream* ss, int indent) cons
}
QuerySolutionNode* CountScanNode::clone() const {
- CountScanNode* copy = new CountScanNode();
+ CountScanNode* copy = new CountScanNode(this->index);
cloneBaseData(copy);
copy->sorts = this->sorts;
- copy->indexKeyPattern = this->indexKeyPattern;
copy->startKey = this->startKey;
copy->startKeyInclusive = this->startKeyInclusive;
copy->endKey = this->endKey;
diff --git a/src/mongo/db/query/query_solution.h b/src/mongo/db/query/query_solution.h
index 224b9512b3c..aca56fcded6 100644
--- a/src/mongo/db/query/query_solution.h
+++ b/src/mongo/db/query/query_solution.h
@@ -217,7 +217,7 @@ private:
};
struct TextNode : public QuerySolutionNode {
- TextNode() {}
+ TextNode(IndexEntry index) : index(std::move(index)) {}
virtual ~TextNode() {}
virtual StageType getType() const {
@@ -244,7 +244,7 @@ struct TextNode : public QuerySolutionNode {
BSONObjSet _sort;
- BSONObj indexKeyPattern;
+ IndexEntry index;
std::unique_ptr<fts::FTSQuery> ftsQuery;
// "Prefix" fields of a text index can handle equality predicates. We group them with the
@@ -433,7 +433,7 @@ struct FetchNode : public QuerySolutionNode {
};
struct IndexScanNode : public QuerySolutionNode {
- IndexScanNode();
+ IndexScanNode(IndexEntry index);
virtual ~IndexScanNode() {}
virtual void computeProperties();
@@ -467,8 +467,7 @@ struct IndexScanNode : public QuerySolutionNode {
BSONObjSet _sorts;
- BSONObj indexKeyPattern;
- bool indexIsMultiKey;
+ IndexEntry index;
int direction;
@@ -480,7 +479,6 @@ struct IndexScanNode : public QuerySolutionNode {
IndexBounds bounds;
- const CollatorInterface* indexCollator;
const CollatorInterface* queryCollator;
};
@@ -699,7 +697,8 @@ struct SkipNode : public QuerySolutionNode {
// This is a standalone stage.
struct GeoNear2DNode : public QuerySolutionNode {
- GeoNear2DNode() : addPointMeta(false), addDistMeta(false) {}
+ GeoNear2DNode(IndexEntry index)
+ : index(std::move(index)), addPointMeta(false), addDistMeta(false) {}
virtual ~GeoNear2DNode() {}
virtual StageType getType() const {
@@ -728,14 +727,15 @@ struct GeoNear2DNode : public QuerySolutionNode {
const GeoNearExpression* nq;
IndexBounds baseBounds;
- BSONObj indexKeyPattern;
+ IndexEntry index;
bool addPointMeta;
bool addDistMeta;
};
// This is actually its own standalone stage.
struct GeoNear2DSphereNode : public QuerySolutionNode {
- GeoNear2DSphereNode() : addPointMeta(false), addDistMeta(false) {}
+ GeoNear2DSphereNode(IndexEntry index)
+ : index(std::move(index)), addPointMeta(false), addDistMeta(false) {}
virtual ~GeoNear2DSphereNode() {}
virtual StageType getType() const {
@@ -764,7 +764,7 @@ struct GeoNear2DSphereNode : public QuerySolutionNode {
const GeoNearExpression* nq;
IndexBounds baseBounds;
- BSONObj indexKeyPattern;
+ IndexEntry index;
bool addPointMeta;
bool addDistMeta;
};
@@ -846,7 +846,7 @@ struct KeepMutationsNode : public QuerySolutionNode {
* *always* skip over the current key to the next key.
*/
struct DistinctNode : public QuerySolutionNode {
- DistinctNode() {}
+ DistinctNode(IndexEntry index) : index(std::move(index)) {}
virtual ~DistinctNode() {}
virtual StageType getType() const {
@@ -860,7 +860,7 @@ struct DistinctNode : public QuerySolutionNode {
return false;
}
bool hasField(const std::string& field) const {
- return !indexKeyPattern[field].eoo();
+ return !index.keyPattern[field].eoo();
}
bool sortedByDiskLoc() const {
return false;
@@ -873,10 +873,10 @@ struct DistinctNode : public QuerySolutionNode {
BSONObjSet sorts;
- BSONObj indexKeyPattern;
+ IndexEntry index;
int direction;
IndexBounds bounds;
- // We are distinct-ing over the 'fieldNo'-th field of 'indexKeyPattern'.
+ // We are distinct-ing over the 'fieldNo'-th field of 'index.keyPattern'.
int fieldNo;
};
@@ -885,7 +885,7 @@ struct DistinctNode : public QuerySolutionNode {
* Btree.
*/
struct CountScanNode : public QuerySolutionNode {
- CountScanNode() {}
+ CountScanNode(IndexEntry index) : index(std::move(index)) {}
virtual ~CountScanNode() {}
virtual StageType getType() const {
@@ -910,7 +910,7 @@ struct CountScanNode : public QuerySolutionNode {
BSONObjSet sorts;
- BSONObj indexKeyPattern;
+ IndexEntry index;
BSONObj startKey;
bool startKeyInclusive;
diff --git a/src/mongo/db/query/query_solution_test.cpp b/src/mongo/db/query/query_solution_test.cpp
index e80f62176ae..7654ca57867 100644
--- a/src/mongo/db/query/query_solution_test.cpp
+++ b/src/mongo/db/query/query_solution_test.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/matcher/extensions_callback_disallow_extensions.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
#include "mongo/db/query/index_bounds_builder.h"
+#include "mongo/db/query/index_entry.h"
#include "mongo/db/query/query_solution.h"
#include "mongo/stdx/memory.h"
#include "mongo/unittest/unittest.h"
@@ -45,8 +46,7 @@ using namespace mongo;
// Min: {a: 1, b: 1, c: 1, d: 1, e: 1}
// Max: {a: 1, b: 1, c: 1, d: 1, e: 1}
TEST(QuerySolutionTest, SimpleRangeAllEqual) {
- IndexScanNode node{};
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1))};
node.bounds.isSimpleRange = true;
node.bounds.startKey = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
node.bounds.endKey = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
@@ -69,8 +69,7 @@ TEST(QuerySolutionTest, SimpleRangeAllEqual) {
// Min: {a: 1, b: 1, c: 1, d: 1, e: 1}
// Max: {a: 2, b: 2, c: 2, d: 2, e: 2}
TEST(QuerySolutionTest, SimpleRangeNoneEqual) {
- IndexScanNode node{};
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1))};
node.bounds.isSimpleRange = true;
node.bounds.startKey = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
node.bounds.endKey = BSON("a" << 2 << "b" << 2 << "c" << 2 << "d" << 2 << "e" << 2);
@@ -89,8 +88,7 @@ TEST(QuerySolutionTest, SimpleRangeNoneEqual) {
// Min: {a: 1, b: 1, c: 1, d: 1, e: 1}
// Max: {a: 1, b: 1, c: 2, d: 2, e: 2}
TEST(QuerySolutionTest, SimpleRangeSomeEqual) {
- IndexScanNode node{};
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1))};
node.bounds.isSimpleRange = true;
node.bounds.startKey = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
node.bounds.endKey = BSON("a" << 1 << "b" << 1 << "c" << 2 << "d" << 2 << "e" << 2);
@@ -112,8 +110,7 @@ TEST(QuerySolutionTest, SimpleRangeSomeEqual) {
// Index: {a: 1, b: 1, c: 1, d: 1, e: 1}
// Intervals: a: [1,1], b: [1,1], c: [1,1], d: [1,1], e: [1,1]
TEST(QuerySolutionTest, IntervalListAllPoints) {
- IndexScanNode node{};
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1))};
OrderedIntervalList a{};
a.name = "a";
@@ -159,8 +156,7 @@ TEST(QuerySolutionTest, IntervalListAllPoints) {
// Index: {a: 1, b: 1, c: 1, d: 1, e: 1}
// Intervals: a: [1,2], b: [1,2], c: [1,2], d: [1,2], e: [1,2]
TEST(QuerySolutionTest, IntervalListNoPoints) {
- IndexScanNode node{};
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1))};
OrderedIntervalList a{};
a.name = "a";
@@ -206,8 +202,7 @@ TEST(QuerySolutionTest, IntervalListNoPoints) {
// Index: {a: 1, b: 1, c: 1, d: 1, e: 1}
// Intervals: a: [1,1], b: [1,1], c: [1,2], d: [1,2], e: [1,2]
TEST(QuerySolutionTest, IntervalListSomePoints) {
- IndexScanNode node{};
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1))};
OrderedIntervalList a{};
a.name = "a";
@@ -368,10 +363,8 @@ TEST(QuerySolutionTest, GetFieldsWithStringBoundsIdentifiesStringsFromObjectType
}
TEST(QuerySolutionTest, IndexScanNodeRemovesNonMatchingCollatedFieldsFromSortsOnSimpleBounds) {
- IndexScanNode node{};
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1))};
CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString);
-
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1);
node.queryCollator = &queryCollator;
node.bounds.isSimpleRange = true;
@@ -387,10 +380,8 @@ TEST(QuerySolutionTest, IndexScanNodeRemovesNonMatchingCollatedFieldsFromSortsOn
}
TEST(QuerySolutionTest, IndexScanNodeGetFieldsWithStringBoundsCorrectlyHandlesEndKeyInclusive) {
- IndexScanNode node{};
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1))};
CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString);
-
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1);
node.queryCollator = &queryCollator;
node.bounds.isSimpleRange = true;
@@ -419,10 +410,8 @@ TEST(QuerySolutionTest, IndexScanNodeGetFieldsWithStringBoundsCorrectlyHandlesEn
// Index: {a: 1}
// Bounds: [MINKEY, MAXKEY]
TEST(QuerySolutionTest, IndexScanNodeRemovesCollatedFieldsFromSortsIfCollationDifferent) {
- IndexScanNode node{};
+ IndexScanNode node{IndexEntry(BSON("a" << 1))};
CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString);
-
- node.indexKeyPattern = BSON("a" << 1);
node.queryCollator = &queryCollator;
OrderedIntervalList oilA{};
@@ -438,11 +427,9 @@ TEST(QuerySolutionTest, IndexScanNodeRemovesCollatedFieldsFromSortsIfCollationDi
}
TEST(QuerySolutionTest, IndexScanNodeDoesNotRemoveCollatedFieldsFromSortsIfCollationMatches) {
- IndexScanNode node{};
+ IndexScanNode node{IndexEntry(BSON("a" << 1))};
CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString);
- node.indexKeyPattern = BSON("a" << 1);
-
OrderedIntervalList oilA{};
oilA.name = "a";
oilA.intervals.push_back(
@@ -459,11 +446,11 @@ TEST(QuerySolutionTest, IndexScanNodeDoesNotRemoveCollatedFieldsFromSortsIfColla
// Index: {a: 1, b: 1, c: 1, d: 1, e: 1}
// Intervals: a: [1,1], b: [1,1], c: [MinKey, MaxKey], d: [1,2], e: [1,2]
TEST(QuerySolutionTest, CompoundIndexWithNonMatchingCollationFiltersAllSortsWithCollatedField) {
- IndexScanNode node{};
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1))};
CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString);
node.queryCollator = &queryCollator;
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1);
+ node.index = IndexEntry(BSON("a" << 1 << "b" << 1 << "c" << 1 << "d" << 1 << "e" << 1));
OrderedIntervalList a{};
a.name = "a";
@@ -504,10 +491,8 @@ TEST(QuerySolutionTest, CompoundIndexWithNonMatchingCollationFiltersAllSortsWith
// Index: {a : 1}
// Bounds: [{}, {}]
TEST(QuerySolutionTest, IndexScanNodeWithNonMatchingCollationFiltersObjectField) {
- IndexScanNode node{};
+ IndexScanNode node{IndexEntry(BSON("a" << 1))};
CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString);
-
- node.indexKeyPattern = BSON("a" << 1);
node.queryCollator = &queryCollator;
OrderedIntervalList oilA{};
@@ -525,10 +510,8 @@ TEST(QuerySolutionTest, IndexScanNodeWithNonMatchingCollationFiltersObjectField)
// Index: {a : 1}
// Bounds: [[], []]
TEST(QuerySolutionTest, IndexScanNodeWithNonMatchingCollationFiltersArrayField) {
- IndexScanNode node{};
+ IndexScanNode node{IndexEntry(BSON("a" << 1))};
CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString);
-
- node.indexKeyPattern = BSON("a" << 1);
node.queryCollator = &queryCollator;
OrderedIntervalList oilA{};
@@ -544,12 +527,10 @@ TEST(QuerySolutionTest, IndexScanNodeWithNonMatchingCollationFiltersArrayField)
}
TEST(QuerySolutionTest, WithNonMatchingCollatorAndNoEqualityPrefixSortsAreNotDuplicated) {
- IndexScanNode node{};
+ IndexScanNode node{IndexEntry(BSON("a" << 1 << "b" << 1))};
CollatorInterfaceMock queryCollator(CollatorInterfaceMock::MockType::kReverseString);
node.queryCollator = &queryCollator;
- node.indexKeyPattern = BSON("a" << 1 << "b" << 1);
-
OrderedIntervalList oilA{};
oilA.name = "a";
oilA.intervals.push_back(
@@ -593,8 +574,8 @@ std::unique_ptr<ParsedProjection> createParsedProjection(const BSONObj& query,
}
TEST(QuerySolutionTest, InclusionProjectionPreservesSort) {
- auto node = stdx::make_unique<IndexScanNode>();
- node->indexKeyPattern = BSON("a" << 1);
+ IndexEntry index(BSON("a" << 1));
+ auto node = stdx::make_unique<IndexScanNode>(index);
BSONObj projection = BSON("a" << 1);
BSONObj match;
@@ -610,8 +591,8 @@ TEST(QuerySolutionTest, InclusionProjectionPreservesSort) {
}
TEST(QuerySolutionTest, ExclusionProjectionDoesNotPreserveSort) {
- auto node = stdx::make_unique<IndexScanNode>();
- node->indexKeyPattern = BSON("a" << 1);
+ IndexEntry index(BSON("a" << 1));
+ auto node = stdx::make_unique<IndexScanNode>(index);
BSONObj projection = BSON("a" << 0);
BSONObj match;
@@ -626,8 +607,7 @@ TEST(QuerySolutionTest, ExclusionProjectionDoesNotPreserveSort) {
}
TEST(QuerySolutionTest, InclusionProjectionTruncatesSort) {
- auto node = stdx::make_unique<IndexScanNode>();
- node->indexKeyPattern = BSON("a" << 1 << "b" << 1);
+ auto node = stdx::make_unique<IndexScanNode>(IndexEntry(BSON("a" << 1 << "b" << 1)));
BSONObj projection = BSON("a" << 1);
BSONObj match;
@@ -643,8 +623,7 @@ TEST(QuerySolutionTest, InclusionProjectionTruncatesSort) {
}
TEST(QuerySolutionTest, ExclusionProjectionTruncatesSort) {
- auto node = stdx::make_unique<IndexScanNode>();
- node->indexKeyPattern = BSON("a" << 1 << "b" << 1);
+ auto node = stdx::make_unique<IndexScanNode>(IndexEntry(BSON("a" << 1 << "b" << 1)));
BSONObj projection = BSON("b" << 0);
BSONObj match;
diff --git a/src/mongo/db/query/stage_builder.cpp b/src/mongo/db/query/stage_builder.cpp
index 9df048b60a0..e790325a439 100644
--- a/src/mongo/db/query/stage_builder.cpp
+++ b/src/mongo/db/query/stage_builder.cpp
@@ -90,8 +90,7 @@ PlanStage* buildStages(OperationContext* txn,
IndexScanParams params;
- params.descriptor =
- collection->getIndexCatalog()->findIndexByKeyPattern(txn, ixn->indexKeyPattern);
+ params.descriptor = collection->getIndexCatalog()->findIndexByName(txn, ixn->index.name);
invariant(params.descriptor);
params.bounds = ixn->bounds;
@@ -223,7 +222,7 @@ PlanStage* buildStages(OperationContext* txn,
params.addDistMeta = node->addDistMeta;
IndexDescriptor* twoDIndex =
- collection->getIndexCatalog()->findIndexByKeyPattern(txn, node->indexKeyPattern);
+ collection->getIndexCatalog()->findIndexByName(txn, node->index.name);
invariant(twoDIndex);
GeoNear2DStage* nearStage = new GeoNear2DStage(params, txn, ws, collection, twoDIndex);
@@ -240,14 +239,14 @@ PlanStage* buildStages(OperationContext* txn,
params.addDistMeta = node->addDistMeta;
IndexDescriptor* s2Index =
- collection->getIndexCatalog()->findIndexByKeyPattern(txn, node->indexKeyPattern);
+ collection->getIndexCatalog()->findIndexByName(txn, node->index.name);
invariant(s2Index);
return new GeoNear2DSphereStage(params, txn, ws, collection, s2Index);
} else if (STAGE_TEXT == root->getType()) {
const TextNode* node = static_cast<const TextNode*>(root);
IndexDescriptor* desc =
- collection->getIndexCatalog()->findIndexByKeyPattern(txn, node->indexKeyPattern);
+ collection->getIndexCatalog()->findIndexByName(txn, node->index.name);
invariant(desc);
const FTSAccessMethod* fam =
static_cast<FTSAccessMethod*>(collection->getIndexCatalog()->getIndex(desc));
@@ -290,8 +289,7 @@ PlanStage* buildStages(OperationContext* txn,
DistinctParams params;
- params.descriptor =
- collection->getIndexCatalog()->findIndexByKeyPattern(txn, dn->indexKeyPattern);
+ params.descriptor = collection->getIndexCatalog()->findIndexByName(txn, dn->index.name);
invariant(params.descriptor);
params.direction = dn->direction;
params.bounds = dn->bounds;
@@ -307,8 +305,7 @@ PlanStage* buildStages(OperationContext* txn,
CountScanParams params;
- params.descriptor =
- collection->getIndexCatalog()->findIndexByKeyPattern(txn, csn->indexKeyPattern);
+ params.descriptor = collection->getIndexCatalog()->findIndexByName(txn, csn->index.name);
invariant(params.descriptor);
params.startKey = csn->startKey;
params.startKeyInclusive = csn->startKeyInclusive;
diff --git a/src/mongo/db/repl/oplog_buffer_collection.cpp b/src/mongo/db/repl/oplog_buffer_collection.cpp
index 818a1e205fd..90a8f120c97 100644
--- a/src/mongo/db/repl/oplog_buffer_collection.cpp
+++ b/src/mongo/db/repl/oplog_buffer_collection.cpp
@@ -47,7 +47,7 @@ namespace {
const char kDefaultOplogCollectionNamespace[] = "local.temp_oplog_buffer";
const char kOplogEntryFieldName[] = "entry";
-const BSONObj kIdObj = BSON("_id" << 1);
+const StringData kIdIdxName = "_id_"_sd;
} // namespace
@@ -222,7 +222,7 @@ bool OplogBufferCollection::_doPop_inlock(OperationContext* txn, Value* value) {
return true;
}
auto scanDirection = StorageInterface::ScanDirection::kForward;
- auto result = _storageInterface->deleteOne(txn, _nss, kIdObj, scanDirection);
+ auto result = _storageInterface->deleteOne(txn, _nss, kIdIdxName, scanDirection);
if (!result.isOK()) {
if (result != ErrorCodes::CollectionIsEmpty) {
fassert(40162, result.getStatus());
@@ -249,7 +249,7 @@ bool OplogBufferCollection::_peekOneSide_inlock(OperationContext* txn,
}
auto scanDirection = front ? StorageInterface::ScanDirection::kForward
: StorageInterface::ScanDirection::kBackward;
- auto result = _storageInterface->findOne(txn, _nss, kIdObj, scanDirection);
+ auto result = _storageInterface->findOne(txn, _nss, kIdIdxName, scanDirection);
if (!result.isOK()) {
if (result != ErrorCodes::CollectionIsEmpty) {
fassert(40163, result.getStatus());
diff --git a/src/mongo/db/repl/storage_interface.h b/src/mongo/db/repl/storage_interface.h
index cf06e9caae0..25873b366ea 100644
--- a/src/mongo/db/repl/storage_interface.h
+++ b/src/mongo/db/repl/storage_interface.h
@@ -29,10 +29,12 @@
#pragma once
+#include <boost/optional.hpp>
#include <iosfwd>
#include <string>
#include "mongo/base/disallow_copying.h"
+#include "mongo/base/string_data.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/repl/collection_bulk_loader.h"
#include "mongo/db/repl/optime.h"
@@ -219,7 +221,7 @@ public:
/**
* Finds the first document returned by a collection or index scan on the collection in the
* requested direction.
- * If "indexKeyPattern" is empty, a collection scan is used to locate the document.
+ * If "indexName" is boost::none, a collection scan is used to locate the document.
*/
enum class ScanDirection {
kForward = 1,
@@ -227,17 +229,17 @@ public:
};
virtual StatusWith<BSONObj> findOne(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) = 0;
/**
* Deletes the first document returned by a collection or index scan on the collection in the
* requested direction. Returns deleted document on success.
- * If "indexKeyPattern" is empty, a collection scan is used to locate the document.
+ * If "indexName" is null, a collection scan is used to locate the document.
*/
virtual StatusWith<BSONObj> deleteOne(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) = 0;
};
diff --git a/src/mongo/db/repl/storage_interface_impl.cpp b/src/mongo/db/repl/storage_interface_impl.cpp
index e7d410b978e..f2fe3bcf5e2 100644
--- a/src/mongo/db/repl/storage_interface_impl.cpp
+++ b/src/mongo/db/repl/storage_interface_impl.cpp
@@ -33,10 +33,12 @@
#include "mongo/db/repl/storage_interface_impl.h"
#include <algorithm>
+#include <boost/optional.hpp>
#include <utility>
#include "mongo/base/status.h"
#include "mongo/base/status_with.h"
+#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/catalog/collection.h"
@@ -412,7 +414,7 @@ DeleteStageParams makeDeleteStageParamsForDeleteOne() {
enum class FindDeleteMode { kFind, kDelete };
StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
StorageInterface::ScanDirection scanDirection,
FindDeleteMode mode) {
auto isFind = mode == FindDeleteMode::kFind;
@@ -432,7 +434,7 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn,
auto direction = isForward ? InternalPlanner::FORWARD : InternalPlanner::BACKWARD;
std::unique_ptr<PlanExecutor> planExecutor;
- if (indexKeyPattern.isEmpty()) {
+ if (!indexName) {
// Use collection scan.
planExecutor = isFind
? InternalPlanner::collectionScan(
@@ -447,22 +449,22 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn,
auto indexCatalog = collection->getIndexCatalog();
invariant(indexCatalog);
bool includeUnfinishedIndexes = false;
- auto indexDescriptor =
- indexCatalog->findIndexByKeyPattern(txn, indexKeyPattern, includeUnfinishedIndexes);
+ IndexDescriptor* indexDescriptor =
+ indexCatalog->findIndexByName(txn, *indexName, includeUnfinishedIndexes);
if (!indexDescriptor) {
return {ErrorCodes::IndexNotFound,
str::stream() << "Index not found, ns:" << nss.ns() << ", index: "
- << indexKeyPattern};
+ << *indexName};
}
if (indexDescriptor->isPartial()) {
return {ErrorCodes::IndexOptionsConflict,
str::stream() << "Partial index is not allowed for this operation, ns:"
<< nss.ns()
<< ", index: "
- << indexKeyPattern};
+ << *indexName};
}
- KeyPattern keyPattern(indexKeyPattern);
+ KeyPattern keyPattern(indexDescriptor->keyPattern());
auto minKey = Helpers::toKeyFormat(keyPattern.extendRangeBound({}, false));
auto maxKey = Helpers::toKeyFormat(keyPattern.extendRangeBound({}, true));
auto bounds =
@@ -494,8 +496,7 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn,
auto state = planExecutor->getNext(&doc, nullptr);
if (PlanExecutor::IS_EOF == state) {
return {ErrorCodes::CollectionIsEmpty,
- str::stream() << "Collection is empty, ns: " << nss.ns() << ", index: "
- << indexKeyPattern};
+ str::stream() << "Collection is empty, ns: " << nss.ns()};
}
invariant(PlanExecutor::ADVANCED == state);
return doc;
@@ -508,16 +509,16 @@ StatusWith<BSONObj> _findOrDeleteOne(OperationContext* txn,
StatusWith<BSONObj> StorageInterfaceImpl::findOne(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) {
- return _findOrDeleteOne(txn, nss, indexKeyPattern, scanDirection, FindDeleteMode::kFind);
+ return _findOrDeleteOne(txn, nss, indexName, scanDirection, FindDeleteMode::kFind);
}
StatusWith<BSONObj> StorageInterfaceImpl::deleteOne(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) {
- return _findOrDeleteOne(txn, nss, indexKeyPattern, scanDirection, FindDeleteMode::kDelete);
+ return _findOrDeleteOne(txn, nss, indexName, scanDirection, FindDeleteMode::kDelete);
}
Status StorageInterfaceImpl::isAdminDbValid(OperationContext* txn) {
diff --git a/src/mongo/db/repl/storage_interface_impl.h b/src/mongo/db/repl/storage_interface_impl.h
index ff8a59b1ecd..7e45c77fae2 100644
--- a/src/mongo/db/repl/storage_interface_impl.h
+++ b/src/mongo/db/repl/storage_interface_impl.h
@@ -32,6 +32,7 @@
#include "mongo/base/disallow_copying.h"
#include "mongo/base/status.h"
#include "mongo/base/status_with.h"
+#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/db/catalog/index_create.h"
#include "mongo/db/db_raii.h"
@@ -106,12 +107,12 @@ public:
StatusWith<BSONObj> findOne(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) override;
StatusWith<BSONObj> deleteOne(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) override;
Status isAdminDbValid(OperationContext* txn) override;
diff --git a/src/mongo/db/repl/storage_interface_impl_test.cpp b/src/mongo/db/repl/storage_interface_impl_test.cpp
index 43da79635cb..8c56cfc5fad 100644
--- a/src/mongo/db/repl/storage_interface_impl_test.cpp
+++ b/src/mongo/db/repl/storage_interface_impl_test.cpp
@@ -28,6 +28,7 @@
#include "mongo/platform/basic.h"
+#include <boost/optional.hpp>
#include <memory>
#include "mongo/bson/bsonmisc.h"
@@ -549,19 +550,19 @@ TEST_F(StorageInterfaceImplWithReplCoordTest, FindOneReturnsInvalidNamespaceIfCo
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("_id" << 1);
+ auto indexName = "_id_"_sd;
ASSERT_EQUALS(ErrorCodes::NamespaceNotFound,
- storage.findOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kForward));
+ storage.findOne(txn, nss, indexName, StorageInterface::ScanDirection::kForward));
}
TEST_F(StorageInterfaceImplWithReplCoordTest, FindOneReturnsIndexNotFoundIfIndexIsMissing) {
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("x" << 1);
+ auto indexName = "nonexistent"_sd;
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_EQUALS(ErrorCodes::IndexNotFound,
- storage.findOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kForward));
+ storage.findOne(txn, nss, indexName, StorageInterface::ScanDirection::kForward));
}
TEST_F(StorageInterfaceImplWithReplCoordTest,
@@ -581,19 +582,19 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
std::vector<BSONObj> docs = {BSON("_id" << 1), BSON("_id" << 1), BSON("_id" << 2)};
ASSERT_OK(loader->insertDocuments(docs.begin(), docs.end()));
ASSERT_OK(loader->commit());
- auto keyPattern = BSON("x" << 1);
+ auto indexName = "x_1"_sd;
ASSERT_EQUALS(ErrorCodes::IndexOptionsConflict,
- storage.findOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kForward));
+ storage.findOne(txn, nss, indexName, StorageInterface::ScanDirection::kForward));
}
TEST_F(StorageInterfaceImplWithReplCoordTest, FindOneReturnsCollectionIsEmptyIfCollectionIsEmpty) {
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("_id" << 1);
+ auto indexName = "_id_"_sd;
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_EQUALS(ErrorCodes::CollectionIsEmpty,
- storage.findOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kForward));
+ storage.findOne(txn, nss, indexName, StorageInterface::ScanDirection::kForward));
}
TEST_F(StorageInterfaceImplWithReplCoordTest,
@@ -601,12 +602,12 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("_id" << 1);
+ auto indexName = "_id_"_sd;
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_OK(
storage.insertDocuments(txn, nss, {BSON("_id" << 0), BSON("_id" << 1), BSON("_id" << 2)}));
ASSERT_EQUALS(BSON("_id" << 0),
- storage.findOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kForward));
+ storage.findOne(txn, nss, indexName, StorageInterface::ScanDirection::kForward));
// Check collection contents. OplogInterface returns documents in reverse natural order.
OplogInterfaceLocal oplog(txn, nss.ns());
@@ -622,13 +623,12 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("_id" << 1);
+ auto indexName = "_id_"_sd;
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_OK(
storage.insertDocuments(txn, nss, {BSON("_id" << 0), BSON("_id" << 1), BSON("_id" << 2)}));
- ASSERT_EQUALS(
- BSON("_id" << 2),
- storage.findOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kBackward));
+ ASSERT_EQUALS(BSON("_id" << 2),
+ storage.findOne(txn, nss, indexName, StorageInterface::ScanDirection::kBackward));
// Check collection contents. OplogInterface returns documents in reverse natural order.
OplogInterfaceLocal oplog(txn, nss.ns());
@@ -647,8 +647,9 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_OK(
storage.insertDocuments(txn, nss, {BSON("_id" << 1), BSON("_id" << 2), BSON("_id" << 0)}));
- ASSERT_EQUALS(BSON("_id" << 1),
- storage.findOne(txn, nss, BSONObj(), StorageInterface::ScanDirection::kForward));
+ ASSERT_EQUALS(
+ BSON("_id" << 1),
+ storage.findOne(txn, nss, boost::none, StorageInterface::ScanDirection::kForward));
// Check collection contents. OplogInterface returns documents in reverse natural order.
OplogInterfaceLocal oplog(txn, nss.ns());
@@ -667,8 +668,9 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_OK(
storage.insertDocuments(txn, nss, {BSON("_id" << 1), BSON("_id" << 2), BSON("_id" << 0)}));
- ASSERT_EQUALS(BSON("_id" << 0),
- storage.findOne(txn, nss, BSONObj(), StorageInterface::ScanDirection::kBackward));
+ ASSERT_EQUALS(
+ BSON("_id" << 0),
+ storage.findOne(txn, nss, boost::none, StorageInterface::ScanDirection::kBackward));
// Check collection contents. OplogInterface returns documents in reverse natural order.
OplogInterfaceLocal oplog(txn, nss.ns());
@@ -684,21 +686,21 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("_id" << 1);
+ auto indexName = "_id_"_sd;
ASSERT_EQUALS(
ErrorCodes::NamespaceNotFound,
- storage.deleteOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kForward));
+ storage.deleteOne(txn, nss, indexName, StorageInterface::ScanDirection::kForward));
}
TEST_F(StorageInterfaceImplWithReplCoordTest, DeleteOneReturnsIndexNotFoundIfIndexIsMissing) {
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("x" << 1);
+ auto indexName = "nonexistent"_sd;
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_EQUALS(
ErrorCodes::IndexNotFound,
- storage.deleteOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kForward));
+ storage.deleteOne(txn, nss, indexName, StorageInterface::ScanDirection::kForward));
}
TEST_F(StorageInterfaceImplWithReplCoordTest,
@@ -706,11 +708,11 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("_id" << 1);
+ auto indexName = "_id_"_sd;
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_EQUALS(
ErrorCodes::CollectionIsEmpty,
- storage.deleteOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kForward));
+ storage.deleteOne(txn, nss, indexName, StorageInterface::ScanDirection::kForward));
}
TEST_F(StorageInterfaceImplWithReplCoordTest,
@@ -718,13 +720,13 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("_id" << 1);
+ auto indexName = "_id_"_sd;
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_OK(
storage.insertDocuments(txn, nss, {BSON("_id" << 0), BSON("_id" << 1), BSON("_id" << 2)}));
ASSERT_EQUALS(
BSON("_id" << 0),
- storage.deleteOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kForward));
+ storage.deleteOne(txn, nss, indexName, StorageInterface::ScanDirection::kForward));
// Check collection contents. OplogInterface returns documents in reverse natural order.
OplogInterfaceLocal oplog(txn, nss.ns());
@@ -739,13 +741,13 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
auto txn = getOperationContext();
StorageInterfaceImpl storage;
auto nss = makeNamespace(_agent);
- auto keyPattern = BSON("_id" << 1);
+ auto indexName = "_id_"_sd;
ASSERT_OK(storage.createCollection(txn, nss, CollectionOptions()));
ASSERT_OK(
storage.insertDocuments(txn, nss, {BSON("_id" << 0), BSON("_id" << 1), BSON("_id" << 2)}));
ASSERT_EQUALS(
BSON("_id" << 2),
- storage.deleteOne(txn, nss, keyPattern, StorageInterface::ScanDirection::kBackward));
+ storage.deleteOne(txn, nss, indexName, StorageInterface::ScanDirection::kBackward));
// Check collection contents. OplogInterface returns documents in reverse natural order.
OplogInterfaceLocal oplog(txn, nss.ns());
@@ -765,7 +767,7 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
storage.insertDocuments(txn, nss, {BSON("_id" << 1), BSON("_id" << 2), BSON("_id" << 0)}));
ASSERT_EQUALS(
BSON("_id" << 1),
- storage.deleteOne(txn, nss, BSONObj(), StorageInterface::ScanDirection::kForward));
+ storage.deleteOne(txn, nss, boost::none, StorageInterface::ScanDirection::kForward));
// Check collection contents. OplogInterface returns documents in reverse natural order.
OplogInterfaceLocal oplog(txn, nss.ns());
@@ -785,7 +787,7 @@ TEST_F(StorageInterfaceImplWithReplCoordTest,
storage.insertDocuments(txn, nss, {BSON("_id" << 1), BSON("_id" << 2), BSON("_id" << 0)}));
ASSERT_EQUALS(
BSON("_id" << 0),
- storage.deleteOne(txn, nss, BSONObj(), StorageInterface::ScanDirection::kBackward));
+ storage.deleteOne(txn, nss, boost::none, StorageInterface::ScanDirection::kBackward));
// Check collection contents. OplogInterface returns documents in reverse natural order.
OplogInterfaceLocal oplog(txn, nss.ns());
diff --git a/src/mongo/db/repl/storage_interface_mock.h b/src/mongo/db/repl/storage_interface_mock.h
index 4b19194832d..30f868f0520 100644
--- a/src/mongo/db/repl/storage_interface_mock.h
+++ b/src/mongo/db/repl/storage_interface_mock.h
@@ -32,6 +32,7 @@
#include "mongo/base/disallow_copying.h"
#include "mongo/base/status.h"
#include "mongo/base/status_with.h"
+#include "mongo/base/string_data.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/repl/storage_interface.h"
@@ -107,11 +108,11 @@ public:
stdx::function<Status(OperationContext* txn, const NamespaceString& nss)>;
using FindOneFn = stdx::function<StatusWith<BSONObj>(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection)>;
using DeleteOneFn = stdx::function<StatusWith<BSONObj>(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection)>;
StorageInterfaceMock() = default;
@@ -169,16 +170,16 @@ public:
StatusWith<BSONObj> findOne(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) override {
- return findOneFn(txn, nss, indexKeyPattern, scanDirection);
+ return findOneFn(txn, nss, indexName, scanDirection);
}
StatusWith<BSONObj> deleteOne(OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) override {
- return deleteOneFn(txn, nss, indexKeyPattern, scanDirection);
+ return deleteOneFn(txn, nss, indexName, scanDirection);
}
Status isAdminDbValid(OperationContext* txn) override {
@@ -218,13 +219,13 @@ public:
};
FindOneFn findOneFn = [](OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) {
return Status{ErrorCodes::IllegalOperation, "FindOneFn not implemented."};
};
DeleteOneFn deleteOneFn = [](OperationContext* txn,
const NamespaceString& nss,
- const BSONObj& indexKeyPattern,
+ boost::optional<StringData> indexName,
ScanDirection scanDirection) {
return Status{ErrorCodes::IllegalOperation, "DeleteOneFn not implemented."};
};
diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp
index bd5f863258a..917f0237c10 100644
--- a/src/mongo/db/ttl.cpp
+++ b/src/mongo/db/ttl.cpp
@@ -182,12 +182,13 @@ private:
}
const BSONObj key = idx["key"].Obj();
+ const StringData name = idx["name"].valueStringData();
if (key.nFields() != 1) {
error() << "key for ttl index can only have 1 field, skipping ttl job for: " << idx;
return;
}
- LOG(1) << "TTL -- ns: " << collectionNSS << " key: " << key;
+ LOG(1) << "TTL -- ns: " << collectionNSS << " key: " << key << " name: " << name;
AutoGetCollection autoGetCollection(txn, collectionNSS, MODE_IX);
Collection* collection = autoGetCollection.getCollection();
@@ -202,7 +203,7 @@ private:
return;
}
- IndexDescriptor* desc = collection->getIndexCatalog()->findIndexByKeyPattern(txn, key);
+ IndexDescriptor* desc = collection->getIndexCatalog()->findIndexByName(txn, name);
if (!desc) {
LOG(1) << "index not found (index build in progress? index dropped?), skipping "
<< "ttl job for: " << idx;