Create an auto increment field on method


#1

Hey, here’s method “saveProject” that writes data collection “Projects”.

    Meteor.methods({
  'saveProject': function (project) {
    check(project.name, String);
    currentId = Projects.findOne({},{sort:{id:-1}}).id || 1;
    project.id = currentId + 1;
    project.userId = Meteor.userId();
    project.dateentered = new Date();
    project.lastupdate = new Date();
    if (!project.datedue) {
      project.datedue = new Date();
    }
    project.invited = [];
    return Projects.insert(project);
  }
});

This code creates a given named “id” and assigns automatic numbering of saved “projects”.

currentId = Projects.findOne({},{sort:{id:-1}}).id || 1;
project.id = currentId + 1;

The problem is that this code works only when the collection is saved first “project”. How to modify this code to work longer while writing the first draft to the collection of data “Projects”


#2

Solution:

if (Projects.find({}).count() === 0) {
      currentId = 0;
      project.id = currentId + 1;
    } else {
      currentId = Projects.findOne({},{sort:{id:-1}}).id || 1;
      project.id = currentId + 1;
    }

#3

The true question is why you would need auto increment ?
Mongo create _id automatically of ObjectID type which is also unique.
And that unique ObjectID contains create time timestamp in it (with precision on 1 second).
So if you want just sort them, you can do it based on this (if you dont need better precision than 1 second).


#4

This is a very dangerous way to create an autoincrement field: it is not atomic and so may result in the same autoincrement value being returned for multiple requests.

You should be using an atomic operation such as MongoDB’s findAndModify on a dedicated key in your collection to manage autoincrements.


#5

Thanks for the help. Now i working on the solution proposed by you

I found another solution in a template file:

Template.dashboard.projects = function(){
   var id_pr = Projects.find();
   var id = id_pr.fetch();
   for (var i = 0; i < id.length; i++) {
        id[i].index = i+1;
    }
    return id;
 }

#6

That has the same problem - it fetches the collection, iterates over it and returns a value. While that’s happening, 100 other clients could be doing the same thing at the same time and all getting the same id.


#7

I have the same need. Considering this solution within the Meteor.call (in the server method) for insert:
document.order = Documents.findOne({},{sort:{order:-1}}).order + 1 || 1;

Uniqueness is needed but I’m not sure it will work for concurrent users. Please advise.


#8

Here, you create a numeric index on the client that identifies the current published projects. If the projects are differently published or sorted, one certain document will have a different numeric index.

So this is probably not what you want.

My advice: do not mess with the _id itself. If you need a second “human-readable” unique name for every document, try to let the user give it a name calculate it on every insert. E.g. if you want numeric names:

// on server only:
// observe only projects without a name
Projects.find({name: null}).observe({
   added({_id}) {
      // give it a name by the number of documents in the collection
      const number = Projects.find().count()+1;
      const name = `Project ${number}`;
      Projects.update(_id, {$set: {name}});
   }
});

(disclaimer: written here and untested)

you can achieve something similar with aldeed:collection2’s autoValue.

Edit: recarding concurrency:

In the above example, it’s possible that two projects get the same name, in particular if you have multiple instances running. But i think it is very unlikely. You could however add a unique index on that field in the mongo-db. I guess, this would throw an error in the code above. So you could catch that and try again… Not the best solution, but i think this is a rare edge-case.

Nevertheless you should only rely on the uniqueness of the _id field and use that as identifier for your application (in methods, actions, whatever).