Update Array Elements in a Document with MQL Positional Operators
Rate this tutorial
MongoDB offers a rich query language that's great for create, read, update, and delete operations as well as complex multi-stage aggregation pipelines. There are many ways to model your data within MongoDB and regardless of how it looks, the MongoDB Query Language (MQL) has you covered.
One of the lesser recognized but extremely valuable features of MQL is in the positional operators that you'd find in an update operation.
Let's say that you have a document and inside that document, you have an array of objects. You need to update one or more of those objects in the array, but you don't want to replace the array or append to it. This is where a positional operator might be valuable.
In this tutorial, we're going to look at a few examples that would benefit from a positional operator within MongoDB.
Let's use the example that we have an array in each of our documents and we want to update only the first match within that array, even if there's a potential for numerous matches.
To do this, we'd probably want to use the
$operator which acts as a placeholder to update the first element matched.
For this example, let's use an old-school Pokemon video game. Take look at the following MongoDB document:
Let's assume that the above document represents the Pokemon information for the Pokemon Red video game. The document is not a true reflection and it is very much incomplete. However, if you're a fan of the game, you'll probably remember the glitch Pokemon named "MissingNo." To make up a fictional story, let's assume the developer, at some point in time, wanted to give that Pokemon an actual name, but forgot.
We can update that particular element in the array by doing something like the following:
In the above example, we are doing a filter for documents that have an array element with a
namefield set to
MissingNo. With MongoDB, you don't need to specify the array index in your filter for the
updateoperator. In the manipulation step, we are using the
$positional operator to change the first occurrence of the match in the filter. Yes, in my example, I am renaming the "MissingNo" Pokemon to that of a Digimon, which is an entirely different brand.
The new document would look like this:
Had "MissingNo" appeared numerous times within the array, only the first occurrence would be updated. If "MissingNo" appeared numerous times, but the surrounding fields were different, you could match on multiple fields using the
$elemMatchoperator to narrow down which particular element should be updated.
Let's say that you have an array in your document and you need to update every element in that array using a single operation. To do this, we might want to take a look at the
$operator which does exactly that.
Using the same Pokemon video game example, let's imagine that we have a team of Pokemon and we've just finished a battle in the game. The experience points gained from the battle need to be distributed to all the Pokemon on your team.
The document that represents our team might look like the following:
At the end of the battle, we want to make sure every Pokemon on our team receives 10 XP. To do this with the
$operator, we can construct an
updateoperation that looks like the following:
Our new document would look like this:
While useful for this example, we don't exactly get to provide criteria in case one of your Pokemon shouldn't receive experience points. If your Pokemon has fainted, maybe they shouldn't get the increase.
We'll learn about filters in the next part of the tutorial.
Let's use the example that we have several array elements that we want to update in a single operation and we don't want to worry about excessive client-side code paired with a replace operation.
To do this, we'd probably want to use the
$[<identifier>]operator which acts as a placeholder to update all elements that match an
To put things into perspective, let's say that we're dealing with Pokemon trading cards, instead of video games, and tracking their values. Our documents might look like this:
Of course, the above snippet isn't a document, but an operation to insert two documents into some
pokemon_collectioncollection within MongoDB. In the above scenario, each document represents a collection of cards for an individual. The
cardsarray has information about the card in the collection as well as the current value.
In our example, we need to update prices of cards, but we don't want to do X number of update operations against the database. We only want to do a single operation to update the values of each of our cards.
Take the following query:
updateoperation is like any other, but with an extra step for our positional operator. The first parameter, which is an empty object, represents our match criteria. Because it is empty, we'll be updating all documents within the collection.
The next parameter is the manipulation we want to do to our documents. Let's skip it for now and look at the
arrayFiltersin the third parameter.
Imagine that we want to update the price for two particular cards that might exist in any person's Pokemon collection. In this example, we want to update the price of the Pikachu and Charizard cards. If you're a Pokemon trading card fan, you'll know that there are many variations of the Pikachu and Charizard card, so we get specific in our
arrayFiltersarray. For each object in the array, the fields of those objects represent an
andcondition. So, for
elemX, which has no specific naming convention, all three fields must be satisfied.
In the above example, we are using
elemYto represent two different filters.
Let's go back to the second parameter in the
updateoperation. If the filter for
elemXcomes back as true because an array item in a document matched, then the
valuefield for that object will be set to a new value. Likewise, the same thing could happen for the
elemYfilter. If a document has an array and one of the filters does not ever match an element in that array, it will be ignored.
If looking at our example, the documents would now look like the following:
If any particular array contained multiple matches for one of the
arrayFiltercriteria, all matches would have their price updated. This means that if I had, say, 100 matching Pikachu cards in my Pokemon collection, all 100 would now have new prices.
You just saw how to use some of the positional operators within the MongoDB Query Language (MQL). These operators are useful when working with arrays because they prevent you from having to do full replaces on the array or extended client-side manipulation.