Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-28503

Date comparisons with null in aggregation do not work as expected

    • Type: Icon: Bug Bug
    • Resolution: Done
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: 3.2.12, 3.4.0
    • Component/s: Aggregation Framework
    • Labels:
      None
    • ALL
    • Hide
      require('../../utils/lib/preload.js');
      
      var config = require('lib/config');
      var MongoClient = require('mongodb').MongoClient;
      var async = require('async');
      
      var mongo;
      
      async.waterfall([
        function(d) {
          config.dbOptions = {
            keepAlive : 3000000,
            connectTimeoutMS : 3000000,
            socketTimeoutMS : 3000000,
          };
          MongoClient.connect(config.db || '', config.dbOptions || {}, d);
        },
        function(connection, d) {
          mongo = connection;
          mongo.collection('test').remove();
          d();
        },
        function(d) {
          mongo.collection('test').insert([
            {
              item : 1,
              test : new Date(1), // new Date(1490000000000),
            },
            {
              item : 2,
              test : null,
            },
          ], function(err) {
            d(err);
          });
        },
        function(d) {
          mongo.collection('test').find({ test : { $lt : new Date(100) } }).limit(0).toArray(function(err, results) {
            console.log('Find > 0', err, results);
            d(err);
          });
        },
        function(d) {
          mongo.collection('test').aggregate([
            { $match : { } },
            { $group : {
              _id : '$item',
              test : { $sum : {
                $cond : [
                  { $lt : [ '$test', new Date(100) ] },
                  1,
                  0,
                ],
              } },
            } },
          ], function(err, results) {
            console.log('Aggregate > 0', err, results);
            d(err);
          });
        },
        function(d) {
          mongo.collection('test').find({ test : { $lt : new Date(-1) } }).limit(0).toArray(function(err, results) {
            console.log('Find < 0', err, results);
            d(err);
          });
        },
        function(d) {
          mongo.collection('test').aggregate([
            { $match : { } },
            { $group : {
              _id : '$item',
              test : { $sum : {
                $cond : [
                  { $lt : [ '$test', new Date(-1) ] },
                  1,
                  0,
                ],
              } },
            } },
          ], function(err, results) {
            console.log('Aggregate < 0', err, results);
            d(err);
          });
        },
      ], function(err) {
        console.log(err);
        mongo.close();
      });
      
      Show
      require( '../../utils/lib/preload.js' ); var config = require( 'lib/config' ); var MongoClient = require( 'mongodb' ).MongoClient; var async = require( 'async' ); var mongo; async.waterfall([ function (d) { config.dbOptions = { keepAlive : 3000000, connectTimeoutMS : 3000000, socketTimeoutMS : 3000000, }; MongoClient.connect(config.db || '', config.dbOptions || {}, d); }, function (connection, d) { mongo = connection; mongo.collection( 'test' ).remove(); d(); }, function (d) { mongo.collection( 'test' ).insert([ { item : 1, test : new Date(1), // new Date(1490000000000), }, { item : 2, test : null , }, ], function (err) { d(err); }); }, function (d) { mongo.collection( 'test' ).find({ test : { $lt : new Date(100) } }).limit(0).toArray( function (err, results) { console.log( 'Find > 0' , err, results); d(err); }); }, function (d) { mongo.collection( 'test' ).aggregate([ { $match : { } }, { $group : { _id : '$item' , test : { $sum : { $cond : [ { $lt : [ '$test' , new Date(100) ] }, 1, 0, ], } }, } }, ], function (err, results) { console.log( 'Aggregate > 0' , err, results); d(err); }); }, function (d) { mongo.collection( 'test' ).find({ test : { $lt : new Date(-1) } }).limit(0).toArray( function (err, results) { console.log( 'Find < 0' , err, results); d(err); }); }, function (d) { mongo.collection( 'test' ).aggregate([ { $match : { } }, { $group : { _id : '$item' , test : { $sum : { $cond : [ { $lt : [ '$test' , new Date(-1) ] }, 1, 0, ], } }, } }, ], function (err, results) { console.log( 'Aggregate < 0' , err, results); d(err); }); }, ], function (err) { console.log(err); mongo.close(); });

      The behavior of the "lt" operator in aggregations appears to be that it yields true when comparing any null value to a date. Look at this example:

      doc1 : { item : 1, test : new Date(1) }
      doc2 : { item : 2, test : null }
      

      Scenario 1
      find with

      { lt : new Date(100) }

      Positive match on doc1 only


      aggregate with

      { $lt : [ '$test', new Date(100) ] }

      Positive match on both doc1 and doc2


      Scenario 2
      find with

      { lt : new Date(-1) }

      No positive match


      aggregate with

      { $lt : [ '$test', new Date(-1) ] }

      Positive match on doc2 only


      I originally thought that scenario 1 could be explained by the value of null being 0, which is why the aggregate matched doc2. However, I could not think of a reason why doc2 is being matched in scenario 2. I am assuming the difference in behavior between find and aggregate within each scenario is due to the additional checks with find.

      This feels like unexpected behavior to me, but please correct me if I am wrong.

            Assignee:
            mark.agarunov Mark Agarunov
            Reporter:
            fpayer Franz Payer
            Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

              Created:
              Updated:
              Resolved: