Home > Mobile >  MongoDB update a field in every document with function
MongoDB update a field in every document with function

Time:11-03

I need to update all my existing documents and encrypt sensitive information. I wrote a script in my code as follows to encrypt the "name" field in every document:

  const update = await User.updateMany(
    {},
    {
      $set: {
        name: {
          $function: {
            body: function (name) {
              return encrypt(name);
            },
            args: ['$name'],
            lang: 'js'
          }
        }
      }
    }
  );

However I am getting this error:

  server/node_modules/mongoose/lib/query.js:4777
  const castError = new CastError();
                    ^

CastError: Cast to string failed for value "{
  '$function': { body: [Function: body], args: [ '$name' ], lang: 'js' }
}" (type Object) at path "name"
    at model.Query.exec (/node_modules/mongoose/lib/query.js:4777:21)
    at model.Query.Query.then (/node_modules/mongoose/lib/query.js:4876:15)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  messageFormat: undefined,
  stringValue: '"{\n'  
    "  '$function': { body: [Function: body], args: [ '$name' ], lang: 'js' }\n"  
    '}"',
  kind: 'string',
  value: {
    '$function': { body: [Function: body], args: [ '$name' ], lang: 'js' }
  },
  path: 'name',
  reason: null,
  valueType: 'Object'
}

Does anyone know what this means? Been stuck on this for a while. Thanks

CodePudding user response:

So you actually have multiple issues with this code, I will start with the error you posted. which is a mongoose casting error.

You're trying to use the aggregation $function function as part of normal update, this is not allowed - mongoose in turn is trying to cast the "name" value into string because this is what it's expecting to get from the schema definition. this is the error you're getting.

So to solve problem #1 you want to be using the aggregation pipeline update syntax which will allow the usage of $function, and to stop using mongoose and it's "Schema" protection is really bad for advanced syntax usage.


The second issue as mentioned in the comments this is not working because of how Mongodb parses and executes the code, it does not work like a normal "compiler" that translates the function call to code on the fly.

Instead it sends this "object" to the internal engine, and there it tries to execute it, so within Mongo's internal score there is no such function.

To overcome this you need to store your custom function inside mongo first, this is actually quite easy as described here:

db.system.js.insertOne(
   {
     _id: "encrypt",
     value : function(x) { return x; }
   }
);

Now with the change from #1 your code should work.

  • Related