Подтвердить что ты не робот

Как проверить, существуют ли эти данные в базе данных во время обновления (Mongoose And Express)

Как выполнить проверки перед сохранением отредактированных данных в мангусте?

Например, если sample.name уже существует в базе данных, пользователь получит некоторую ошибку, что-то вроде этого, здесь мой код ниже

//Post: /sample/edit
app.post(uri + '/edit', function (req, res, next) {
  Sample.findById(req.param('sid'), function (err, sample) {

    if (err) {
      return next(new Error(err));
    }

    if (!sample) {
      return next(new Error('Invalid reference to sample information'));
    }

    // basic info
    sample.name = req.body.supplier.name;
    sample.tin = req.body.supplier.tin;

    // contact info
    sample.contact.email = req.body.supplier.contact.email;
    sample.contact.mobile = req.body.supplier.contact.mobile;
    sample.contact.landline = req.body.supplier.contact.landline;
    sample.contact.fax = req.body.supplier.contact.fax;

    // address info
    sample.address.street = req.body.supplier.address.street;
    sample.address.city = req.body.supplier.address.city;
    sample.address.state = req.body.supplier.address.state;
    sample.address.country = req.body.supplier.address.country;
    sample.address.zip = req.body.supplier.address.zip;

    sample.save(function (err) {
      if (err) {
        return next(new Error(err));
      }

      res.redirect(uri + '/view/' + sample._id);
    });

  });
});
4b9b3361

Ответ 1

Обычно вы можете использовать проверку mongoose, но так как вам нужен результат async (db-запрос для существующих имен), а валидаторы не поддерживают promises (из того, что я могу сказать), вам нужно будет создать свою собственную функцию и передать обратный вызов. Вот пример:

var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

mongoose.connect('mongodb://localhost/testDB');

var UserSchema = new Schema({
    name: {type:String}
});

var UserModel = mongoose.model('UserModel',UserSchema);

function updateUser(user,cb){
    UserModel.find({name : user.name}, function (err, docs) {
        if (docs.length){
            cb('Name exists already',null);
        }else{
            user.save(function(err){
                cb(err,user);
            });
        }
    });
}

UserModel.findById(req.param('sid'),function(err,existingUser){
   if (!err && existingUser){
       existingUser.name = 'Kevin';
       updateUser(existingUser,function(err2,user){
           if (err2 || !user){
               console.log('error updated user: ',err2);
           }else{
               console.log('user updated: ',user);
           }

       });
   } 
});

ОБНОВЛЕНИЕ: лучший способ

Предзахват кажется более естественным местом для остановки сохранения:

UserSchema.pre('save', function (next) {
    var self = this;
    UserModel.find({name : self.name}, function (err, docs) {
        if (!docs.length){
            next();
        }else{                
            console.log('user exists: ',self.name);
            next(new Error("User exists!"));
        }
    });
}) ;

ОБНОВЛЕНИЕ 2: пользовательские валидаторы Async

Похоже, что mongoose поддерживает асинхронные пользовательские валидаторы теперь, чтобы, вероятно, было естественным решением:

    var userSchema = new Schema({
      name: {
        type: String,
        validate: {
          validator: function(v, cb) {
            User.find({name: v}, function(err,docs){
               cb(docs.length == 0);
            });
          },
          message: 'User already exists!'
        }
      }
    });

Ответ 2

Другим способом продолжения использования используемого примера @nfreeze является этот метод проверки:

UserModel.schema.path('name').validate(function (value, res) {
    UserModel.findOne({name: value}, 'id', function(err, user) {
        if (err) return res(err);
        if (user) return res(false);
        res(true);
    });
}, 'already exists');

Ответ 3

Вот еще один способ сделать это в меньшем коде.

ОБНОВЛЕНИЕ 3: Статика класса асинхронной модели

Подобно опции 2, это позволяет вам создать функцию, напрямую связанную с схемой, но вызываться из того же файла с помощью модели.

model.js

 userSchema.statics.updateUser = function(user, cb) {
  UserModel.find({name : user.name}).exec(function(err, docs) {
    if (docs.length){
      cb('Name exists already', null);
    } else {
      user.save(function(err) {
        cb(err,user);
      }
    }
  });
}

Вызов из файла

var User = require('./path/to/model');

User.updateUser(user.name, function(err, user) {
  if(err) {
    var error = new Error('Already exists!');
    error.status = 401;
    return next(error);
  }
});

Ответ 4

В дополнение к уже опубликованным примерам, здесь используется другой подход с использованием express-async-wrap и асинхронных функций (ES2017).

маршрутизатор

router.put('/:id/settings/profile', wrap(async function (request, response, next) {
    const username = request.body.username
    const email = request.body.email
    const userWithEmail = await userService.findUserByEmail(email)
    if (userWithEmail) {
        return response.status(409).send({message: 'Email is already taken.'})
    }
    const userWithUsername = await userService.findUserByUsername(username)
    if (userWithUsername) {
        return response.status(409).send({message: 'Username is already taken.'})
    }
    const user = await userService.updateProfileSettings(userId, username, email)
    return response.status(200).json({user: user})
}))

UserService

async function updateProfileSettings (userId, username, email) {
    try {
        return User.findOneAndUpdate({'_id': userId}, {
            $set: {
                'username': username,
                'auth.email': email
            }
        }, {new: true})
    } catch (error) {
        throw new Error(`Unable to update user with id "${userId}".`)
    }
}

async function findUserByEmail (email) {
    try {
        return User.findOne({'auth.email': email.toLowerCase()})
    } catch (error) {
        throw new Error(`Unable to connect to the database.`)
    }
}

async function findUserByUsername (username) {
    try {
        return User.findOne({'username': username})
    } catch (error) {
        throw new Error(`Unable to connect to the database.`)
    }
}

// other methods

export default {
    updateProfileSettings,
    findUserByEmail,
    findUserByUsername,
}

Ресурсы

асинхронная функция

await

express-async-wrap

Ответ 5

Если вы ищете по уникальному индексу, то использование UserModel.count может быть для вас лучше, чем UserModel.findOne, потому что оно возвращает весь документ (т.е. делает чтение) вместо того, чтобы возвращать только int.

Ответ 6

Для всех, кто падает на это старое решение. Существует лучший способ из документов Мангуста.

var s = new Schema({ name: { type: String, unique: true }});
s.path('name').index({ unique: true });