2

I am using Mongo version 2.2.33. This is for a large project, mostly legacy code, and updating things is not up to me. If I had a newer version of Mongo I would use $indexOfArray, but since I can't, how can I achieve the same thing?

This is the code I wrote before I realized what version of mongo we were using:

exports.getActionHistoryIndex = function (historyArray, deviceId, cb) {
  db.actions.aggregate({$indexOfArray: {historyArray, deviceId}}, function (err, index) {
    if (err) {
      return cb(err)
    } else {
      return cb(null, index)
    }
  })
}

When I call the function, like this:

actionRepo.getActionHistoryIndex(action.history, device._id, (err, res) => {
    if (err) console.log(err)
    console.log(res)
  })

I get this error, because $indexOfArray is onlay available after 3.8, I think:

 name: 'MongoError',
  message: 'Unrecognized pipeline stage name: \'$indexOfArray\'',
  ok: 0,
  errmsg: 'Unrecognized pipeline stage name: \'$indexOfArray\'',
  code: 40324,
  codeName: 'Location40324' }
undefined

Is there an ease way to achieve this? Basically, the problem is that I have a db collection with an array that contains objects. I need to be able to search among the objects in the array for a certain _id and then edit another attribute in the same object. So my plan is to get the index of the correct object, and then use the index to update the attribute that needs to be changed.

Any help is appreciated.

EDIT: This is a sample document from the action collection.

_id : ObjectId('ABC')
history: [Array]
  0: {Object}
    device_id: ObjectId("123")
    triggered: false
  1: {Object}
    device_id: ObjectId("456")
    triggered: true

Upon receiving user input, I need to change the triggered boolean. I have the action document ObjectId and I have the device_id that corresponds to the different objects in the history array. So I'm looking for a query/update that will allow me to change the triggered boolean.

EDIT2: This is the function I have after reading answers:

exports.updateHistory = function (action, deviceId, triggered, cb) {
  console.log(action._id + '  ||  action._id')
  console.log(deviceId + '  ||  deviceId')
  console.log(triggered + '  ||  triggered')
  db.actions.updateOne(
    {_id: action._id, 'history.device_id': deviceId},
    {$set: { 'history.$.triggered': triggered }},
    {w: 1},
    function (err, results) {
      if (err) {
        return cb(err)
      }
      return cb(null, results)
    })
}

All of the log statements are correct, and I don't get any errors, but my documents aren't changed at all. This function gets called three times for three different action documents. The triggered value for all 3 is currently false. When this function runs, the triggered value passed in is true, but it doesn't update the document. Any suggestions?

Brian
  • 385
  • 1
  • 5
  • 23
  • 1
    You don't have to get index of an object in an array in-order to update a specific field in that particular object, You can do it using positional operators in mongoDB, Please give us sample docs & required o/p someone can help you with query !! – whoami - fakeFaceTrueSoul Dec 12 '19 at 06:02
  • 1
    I've updated my answer with a sample document, and what I'm trying to accomplish. Thanks. – Brian Dec 12 '19 at 06:12

2 Answers2

1

Actually You are approaching the problem in wrong way. Array is dynamic in nature. If your application is live and interactions are happening than there's no surety that the index you are updating is going to be the exact same while you get it from query.As some one might have document inserted or removed from it.

As for your problem you can do it like:

db.collection.update({"_id" : <parent_id>, "history.device_id" : "<you-passed-device-id>"},  
                     {$set : {history.$.triggered: false} },
                     false , 
                    true);

Not sure if https://docs.mongodb.com/manual/reference/operator/update/positional/ has been introduced in your version of mongo server.

This way you are not dependent on the index and directly updating the object of you need.

Dhaval Chaudhary
  • 5,428
  • 2
  • 24
  • 39
  • I updated my question above. I tried both `update` and `updateOne` and neither had any effect. – Brian Dec 12 '19 at 15:47
1

Please try :

db.yourCollectionName.updateOne({"_id" : ObjectId('ABC'),
'history.device_id':ObjectId("123")}, { $set: { "history.$.triggered" : false } })

From the above we're using .updateOne() as we're just updating a specific object by passing unique _id :ObjectId('ABC'). Also passing this is beneficial in order to match the exact one document using indexed key & also it ensures you're actually changing proper action document - just in case same device_id exists in history array of multiple action documents !! $ operator will actually update the first matching object/element in an array w.r.t. passed in filter, otherwise if you need all elements which matches to filter query to be modified then you can try $[] on >=3.6 versions.

Ref : mongoDB $(update)

whoami - fakeFaceTrueSoul
  • 17,086
  • 6
  • 32
  • 46
  • I updated my question above. Nothing happens. I tried using Mongo's ObjectID, just to be safe, but it had no effect. – Brian Dec 12 '19 at 15:46
  • @Brian : Did you check the type of `action._id` and `deviceId` do match with the type of `_id` of doc & type of `device_id` in the actual document !! Please try a find method with same filter and check whether you’re getting any docs back if yes then let’s see if there is any issue with update - it shouldn’t be any if types do match.. – whoami - fakeFaceTrueSoul Dec 12 '19 at 15:53
  • I think you are right about this. When I do a find with a filter query only including this: `{_id: action._id}` it finds the correct documents, but when I add the second half: `{_id: action._id, 'history.device_id': device._id}` then I get no documents returned. – Brian Dec 12 '19 at 16:05
  • @Brian : If types do not match then we need to make `device._id` as `ObjectId()`(convert to whatever type exists in DB), Please check this for `mongoose` :: https://stackoverflow.com/questions/6578178/node-js-mongoose-js-string-to-objectid-function Or this for `mongoDB` :: https://stackoverflow.com/questions/7825700/convert-string-to-objectid-in-mongodb – whoami - fakeFaceTrueSoul Dec 12 '19 at 16:08
  • 1
    Yes, that was the problem. In the database, some of the device_ids are ObjectID and some are string.... Very annoying. Thanks for the help!! – Brian Dec 12 '19 at 16:11