skip to Main Content

I am having a problem when performing a push to a nested array, I am trying to add one or more objects within the nested array "fieldList", when I execute the push it tells me ok but when I check it it has not updated it.

{
    _id: ObjectId('6628363d6ed5718acbd2af72'),
    label: 'Movimiento Vehicular IDAAN',
    folders: [
      {
        name: 'Datos Generales',
        fieldList: [],
        _id: ObjectId('6628367b6ed5718acbd2af9f')
      },
      {
        name: 'Datos de Vehículo y Conductor',
        fieldList: [],
        _id: ObjectId('6628555de630217d0abafcc5')
      }
    ],
    createdAt: ISODate('2024-04-23T22:29:17.213Z'),
    updatedAt: ISODate('2024-04-24T00:42:05.879Z')
  }

I am trying to add it with $push and the dot notation { $push: { "folders.fieldList": newfield}} tells me that it was executed ok but it is not added. This is the code I am using:

Object to add:

{
  "name": "Dia",
  "label": "Dia",
  "order": 11,
  "type": "number",
  "requ": true,
  "par": "Datos Generales"
}

Update Script:

export const newfieldwithfolder = async (req: Request, res: Response) => {
  try {
    const { name, label, order, type, requ, par } = req.body;
    const query = { _id: req.params.id };
    const updateQuery = new Fields({
      name,
      label,
      order,
      type,
      requ,
      par,
    });
    const newfield = { $push: { "folders.fieldList": updateQuery } };
    const result = await Template.updateOne(query, newfield);
    console.log(result);
    res.status(201).json({ message: "Fields have been added successfully!" });
  } catch (error) {
    console.error(error);
    res.status(500).json({ success: false, error: "Something went Wrong!" });
  }
};

2

Answers


  1. @prasad_ has already answered it. The objective of this post is to add some context to the the same answer.

    Short answer:

    Array of sub documents, and Nested array of sub documents as this original question belongs to, are updated using filtered positional operator $[]. The respective arrays are to be queried first and its indexes are found and used in updating. The filtered positional operation in conjunction with arrayFilters is doing this job. The section “Action log” enclosed below would have sample statements in this regard. Request read the section in order with the help of comments enclosed over there.

    Detailed answer:

    It is a case of accessing or updating a nested document structure.

    As we know, nested structures can be mainly of two types:

    1. Sub document or Nested sub documents
    2. Array of sub documents or Nested array of sub documents

    For brevity and consistency while reading, “Sub documents” and “Array of sub documents” are the two terms used in the following sections.

    Sub documents are relatively simple to handle as it is basically a grouping mechanism. The notable point is that all keys in a Sub document are still identifiable by the _id of a top level document and accessible by dot notation.

    A sample document with one sub document

    test> { _id : 1, a : { a : 1 } } 
    

    The Sub document { a : 1} above is identifiable by the top level document _id and its key “a” is accessible by dot notation as below :

    test> db.collection.updateOne({_id : 1}, { $set : { "a.a" : 2 } } )
    test> db.a.find();
    [ { _id: 1, a: { a: 2 } } ]
    

    However, an Array of sub documents is more than a simple Sub document.

    Although like a Sub document, an Array of sub documents is still identifiable by the top level _id, and accessible by dot notation, still the very “array nature” differentiates it from the simplicity of a Sub document. Its “array nature” necessitates another dimension – “position” or “index”, also to be taken care. The fundamental point is that the respective arrays need to be searched and the array indexes are to be found prior to access. This is what it makes different from accessing a simple Sub document. The same steps are involved in the case of a Nested Array of sub documents as well. Under this background, the following action log may make more sense.

    Action log:

    // Using MongoDB:       7.0.2
    // Using Mongosh:       2.1.1
    
    test> use test     // using test db
    test> t = db.test // just for convenience
    test> t.drop()  // optional statement
    
    // “a” is a key of an Array of sub documents
    // “a.a” is a key of Nested Array of sub documents
    // the following statement creates a document,
    // note that the value of the array key “a.a” now is an empty array.
    test> t.insertOne({ _id:'1', a: [ { _id:'1.1', a : [ ] } ] } );
    test> t.find();
    [
      { _id: '1', a: [ { _id: '1.1', a: [] } ] }
    ]
    
    // the following statement inserts a new object into the array key “a.a” 
    // the filtered array expression “a.$[i]” acts a placeholder for 
    // the matched elements in the arrayFilter expression - {arrayFilters:[{"i._id":'1.1'}]}
    // Therefore it essentially finds the matching object in the array key “a” and 
    // then pushes a new object { a: ‘1.1.1’ } into the key “a” within the matched object. 
    // Please see the output as well.
    test> t.updateOne({_id:'1'}, { $push: {"a.$[i].a":{a:'1.1.1'}}}, {arrayFilters:[{"i._id":'1.1'}]});
    test> t.find();
    [
      {
        _id: '1',
        a: [
          { _id: '1.1', a: [ { a: '1.1.1' } ] }
        ]
      }
    ]
    
    // the following statement differs from the previous statement not only from action but also
    // from the array (nested array)  it tries to access. This query tries to update
    // the nested array field “a.a”.
    // therefore the arrayFilter expression -  {arrayFilters:[{"i._id":'1.1'},{"j.a":'1.1.1'}]}), 
    // has one more conditional - {"j.a":'1.1.1'}]}. 
    // Please recall that the key “a.a” unlike the key “a” is a Nested array of sub documents. 
    // Therefore both query conditionals are to be matched to locate the target object. 
    test> t.updateOne({_id:'1'}, { $set: {"a.$[i].a.$[j].a":'1.1.2'}}, {arrayFilters:[{"i._id":'1.1'},{"j.a":'1.1.1'}]});
    test> t.find();
    [
      {
        _id: '1',
        a: [
          { _id: '1.1', a: [ { a: '1.1.2' } ] }
        ]
      }
    ]
    

    filtered positional operator $[]

    Login or Signup to reply.
  2. If you specify the field name from the object in the folders array that you want to update you can use the $ positional operator to only update that element from the folders array and push your new object into that fieldList array.

    Looking at your object that you want to push, you have a "par": "Datos Generales" key value so assuming this refers the folders.name field you can do:

    const { name, label, order, type, requ, par } = req.body;
    // also match where 'folders.name' is equal to par
    const query = { 
       _id: req.params.id, 
       'folders.name': par 
    };
    const updateQuery = new Fields({
       name,
       label,
       order,
       type,
       requ,
       par,
    });
    // now use the $ positional operator to only update the folders array element that matched
    const newfield = { 
       $push: { 
          "folders.$.fieldList": updateQuery 
       } 
    };
    const result = await Template.updateOne(query, newfield);
    

    See HERE for a simple example using the db.collection.update() method.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search