2

I want to rename tags in our documents' tags array, e.g. change all tags a in the collection to c. The documents look something like this:

[ { _id: …, tags: ['a', 'b', 'c'] },
  { _id: …, tags: ['a', 'b'] },
  { _id: …, tags: ['b', 'c', 'd'] } ]

I need to keep tags unique. This means, an update like this will not work, because the first document will end up containing tag c twice:

db.docs.update(
    { tags: 'a' },
    { $set: { 'tags.$': 'c' } }
)

So, I tried this alternatively:

db.docs.update(
    { tags: 'a' },
    {
        $pull: { 'a' },
        $addToSet: { 'c' }
    }
)

But this gives a MongoError: Cannot update 'tags' and 'tags' at the same time.

Any chance of renaming the tags with one single update?

qqilihq
  • 10,794
  • 7
  • 48
  • 89
  • Possible duplicate of [Pull and addtoset at the same time with mongo](http://stackoverflow.com/questions/24300148/pull-and-addtoset-at-the-same-time-with-mongo) – Neo-coder Jan 26 '17 at 18:47

1 Answers1

1

According to official MongoDB documentation, there is no way of expressing "replace" operation on a set of elements. So I guess, there isn't a way to do this in single update.

Update: After some more investigation, I came across this document. If I understand it correctly, your query should look like this:

db.docs.update({
       tags: 'a'
  }, {
    $set: { 'tags.$': 'c'}
  })

Where 'tags.$' represents selector of the first element in "tags" array that matches the query, so it replaces first occurence of 'a' with 'c'. As I understand, your "tags" array does not contain duplicates, so first match will be the only match.

M. Prokhorov
  • 3,894
  • 25
  • 39
  • Thank you for you response. Concerning your update: This is what I tried first. But it'll create duplicates: If `c` is already in the array, `a` will be renamed to `c`, and there are two `c`s in the array. – qqilihq Jan 26 '17 at 18:27
  • Ah! It seems I've missed that part. Then I guess first part of my answer apply, which states that you can't express that intent clearly in single operation. I don't quite know if mongodb update doc can contain nested queries. Even if it can, then it's quite a hassle to write, because what you will need to express is "distinct( union(minus($row.tags, ['a']), ['c']) )", in sql terms, which looks painful even in this simplified form. – M. Prokhorov Jan 27 '17 at 10:57
  • I guess what I'm trying to say then, is you'll have to first addToSet a 'c' element everywhere where 'a' is contained, and then run second update that will remove 'a'. – M. Prokhorov Jan 27 '17 at 11:05
  • This was what I had, but I had hoped there was some more elegant solution :) But your feedback and the older question linked above also confirms that this is the way to go. Thanks! – qqilihq Jan 27 '17 at 15:31