DanM
DanM

Reputation: 7197

MongoDB / Node.JS: Inserting documents in a loop - variables not updating?

I'm relatively (read: very) new to MongoDB, and am encountering something that's got me confused.

I have an array of Employee objects, each of which looks basically like this:

{
  "Name" : "Jack Jackson",
  "Title" : "Senior Derp Engineer",
  "Specialties" : [
    "Kicking",
    "Screaming",
    "Throwing Tantrums"
  ]
}

...and I'd like to insert each of them into my collection as a document.

This is the code (simplified):

var collection = db.collection('Employees');
for (var ix=0; ix<allEntries.length; ix++) {
  var item = allEntries[ix];
  collection.insert(item, function(err, docs) {
    if (err) { MyErrorCallback(err); }
    else {
      // Insert specialties
      console.log(item["_id"] + ": " + item["Specialties"].length + " specialties.");
      // ...Code goes here which will insert a document into another collection associating the item's ObjectID with each specialty in the list.
    }
  });
}

I'd expect that for a list of 100 Employees, my console output would consist of the newly-created ObjectID for each of the 100 items in allEntries, along with a count of the number of specialties in that item. Instead, what I get is the ObjectID for the first item inserted, and the count of the Specialties for the first item inserted, repeated 100 times.

Why is this? How do I access the just-inserted item including its newly-generated ID, so I can do things with it? This seems like some kind of scoping issue, but it's not making sense to me.

Many thanks.

UPDATE: I self-answered below with the correct way to retrieve the object; however I'm still very interested if anyone can explain the variable scoping here. It's confusing.

Upvotes: 1

Views: 3211

Answers (3)

Zazama
Zazama

Reputation: 275

This is the way Node.js works and I spent soooo much time to understand what's happening in such situation like yours. Node.js wants to be non-blocking, which means that this code is ran asynchronous.

As an example:

Your for loop runs and sets item to e.g. "1". Now it calls the mongoDB function. Everything is fine until now. The thing is: Node.js will not wait with continuing the for loop until your callback function is called. The for loop will go on. This means, that item will get the value e.g. "2" now. When the callback is called, the for-loop will have ended. Every single function will log: "2", because that's the actual value of the variable.

You can see an example if you print out the value of i inside the callback function.

Upvotes: 4

cesarluis
cesarluis

Reputation: 907

Your docs variable in the insert callback function contains the new inserted item with its objectID so just do what you want with it.

Try console.log(docs) inside the callback to see what is happening.

Upvotes: 1

DanM
DanM

Reputation: 7197

I figured out how to get the object:

var collection = db.collection('Employees');
for (var ix=0; ix<allEntries.length; ix++) {
  var item = allEntries[ix];
  collection.insert(item, function(err, docs) {
    if (err) { MyErrorCallback(err); }
    else {
      // Insert specialties
      var insertedItem = docs["ops"][0];
      console.log(insertedItem["_id"] + ": " + insertedItem["Specialties"].length + " specialties.");
      // ...Code goes here which will insert a document into another collection associating the item's ObjectID with each specialty in the list.
    }
  });
}

(Info about that "ops" item here).

However, I definitely don't understand the way scope is working here, since the function passed into collection.insert() has access to item, but it's not behaving in any way that makes sense to me...?

Upvotes: 0

Related Questions