summaryrefslogtreecommitdiff
path: root/jstests/core/existsa.js
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2014-01-14 14:09:42 -0500
committerRandolph Tan <randolph@10gen.com>2014-02-28 16:26:33 -0500
commit5595b945603b0712c537787e31e6da661c424fee (patch)
tree90945ee3fe4931032f3af2d397bb755fbf5d30ef /jstests/core/existsa.js
parentcd62080dcb036e83f8fca6d68d9bcab67bf7a21c (diff)
downloadmongo-5595b945603b0712c537787e31e6da661c424fee.tar.gz
SERVER-12127 migrate js tests to jscore suite when not related to writes
Moved test jstest/[a-i].js -> jstests/core/ and made changes to comply with write command api
Diffstat (limited to 'jstests/core/existsa.js')
-rw-r--r--jstests/core/existsa.js114
1 files changed, 114 insertions, 0 deletions
diff --git a/jstests/core/existsa.js b/jstests/core/existsa.js
new file mode 100644
index 00000000000..9ef7e9f374c
--- /dev/null
+++ b/jstests/core/existsa.js
@@ -0,0 +1,114 @@
+// Sparse indexes are disallowed for $exists:false queries. SERVER-3918
+
+t = db.jstests_existsa;
+t.drop();
+
+t.save( {} );
+t.save( { a:1 } );
+t.save( { a:{ x:1 }, b:1 } );
+
+/** Configure testing of an index { <indexKeyField>:1 }. */
+function setIndex( _indexKeyField ) {
+ indexKeyField = _indexKeyField;
+ indexKeySpec = {};
+ indexKeySpec[ indexKeyField ] = 1;
+ t.ensureIndex( indexKeySpec, { sparse:true } );
+ indexCursorName = 'BtreeCursor ' + indexKeyField + '_1';
+}
+setIndex( 'a' );
+
+/** Validate the prefix of 'str'. */
+function assertPrefix( prefix, str ) {
+ assert.eq( prefix, str.substring( 0, prefix.length ) );
+}
+
+/** @return count when hinting the index to use. */
+function hintedCount( query ) {
+ assertPrefix( indexCursorName, t.find( query ).hint( indexKeySpec ).explain().cursor );
+ return t.find( query ).hint( indexKeySpec ).itcount();
+}
+
+/** The query field does not exist and the sparse index is not used without a hint. */
+function assertMissing( query, expectedMissing, expectedIndexedMissing ) {
+ expectedMissing = expectedMissing || 1;
+ expectedIndexedMissing = expectedIndexedMissing || 0;
+ assert.eq( expectedMissing, t.count( query ) );
+ assert.eq( 'BasicCursor', t.find( query ).explain().cursor );
+ // We also shouldn't get a different count depending on whether
+ // an index is used or not.
+ assert.eq( expectedIndexedMissing, hintedCount( query ) );
+}
+
+/** The query field exists and the sparse index is used without a hint. */
+function assertExists( query, expectedExists ) {
+ expectedExists = expectedExists || 2;
+ assert.eq( expectedExists, t.count( query ) );
+ assert.eq( 0, t.find( query ).explain().cursor.indexOf('BtreeCursor') );
+ // An $exists:true predicate generates no index filters. Add another predicate on the index key
+ // to trigger use of the index.
+ andClause = {}
+ andClause[ indexKeyField ] = { $ne:null };
+ Object.extend( query, { $and:[ andClause ] } );
+ assert.eq( expectedExists, t.count( query ) );
+ assertPrefix( indexCursorName, t.find( query ).explain().cursor );
+ assert.eq( expectedExists, hintedCount( query ) );
+}
+
+/** The query field exists and the sparse index is not used without a hint. */
+function assertExistsUnindexed( query, expectedExists ) {
+ expectedExists = expectedExists || 2;
+ assert.eq( expectedExists, t.count( query ) );
+ assert.eq( 'BasicCursor', t.find( query ).explain().cursor );
+ // Even with another predicate on the index key, the sparse index is disallowed.
+ andClause = {}
+ andClause[ indexKeyField ] = { $ne:null };
+ Object.extend( query, { $and:[ andClause ] } );
+ assert.eq( expectedExists, t.count( query ) );
+ assert.eq( 'BasicCursor', t.find( query ).explain().cursor );
+ assert.eq( expectedExists, hintedCount( query ) );
+}
+
+// $exists:false queries match the proper number of documents and disallow the sparse index.
+assertMissing( { a:{ $exists:false } } );
+assertMissing( { a:{ $not:{ $exists:true } } } );
+assertMissing( { $and:[ { a:{ $exists:false } } ] } );
+assertMissing( { $or:[ { a:{ $exists:false } } ] } );
+assertMissing( { $nor:[ { a:{ $exists:true } } ] } );
+assertMissing( { 'a.x':{ $exists:false } }, 2, 1 );
+
+// Currently a sparse index is disallowed even if the $exists:false query is on a different field.
+assertMissing( { b:{ $exists:false } }, 2, 1 );
+assertMissing( { b:{ $exists:false }, a:{ $ne:6 } }, 2, 1 );
+assertMissing( { b:{ $not:{ $exists:true } } }, 2, 1 );
+
+// Top level $exists:true queries match the proper number of documents
+// and use the sparse index on { a : 1 }.
+assertExists( { a:{ $exists:true } } );
+
+// Nested $exists queries match the proper number of documents and disallow the sparse index.
+assertExistsUnindexed( { $nor:[ { a:{ $exists:false } } ] } );
+assertExistsUnindexed( { $nor:[ { 'a.x':{ $exists:false } } ] }, 1 );
+assertExistsUnindexed( { a:{ $not:{ $exists:false } } } );
+
+// Nested $exists queries disallow the sparse index in some cases where it is not strictly
+// necessary to do so. (Descriptive tests.)
+assertExistsUnindexed( { $nor:[ { b:{ $exists:false } } ] }, 1 ); // Unindexed field.
+assertExists( { $or:[ { a:{ $exists:true } } ] } ); // $exists:true not $exists:false.
+
+// Behavior is similar with $elemMatch.
+t.drop();
+t.save( { a:[ {} ] } );
+t.save( { a:[ { b:1 } ] } );
+t.save( { a:[ { b:1 } ] } );
+setIndex( 'a.b' );
+
+assertMissing( { a:{ $elemMatch:{ b:{ $exists:false } } } } );
+// A $elemMatch predicate is treated as nested, and the index should be used for $exists:true.
+assertExists( { a:{ $elemMatch:{ b:{ $exists:true } } } } );
+
+// A non sparse index will not be disallowed.
+t.drop();
+t.save( {} );
+t.ensureIndex( { a:1 } );
+assert.eq( 1, t.find( { a:{ $exists:false } } ).itcount() );
+assert.eq( 'BtreeCursor a_1', t.find( { a:{ $exists:false } } ).explain().cursor );