简体   繁体   中英

Why can I update one field but not another in MongoDB? (MEAN Stack)

I have two fields: "password_hash" and "password_token" in my db.user mongoDB collection. I can update "password_hash" from API (curl, through an express route, into a mongoose model) but I can't update "password_token" using nearly the exact same code. I can modify either 'manually' from Studio3t, but that would defeat the purpose of writing an API. I also think that means this behavior is not related to indexes, validators, etc. on the database collection, since I'm allowed to save values in either field. When I try to do the same from the API, I get no error message, only { ok: 0, n: 0, nModified: 0 } and no updated field.

Here is the abbreviated document:

"username" : "user1", 
"password_hash" : "$2a$10$pVTT8FS.WV.sba1wvVwYMu7hMqaGkP1toJx5PGMXrl/ZnLyLtqsYy", 
"password_token" : ""

Here are the curl commands I'm using:

# curl -w '\n' -H "Content-Type: application/json" -X POST -d '{ "username": "user1"}' http://localhost:3000/users/save_hash
{"success":true,"message":"Saved a hash for user1."}

# curl -w '\n' -H "Content-Type: application/json" -X POST -d '{ "username": "user1"}' http://localhost:3000/users/reset_user
{"success":true,"message":"Saved a reset token for user1."}

...both return success, as written, but the second has no effect on the DB field "password_token".

Here are the structurally identical express routes:

router.post('/save_hash', (req, res, next) => {
    const username = req.body.username;
    User.getUserByUsername(username, (err, user) => {
        if (err) logger.error(err);
        if (user) {
            bcrypt.hash(user.password, 10, (err, hash) => {
                if (hash) {
                    User.saveHash(user.username, hash, err);
                    return res.json({success: true, message: 'Saved a hash for ' + user.username + '.'});
                }
            })
        }
    })
});

router.post('/reset_user', (req, res, next) => {
    const username = req.body.username;
    User.getUserByUsername(username, (err, user) => {
        if (err) logger.error(err);
        if (user) {
            crypto.randomBytes(20, (err, buffer) => {
                if (buffer) {
                    const token = buffer.toString('hex');
                    User.resetToken(user.username, 'test', err);
                    if (err) {logger.error(err) } else {
                        return res.json({success: true, message: 'Saved a reset token for ' + user.username + '.'});
                    }
                }
            });
        }
    })
});

And here are the structurally identical methods on the models:

module.exports.saveHash = function (username, password_hash, callback) {
    const query = {'username': username};
    User.update(query, { $set: { 'password_hash': password_hash }}, (err_update, raw) => {
        if (err_update) { logger.error(err_update); } else {console.log('raw data: ', raw)}
    })
};

module.exports.resetToken = function (username, token, callback) {
    const query = {'username': username};
    User.update(query, { $set: { 'password_token': token}}, (err_update, raw) => {
        if (err_update) { logger.error(err_update); } else {console.log('raw data: ', raw)}
    })
};

The curl, route, and model for "password_hash" work as expected. The same duplicated to update "password_token" do not work. Even the following modifications work:

module.exports.resetToken ...
User.update(query, { $set: { 'password_hash': token}}... // works. updates "password_hash" to the token value.

module.exports.resetToken ...
User.update(query, { $set: { 'password_hash': 'test'}}... // works. updates "password_hash" to 'test'.

...but if I modify saveHash(), which currently works to update "password_hash", so that it should instead "password_token", it does not modify "password_token":

module.exports.saveHash ...
User.update(query, { $set: { 'password_token': 'test'}}... /* Does not work. returns  { ok: 0, n: 0, nModified: 0 } ...and does not modify "password_token". */

...So it appears there is 'something wrong' with the password_token field itself, since I can update a different field with the exact same code.

For what it's worth: all the documentation I've read says I should be able to call update() on a field that does not yet exist and it will be created by an implicit $set, but I have not found that to be the case. I have had to create both "password_hash" and "password_token" using MongoShell before they can be updated:

db.users.update({},
  {$set : {"password_token":""}},
  {upsert:false,
  multi:true})

...so I wonder if I might be in version hell and I'm finally seeing a symptom.

TL:DR; I can update one field "password_hash" just fine, but another field, "password_token" (and any additional fields I add) cannot be updated and return no error, using basically the same curl, route, and model method. What gives?

The simple answer, given by JohnnyHK, is that the new fields I'm creating in the DB were not also defined in my User schema.

I have actually made this same mistake before. The lack of error and the only tip off being that the update operation failed means I need to tie a string around my finger any time I add a new field to a document.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM