Dranna
Dranna

Reputation: 505

Access array elements via mongoose

With this document :

{
    "_id" : 8,
    "updatedAt" : ISODate("2016-04-13T05:07:38.905Z"),
    "createdAt" : ISODate("2016-04-13T04:15:37.612Z"),
    "author" : "[email protected]",
    "urgency" : "slow",
    "state" : "pending",
    "comment" : "",
    "requests" : [
        {
            "value" : 1,
            "product" : "Slethoxynal",
            "_id" : ObjectId("570dc7e91d15852f1c2ae66a")
        },
        {
            "value" : 1,
            "product" : "Thyrploxynénol",
            "_id" : ObjectId("570dc7e91d15852f1c2ae66b")
        }
    ],
    "__v" : 0
}

I use this javascript function :

function closeRequest(req, res, next) {
    Request.findOne({ '_id': req.body.id}, function(err, request){
        debug(request);
        debug("Length is %s", request.requests.length);
        for(var i=0;i<request.requests.length;i++){
            debug("We validate the element %s of the request %s", i, req.body.id);
            console.log(i);
            Material.findOne({'title': request.requests[i].product}).exec(function(err, material){
            debug("Inside i is : %i",i);
            debug("Product is %s", request.requests[i].product);
            debug("Material found is %s", material.title);
            material.amount -= request.requests[i].value;
            material.save(function(err) {
            if(err)
                res.send(err);
            });
        });
        }
        request.state='accepted';
        request.save(function(err) {
            if(err)
                res.send(err);
        });
    });

    res.redirect('back');
  }

And my console prints :

0
1
Inside i is 2

Then the function crashes with

TypeError: Cannot read property 'product' of undefined

At line

debug("Product is %s", request.requests[i].product);

The goal of this function is to validate one request (we put its state from 'pending' to 'accepted'). But as we do that, the stocks in the warehouse needs to be decremented (the 'amount' parameters). It seems pretty logical that we cannot access the product property since i had a value of 2 and there is no third element in our array. But why is my iteration variable going out of its range ? And how can i solve that ? The fact that the console prints "Inside" only once makes me wonder if it's not another asynchronous problem.

Upvotes: 1

Views: 602

Answers (1)

lipp
lipp

Reputation: 5926

This is a problem about async execution of your code. In particular the for loop "triggers" multiple async functions and continuous execution. After looping through your loop i===2. As the first Material.findOne callback gets executed requests[2] === undefined (as requests.length === 2) throws your error.

I'd suggest to go like this:

var updateMaterialFns = request.requests.map(function(req, i) {
  return function(done) {
    debug("We validate the element %s of the request %s", i, req.body.id);
    console.log(i);
    Material.findOne({'title': req.product}).exec(function(err, material){
      if (err) {
        done(err)
      }
      debug("Inside i is : %i",i);
      debug("Product is %s", request.requests[i].product);
      debug("Material found is %s", material.title);
      material.amount -= request.requests[i].value;
      material.save(done)
    })
  }
})

async.parallel(updateMaterialFns, function(err) {
  if (err) {
    res.send(err)
  }
})

async is a node/npm module featuring many functions to deal with the execution and control flow of async programming.

Upvotes: 1

Related Questions