text: // Check $match pipeline stage.
  // - Filtering behavior equivalent to a mongo query.
  // - $where and geo operators are not allowed
  load('jstests/aggregation/extras/utils.js');
  
  t = db.jstests_aggregation_match;
  t.drop();
  
  identityProjection = { _id:'$_id', a:'$a' };
  
  /** Assert that an aggregation generated the expected error. */
  function assertError( expectedCode, matchSpec ) {
      matchStage = { $match:matchSpec };
      // Check where matching is folded in to DocumentSourceCursor.
      assertErrorCode(t, [matchStage], expectedCode)
      // Check where matching is not folded in to DocumentSourceCursor.
      assertErrorCode(t, [{$project: identityProjection}, matchStage], expectedCode)
  }
  
  /** Assert that the contents of two arrays are equal, ignoring element ordering. */
  function assertEqualResultsUnordered( one, two ) {
      oneStr = one.map( function( x ) { return tojson( x ); } );
      twoStr = two.map( function( x ) { return tojson( x ); } );
      oneStr.sort();
      twoStr.sort();
      assert.eq( oneStr, twoStr );
  }
  
  /** Assert that an aggregation result is as expected. */
  function assertResults( expectedResults, matchSpec ) {
      findResults = t.find( matchSpec ).toArray();
      if ( expectedResults ) {
          assertEqualResultsUnordered( expectedResults, findResults );
      }
      matchStage = { $match:matchSpec };
      // Check where matching is folded in to DocumentSourceCursor.
      assertEqualResultsUnordered( findResults, t.aggregate( matchStage ).result );
      // Check where matching is not folded in to DocumentSourceCursor.
      assertEqualResultsUnordered( findResults,
                                   t.aggregate( { $project:identityProjection },
                                                matchStage ).result );    
  }
  
  // Invalid matcher syntax.
  assertError( 10073, { a:{ $mod:[ 0 /* invalid */, 0 ] } } );
  
  // $where not allowed.
  assertError( 16395, { $where:'true' } );
  
  // Geo not allowed.
  assertError( 16424, { $match:{ a:{ $near:[ 0, 0 ] } } } );
  
  // Update modifier not allowed.
  if ( 0 ) { // SERVER-6650
  assertError( 0, { a:1, $inc:{ b:1 } } );
  }
  
  // Aggregation expression not allowed.
  if ( 0 ) { // SERVER-6650
  assertError( 0, { a:1, b:{ $gt:{ $add:[ 1, 1 ] } } } );
  }
  
  function checkMatchResults( indexed ) {
  
      // No results.
      t.remove();
      assertResults( [], {} );
      
      t.save( { _id:0, a:1 } );
      t.save( { _id:1, a:2 } );
      t.save( { _id:2, a:3 } );
  
      // Empty query.
      assertResults( [ { _id:0, a:1 }, { _id:1, a:2 }, { _id:2, a:3 } ], {} );
  
      // Simple queries.
      assertResults( [ { _id:0, a:1 } ], { a:1 } );
      assertResults( [ { _id:1, a:2 } ], { a:2 } );
      assertResults( [ { _id:1, a:2 }, { _id:2, a:3 } ], { a:{ $gt:1 } } );
      assertResults( [ { _id:0, a:1 }, { _id:1, a:2 } ], { a:{ $lte:2 } } );
      assertResults( [ { _id:0, a:1 }, { _id:2, a:3 } ], { a:{ $in:[ 1, 3 ] } } );
  
      // Regular expression.
      t.remove();
      t.save( { _id:0, a:'x' } );
      t.save( { _id:1, a:'yx' } );
      assertResults( [ { _id:0, a:'x' } ], { a:/^x/ } );
      assertResults( [ { _id:0, a:'x' }, { _id:1, a:'yx' } ], { a:/x/ } );
  
      // Dotted field.
      t.remove();
      t.save( { _id:0, a:{ b:4 } } );
      t.save( { _id:1, a:2 } );
      assertResults( [ { _id:0, a:{ b:4 } } ], { 'a.b':4 } );
  
      // Value within an array.
      t.remove();
      t.save( { _id:0, a:[ 1, 2, 3 ] } );
      t.save( { _id:1, a:[ 2, 2, 3 ] } );
      t.save( { _id:2, a:[ 2, 2, 2 ] } );
      assertResults( [ { _id:0, a:[ 1, 2, 3 ] }, { _id:1, a:[ 2, 2, 3 ] } ], { a:3 } );
  
      // Missing, null, $exists matching.
      t.remove();
      t.save( { _id:0 } );
      t.save( { _id:1, a:null } );
      if ( 0 ) { // SERVER-6571
      t.save( { _id:2, a:undefined } );
      }
      t.save( { _id:3, a:0 } );
      assertResults( [ { _id:0 }, { _id:1, a:null } ], { a:null } );
      if ( !indexed ) { // SERVER-1160
          assertResults( null, { a:{ $in:[ undefined ] } } );
      }
      assertResults( null, { a:{ $exists:true } } );
      assertResults( null, { a:{ $exists:false } } );
  
      // $elemMatch
      t.remove();
      t.save( { _id:0, a:[ 1, 2 ] } );
      t.save( { _id:1, a:[ 1, 2, 3 ] } );
      assertResults( [ { _id:1, a:[ 1, 2, 3 ] } ], { a:{ $elemMatch:{ $gt:1, $mod:[ 2, 1 ] } } } );
  
      t.remove();
      t.save( { _id:0, a:[ { b:1 }, { c:2 } ] } );
      t.save( { _id:1, a:[ { b:1, c:2 } ] } );
      assertResults( [ { _id:1, a:[ { b:1, c:2 } ] } ], { a:{ $elemMatch:{ b:1, c:2 } } } );
  
      // $size
      t.remove();
      t.save( {} );
      t.save( { a:null } );
      t.save( { a:[] } );
      t.save( { a:[ 1 ] } );
      t.save( { a:[ 1, 2 ] } );
      assertResults( null, { a:{ $size:0 } } );
      assertResults( null, { a:{ $size:1 } } );
      assertResults( null, { a:{ $size:2 } } );
  
      // $type
      t.remove();
      t.save( {} );
      t.save( { a:null } );
      if ( 0 ) { // SERVER-6571
      t.save( { a:undefined } );
      }
      t.save( { a:NumberInt( 1 ) } );
      t.save( { a:NumberLong( 2 ) } );
      t.save( { a:66.6 } );
      t.save( { a:'abc' } );
      t.save( { a:/xyz/ } );
      t.save( { a:{ q:1 } } );
      t.save( { a:true } );
      t.save( { a:new Date() } );
      t.save( { a:new ObjectId() } );
      for( type = 1; type <= 18; ++type ) {
          if ( indexed && type == 17 ) {
              // SERVER-3304
              continue;
          }
          assertResults( null, { a:{ $type:type } } );
      }
  
      t.remove();
      t.save( { _id:0, a:1 } );
      t.save( { _id:1, a:2 } );
      t.save( { _id:2, a:3 } );
      assertResults( [ { _id:0, a:1 } ], { a:1 } );
      assertResults( [ { _id:1, a:2 } ], { a:2 } );
      assertResults( [ { _id:1, a:2 }, { _id:2, a:3 } ], { a:{ $gt:1 }} );
      assertResults( [ { _id:0, a:1 }, { _id:1, a:2 } ], { a:{ $lte:2 }} );
      assertResults( [ { _id:0, a:1 }, { _id:2, a:3 } ], { a:{ $in:[ 1, 3 ] }} );
  
      // $and
      assertResults( [ { _id:1, a:2 } ], { $and:[ { a:2 }, { _id:1 } ] } );
      assertResults( [], { $and:[ { a:1 }, { _id:1 } ] } );
      assertResults( [ { _id:1, a:2 }, { _id:2, a:3 } ],
                     { $and:[ { $or:[ { _id:1 }, { a:3 } ] }, { $or:[ { _id:2 }, { a:2 } ] } ] } );
  
      // $or
      assertResults( [ { _id:0, a:1 }, { _id:2, a:3 } ], { $or:[ { _id:0 }, { a:3 } ] } );
  }
  
  checkMatchResults( false );
  t.ensureIndex( { a:1 } );
  checkMatchResults( true );
  t.ensureIndex( { 'a.b':1 } );
  t.ensureIndex( { 'a.c':1 } );
  checkMatchResults( true );
  