summaryrefslogtreecommitdiff
path: root/jstests/core/sortk.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/sortk.js')
-rw-r--r--jstests/core/sortk.js140
1 files changed, 140 insertions, 0 deletions
diff --git a/jstests/core/sortk.js b/jstests/core/sortk.js
new file mode 100644
index 00000000000..3895a34c3ac
--- /dev/null
+++ b/jstests/core/sortk.js
@@ -0,0 +1,140 @@
+// End-to-end testing for index scan explosion + merge sort.
+// SERVER-5063 and SERVER-1205.
+t = db.jstests_sortk;
+t.drop();
+
+function resetCollection() {
+ t.drop();
+ t.save( { a:1, b:1 } );
+ t.save( { a:1, b:2 } );
+ t.save( { a:1, b:3 } );
+ t.save( { a:2, b:4 } );
+ t.save( { a:2, b:5 } );
+ t.save( { a:2, b:0 } );
+}
+
+resetCollection();
+t.ensureIndex( { a:1, b:1 } );
+
+function simpleQuery( extraFields, sort, hint ) {
+ query = { a:{ $in:[ 1, 2 ] } };
+ Object.extend( query, extraFields );
+ sort = sort || { b:1 };
+ hint = hint || { a:1, b:1 };
+ return t.find( query ).sort( sort ).hint( hint );
+}
+
+function simpleQueryWithLimit( limit ) {
+ return simpleQuery().limit( limit );
+}
+
+// The limit is -1.
+assert.eq( 0, simpleQueryWithLimit( -1 )[ 0 ].b );
+
+// The limit is -2.
+assert.eq( 0, simpleQueryWithLimit( -2 )[ 0 ].b );
+assert.eq( 1, simpleQueryWithLimit( -2 )[ 1 ].b );
+
+// A skip is applied.
+assert.eq( 1, simpleQueryWithLimit( -1 ).skip( 1 )[ 0 ].b );
+
+// No limit is applied.
+assert.eq( 6, simpleQueryWithLimit( 0 ).itcount() );
+assert.eq( 6, simpleQueryWithLimit( 0 ).explain().nscanned );
+assert.eq( 5, simpleQueryWithLimit( 0 ).skip( 1 ).itcount() );
+
+// The query has additional constriants, preventing limit optimization.
+assert.eq( 2, simpleQuery( { $where:'this.b>=2' } ).limit( -1 )[ 0 ].b );
+
+// The sort order is the reverse of the index order.
+assert.eq( 5, simpleQuery( {}, { b:-1 } ).limit( -1 )[ 0 ].b );
+
+// The sort order is the reverse of the index order on a constrained field.
+assert.eq( 0, simpleQuery( {}, { a:-1, b:1 } ).limit( -1 )[ 0 ].b );
+
+// Without a hint, multiple cursors are attempted.
+assert.eq( 0, t.find( { a:{ $in:[ 1, 2 ] } } ).sort( { b:1 } ).limit( -1 )[ 0 ].b );
+explain = t.find( { a:{ $in:[ 1, 2 ] } } ).sort( { b:1 } ).limit( -1 ).explain( true );
+assert.eq( 1, explain.n );
+
+// The expected first result now comes from the first interval.
+t.remove( { b:0 } );
+assert.eq( 1, simpleQueryWithLimit( -1 )[ 0 ].b );
+
+// With three intervals.
+
+function inThreeIntervalQueryWithLimit( limit ) {
+ return t.find( { a:{ $in: [ 1, 2, 3 ] } } ).sort( { b:1 } ).hint( { a:1, b:1 } ).limit( limit );
+}
+
+assert.eq( 1, inThreeIntervalQueryWithLimit( -1 )[ 0 ].b );
+assert.eq( 1, inThreeIntervalQueryWithLimit( -2 )[ 0 ].b );
+assert.eq( 2, inThreeIntervalQueryWithLimit( -2 )[ 1 ].b );
+t.save( { a:3, b:0 } );
+assert.eq( 0, inThreeIntervalQueryWithLimit( -1 )[ 0 ].b );
+assert.eq( 0, inThreeIntervalQueryWithLimit( -2 )[ 0 ].b );
+assert.eq( 1, inThreeIntervalQueryWithLimit( -2 )[ 1 ].b );
+
+// The index is multikey.
+t.remove({});
+t.save( { a:1, b:[ 0, 1, 2 ] } );
+t.save( { a:2, b:[ 0, 1, 2 ] } );
+t.save( { a:1, b:5 } );
+assert.eq( 3, simpleQueryWithLimit( -3 ).itcount() );
+
+// The index ordering is reversed.
+resetCollection();
+t.ensureIndex( { a:1, b:-1 } );
+
+// The sort order is consistent with the index order.
+assert.eq( 5, simpleQuery( {}, { b:-1 }, { a:1, b:-1 } ).limit( -1 )[ 0 ].b );
+
+// The sort order is the reverse of the index order.
+assert.eq( 0, simpleQuery( {}, { b:1 }, { a:1, b:-1 } ).limit( -1 )[ 0 ].b );
+
+// An equality constraint precedes the $in constraint.
+t.drop();
+t.ensureIndex( { a:1, b:1, c:1 } );
+t.save( { a:0, b:0, c:-1 } );
+t.save( { a:0, b:2, c:1 } );
+t.save( { a:1, b:1, c:1 } );
+t.save( { a:1, b:1, c:2 } );
+t.save( { a:1, b:1, c:3 } );
+t.save( { a:1, b:2, c:4 } );
+t.save( { a:1, b:2, c:5 } );
+t.save( { a:1, b:2, c:0 } );
+
+function eqInQueryWithLimit( limit ) {
+ return t.find( { a:1, b:{ $in:[ 1, 2 ] } } ).sort( { c: 1 } ).hint( { a:1, b:1, c:1 } ).
+ limit( limit );
+}
+
+function andEqInQueryWithLimit( limit ) {
+ return t.find( { $and:[ { a:1 }, { b:{ $in:[ 1, 2 ] } } ] } ).sort( { c: 1 } ).
+ hint( { a:1, b:1, c:1 } ).limit( limit );
+}
+
+// The limit is -1.
+assert.eq( 0, eqInQueryWithLimit( -1 )[ 0 ].c );
+assert.eq( 0, andEqInQueryWithLimit( -1 )[ 0 ].c );
+
+// The limit is -2.
+assert.eq( 0, eqInQueryWithLimit( -2 )[ 0 ].c );
+assert.eq( 1, eqInQueryWithLimit( -2 )[ 1 ].c );
+assert.eq( 0, andEqInQueryWithLimit( -2 )[ 0 ].c );
+assert.eq( 1, andEqInQueryWithLimit( -2 )[ 1 ].c );
+
+function inQueryWithLimit( limit, sort ) {
+ sort = sort || { b:1 };
+ return t.find( { a:{ $in:[ 0, 1 ] } } ).sort( sort ).hint( { a:1, b:1, c:1 } ).limit( limit );
+}
+
+// The index has two suffix fields unconstrained by the query.
+assert.eq( 0, inQueryWithLimit( -1 )[ 0 ].b );
+
+// The index has two ordered suffix fields unconstrained by the query.
+assert.eq( 0, inQueryWithLimit( -1, { b:1, c:1 } )[ 0 ].b );
+
+// The index has two ordered suffix fields unconstrained by the query and the limit is -2.
+assert.eq( 0, inQueryWithLimit( -2, { b:1, c:1 } )[ 0 ].b );
+assert.eq( 1, inQueryWithLimit( -2, { b:1, c:1 } )[ 1 ].b );