0

According to this answer, removing an element from an array by index implies two distinct updates:

db.lists.update({}, {$unset : {"interests.0" : 1 }})
db.lists.update({}, {$pull :  {"interests" : null}})

This works... but it introduces some complexity to make the operation atomic and prevent race conditions. Is there any update on that topic?

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
j3d
  • 9,492
  • 22
  • 88
  • 172
  • unless it's a cron job, it can probably be handled client side, and the collection updated atomically after the fact. in the former case, perhaps you can update changes atomically after they have been made. – aug2uag Mar 07 '14 at 23:38

1 Answers1

1

Yes and No, really. Consider the following array:

  { array: [ { a: "a", b: "b" }, { a:"a", b:"c" } ] }

So you could $pull the second element from here with the following statement:

  db.collection.update({}, {$pull: { array: { a: "a", b: "b"} } })

And that would remove the matching document. But if did the same thing on this array:

  { array: [ { a: "a", b: "b" }, { a: "a", b:"c", c: "c" }, { a:"a", b:"c" } ] }

Then this would be the result:

  { array: [ { a: "a", b: "b" }] }

So the query matched two of the documents in the array, and pulled them both.

Unfortunately the seemingly obvious:

  db.collection.update({}, {$pull: { array: { $elemMatch:{ a: "a", b: "b" } }} })

Just wont work. Which is a pity. In the too hard basket to parse at this time.

So the moral of the story is, if you really concerned about concurrency and race conditions for this type of update, .find() the matching element first, then update() with a statement that matches the whole document, as long as it will not also partially match another document in the array.

If you cannot do that, then the method you describe in the question is currently the only way.

There is a JIRA issue on this, but it is not exactly terribly popular.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317