summaryrefslogtreecommitdiff
path: root/jstests/sorth.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/sorth.js')
-rw-r--r--jstests/sorth.js140
1 files changed, 140 insertions, 0 deletions
diff --git a/jstests/sorth.js b/jstests/sorth.js
new file mode 100644
index 00000000000..1072975a3ec
--- /dev/null
+++ b/jstests/sorth.js
@@ -0,0 +1,140 @@
+// Tests for the $in/sort/limit optimization combined with inequality bounds. SERVER-5777
+
+
+t = db.jstests_sorth;
+t.drop();
+
+/** Assert that the 'a' and 'b' fields of the documents match. */
+function assertMatch( expectedMatch, match ) {
+ if (undefined !== expectedMatch.a) {
+ assert.eq( expectedMatch.a, match.a );
+ }
+ if (undefined !== expectedMatch.b) {
+ assert.eq( expectedMatch.b, match.b );
+ }
+}
+
+/** Assert an expected document or array of documents matches the 'matches' array. */
+function assertMatches( expectedMatches, matches ) {
+ if ( expectedMatches.length == null ) {
+ assertMatch( expectedMatches, matches[ 0 ] );
+ }
+ for( i = 0; i < expectedMatches.length; ++i ) {
+ assertMatch( expectedMatches[ i ], matches[ i ] );
+ }
+}
+
+/** Generate a cursor using global parameters. */
+function find( query ) {
+ return t.find( query ).sort( _sort ).limit( _limit ).hint( _hint );
+}
+
+/** Check the expected matches for a query. */
+function checkMatches( expectedMatch, query ) {
+ result = find( query ).toArray();
+ assertMatches( expectedMatch, result );
+ explain = find( query ).explain();
+ assert.eq( expectedMatch.length || 1, explain.n );
+}
+
+/** Reset data, index, and _sort and _hint globals. */
+function reset( sort, index ) {
+ 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:0 } );
+ t.save( { a:2, b:3 } );
+ t.save( { a:2, b:5 } );
+ t.ensureIndex( index );
+ _sort = sort;
+ _hint = index;
+}
+
+function checkForwardDirection( sort, index ) {
+ reset( sort, index );
+
+ _limit = -1;
+
+ // Lower bound checks.
+ checkMatches( { a:2, b:0 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:0 } } );
+ checkMatches( { a:1, b:1 }, { a:{ $in:[ 1, 2 ] }, b:{ $gt:0 } } );
+ checkMatches( { a:1, b:1 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:1 } } );
+ checkMatches( { a:1, b:2 }, { a:{ $in:[ 1, 2 ] }, b:{ $gt:1 } } );
+ checkMatches( { a:1, b:2 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:2 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $gt:2 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:3 } } );
+ checkMatches( { a:2, b:5 }, { a:{ $in:[ 1, 2 ] }, b:{ $gt:3 } } );
+ checkMatches( { a:2, b:5 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:4 } } );
+ checkMatches( { a:2, b:5 }, { a:{ $in:[ 1, 2 ] }, b:{ $gt:4 } } );
+ checkMatches( { a:2, b:5 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:5 } } );
+
+ // Upper bound checks.
+ checkMatches( { a:2, b:0 }, { a:{ $in:[ 1, 2 ] }, b:{ $lte:0 } } );
+ checkMatches( { a:2, b:0 }, { a:{ $in:[ 1, 2 ] }, b:{ $lt:1 } } );
+ checkMatches( { a:2, b:0 }, { a:{ $in:[ 1, 2 ] }, b:{ $lte:1 } } );
+ checkMatches( { a:2, b:0 }, { a:{ $in:[ 1, 2 ] }, b:{ $lt:3 } } );
+
+ // Lower and upper bounds checks.
+ checkMatches( { a:2, b:0 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:0, $lte:0 } } );
+ checkMatches( { a:2, b:0 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:0, $lt:1 } } );
+ checkMatches( { a:2, b:0 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:0, $lte:1 } } );
+ checkMatches( { a:1, b:1 }, { a:{ $in:[ 1, 2 ] }, b:{ $gt:0, $lte:1 } } );
+ checkMatches( { a:1, b:2 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:2, $lt:3 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:2.5, $lte:3 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $gt:2.5, $lte:3 } } );
+
+ // Limit is -2.
+ _limit = -2;
+ checkMatches( [ { a:2, b:0 }, { a:1, b:1 } ],
+ { a:{ $in:[ 1, 2 ] }, b:{ $gte:0 } } );
+ // We omit 'a' here because it's not defined whether or not we will see
+ // {a:2, b:3} or {a:1, b:3} first as our sort is over 'b'.
+ checkMatches( [ { a:1, b:2 }, { b:3 } ],
+ { a:{ $in:[ 1, 2 ] }, b:{ $gt:1 } } );
+ checkMatches( { a:2, b:5 }, { a:{ $in:[ 1, 2 ] }, b:{ $gt:4 } } );
+
+ // With an additional document between the $in values.
+ t.save( { a:1.5, b:3 } );
+ checkMatches( [ { a:2, b:0 }, { a:1, b:1 } ],
+ { a:{ $in:[ 1, 2 ] }, b:{ $gte:0 } } );
+}
+
+// Basic test with an index suffix order.
+checkForwardDirection( { b:1 }, { a:1, b:1 } );
+// With an additonal index field.
+checkForwardDirection( { b:1 }, { a:1, b:1, c:1 } );
+// With an additonal reverse direction index field.
+checkForwardDirection( { b:1 }, { a:1, b:1, c:-1 } );
+// With an additonal ordered index field.
+checkForwardDirection( { b:1, c:1 }, { a:1, b:1, c:1 } );
+// With an additonal reverse direction ordered index field.
+checkForwardDirection( { b:1, c:-1 }, { a:1, b:1, c:-1 } );
+
+function checkReverseDirection( sort, index ) {
+ reset( sort, index );
+ _limit = -1;
+
+ checkMatches( { a:2, b:5 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:0 } } );
+ checkMatches( { a:2, b:5 }, { a:{ $in:[ 1, 2 ] }, b:{ $gte:5 } } );
+
+ checkMatches( { a:2, b:5 }, { a:{ $in:[ 1, 2 ] }, b:{ $lte:5 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $lt:5 } } );
+ checkMatches( { a:1, b:2 }, { a:{ $in:[ 1, 2 ] }, b:{ $lt:3 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $lt:3.1 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $lt:3.5 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $lte:3 } } );
+
+ checkMatches( { a:2, b:5 }, { a:{ $in:[ 1, 2 ] }, b:{ $lte:5, $gte:5 } } );
+ checkMatches( { a:1, b:1 }, { a:{ $in:[ 1, 2 ] }, b:{ $lt:2, $gte:1 } } );
+ checkMatches( { a:1, b:2 }, { a:{ $in:[ 1, 2 ] }, b:{ $lt:3, $gt:1 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $lt:3.5, $gte:3 } } );
+ checkMatches( { a:1, b:3 }, { a:{ $in:[ 1, 2 ] }, b:{ $lte:3, $gt:0 } } );
+}
+
+// With a descending order index.
+checkReverseDirection( { b:-1 }, { a:1, b:-1 } );
+checkReverseDirection( { b:-1 }, { a:1, b:-1, c:1 } );
+checkReverseDirection( { b:-1 }, { a:1, b:-1, c:-1 } );
+checkReverseDirection( { b:-1, c:1 }, { a:1, b:-1, c:1 } );
+checkReverseDirection( { b:-1, c:-1 }, { a:1, b:-1, c:-1 } );