I recently had to deal with a small issue working with update operations and aggregation pipelines in MongoDB. I couldn’t find much help online to fix it, but I finally managed to understand it and decided to write my findings down to help anyone that may come across the same issue in the future.
The code
I was trying to create an update operation using aggregation pipelines on a Node.js project with mongoose. My code looked something like this:
// Model schema
let productSchema = new Schema({
name: { type: String, required: true },
description: { type: String, required: false },
currency: { type: String, required: false },
price: { type: Number, required: true },
createdAt: { type: Date, default: Date.now, required: true },
updatedAt: { type: Date, required: false },
tags: [String],
});
// Update operation
module.exports.update = async (product) => {
await productModel.findByIdAndUpdate(product._id,
[{
$set:
{
name: product.name,
description: product.description,
currency: product.currency,
price: product.price,
updatedAt: '$$NOW',
tags: { $concatArrays: [ '$tags', product.newTags ] }
}
}],
{
runValidators: true
});
}
The previous code is supposed to find a product by id and update its properties, setting the current date in the updatedAt
property and concatenating the existent tags with some new tags.
Looks fine, right?
The error
But when I executed the update function, the following error was thrown by MongoDB:
{
"ok": 0,
"errmsg": "'$' by itself is not a valid FieldPath",
"code": 16872,
"codeName": "Location16872",
"name": "MongoError"
}
So after some debugging and tests I realized where the issue was. First let me show you how the product
object that I was receiving in the arguments looked like:
{
"name": "Lenovo Thinkbook 13s",
"description": "The ThinkBook 13s laptop is a lightweight and durable, business laptop with amazing entertainment features.",
"currency": "$",
"price": 723,
"newTags": [ "laptop", "lenovo" ]
}
The issue was in the currency
property value. The value was “$”, which is a character used by MongoDB to identify field paths. So the error message was actually really clear, but it was not obvious for me at first sight.
The solution
Luckily the solution to this issue was pretty simple, I just had to use the $literal
operator to wrap up my currency
new value, like this:
module.exports.update = async (product) => {
await productModel.findByIdAndUpdate(product._id,
[{
$set:
{
name: product.name,
description: product.description,
currency: { $literal: product.currency }, // <--- here!
price: product.price,
updatedAt: '$$NOW',
tags: { $concatArrays: [ '$tags', product.newTags ] }
}
}],
{
runValidators: true
});
}
About $literal
$literal
is an aggregation pipeline operator that receives a value and makes sure the aggregation pipeline doesn’t interpret it as a field path, and uses its literal value instead.
Read more about this operator in MongoDB’s docs.
Test repo
I created a github repo to test the issue and its solution:
Thoughts? 💬
Have you ever come across this issue? Was this article useful?