1

I'm a hobbyist web programmer who just started learning MongoDB/Mongoose and I can't seem to figure out how to append to a string located in a deep nested array. I am trying to append a string to the end of hours: String. Below is the Schema I'm working with:

const TestSchema = new Schema({
    userID: Number, 
    years: [
        {
            year: Number,
            months: [{
                month: Number,
                days: [{
                    day: Number,
                    hours: String
                }]
            }]
        }
    ]
});

Here is what I have so far. I tried to extend upon this answer here: https://stackoverflow.com/a/56589089 .But this is giving me a Cannot specify arrayFilters and a pipeline update error.

TestModel.findOneAndUpdate(
  { "years.months.days.day": 12 },
  [
    {
      $set: {
        "years.$[index1].months.$[index2].days.$[index3].hours": {
          $concat: [
            "$years.$[index1].months.$[index2].days.$[index3].hours",
            " 44:44:44"
          ]
        }
      }
    }
  ],
  {
    arrayFilters: [
      { "index1.year": 2020 },
      { "index2.month": 7 },
      { "index3.day": 12 }
    ]
  }
).catch(error => {
  console.log("error>>" + error);
});

Edit: Below is code with which I created an instance of the model

var test = new TestModel({
    userID: 5,
    years: [{
        year: 2020, 
        months: [{
            month: 7,
            days: [{
                day: 12,
                hours: "4:4:4 5:5:5"
            }]
        }]
    }]
})

test.save().then(function(){
    console .log("testSaved>>" + !test.isNew);
});

Here is a screenshot of the data in the db: enter image description here Any help would be greatly appreciated.

7
  • did you try to remove the brackets around $set? actually you can leave the condition blank { "years.months.days.day": 12 } to this {} Commented Aug 12, 2020 at 6:28
  • @Ifaruki I removed the square brackets which then gives me error>>CastError: Cast to string failed for value "{ '$concat': [ '$years.$[index1].months.$[index2].days.$[index3].hours', ' 44:44:44' ] }" at path "hours" Commented Aug 12, 2020 at 6:36
  • Why do you need aggregate update i.e because of $concat? Commented Aug 12, 2020 at 6:50
  • @Ifaruki hours is a String as you can see in the schema i posted. The value of hours in the single instance in db is "4:4:4 5:5:5" @Gibbs I was pretty much following the code in the post I referred to Commented Aug 12, 2020 at 6:57
  • 1
    @turivishal Yeah, that's correct Commented Aug 12, 2020 at 7:18

1 Answer 1

1

Update is not supporting both operation together "arrayFilters" and "aggregation pipeline", you need to use only single operation from both,

So here you need to use only update aggregation pipeline, using nested $map,

TestModel.findOneAndUpdate({
    years: {
      $elemMatch: {
        year: 2020,
        months: {
          $elemMatch: {
            month: 7,
            days: { $elemMatch: { day: 12 } }
          }
        }
      }
    }
  },
  [{
    $set: {
      years: {
        $map: {
          input: "$years",
          as: "y",
          in: {
            $mergeObjects: [
              "$$y",
              {
                months: {
                  $map: {
                    input: "$$y.months",
                    as: "m",
                    in: {
                      $mergeObjects: [
                        "$$m",
                        {
                          days: {
                            $map: {
                              input: "$$m.days",
                              as: "d",
                              in: {
                                $mergeObjects: [
                                  "$$d",
                                  {
                                    hours: {
                                      $cond: {
                                        if: { 
                                          $and: [
                                            { $eq: ["$$y.year", 2020] },
                                            { $eq: ["$$m.month", 7] },
                                            { $eq: ["$$d.day", 12] }
                                          ] 
                                        },
                                        then: { $concat: ["$$d.hours", " 44:44:44"] },
                                        else: "$$d.hours"
                                      }
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            ]
          }
        }
      }
    }
  }]
)
Sign up to request clarification or add additional context in comments.

2 Comments

That worked! Thanks. Just curious, looking at the size of the code required, is it a bad practice to use nested arrays in mongodb?
depends on the project features, how data required and how your are storing in db, sometime its good, and sometime its bad, i am not sure why you are storing just dates in nested array, but there should be simple ways to store instead of nested array. and if you say code size, i think this is the only direct way to update using single query, otherwise you can do using 2 queries.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.