summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron <aaron@10gen.com>2009-04-20 13:51:54 -0400
committerAaron <aaron@10gen.com>2009-04-20 13:51:54 -0400
commit80847b561c5851fa763866af06bb021af893f31a (patch)
tree4b6c080eae2f6ab024ab1d4cbe61d19245746f57
parentc34ef0f68d7f0dd493bdd2e50798f2c0211a71a7 (diff)
downloadmongo-80847b561c5851fa763866af06bb021af893f31a.tar.gz
add unique option when creating index in c++, js drivers; basic unique key unit tests
-rw-r--r--client/dbclient.cpp5
-rw-r--r--client/dbclient.h6
-rw-r--r--dbtests/perf/perftest.cpp8
-rw-r--r--dbtests/queryoptimizertests.cpp2
-rw-r--r--dbtests/querytests.cpp35
-rw-r--r--jstests/index8.js30
-rw-r--r--mongo.xcodeproj/project.pbxproj2
-rw-r--r--shell/collection.js41
8 files changed, 111 insertions, 18 deletions
diff --git a/client/dbclient.cpp b/client/dbclient.cpp
index 43102f64e93..292ced6ca29 100644
--- a/client/dbclient.cpp
+++ b/client/dbclient.cpp
@@ -518,7 +518,7 @@ namespace mongo {
say( toSend );
}
- bool DBClientBase::ensureIndex( const string &ns , BSONObj keys , const string & name ) {
+ bool DBClientBase::ensureIndex( const string &ns , BSONObj keys , bool unique, const string & name ) {
BSONObjBuilder toSave;
toSave.append( "ns" , ns );
toSave.append( "key" , keys );
@@ -556,6 +556,9 @@ namespace mongo {
toSave.append( "name" , ss.str() );
cacheKey += ss.str();
}
+
+ if ( unique )
+ toSave.appendBool( "unique", unique );
if ( _seenIndexes.count( cacheKey ) )
return 0;
diff --git a/client/dbclient.h b/client/dbclient.h
index cb6825a9f9a..21d1485b011 100644
--- a/client/dbclient.h
+++ b/client/dbclient.h
@@ -542,12 +542,14 @@ namespace mongo {
/** Create an index if it does not already exist.
ensureIndex calls are remembered so it is safe/fast to call this function many
times in your code.
- @param name if not isn't specified, it will be created from the keys (recommended)
+ @param ns collection to be indexed
@param keys the "key pattern" for the index. e.g., { name : 1 }
+ @param unique if true, indicates that key uniqueness should be enforced for this index
+ @param name if not isn't specified, it will be created from the keys (recommended)
@return whether or not sent message to db.
should be true on first call, false on subsequent unless resetIndexCache was called
*/
- virtual bool ensureIndex( const string &ns , BSONObj keys , const string &name = "" );
+ virtual bool ensureIndex( const string &ns , BSONObj keys , bool unique = false, const string &name = "" );
/**
clears the index cache, so the subsequent call to ensureIndex for any index will go to the server
diff --git a/dbtests/perf/perftest.cpp b/dbtests/perf/perftest.cpp
index e995a9347e8..3d383796277 100644
--- a/dbtests/perf/perftest.cpp
+++ b/dbtests/perf/perftest.cpp
@@ -111,7 +111,7 @@ namespace Insert {
const char *names = "aaaaaaaaaa";
for( int i = 0; i < 10; ++i ) {
client_->resetIndexCache();
- client_->ensureIndex( ns_.c_str(), BSON( "_id" << 1 ), names + i );
+ client_->ensureIndex( ns_.c_str(), BSON( "_id" << 1 ), false, names + i );
}
}
void run() {
@@ -612,7 +612,7 @@ namespace Plan {
const char *names = "aaaaaaaaaa";
for( int i = 0; i < 10; ++i ) {
client_->resetIndexCache();
- client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), names + i );
+ client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i );
}
lk_.reset( new dblock );
setClient( ns_.c_str() );
@@ -635,7 +635,7 @@ namespace Plan {
const char *names = "aaaaaaaaaa";
for( int i = 0; i < 10; ++i ) {
client_->resetIndexCache();
- client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), names + i );
+ client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i );
}
lk_.reset( new dblock );
setClient( ns_.c_str() );
@@ -654,7 +654,7 @@ namespace Plan {
const char *names = "aaaaaaaaaa";
for( int i = 0; i < 10; ++i ) {
client_->resetIndexCache();
- client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), names + i );
+ client_->ensureIndex( ns_.c_str(), BSON( ( names + i ) << 1 ), false, names + i );
}
lk_.reset( new dblock );
setClient( ns_.c_str() );
diff --git a/dbtests/queryoptimizertests.cpp b/dbtests/queryoptimizertests.cpp
index 52569045048..85570e7eb48 100644
--- a/dbtests/queryoptimizertests.cpp
+++ b/dbtests/queryoptimizertests.cpp
@@ -263,7 +263,7 @@ namespace QueryOptimizerTests {
ss << indexNum_++;
string name = ss.str();
client_.resetIndexCache();
- client_.ensureIndex( ns(), key, name.c_str() );
+ client_.ensureIndex( ns(), key, false, name.c_str() );
NamespaceDetails *d = nsd();
for( int i = 0; i < d->nIndexes; ++i ) {
if ( d->indexes[ i ].indexName() == name )
diff --git a/dbtests/querytests.cpp b/dbtests/querytests.cpp
index a6046e2ab89..78a3731e214 100644
--- a/dbtests/querytests.cpp
+++ b/dbtests/querytests.cpp
@@ -415,6 +415,39 @@ namespace QueryTests {
checkIndex();
}
};
+
+ class UniqueIndex : public ClientBase {
+ public:
+ ~UniqueIndex() {
+ client().dropCollection( "querytests.UniqueIndex" );
+ }
+ void run() {
+ const char *ns = "querytests.UniqueIndex";
+ client().ensureIndex( ns, BSON( "a" << 1 ), true );
+ client().insert( ns, BSON( "a" << 4 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 4 << "b" << 3 ) );
+ ASSERT_EQUALS( 1U, client().count( ns, BSONObj() ) );
+ client().dropCollection( ns );
+ client().ensureIndex( ns, BSON( "b" << 1 ), true );
+ client().insert( ns, BSON( "a" << 4 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 4 << "b" << 3 ) );
+ ASSERT_EQUALS( 2U, client().count( ns, BSONObj() ) );
+ }
+ };
+
+ class UniqueIndexPreexistingData : public ClientBase {
+ public:
+ ~UniqueIndexPreexistingData() {
+ client().dropCollection( "querytests.UniqueIndexPreexistingData" );
+ }
+ void run() {
+ const char *ns = "querytests.UniqueIndexPreexistingData";
+ client().insert( ns, BSON( "a" << 4 << "b" << 2 ) );
+ client().insert( ns, BSON( "a" << 4 << "b" << 3 ) );
+ client().ensureIndex( ns, BSON( "a" << 1 ), true );
+ ASSERT_EQUALS( 0U, client().count( "querytests.system.indexes", BSON( "ns" << ns ) ) );
+ }
+ };
class All : public UnitTest::Suite {
public:
@@ -438,6 +471,8 @@ namespace QueryTests {
add< MultiNe >();
add< EmbeddedNe >();
add< AutoResetIndexCache >();
+ add< UniqueIndex >();
+ add< UniqueIndexPreexistingData >();
}
};
diff --git a/jstests/index8.js b/jstests/index8.js
new file mode 100644
index 00000000000..8bbf01c920a
--- /dev/null
+++ b/jstests/index8.js
@@ -0,0 +1,30 @@
+// Test key uniqueness
+
+t = db.jstests_index8;
+t.drop();
+
+t.ensureIndex( { a: 1 } );
+t.ensureIndex( { b: 1 }, true );
+t.ensureIndex( { c: 1 }, [ false, "cIndex" ] );
+
+checkIndexes = function() {
+// printjson( db.system.indexes.find( { ns: "test.jstests_index8" } ).toArray() );
+ indexes = db.system.indexes.find( { ns: "test.jstests_index8" } ).sort( { key: 1 } );
+ assert( !indexes[ 0 ].unique );
+ assert( indexes[ 1 ].unique );
+ assert( !indexes[ 2 ].unique );
+ assert.eq( "cIndex", indexes[ 2 ].name );
+}
+
+checkIndexes();
+
+t.reIndex();
+checkIndexes();
+
+t.save( { a: 2, b: 1 } );
+t.save( { a: 2 } );
+assert.eq( 2, t.find().count() );
+
+t.save( { b: 4 } );
+t.save( { b: 4 } );
+assert.eq( 3, t.find().count() );
diff --git a/mongo.xcodeproj/project.pbxproj b/mongo.xcodeproj/project.pbxproj
index a109824d503..0a017643cfd 100644
--- a/mongo.xcodeproj/project.pbxproj
+++ b/mongo.xcodeproj/project.pbxproj
@@ -107,6 +107,7 @@
934223C70EF16DB400608550 /* scanandorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scanandorder.h; sourceTree = "<group>"; };
934223C80EF16DB400608550 /* storage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = storage.h; sourceTree = "<group>"; };
934223C90EF16DB400608550 /* tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tests.cpp; sourceTree = "<group>"; };
+ 9343373F0F9CD6900019D5C0 /* index8.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = index8.js; sourceTree = "<group>"; };
934DD87C0EFAD23B00459CC1 /* background.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = background.cpp; sourceTree = "<group>"; };
934DD87D0EFAD23B00459CC1 /* background.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = background.h; sourceTree = "<group>"; };
934DD87F0EFAD23B00459CC1 /* builder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builder.h; sourceTree = "<group>"; };
@@ -539,6 +540,7 @@
93A8D1D10F37544800C92B85 /* jstests */ = {
isa = PBXGroup;
children = (
+ 9343373F0F9CD6900019D5C0 /* index8.js */,
93AE6FB10F9631A200857F1C /* disk */,
93DCDB5B0F93ED98005349BC /* nin.js */,
9350E1220F8CFFB300B07A1C /* error2.js */,
diff --git a/shell/collection.js b/shell/collection.js
index 75cc0488551..36370394c61 100644
--- a/shell/collection.js
+++ b/shell/collection.js
@@ -141,19 +141,40 @@ DBCollection.prototype._genIndexName = function( keys ){
return name;
}
-DBCollection.prototype.createIndex = function( keys , name ){
+DBCollection.prototype._indexSpec = function( keys, options ) {
+ var name;
+ var unique = false;
+ if ( !isObject( options ) ) {
+ options = [ options ];
+ }
+ for( var i = 0; i < options.length; ++i ) {
+ var o = options[ i ];
+ if ( isString( o ) ) {
+ name = o;
+ } else if ( typeof( o ) == "boolean" ) {
+ unique = o;
+ }
+ }
name = name || this._genIndexName( keys );
- var o = { ns : this._fullName , key : keys , name : name };
+ var ret = { ns : this._fullName , key : keys , name : name };
+ if ( unique == true ) {
+ ret.unique = true;
+ }
+ return ret;
+}
+
+DBCollection.prototype.createIndex = function( keys , options ){
+ var o = this._indexSpec( keys, options );
this._db.getCollection( "system.indexes" ).insert( o );
}
-DBCollection.prototype.ensureIndex = function( keys , name ){
- name = name || this._genIndexName( keys );
+DBCollection.prototype.ensureIndex = function( keys , options ){
+ var name = this._indexSpec( keys, options ).name;
this._indexCache = this._indexCache || {};
if ( this._indexCache[ name ] )
return false;
- this.createIndex( keys , name );
+ this.createIndex( keys , options );
this._indexCache[name] = true;
return true;
}
@@ -163,10 +184,10 @@ DBCollection.prototype.resetIndexCache = function(){
}
DBCollection.prototype.reIndex = function(){
- var keys = this.getIndexKeys();
+ var specs = this.getIndexSpecs();
this.dropIndexes();
- for ( var i in keys ){
- this.ensureIndex( keys[i] );
+ for ( var i in specs ){
+ this.ensureIndex( specs[i].key, [ specs[i].unique, specs[i].name ] );
}
}
@@ -213,10 +234,10 @@ DBCollection.prototype.getIndexes = function(){
return this.getDB().getCollection( "system.indexes" ).find( { ns : this.getFullName() } );
}
-DBCollection.prototype.getIndexKeys = function(){
+DBCollection.prototype.getIndexSpecs = function(){
return this.getIndexes().toArray().map(
function(i){
- return i.key;
+ return i;
}
);
}