0

I am trying to remove an object from every item of an array but leaving the rest of the object and array element as is. For example:

[
  {
    _id: 1,
    analytics: [
      {
        date: ISODate("2017-09-10T14:00:00Z"),
        country: { uk: 1, fr: 5, de: 15 },
        browser: { ie: 2, ff: 5, ch: 14 }
      },
    {
        date: ISODate("2017-09-10T14:00:00Z"),
        country: { uk: 1, fr: 5, de: 15 },
        browser: { ie: 2, ff: 5, ch: 14 }
      },
    ]
   },
   {
     _id: 2,
     analytics: [
      {
        date: ISODate("2017-09-10T14:00:00Z"),
        country: { uk: 5, fr: 1, de: 5 },
        browser: { ie: 14, ff: 2, ch: 5 }
      },
      {
        date: ISODate("2017-09-10T15:00:00Z"),
        country: { uk: 5, fr: 1, de: 5 },
        browser: { ie: 14, ff: 2, ch: 5 }
    ]
  }
]

I want to run a command that will remove browser object from each member of the array, for each document. The result would look like

[
  {
    _id: 1,
    analytics: [
      {
        date: ISODate("2017-09-10T14:00:00Z"),
        country: { uk: 1, fr: 5, de: 15 }
      },
    {
        date: ISODate("2017-09-10T14:00:00Z"),
        country: { uk: 1, fr: 5, de: 15 }
      },
    ]
   },
   {
     _id: 2,
     analytics: [
      {
        date: ISODate("2017-09-10T14:00:00Z"),
        country: { uk: 5, fr: 1, de: 5 }
      },
      {
        date: ISODate("2017-09-10T15:00:00Z"),
        country: { uk: 5, fr: 1, de: 5 }
    ]
  }
]

something the lines of: db.collection.update({}, { $pull: { analytics: { country: { $exists: true } } } }, { multi: true}); ? thanks

dmo
  • 5,102
  • 3
  • 25
  • 28
  • yes you are right, the previous solution didn't work for this case. I removed the answer. It seems that what you need to a aggregation pipeline. I will see if I could quickly hook up a sample query. – spiritwalker Sep 14 '17 at 00:26
  • 1
    It's not possible at present in a "single statement". Note than in the next release of MongoDB you can do: `db.collection.update({},{ "$unset": { "analytics.$[].browser": "" } },{ "multi": true })` [as is noted on an answer](https://stackoverflow.com/a/46054172/2313887) to the linked duplicate. For now though, you need to iterate each array element or keep updating until no more items are left. Which is also noted within the provided answers. – Neil Lunn Sep 14 '17 at 00:50
  • A solution that works involves building an `aggregate()` to separate the initial list in different docs, then performing a `forEach()` on these docs, get the length of the "analytics" array for each of them, and manually delete the browser property like you would do for a javascript array before saving each doc. As noted above, it is therefore not a "single statement", and you have to build your own function for it. – Nicolas Couvrat Sep 14 '17 at 00:57
  • @neilLunn look forward to that feature in 3.5.12 – dmo Sep 14 '17 at 01:07
  • Well we're actually up to 3.5.13 as of the last few days. The "official" release is 3.6. But it was introduced into the development branch at 3.5.12. You can download and "play with" these builds at your pleasure. But they are unstable and not for production use, and various new features are still subject to change. The linked duplicate has several useful answers with practical and efficient solutions for now. – Neil Lunn Sep 14 '17 at 01:12
  • @NeilLunn it is good to know you can do "analytics.$[].browser" in next version. – spiritwalker Sep 14 '17 at 02:43

1 Answers1

0

Neil lunn is correct. Instead of expecting one update statement to do everything for you, you can write your own scripts and loop through and then do delete.

db.getCollection('yourcollection').find({"analytics.country" : {"$exists" : true}}).
forEach(function(item) {
        item.analytics.forEach(function(analytic){
            delete analytic.browser;                
        })         
        db.getCollection('yourcollection').save(item)
})
spiritwalker
  • 2,257
  • 14
  • 9
  • Because (at least what you gave us) is actually an array of objects, you have to iterate on it as well if you want to update every object at once. The way to do that is already described in the answer above (hint: use `forEach()`), so you just have to adapt it to your situation – Nicolas Couvrat Sep 14 '17 at 01:01
  • @NicolasCouvrat dmo's comments was made against my previous solution which did have a problem of removing multiples. what you see now is a updated anwser. cheers. – spiritwalker Sep 14 '17 at 01:06