Mongoose: FAQ about casting aggregation pipeline stages

Created on 24 Feb 2018  路  7Comments  路  Source: Automattic/mongoose

Do you want to request a feature or report a bug?

Not sure if it's a bug or not and neither if it's related to mongoose or mongo

What is the current behavior?
I could find elements greater as a Date.now() but if I do the same with aggregate it doesn't find any elements

let query = {
     something: : {$gte: Date.now()}
};

Collection.find(query).exec(function (err, results) {
     console.log(results.length); // --> All good, x elements found
}

Collection.aggregate([{$match: query}], function (err, items) {
     console.log(results.length); // --> 0 elements found
}

if I modify the query with new Date() instead of Date.now(), problem is solved

  let query = {
     something: : {$gte: new Date()}
};

Collection.aggregate([{$match: query}], function (err, items) {
     console.log(results.length); // --> All good, x elements found
}

If the current behavior is a bug, please provide the steps to reproduce.

See above code

What is the expected behavior?

Would be cool to have the same behavior between find and aggregate regarding this comparison.

Please mention your node.js, mongoose and MongoDB version.

Node v9.4.0
mongoose v5.0.6
mongo v3.2.14

docs

Most helpful comment

Fixed in #6184

All 7 comments

it looks like this is a difference in the way find and aggregate work in mongodb. I created a test collection with this populate script:

#!/usr/bin/env node
'use strict'

const mongoose = require('../lib/test_db')
const Schema = mongoose.Schema
const faker = require('faker')

const testSchema = new Schema({
  what: String,
  when: Date
})

const Test = mongoose.model('test', testSchema)

const docs = []

for (let i = 0; i < 100; i++) {
  docs.push(new Test({
    what: faker.company.bsAdjective(),
    when: faker.date.past()
  }))
  docs.push(new Test({
    what: faker.hacker.adjective(),
    when: faker.date.future()
  }))
}

Test.create(docs)
  .then(
    (res) => console.log(res),
    (rej) => console.error(rej)
  )

my find script:

#!/usr/bin/env node
'use strict'

const mongoose = require('../lib/test_db')
const Schema = mongoose.Schema

const testSchema = new Schema({
  what: String,
  when: Date
})

const Test = mongoose.model('test', testSchema)

const query = {
  when: { $lte: Date.now() }
}

Test.find(query).exec((err, res) => {
  if (err) { return console.error(err) }
  console.log(`I found ${res.length} documents`)
})

it's output:

InspiredMacPro:Help lineus$ ./mongoose5/6170/find.js
I found 100 documents
^C

my aggregate script:

#!/usr/bin/env node
'use strict'

const mongoose = require('../lib/test_db')
const Schema = mongoose.Schema

const testSchema = new Schema({
  what: String,
  when: Date
})

const Test = mongoose.model('test', testSchema)

const query = {
  when: { $lte: new Date(Date.now()) }
}

Test.aggregate([{ $match: query }], (err, res) => {
  if (err) { return console.error(err) }
  console.log(`I aggregated ${res.length} documents`)
})

it's output:

InspiredMacPro:Help lineus$ ./mongoose5/6170/aggregate.js
I aggregated 100 documents
^C

i couldn't get it to work until I found this old bug which alludes to using toDate ( I'm assuming from moments.js ) in the query. While testing in the mongo shell, I used mongodb's builtin Date object ( new Date() returns a date object instead of a string ) instead and it works.
If using the same query is important, or just preferable to you, find can accept the date object as well.

Mongoose does not cast aggregation pipeline stages because with $project, $group, etc. the type of a property may change during the aggregation. We've considered supporting casting if the first stage is $match but that seems like too much magic. If you want to query by date using the aggregation framework, you're responsible for ensuring that you're passing in a valid date.

@lineus @vkarpov15 thx a lot for debugging this and for the explanation

well noted, aggregate need valid date aka use new Date() instead of Date.now() in case you want to compare with actual date in an aggregation. I'm all fine with than then.

again thx a lot for taking the time to look at this, really appreciated!

I'm going to re-open this so I can track putting this in the FAQ

Still not working with me. I turned on mongoose debug flag. and I am using

    const responses = await responseModel.aggregate([
      {
        $match: {
          myId: mongoose.Types.ObjectId('00000000000000000000000a'),
        },
      },
    ]);

and here is the result in console:

Mongoose: Responses.aggregate([ { '$match': { workflowId: 00000000000000000000000a } } ], {})

sorry for misunderstanding. It is working correctly however the logs are incorrect that's why I got confused.

Fixed in #6184

Was this page helpful?
0 / 5 - 0 ratings