summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Rassi <rassi@10gen.com>2014-08-01 03:44:36 -0400
committerJason Rassi <rassi@10gen.com>2014-08-01 15:37:21 -0400
commit1f00ffcd22e671f5adeece53c68b5e462ba01ec0 (patch)
treec0f2bf4920a9a812870f1e980b3a477ac9910502
parentf76e04154f73286fc1ce4a023116faa4170bcfdf (diff)
downloadmongo-1f00ffcd22e671f5adeece53c68b5e462ba01ec0.tar.gz
SERVER-14738 Correctly determine if update w/ text index is in-place
-rw-r--r--jstests/core/fts_index3.js124
-rw-r--r--src/mongo/SConscript8
-rw-r--r--src/mongo/db/catalog/collection_info_cache.cpp47
-rw-r--r--src/mongo/db/catalog/collection_info_cache.h8
-rw-r--r--src/mongo/db/ops/SConscript2
-rw-r--r--src/mongo/db/ops/update.cpp8
-rw-r--r--src/mongo/db/ops/update_driver.cpp2
-rw-r--r--src/mongo/db/ops/update_driver.h6
-rw-r--r--src/mongo/db/ops/update_driver_test.cpp4
-rw-r--r--src/mongo/db/ops/update_lifecycle.h2
-rw-r--r--src/mongo/db/ops/update_lifecycle_impl.cpp2
-rw-r--r--src/mongo/db/ops/update_lifecycle_impl.h2
-rw-r--r--src/mongo/db/update_index_data.cpp (renamed from src/mongo/db/index_set.cpp)49
-rw-r--r--src/mongo/db/update_index_data.h (renamed from src/mongo/db/index_set.h)32
-rw-r--r--src/mongo/db/update_index_data_test.cpp (renamed from src/mongo/db/index_set_test.cpp)35
15 files changed, 279 insertions, 52 deletions
diff --git a/jstests/core/fts_index3.js b/jstests/core/fts_index3.js
new file mode 100644
index 00000000000..bb94704eaf9
--- /dev/null
+++ b/jstests/core/fts_index3.js
@@ -0,0 +1,124 @@
+// Test that updates to fields in a text-indexed document are correctly reflected in the text index.
+var coll = db.fts_index3;
+
+// 1) Create a text index on a single field, insert a document, update the value of the field, and
+// verify that $text with the new value returns the document.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({a: "text"}));
+assert.writeOK(coll.insert({a: "hello"}));
+assert.eq(1, coll.find({$text: {$search: "hello"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {a: "world"}}));
+assert.eq(0, coll.find({$text: {$search: "hello"}}).itcount());
+assert.eq(1, coll.find({$text: {$search: "world"}}).itcount());
+
+// 2) Same as #1, but with a wildcard text index.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({"$**": "text"}));
+assert.writeOK(coll.insert({a: "hello"}));
+assert.eq(1, coll.find({$text: {$search: "hello"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {a: "world"}}));
+assert.eq(0, coll.find({$text: {$search: "hello"}}).itcount());
+assert.eq(1, coll.find({$text: {$search: "world"}}).itcount());
+
+// 3) Create a compound text index with an index prefix, insert a document, update the value of the
+// index prefix field, and verify that $text with the new value returns the document.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({a: 1, b: "text"}));
+assert.writeOK(coll.insert({a: 1, b: "hello"}));
+assert.eq(1, coll.find({a: 1, $text: {$search: "hello"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {a: 2}}));
+assert.eq(0, coll.find({a: 1, $text: {$search: "hello"}}).itcount());
+assert.eq(1, coll.find({a: 2, $text: {$search: "hello"}}).itcount());
+
+// 4) Same as #3, but with a wildcard text index.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({a: 1, "$**": "text"}));
+assert.writeOK(coll.insert({a: 1, b: "hello"}));
+assert.eq(1, coll.find({a: 1, $text: {$search: "hello"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {a: 2}}));
+assert.eq(0, coll.find({a: 1, $text: {$search: "hello"}}).itcount());
+assert.eq(1, coll.find({a: 2, $text: {$search: "hello"}}).itcount());
+
+// 5) Create a compound text index with an index suffix, insert a document, update the value of the
+// index suffix field, and verify that $text with the new value returns the document.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({a: "text", b: 1}));
+assert.writeOK(coll.insert({a: "hello", b: 1}));
+assert.eq(1, coll.find({b: 1, $text: {$search: "hello"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {b: 2}}));
+assert.eq(0, coll.find({b: 1, $text: {$search: "hello"}}).itcount());
+assert.eq(1, coll.find({b: 2, $text: {$search: "hello"}}).itcount());
+
+// 6) Same as #5, but with a wildcard text index.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({"$**": "text", b: 1}));
+assert.writeOK(coll.insert({a: "hello", b: 1}));
+assert.eq(1, coll.find({b: 1, $text: {$search: "hello"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {b: 2}}));
+assert.eq(0, coll.find({b: 1, $text: {$search: "hello"}}).itcount());
+assert.eq(1, coll.find({b: 2, $text: {$search: "hello"}}).itcount());
+
+// 7) Create a text index on a single field, insert a document, update the language of the document
+// (so as to change the stemming), and verify that $text with the new language returns the document.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({a: "text"}));
+assert.writeOK(coll.insert({a: "testing", language: "es"}));
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {language: "en"}}));
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+
+// 8) Same as #7, but with a wildcard text index.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({"$**": "text"}));
+assert.writeOK(coll.insert({a: "testing", language: "es"}));
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {language: "en"}}));
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+
+// 9) Create a text index on a single nested field, insert a document, update the language of the
+// subdocument (so as to change the stemming), and verify that $text with the new language returns
+// the document.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({"a.b": "text"}));
+assert.writeOK(coll.insert({a: {b: "testing", language: "es"}}));
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {"a.language": "en"}}));
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+
+// 10) Same as #9, but with a wildcard text index.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({"$**": "text"}));
+assert.writeOK(coll.insert({a: {b: "testing", language: "es"}}));
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {"a.language": "en"}}));
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+
+// 11) Create a text index on a single field with a custom language override, insert a document,
+// update the language of the document (so as to change the stemming), and verify that $text with
+// the new language returns the document.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({a: "text"}, {language_override: "idioma"}));
+assert.writeOK(coll.insert({a: "testing", idioma: "es"}));
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {idioma: "en"}}));
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+
+// 12) Same as #11, but with a wildcard text index.
+coll.drop();
+assert.commandWorked(coll.ensureIndex({"$**": "text"}, {language_override: "idioma"}));
+assert.writeOK(coll.insert({a: "testing", idioma: "es"}));
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
+assert.writeOK(coll.update({}, {$set: {idioma: "en"}}));
+assert.eq(0, coll.find({$text: {$search: "testing", $language: "es"}}).itcount());
+assert.eq(1, coll.find({$text: {$search: "testing", $language: "en"}}).itcount());
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index a9b8d0ebaae..6a230b45b64 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -149,8 +149,8 @@ env.CppUnitTest('bsonobjbuilder_test', ['bson/bsonobjbuilder_test.cpp'],
env.CppUnitTest('namespacestring_test', ['db/namespace_string_test.cpp'],
LIBDEPS=['bson'])
-env.CppUnitTest('index_set_test', ['db/index_set_test.cpp'],
- LIBDEPS=['bson','index_set'])
+env.CppUnitTest('update_index_data_test', ['db/update_index_data_test.cpp'],
+ LIBDEPS=['bson','update_index_data','db/common'])
env.Library('path',
['db/matcher/path.cpp',
@@ -547,7 +547,7 @@ if has_option( "mm" ):
else:
mmapFiles += [ "util/mmap_${OS_FAMILY}.cpp" ]
-env.Library('index_set', [ 'db/index_set.cpp' ] )
+env.Library('update_index_data', [ 'db/update_index_data.cpp' ], LIPDEPS=[ 'db/common' ])
# Global Configuration. Used by both mongos and mongod.
env.Library('global_environment_experiment',
@@ -907,8 +907,8 @@ serveronlyLibdeps = ["coreshard",
"defaultversion",
"global_optime",
"index_key_validate",
- "index_set",
'range_deleter',
+ "update_index_data",
's/metadata',
's/batch_write_types',
"db/catalog/collection_options",
diff --git a/src/mongo/db/catalog/collection_info_cache.cpp b/src/mongo/db/catalog/collection_info_cache.cpp
index 85122d0d926..aea1af88c42 100644
--- a/src/mongo/db/catalog/collection_info_cache.cpp
+++ b/src/mongo/db/catalog/collection_info_cache.cpp
@@ -32,10 +32,12 @@
#include "mongo/db/catalog/collection_info_cache.h"
-#include "mongo/db/d_concurrency.h"
-#include "mongo/db/query/plan_cache.h"
#include "mongo/db/catalog/collection.h"
+#include "mongo/db/d_concurrency.h"
+#include "mongo/db/fts/fts_spec.h"
#include "mongo/db/index/index_descriptor.h"
+#include "mongo/db/index_legacy.h"
+#include "mongo/db/query/plan_cache.h"
#include "mongo/util/debug_util.h"
#include "mongo/util/log.h"
@@ -61,13 +63,40 @@ namespace mongo {
void CollectionInfoCache::computeIndexKeys() {
_indexedPaths.clear();
- IndexCatalog::IndexIterator i = _collection->getIndexCatalog()->getIndexIterator( true );
- while( i.more() ) {
- BSONObj key = i.next()->keyPattern();
- BSONObjIterator j( key );
- while ( j.more() ) {
- BSONElement e = j.next();
- _indexedPaths.addPath( e.fieldName() );
+ IndexCatalog::IndexIterator i = _collection->getIndexCatalog()->getIndexIterator(true);
+ while (i.more()) {
+ IndexDescriptor* descriptor = i.next();
+
+ if (descriptor->getAccessMethodName() != IndexNames::TEXT) {
+ BSONObj key = descriptor->keyPattern();
+ BSONObjIterator j(key);
+ while (j.more()) {
+ BSONElement e = j.next();
+ _indexedPaths.addPath(e.fieldName());
+ }
+ }
+ else {
+ fts::FTSSpec ftsSpec(descriptor->infoObj());
+
+ if (ftsSpec.wildcard()) {
+ _indexedPaths.allPathsIndexed();
+ }
+ else {
+ for (size_t i = 0; i < ftsSpec.numExtraBefore(); ++i) {
+ _indexedPaths.addPath(ftsSpec.extraBefore(i));
+ }
+ for (fts::Weights::const_iterator it = ftsSpec.weights().begin();
+ it != ftsSpec.weights().end();
+ ++it) {
+ _indexedPaths.addPath(it->first);
+ }
+ for (size_t i = 0; i < ftsSpec.numExtraAfter(); ++i) {
+ _indexedPaths.addPath(ftsSpec.extraAfter(i));
+ }
+ // Any update to a path containing "language" as a component could change the
+ // language of a subdocument. Add the override field as a path component.
+ _indexedPaths.addPathComponent(ftsSpec.languageOverrideField());
+ }
}
}
diff --git a/src/mongo/db/catalog/collection_info_cache.h b/src/mongo/db/catalog/collection_info_cache.h
index 874018fdc89..3948c55e5c0 100644
--- a/src/mongo/db/catalog/collection_info_cache.h
+++ b/src/mongo/db/catalog/collection_info_cache.h
@@ -32,9 +32,9 @@
#include <boost/scoped_ptr.hpp>
-#include "mongo/db/index_set.h"
-#include "mongo/db/query/query_settings.h"
#include "mongo/db/query/plan_cache.h"
+#include "mongo/db/query/query_settings.h"
+#include "mongo/db/update_index_data.h"
namespace mongo {
@@ -73,7 +73,7 @@ namespace mongo {
/* get set of index keys for this namespace. handy to quickly check if a given
field is indexed (Note it might be a secondary component of a compound index.)
*/
- const IndexPathSet& indexKeys() {
+ const UpdateIndexData& indexKeys() {
if ( !_keysComputed )
computeIndexKeys();
return _indexedPaths;
@@ -97,7 +97,7 @@ namespace mongo {
// --- index keys cache
bool _keysComputed;
- IndexPathSet _indexedPaths;
+ UpdateIndexData _indexedPaths;
// A cache for query plans.
boost::scoped_ptr<PlanCache> _planCache;
diff --git a/src/mongo/db/ops/SConscript b/src/mongo/db/ops/SConscript
index 8748b5586e1..82f7a214c3c 100644
--- a/src/mongo/db/ops/SConscript
+++ b/src/mongo/db/ops/SConscript
@@ -207,7 +207,7 @@ env.Library(
'$BUILD_DIR/mongo/bson',
'$BUILD_DIR/mongo/db/common',
'$BUILD_DIR/mongo/db/query/query_planner',
- '$BUILD_DIR/mongo/index_set',
+ '$BUILD_DIR/mongo/update_index_data',
'update',
],
)
diff --git a/src/mongo/db/ops/update.cpp b/src/mongo/db/ops/update.cpp
index 4a95d8c92c9..921f7fd02e6 100644
--- a/src/mongo/db/ops/update.cpp
+++ b/src/mongo/db/ops/update.cpp
@@ -37,9 +37,10 @@
#include "mongo/bson/mutable/algorithm.h"
#include "mongo/bson/mutable/document.h"
#include "mongo/client/dbclientinterface.h"
+#include "mongo/db/catalog/collection.h"
#include "mongo/db/catalog/database_holder.h"
#include "mongo/db/clientcursor.h"
-#include "mongo/db/index_set.h"
+#include "mongo/db/operation_context_impl.h"
#include "mongo/db/ops/update_driver.h"
#include "mongo/db/ops/update_executor.h"
#include "mongo/db/ops/update_lifecycle.h"
@@ -47,10 +48,9 @@
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/lite_parsed_query.h"
#include "mongo/db/query/query_planner_common.h"
-#include "mongo/db/repl/repl_coordinator_global.h"
#include "mongo/db/repl/oplog.h"
-#include "mongo/db/operation_context_impl.h"
-#include "mongo/db/catalog/collection.h"
+#include "mongo/db/repl/repl_coordinator_global.h"
+#include "mongo/db/update_index_data.h"
#include "mongo/platform/unordered_set.h"
#include "mongo/util/log.h"
diff --git a/src/mongo/db/ops/update_driver.cpp b/src/mongo/db/ops/update_driver.cpp
index 9492cb13851..672aad85d73 100644
--- a/src/mongo/db/ops/update_driver.cpp
+++ b/src/mongo/db/ops/update_driver.cpp
@@ -390,7 +390,7 @@ namespace mongo {
return _affectIndices;
}
- void UpdateDriver::refreshIndexKeys(const IndexPathSet* indexedFields) {
+ void UpdateDriver::refreshIndexKeys(const UpdateIndexData* indexedFields) {
_indexedFields = indexedFields;
}
diff --git a/src/mongo/db/ops/update_driver.h b/src/mongo/db/ops/update_driver.h
index 29a702a1917..0b9fd951cb8 100644
--- a/src/mongo/db/ops/update_driver.h
+++ b/src/mongo/db/ops/update_driver.h
@@ -35,11 +35,11 @@
#include "mongo/base/status.h"
#include "mongo/bson/mutable/document.h"
#include "mongo/db/field_ref_set.h"
-#include "mongo/db/index_set.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/ops/modifier_interface.h"
#include "mongo/db/ops/modifier_table.h"
#include "mongo/db/query/canonical_query.h"
+#include "mongo/db/update_index_data.h"
namespace mongo {
@@ -107,7 +107,7 @@ namespace mongo {
bool isDocReplacement() const;
bool modsAffectIndices() const;
- void refreshIndexKeys(const IndexPathSet* indexedFields);
+ void refreshIndexKeys(const UpdateIndexData* indexedFields);
bool logOp() const;
void setLogOp(bool logOp);
@@ -153,7 +153,7 @@ namespace mongo {
// applied that participate in indices?
//
// NOTE: Owned by the collection's info cache!.
- const IndexPathSet* _indexedFields;
+ const UpdateIndexData* _indexedFields;
//
// mutable properties after parsing
diff --git a/src/mongo/db/ops/update_driver_test.cpp b/src/mongo/db/ops/update_driver_test.cpp
index b9213821cb3..27f26d9dd3a 100644
--- a/src/mongo/db/ops/update_driver_test.cpp
+++ b/src/mongo/db/ops/update_driver_test.cpp
@@ -31,7 +31,7 @@
#include "mongo/base/string_data.h"
#include "mongo/bson/mutable/document.h"
#include "mongo/bson/mutable/mutable_bson_test_utils.h"
-#include "mongo/db/index_set.h"
+#include "mongo/db/update_index_data.h"
#include "mongo/db/json.h"
#include "mongo/unittest/unittest.h"
@@ -39,7 +39,7 @@ namespace {
using mongo::BSONObj;
using mongo::fromjson;
- using mongo::IndexPathSet;
+ using mongo::UpdateIndexData;
using mongo::mutablebson::Document;
using mongo::StringData;
using mongo::UpdateDriver;
diff --git a/src/mongo/db/ops/update_lifecycle.h b/src/mongo/db/ops/update_lifecycle.h
index 4cb5076aa88..ed5b55d3e5d 100644
--- a/src/mongo/db/ops/update_lifecycle.h
+++ b/src/mongo/db/ops/update_lifecycle.h
@@ -56,7 +56,7 @@ namespace mongo {
/**
* Return a pointer to any indexes if there is a collection.
*/
- virtual const IndexPathSet* getIndexKeys() const = 0;
+ virtual const UpdateIndexData* getIndexKeys() const = 0;
/**
* Returns the shard keys as immutable fields
diff --git a/src/mongo/db/ops/update_lifecycle_impl.cpp b/src/mongo/db/ops/update_lifecycle_impl.cpp
index 5ae625923e5..87648c37403 100644
--- a/src/mongo/db/ops/update_lifecycle_impl.cpp
+++ b/src/mongo/db/ops/update_lifecycle_impl.cpp
@@ -61,7 +61,7 @@ namespace mongo {
return _collection;
}
- const IndexPathSet* UpdateLifecycleImpl::getIndexKeys() const {
+ const UpdateIndexData* UpdateLifecycleImpl::getIndexKeys() const {
if (_collection)
return &_collection->infoCache()->indexKeys();
return NULL;
diff --git a/src/mongo/db/ops/update_lifecycle_impl.h b/src/mongo/db/ops/update_lifecycle_impl.h
index c5a5b15ac67..fa612463757 100644
--- a/src/mongo/db/ops/update_lifecycle_impl.h
+++ b/src/mongo/db/ops/update_lifecycle_impl.h
@@ -52,7 +52,7 @@ namespace mongo {
virtual bool canContinue() const;
- virtual const IndexPathSet* getIndexKeys() const;
+ virtual const UpdateIndexData* getIndexKeys() const;
virtual const std::vector<FieldRef*>* getImmutableFields() const;
diff --git a/src/mongo/db/index_set.cpp b/src/mongo/db/update_index_data.cpp
index 5e16d0d90b0..2d93b5b6381 100644
--- a/src/mongo/db/index_set.cpp
+++ b/src/mongo/db/update_index_data.cpp
@@ -1,4 +1,4 @@
-// index_set.cpp
+// update_index_data.cpp
/**
* Copyright (C) 2013 10gen Inc.
@@ -29,32 +29,47 @@
*/
#include "mongo/bson/util/builder.h"
-#include "mongo/db/index_set.h"
+#include "mongo/db/field_ref.h"
+#include "mongo/db/update_index_data.h"
namespace mongo {
- void IndexPathSet::addPath( const StringData& path ) {
+ UpdateIndexData::UpdateIndexData() : _allPathsIndexed( false ) { }
+
+ void UpdateIndexData::addPath( const StringData& path ) {
string s;
if ( getCanonicalIndexField( path, &s ) ) {
- _canonical.insert( s );
+ _canonicalPaths.insert( s );
}
else {
- _canonical.insert( path.toString() );
+ _canonicalPaths.insert( path.toString() );
}
}
- void IndexPathSet::clear() {
- _canonical.clear();
+ void UpdateIndexData::addPathComponent( const StringData& pathComponent ) {
+ _pathComponents.insert( pathComponent.toString() );
+ }
+
+ void UpdateIndexData::allPathsIndexed() {
+ _allPathsIndexed = true;
}
- bool IndexPathSet::mightBeIndexed( const StringData& path ) const {
+ void UpdateIndexData::clear() {
+ _canonicalPaths.clear();
+ }
+
+ bool UpdateIndexData::mightBeIndexed( const StringData& path ) const {
+ if ( _allPathsIndexed ) {
+ return true;
+ }
+
StringData use = path;
string x;
if ( getCanonicalIndexField( path, &x ) )
use = StringData( x );
- for ( std::set<string>::const_iterator i = _canonical.begin();
- i != _canonical.end();
+ for ( std::set<string>::const_iterator i = _canonicalPaths.begin();
+ i != _canonicalPaths.end();
++i ) {
StringData idx( *i );
@@ -66,10 +81,22 @@ namespace mongo {
return true;
}
+ FieldRef pathFieldRef( path );
+ for ( std::set<string>::const_iterator i = _pathComponents.begin();
+ i != _pathComponents.end();
+ ++i ) {
+ const string& pathComponent = *i;
+ for ( size_t partIdx = 0; partIdx < pathFieldRef.numParts(); ++partIdx ) {
+ if ( pathComponent == pathFieldRef.getPart( partIdx ) ) {
+ return true;
+ }
+ }
+ }
+
return false;
}
- bool IndexPathSet::_startsWith( const StringData& a, const StringData& b ) const {
+ bool UpdateIndexData::_startsWith( const StringData& a, const StringData& b ) const {
if ( !a.startsWith( b ) )
return false;
diff --git a/src/mongo/db/index_set.h b/src/mongo/db/update_index_data.h
index b0a25a73773..90541b35df2 100644
--- a/src/mongo/db/index_set.h
+++ b/src/mongo/db/update_index_data.h
@@ -1,4 +1,4 @@
-// index_set.h
+// update_index_data.h
/**
* Copyright (C) 2013 10gen Inc.
@@ -42,10 +42,33 @@ namespace mongo {
*/
bool getCanonicalIndexField( const StringData& fullName, std::string* out );
- class IndexPathSet {
+
+ /**
+ * Holds pre-processed index spec information to allow update to quickly determine if an update
+ * can be applied as a delta to a document, or if the document must be re-indexed.
+ */
+ class UpdateIndexData {
public:
+ UpdateIndexData();
+
+ /**
+ * Register a path. Any update targeting this path (or a parent of this path) will
+ * trigger a recomputation of the document's index keys.
+ */
void addPath( const StringData& path );
+ /**
+ * Register a path component. Any update targeting a path that contains this exact
+ * component will trigger a recomputation of the document's index keys.
+ */
+ void addPathComponent( const StringData& pathComponent );
+
+ /**
+ * Register the "wildcard" path. All updates will trigger a recomputation of the document's
+ * index keys.
+ */
+ void allPathsIndexed();
+
void clear();
bool mightBeIndexed( const StringData& path ) const;
@@ -54,7 +77,10 @@ namespace mongo {
bool _startsWith( const StringData& a, const StringData& b ) const;
- std::set<std::string> _canonical;
+ std::set<std::string> _canonicalPaths;
+ std::set<std::string> _pathComponents;
+
+ bool _allPathsIndexed;
};
}
diff --git a/src/mongo/db/index_set_test.cpp b/src/mongo/db/update_index_data_test.cpp
index 595fac04238..244356d2845 100644
--- a/src/mongo/db/index_set_test.cpp
+++ b/src/mongo/db/update_index_data_test.cpp
@@ -1,4 +1,4 @@
-// index_set_tests.cpp
+// update_index_data_tests.cpp
/* Copyright 2012 10gen Inc.
*
@@ -29,12 +29,12 @@
#include "mongo/unittest/unittest.h"
-#include "mongo/db/index_set.h"
+#include "mongo/db/update_index_data.h"
namespace mongo {
- TEST( IndexPathSetTest, Simple1 ) {
- IndexPathSet a;
+ TEST( UpdateIndexDataTest, Simple1 ) {
+ UpdateIndexData a;
a.addPath( "a.b" );
ASSERT_TRUE( a.mightBeIndexed( "a.b" ) );
ASSERT_TRUE( a.mightBeIndexed( "a" ) );
@@ -45,14 +45,35 @@ namespace mongo {
ASSERT_FALSE( a.mightBeIndexed( "a.c" ) );
}
- TEST( IndexPathSetTest, Simple2 ) {
- IndexPathSet a;
+ TEST( UpdateIndexDataTest, Simple2 ) {
+ UpdateIndexData a;
a.addPath( "ab" );
ASSERT_FALSE( a.mightBeIndexed( "a" ) );
}
+ TEST( UpdateIndexDataTest, Component1 ) {
+ UpdateIndexData a;
+ a.addPathComponent( "a" );
+ ASSERT_FALSE( a.mightBeIndexed( "" ) );
+ ASSERT_TRUE( a.mightBeIndexed( "a" ) );
+ ASSERT_TRUE( a.mightBeIndexed( "b.a" ) );
+ ASSERT_TRUE( a.mightBeIndexed( "a.b" ) );
+ ASSERT_TRUE( a.mightBeIndexed( "b.a.c" ) );
+ ASSERT_FALSE( a.mightBeIndexed( "b.c" ) );
+ ASSERT_FALSE( a.mightBeIndexed( "ab" ) );
+ }
+
+ TEST( UpdateIndexDataTest, AllPathsIndexed1 ) {
+ UpdateIndexData a;
+ a.allPathsIndexed();
+ ASSERT_TRUE( a.mightBeIndexed( "a" ) );
+ ASSERT_TRUE( a.mightBeIndexed( "" ) );
+ a.addPathComponent( "a" );
+ ASSERT_TRUE( a.mightBeIndexed( "a" ) );
+ ASSERT_TRUE( a.mightBeIndexed( "b" ) );
+ }
- TEST( IndexPathSetTest, getCanonicalIndexField1 ) {
+ TEST( UpdateIndexDataTest, getCanonicalIndexField1 ) {
string x;
ASSERT_FALSE( getCanonicalIndexField( "a", &x ) );