DEV Community

loading...

Fixing MongoDB's error "'$' by itself is not a valid FieldPath"

paulasantamaria profile image Paula Santamaría ・2 min read

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:

GitHub logo pawap90 / mongoose-literal-examples

A Node.js + Mongoose + Jest project that demonstrates how to use MongoDB's $literal operator to fix some common issues.

A Node.js + Mongoose + Jest project that demonstrates how to use MongoDB's $literal operator to fix some common issues.

This repo was build as an example for my article Fixing MongoDB's error "'$' by itself is not a valid FieldPath".

Dependencies

What you need to run this project:

  • Node.js

(MongoDB is not required because it'll run in memory by the package mongodb-memory-server).

Try it out

1. Install dependencies

npm install

2. Run tests

npm test

Contribute

Feel free to contribute to this project either by leaving your comments and suggestions in the Issues section or creating a PR.

Testing MongoDB in memory

To learn more about how this project executes tests on a dynamically generated database in memory, check my article Testing Node.js + Mongoose with an in-memory database

Thoughts? 💬

Have you ever come across this issue? Was this article useful?

Discussion (4)

Collapse
ziizium profile image
Habdul Hazeez

Unrelated to your post but it has been a while since you blogged, so, welcome back!.

Collapse
paulasantamaria profile image
Paula Santamaría Author

Thank you! 🤗 I'm really happy to be back.
I probably won't be writing every week as I used to, but I already have a few ideas of stuff I want to write about in the near future, so I'll try not to disappear for so long again 😅

Collapse
gklijs profile image
Gerard Klijs

Thanks, haven't come across this one, as we model currency according to ISO. But it's nice to know there is an easy solution, I hope I'll remember it when I do need it.

Collapse
paulasantamaria profile image
Paula Santamaría Author

Glad to help! I only realize about this issue accidentally because some client wanted the default (local) currency to be represented as "$" and I just thought "yeah, ok, no big deal" 😅

Forem Open with the Forem app