diff options
author | Jason Rassi <rassi@10gen.com> | 2014-08-01 03:44:36 -0400 |
---|---|---|
committer | Jason Rassi <rassi@10gen.com> | 2014-08-01 15:37:21 -0400 |
commit | 1f00ffcd22e671f5adeece53c68b5e462ba01ec0 (patch) | |
tree | c0f2bf4920a9a812870f1e980b3a477ac9910502 | |
parent | f76e04154f73286fc1ce4a023116faa4170bcfdf (diff) | |
download | mongo-1f00ffcd22e671f5adeece53c68b5e462ba01ec0.tar.gz |
SERVER-14738 Correctly determine if update w/ text index is in-place
-rw-r--r-- | jstests/core/fts_index3.js | 124 | ||||
-rw-r--r-- | src/mongo/SConscript | 8 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_info_cache.cpp | 47 | ||||
-rw-r--r-- | src/mongo/db/catalog/collection_info_cache.h | 8 | ||||
-rw-r--r-- | src/mongo/db/ops/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/ops/update.cpp | 8 | ||||
-rw-r--r-- | src/mongo/db/ops/update_driver.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/ops/update_driver.h | 6 | ||||
-rw-r--r-- | src/mongo/db/ops/update_driver_test.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/ops/update_lifecycle.h | 2 | ||||
-rw-r--r-- | src/mongo/db/ops/update_lifecycle_impl.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/ops/update_lifecycle_impl.h | 2 | ||||
-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 ) ); |