Edit an element within an object, within an array, with Mongoose

Asked

Viewed 1,704 times

2

I could use a hand! I am new to Node.JS and decided to "create a system" to put into practice what I already know and create challenges to encourage my learning! I did a beginner workshop (with Jean Carlo Suissa) that gave me a good basis, but I need "something more"!

I’ve done a lot of research, but I still can’t solve the problem below:

Schema

Enterprise

, ativa: {type: Boolean, default: true}
, prestacao_servico:
  [ { inicio: {type: Date, default: Date.now, trim: true}
  , fim: {type: Date, default: '', trim: true}
    }
  ]

Every time a contract is renewed (activate) or the company is registered, an element will be inserted in the array prestacao_servico determining the date of beginning of this contract, with the field end having null value and boolean active will be set to true. And when the company is shut down (deactivate) should be searched inside the array prestacao_servico the item that has the field end null (there can only be one item of this timpo in the array) and exchange its value for the closing date Date.now(), the value of active for false.

Example:

{
  "_id": "54eec2379881c0d032856dd5",
  "razao_social": "Empresa 1 EIRELI me",
  "__v": 3,
  "contato": {
    "telefone": "1212341234",
    "celular": "12123451234",
    "email": "[email protected]"
  },
  "endereco": {
    "cep": "12345123",
    "logradouro": "Rua A",
    "bairro": "Bairro A",
    "cidade": "Cidade 1",
    "uf": "RJ",
    "pais": "Brasil",
    "complemento": "",
    "numero": "123"
  },
  "simples_nacional": {
    "codigo": "123412341234"
  },
  "documentos": {
    "cnpj": "12123123123412",
    "inscricao_estadual": "12345678"
  },
  "opcao": {
    "sociedade": 1,
    "enquadramento": 1,
    "tributacao": 2
  },
  "prestacao_servico": [
    {
      "_id": "54eec2379881c0d032856dd6",
      "fim": "2015-02-26T07:06:14.741Z",
      "inicio": "2015-01-08T21:00:00.000Z"
    },
    {
      "_id": "54eec305f32a497533dc8eb1",
      "fim": "2015-02-26T07:08:42.451Z",
      "inicio": "2015-02-26T06:53:57.543Z"
    },
    {
      "_id": "54eec7505b3f2dd13688589d",
      "fim": null,
      "inicio": "2015-02-26T07:12:16.775Z"
    }
  ],
  "ativa": true
}

The company above this active ("ativa": true) and had his contract initiated at the time 2015-02-26T07:12:16.775Z. By disabling it, the field end, of the object whose _id is 54eec7505b3f2dd13688589d, must receive the value corresponding to "time of deactivation".

Problem:

How to locate the enterprise (for _id), then locate the item (in prestacao_servico) which has the value of end null and edit it by inserting the moment? I already used the query {_id: req.params.id, prestacao_servico: {$elemMatch: {fim: null}}} within the find, but use the $elemMatch it makes no difference because it will not have another company with the same ID, even because the field documentos.cnpj is defined as unique. The Aggregate did not return any result.

I found a solution, but it seems to me to be a great gambiarra:

_Model.findOne({_id: req.params.id, "prestacao_servico.fim": null}, function(err, data){
  if(err) {
    // ERRO
  }
  var count = 0
  , i
  ;
  data.prestacao_servico.forEach(function(element, index, array) {
    if(element.fim === null) {
      count++;
      i = index;
    }
  });
  if(count > 1) {
    // ERRO
  }
  else {
    data.prestacao_servico[i].fim = Date.now();
    data
    .save(function(e, d) {
      if(err) {
        // ERRO
      }
      else {
        // DATA
      }
    });
  }
});

Any hint?

SOLUTION

I followed my reasoning, applied the improvements cited by Sergio and tried to improve some aspects, too. Finally, ended like this:

  var dados = req.params
    , query = {_id: dados.id}
    ;
  var promise = _Model.findOne(query).exec();
  promise
    .then(function(e) {
      ps = e.prestacao_servico.filter(function(item) {
        return !item.fim;
      });
      if(ps.length !== 1) throw new _SigError();
      ps[0].fim = Date.now();
      e.save();
      return e;
    })
    .then(function(data) {
      cb(null, data, res);
    })
    .then(null, function(err) {
      cb(err, null, res);
    });

1 answer

2

I think it really has to be that way. I suggest a small change in the code but basically your idea is right:

_Model.findOne({_id: req.params.id, "prestacao_servico.fim": null}, function(err, data){
    if(err) {
        return 'Erro!'; // é importante dar return aqui para não continuar
    }
    var ativos = data.prestacao_servico.filter(function(ps){
        return !ps.fim; // assumindo que tem null e não "null" (string)
    });
    if (ativos.length > 1) return 'Erro! Há mais que um registo ativo!';
    if (!ativos) return 'Erro! Todos os registos estão fechados!';
    ativos[0].fim = Date.now();
    data.save(function(err, data) { // repara que aqui tinhas "(e, d)" mas em baixo usas "err" e não "e"
      if(err) { 
        // ERRO
      }
      else {
        // OK, dar confirmação ao usuário
      }
    });
});

Another option is to use findOneAndUpdate([query], [doc], [options], [callback]), but the code within the callback is identical.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.