"MinimongoError: Key $gte must not start with '$'"

Inside of a method, I am trying to filter by using the $gte (greater than or equal) operator, but minimongo does not know how to handle it.

Has anyone else had this issue?

Versions:
Meteor 1.5.1
Mongo 3.2.12

need your query here, to see if you have a format problem

Maybe related to #8775.

Put together a good simple repro and add it to the issue.

Here is the mongo query that is throwing the error

    Inventory.update({ upcCode, date: {
      $gte: moment(date).startOf('day').toDate(),
      $lte: moment(date).endOf('day').toDate(),
    } },{ $set: { ...entry } }, { upsert: true })

It seems that minimongo can not handle $ operators during mutate operations. I was able to solve this just by refactoring (and avoiding using $ operators in an update query). This is not ideal but works until #8775 gets fixed I suppose.

    const invEntry = Inventory.findOne({ upcCode, date: {
      $gte: moment(date).startOf('day').toDate(),
      $lte: moment(date).endOf('day').toDate(),
    } })

    if (typeof invEntry !== 'undefined') {
     Inventory.update({ _id: invEntry._id },{ $set: { ...entry } }, { upsert: true })
    } else {
     Inventory.insert(entry)
    }

Please update the issue with a simple repro. The fix was in as of 1.5.1-beta.2. Maybe you found a new case.

I take that back. My repro was fixed as of 1.5.1-beta.2 but the full fix was a little more complex… Would still be good to get your repro into the issue… or pr #8815.

Here is the entire method. I would send a link to the repo but this is not an open-source project.

Meteor.methods({
  'inventory.upsert'(obj, quantity, date) {
    check(obj, {
      _id: String,
      date: Date,
      difference: Number,
      name: String,
      quantity: Number,
      quantityExpected: Number,
      type: String,
      unitValue: Number,
      upcCode: Number,
      children: Match.Optional(Array)
    });
    check(quantity, Number)
    check(date, Date)

    const { upcCode, name, unitValue } = obj

    const value = unitValue * quantity

    const entry = {
      upcCode,
      name,
      value,
      date,
      quantity,
    }

    const invEntry = Inventory.findOne({ upcCode, date: {
      $gte: moment(date).startOf('day').toDate(),
      $lte: moment(date).endOf('day').toDate(),
    } })

    if (typeof invEntry !== 'undefined') {
      entry.quantity = invEntry.quantity + quantity
    }
    
    if (! Meteor.userId()) {
      throw new Meteor.Error('not-authorized');
    }

    Inventory.update({ upcCode, date: {
      $gte: moment(date).startOf('day').toDate(),
      $lte: moment(date).endOf('day').toDate(),
    } },{ $set: { ...entry } }, { upsert: true })

  }
})

Thanks for taking a look!

is upcCode shorthanding the _id? Like
{_id: upcCode}, {....
or is it a key name like: {upcCode: upcCode,

just asking so I’m not mistaking what you mean

It is just es6 shorthand for {upcCode: upcCode}

If I remember correctly I think the client can’t do updates without specifying specific _ids, but that’s more of a security thing I believe. Is that the issue?

Yeah that seems to be it. But why would this affect security if it only would affect the client-side collection? The server would still have the correct (secure) information if someone were to take advantage of it no?

Just curious what you are using a findOne() but not using the _id? That could potentially have different desired results over time. Since the findOne could return differnt documents potentially. Maybe that’s ok for your use case.

I think a good reason they don’t allow it on the client, is the current documents you may have available in the local collection would be much different than running that command on the server. Then the command would get sync’d over and update a different set of documents.

const updateIds = items.find({ upcCode, date: {
      $gte: moment(date).startOf('day').toDate(),
      $lte: moment(date).endOf('day').toDate(),
    } , {fields: {_id: 1}}.fetch()

items.update({_id: {$in: updateIds}}, ....

if you need to update multiple _ids from client (antipattern anyways)

Just to clarify, I’m not explicitly running this on the client side, it is inside of a method so meteor runs it on the client side automatically.

I think having this restriction inside of a method makes no sense. Anything that is allowed on the server should be allowed. We shouldn’t have to restructure our code to make Optimistic UI work properly.

Minimongo is basically just temporary data anyway. It is updated anytime the real db detects a change.

I don’t remember seeing this before updating to 1.5.1 anyway…

I am using findOne because there is a unique index on my db that only would allow one record that matches that query.