web-dev-qa-db-fra.com

Comment vérifier si ces données existent déjà dans la base de données lors de la mise à jour (Mongoose And Express)

Comment faire des validations avant d'enregistrer les données éditées dans la mangouste?

Par exemple, si sample.name existe déjà dans la base de données, l'utilisateur recevra une sorte d'erreur, quelque chose comme ça, voici mon code ci-dessous

//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);
    });

  });
});
24
Miguel Lorenzo

En règle générale, vous pouvez utiliser validation mangouste mais comme vous avez besoin d'un résultat asynchrone (requête db pour les noms existants) et que les valideurs ne prennent pas en charge les promesses (d'après ce que je peux dire), vous devrez créer votre propre fonction et passer un rappel. Voici un exemple:

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);
           }

       });
   } 
});

MISE À JOUR: Une meilleure façon

Le pré crochet semble être un endroit plus naturel pour arrêter la sauvegarde:

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!"));
        }
    });
}) ;

MISE À JOUR 2: validateurs personnalisés asynchrones

Il semble que mangouste supporte désormais les validateurs personnalisés asynchrones, ce qui serait probablement la solution naturelle:

    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!'
        }
      }
    });
56
mr.freeze

Une autre façon de continuer avec l'exemple @nfreeze utilisé est cette méthode de validation:

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');
8
John Linhart

Voici une autre façon d'accomplir cela en moins de code.

MISE À JOUR 3: Statique de classe de modèle asynchrone

Similaire à l'option 2, cela vous permet de créer une fonction directement liée au schéma, mais appelée à partir du même fichier à l'aide du modèle.

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);
      }
    }
  });
}

Appel à partir d'un fichier

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);
  }
});
2
DBrown

En plus des exemples déjà publiés, voici une autre approche utilisant express-async-wrap et des fonctions asynchrones (ES2017).

Routeur

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,
}

Ressources

fonction asynchrone

attendre

express-async-wrap

2
Nik Denisov

Si vous recherchez par un index unique, utiliser UserModel.count peut en fait être meilleur pour vous que UserModel.findOne car il retourne le document entier (c'est-à-dire qu'il fait une lecture) au lieu de renvoyer juste un entier.

1
Dale Annin

Pour tous ceux qui tombent sur cette ancienne solution. Il y a une meilleure façon à partir des documents mangouste .

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