Automatically increment order numbers

in some business systems… they have a table / collection that contains the last order number that everytime an order is about to be submitted, the system will get a po number from that table / collection, then increment it once the submission is successful

3 Likes

This sounds like it could be a vialbe option. Right now I’m trying to wrap my head around https://github.com/Konecty/meteor-mongo-counter/#api and wishing there was a friggin tutorial with syntax :frowning:

i suggest that the timing of assigning of PO number will be after clicking submit button, because if you rely on auto increment counters you’ll face a problem when the system is being used by multiple user. lets say that user 1 and user 2 is creating a PO at the same time, and since the last PO number in the database is 10… both upon opening their windows. were assigned the PO numbers 11 since logically it is the next number. so when user 1 submits the PO first. it will successfully accept it since it is not yet existing in the database. however by the time the 2nd user submits it will have an error since 11 was already used as a PO number… just my 3 cents :smile:

do this

1 Like

It does feel like something in the database’s wheelhouse…

This is what I’ve done:

Every collection needing an “autoincrement” gets an extra document added: {_id: "autoincrement", value: 0}. I usually put the insert(s) in the server-side Meteor.startup():

Meteor.startup(function() {
  try {
    MyCollection.insert({_id: 'autoincrement', value: 0});
  } catch(err) {
    // Will always get here once the doc's in place, so just ignore
  }
  try {
    AnotherCollection.insert({_id: 'autoincrement', value: 0});
  } catch(err) {
    // Will always get here once the doc's in place, so just ignore
  }
});

Define a helper function to do the autoincrement on a collection (note the node syntax):

const doAutoincrement = function(collection, callback) {
  collection.rawCollection().findAndModify({
    _id: 'autoincrement'
  }, [], {
    $inc: {
      value: 1
    }
  }, {
    'new': true
  }, callback);
}

and define a (synchronous) function to get the next autoincrement value for a collection:

const nextAutoincrement = function(collection) {
  return Meteor.wrapAsync(doAutoincrement)(collection).value;
}

Then, when I want the next autoincrement value:

let nextValue = nextAutoincrement(MyCollection); // 1
nextValue = nextAutoincrement(MyCollection); // 2
nextValue = nextAutoincrement(MyCollection); // 3
let anotherNextValue = nextAutoincrement(AnotherCollection); // 1

Using findAndModify is atomic and is safe even when multiple server instances are being used.

6 Likes

@robfallows, May I know where I should be defining the helper, synchronous functions and where should I be defining the statement to obtain the next autoincremented value.

All of this stuff is in the server-only code, which means that you would normally insert via a Meteor.method. If you need a way of enforcing this via client-side inserts, I suggest using matb33:collection-hooks.

Hi! what is it "rawcollection?"
if i do like
Counters = new Mongo.Collection('counters');
and

          function getNextSequence(order) {
             const ret = Counters.findAndModify(
                    {
                      query: { _id: order },
                      update: { $inc: { seq: 1 } },
                      new: true
                    }
             );
             return ret.seq;
          }

i get
findAndModify is not a function

rawCollection() provides direct access to the underlying MongoDB node library.

That is useful because we get access to the full wealth of methods and options, most of which are not available in Meteor’s minimongo implementation.

Pros:

  • Meteor’s pub/sub can still be used for reactive updates.
  • rawCollection() methods can make use of async and await, meaning sync-style coding can be used.
  • Meteor’s Promise implementation works will with Fibers, so rawCollection() methods can be mixed with minimongo collection methods.

Cons:

  • Only available on the server.

Therefore, your example code may be rewritten as:

async function getNextSequence(order) {
  const ret = await Counters.rawCollection().findAndModify(
    {
      query: { _id: order },
      update: { $inc: { seq: 1 } },
      new: true,
    }
  );
  return ret.seq;
}
1 Like

you mean, that rawCollection() work with external mongo only?

No - I mean you don’t have access to rawCollection() in client code.

Thanks for explanation
:+1:

1 Like

don’t work for me

// collection hook
import Purchases from '../purchases';

Purchases.before.insert(function (userId, doc) {
    doc._id = getNextSequence('purchaseId');
});

import Counters from '../../counters/counters';
function getNextSequence(name) {
    const ret = Counters.rawCollection().findAndModify(
        {
            query: {_id: name},
            update: {$inc: {seq: 1}},
            new: true,
            upsert: true,
        }
    );

    console.log(ret.seq);

    return ret.seq;
}

And then I tried with Meteor Method, but still don’t work (base on Meteor 1.5)

import Counters from './counters';

Meteor.methods({
    getNextSequence: function (name) {
        const ret = Counters.rawCollection().findAndModify(
            {
                query: {_id: name},
                update: {$inc: {seq: 1}},
                new: true,
                upsert: true
            }
        );

        console.log(ret.seq);

        return ret.seq;
    }
});

Check my code - I used async and await.

I tried with async and await, but still don’t work

Meteor.methods({
    getNextSequence: async function (name) {
        const ret = await Counters.rawCollection().findAndModify(
            {
                query: {_id: name},
                update: {$inc: {seq: 1}},
                new: true,
                upsert: true
            }
        );

        console.log(ret.seq);

        return ret.seq;
    }
});

@orloff I had this same issue with FindAndModify is not a function. I got it working after installing meteor-find-and-modify
meteor add fongandrew:find-and-modify. Though this package isn’t actively maintained at the moment, it worked for me. It adds findAndModify support to Meteor’s MongoDB Collections. It should work on both the server and client.

@martineboh, thanks for your share.
but this package isn’t actively maintained

Yes, but works! I have had no issues using it since Meteor 4+.