How to search and update data with Mongoose?

Asked

Viewed 1,272 times

0

I’m using mongodb with Mongoose in nodejs, I have my schema like this:

const UserSchema = new mongoose.Schema({
    username: {
        type: String,
        unique: true,
        require: true
    },
    balances: {
        dolar: {
            manualBalance: {
                type: Number,
                select: false
            },
            paidBalance: {
                type: Number,
                select: false
            }
        }
    }

});

So I try to do this:

router.post('/get_user_balance', async (req, res) => {
    const { username } = req.body;
    User.findOneAndUpdate({ username: username }, { $set: { balances: {dolar: {manualBalance: '1.50' }}}});
        const balances = await User.findOne({ username }).select('+balances.dolar.manualBalance');
        console.log(balances);
}

and receive from nodejs the error:

(node:22826) UnhandledPromiseRejectionWarning: MongoError: Projection cannot have a mix of inclusion and exclusion.

I believe it’s .select('+balances.dolar.manualBalance'); that is wrong. So I also tried without select and removed the "select: false" from my schema, retained the database and created a user again, and received from the console.log only the username, instead of also receiving the field "balances".

{
  _id: 5ab00b7a92fe402bb85f407f,
  username: 'Jerildo',
  __v: 0
}

I’m registering users like this:

const user = await User.create(req.body);

where req.body is : {username: "jerildo"}

I know it must be simple thing that I am wrong for not knowing so well the mongodb or the Mongoose, always worked with Redis, this is new for me. Then I ask for a force here.

  • In production do not use user input directly to the database... Select, escape strings and if possible (and highly recommended) disable javascript execution in the database. In the configuration file set: security -> javascriptEnabled: false

  • @Lauromoraes, I appreciate the information, but it’s not in production, I haven’t looked at security yet, but I’ll be studying security for mongodb later. I first need to learn how to use it, and as you can see in the question, I’m not sure XD

  • I don’t know why you’re wearing async...could simply chain. Using async "unbound" should wait (await) on first (findOneAndUpdate) and second (findOne) request. You must pass the argument new: true in findOneAndUpdate() if you want to return the modified document. I will edit my reply.

  • I am using async because my API uses async. findOneAndUpdate() only returns one document. But let’s ignore findOneAndUpdate(), just the other findOnde(), it shouldn’t return me {username: "jerildo", Balances:{dolar: {manualBalance: 0}}} ? because returns me only the username?

  • I am editing my answer with an example that I tested locally... this returning me both user and balance. I finish soon

  • Could you recommend me some module to handle the "escape strings"? Sanitize for what I saw is just reject case (/^\$/.test(mystring)) be it true , so the user will not be able to inject using $ne for example, certain?

  • https://www.npmjs.com/package/express-mongo-sanitize

  • It was exactly this module I saw, just does this check (/$/.test(mystring)) even kkkkk and to handle the "escape strings"?

  • This module not only checks, it accepts opposites and can delete "injection" entries or even replace pre-defined "malicious" entries. See the source code on Github: https://github.com/fiznool/express-mongo-sanitize/blob/master/index.js . But the best Nosql injection prevention in Mongodb is (and remains) disabling Javascript in the database

  • 1

    I mistook it for Mongo-Sanitize, I’ll read it here.

  • @Lauromoraes I read here https://zanon.io/posts/nosql-injection-in-mongodb that Mongoose does not need to sanitize, it already converts to string when you specify the type: String. Thus avoiding the injection.

  • You can also check here http://mongoosejs.com/docs/validation.html about Mongoose convert to string.

Show 7 more comments

1 answer

1


As quoted in the comments:

I don’t know why you’re using async...

Right when using async should wait (await) both on the first request (findOneAndUpdate()) and on the second (findOne()).

By default the function findOneAndUpdate() returns the original document, if you want to return the modified document you must pass an argument in the example option object:

User.findOneAndUpdate({}, {}, {new: true})

Assuming you have created a document, then the following example:

await User.findOneAndUpdate({
    username: 'Jerildo'
}, {
    $set: {
        balances: {
            dolar: {
                manualBalance: '1.50'
            }
        }
    }
}, {
    new: true // retorna o novo objeto
})

const balances = await User.findOne({ username: 'Jerildo' }).select('+balances.dolar.manualBalance')
console.log(balances)

It will return the modified document (in the console.log())

inserir a descrição da imagem aqui

inserir a descrição da imagem aqui

Optionally you can chain the answer (result) to findOneAndUpdate() just like:

await USER.findOneAndUpdate({
    username: 'Jerildo'
}, {
    $set: {
        balances: {
            dolar: {
                manualBalance: '1.50'
            }
        }
    }
}, {
    new: true // retorna o novo objeto
}, (err, doc) => {
    if ( err ) {
        console.error(err)
    }
    // o documento com o novo valor (atulizado)
    console.log(doc)
})

NOTE: I define the username for this example, you should check (validate) your request scheme to ensure that there is a username.

  • But in the same question I say that I also tested without the "select: false". Read it more carefully again.

  • Yes, my inattention. Try with .exec() and say what comes back

  • I tried with ". exec()" and I get the same error: name: 'MongoError',
 message: 'Projection cannot have a mix of inclusion and exclusion.',

  • I still get the same error with your example.

  • I managed to make it work by returning User.findOneAndUpdate to the const balances, since the other findOne was being unnecessary. But there is something wrong with this line const balances = await User.findOne({ username: 'Jerildo' }).select('+balances.dolar.manualBalance') and something tells me q is in "+balances.dolar.manualBalance", as is your schema?

  • Um... I can only imagine that you have an excerpt of "conflicting" code. This is my Gist containing exactly the example I posted here, the only differential is the schema, model and connection configuration (local). Test and tell if funcinone? https://gist.github.com/subversivo58/6b130422b7b306bd6586d77c7b365056

  • Opa, my mistake, I had forgotten to again add the line "select: false" in my schema.

Show 2 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.