【问题标题】:How to check if that data already exist in the database during update (Mongoose And Express)如何在更新期间检查该数据是否已存在于数据库中(Mongoose And Express)
【发布时间】:2013-05-28 18:56:32
【问题描述】:

如何在mongoose中保存编辑后的数据之前进行验证?

例如,如果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);
    });

  });
});

【问题讨论】:

    标签: node.js validation express mongoose


    【解决方案1】:

    通常您可以使用mongoose validation,但由于您需要异步结果(数据库查询现有名称)并且验证器不支持承诺(据我所知),您需要创建自己的函数并传递打回来。这是一个例子:

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

    更新:更好的方法

    pre 钩子似乎是一个更自然的停止保存的地方:

    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:异步自定义验证器

    看起来 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!'
            }
          }
        });
    

    【讨论】:

    • 如果用户存在,你没有调用“next()”,我认为你应该调用 next() 并出现错误
    • 为什么不在名称字段中使用unique 参数?
    • 用户将如何使用 save() 方法?
    • 当我在pre 中间件中放入find 时出现此错误:Uncaught TypeError: Object #<Object> has no method 'find'
    • 使用唯一索引是这里的最佳做法。否则,您必须在插入之前查询集合,而如果您使用唯一索引 mongodb 只会拒绝插入,从而节省往返时间和查询集合所花费的时间。唯一索引将是这里最好的解决方案。
    【解决方案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');
    

    【讨论】:

    • 什么是路径?你能详细说明一下吗
    • 这对我有用,似乎是最优雅的方式,但它会引发弃用错误,即 DeprecationWarning: Implicit async custom validators (custom validators that take 2 arguments) is deprecated in mongoose >= 4.9 .0.
    • mongoosejs.com/docs/validation.html#async-custom-validators 即使您不想使用异步验证器,也要小心,因为 mongoose 4 会假定所有接受 2 个参数的函数都是异步的,例如 validator.isEmail 函数。从 4.9.0 开始,此行为被视为已弃用,您可以通过在自定义验证器上指定 isAsync: false 来关闭它。
    【解决方案3】:

    除了已经发布的示例之外,这里还有另一种使用 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})
    }))
    

    用户服务

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

    资源

    async function

    await

    express-async-wrap

    【讨论】:

      【解决方案4】:

      对于任何陷入这种旧解决方案的人。有一个更好的方法 from the mongoose docs.

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

      【讨论】:

      • (node:18024) DeprecationWarning: collection.ensureIndex is deprecated. Use createIndexes instead.
      • 哎呀。使用mongoose.connect(DATABASE_URL, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true }, err => { ... 排除所有警告...
      【解决方案5】:

      这是用更少的代码完成此任务的另一种方法。

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

      【讨论】:

        【解决方案6】:

        如果电子邮件或电话号码已存在于数据库中,请使用一个查询进行检查

        let userDB = await UserS.findOne({ $or: [
          { email: payload.email },
          { phoneNumber: payload.phoneNumber }
        ] })
        
        if (userDB) {
          if (payload.email == userDB.email) {
            throw new BadRequest({ message: 'E-mail already exists' })
          } else if (payload.phoneNumber == userDB.phoneNumber) {
            throw new BadRequest({ message: 'phoneNumber already exists' })
          }
        }
        

        【讨论】:

          【解决方案7】:

          如果您通过唯一索引进行搜索,那么使用 UserModel.count 实际上可能比 UserModel.findOne 更适合您,因为它返回整个文档(即进行读取)而不是只返回一个 int。

          【讨论】:

            【解决方案8】:

            有一个更简单的方法使用the mongoose exists function

            router.post("/groups/members", async (ctx) => {
                const group_name = ctx.request.body.group_membership.group_name;
                const member_name = ctx.request.body.group_membership.group_members;
                const GroupMembership = GroupModels.GroupsMembers;
                console.log("group_name : ", group_name, "member : ", member_name);
                try {
                    if (
                        (await GroupMembership.exists({
                            "group_membership.group_name": group_name,
                        })) === false
                    ) {
                        console.log("new function");
                        const newGroupMembership = await GroupMembership.insertMany({
                            group_membership: [
                                { group_name: group_name, group_members: [member_name] },
                            ],
                        });
                        await newGroupMembership.save();
                    } else {
                        const UpdateGroupMembership = await GroupMembership.updateOne(
                            { "group_membership.group_name": group_name },
                            { $push: { "group_membership.$.group_members": member_name } },
                        );
                        console.log("update function");
                        await UpdateGroupMembership.save();
                    }
                    ctx.response.status = 201;
                    ctx.response.message = "A member added to group successfully";
                } catch (error) {
                    ctx.body = {
                        message: "Some validations failed for Group Member Creation",
                        error: error.message,
                    };
                    console.log(error);
                    ctx.throw(400, error);
                }
            });
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-06-24
              • 1970-01-01
              • 1970-01-01
              • 2013-06-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-04-10
              相关资源
              最近更新 更多