如何在更新期间检查该数据是不是已存在于数据库中(Mongoose And Express)

Posted

技术标签:

【中文标题】如何在更新期间检查该数据是不是已存在于数据库中(Mongoose And Express)【英文标题】: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);
    );

  );
);

【问题讨论】:

【参考方案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);
    
);

【讨论】:

以上是关于如何在更新期间检查该数据是不是已存在于数据库中(Mongoose And Express)的主要内容,如果未能解决你的问题,请参考以下文章

如何检查某个数据是不是已存在于firestore中

如何验证输入电子邮件是不是已存在于数据库中?

如何检查数据库中是不是已存在该值?

如何检查对象是不是已存在于列表中

如果 URL 已存在于数据库中,如何使用 PHP 检查?

Sql Server 2008如何检查表中是不是存在列? [复制]