node.js 如何在更新期间检查该数据是否已存在于数据库中(Mongoose 和 Express)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16882938/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to check if that data already exist in the database during update (Mongoose And Express)
提问by Miguel Lorenzo
How to do validations before saving the edited data in mongoose?
在 mongoose 中保存编辑过的数据之前如何进行验证?
For example, if sample.namealready exists in the database, the user will receive a some sort of error, something like that, here's my code below
例如,如果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);
});
});
});
回答by mr.freeze
Typically you could use mongoose validationbut since you need an async result (db query for existing names) and validators don't support promises (from what I can tell), you will need to create your own function and pass a callback. Here is an example:
通常您可以使用猫鼬验证,但由于您需要异步结果(现有名称的数据库查询)并且验证器不支持承诺(据我所知),您将需要创建自己的函数并传递回调。下面是一个例子:
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);
}
});
}
});
UPDATE: A better way
更新:更好的方法
The pre hook seems to be a more natural place to stop the save:
pre hook 似乎是一个更自然的停止保存的地方:
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!"));
}
});
}) ;
UPDATE 2: Async custom validators
更新 2:异步自定义验证器
It looks like mongoose supports async custom validators now so that would probably be the natural solution:
看起来 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!'
}
}
});
回答by John Linhart
Another way to continue with the example @nfreeze used is this validation method:
继续使用@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');
回答by WebSam101
For anybody falling on this old solution. There is a better way from the mongoose docs.
对于任何陷入这个旧解决方案的人。mongoose docs有一个更好的方法 。
var s = new Schema({ name: { type: String, unique: true }});
s.path('name').index({ unique: true });
回答by DBrown
Here is another way to accomplish this in less code.
这是用更少的代码完成此任务的另一种方法。
UPDATE 3: Asynchronous model class statics
更新 3:异步模型类静态
Similar to option 2, this allows you to create a function directly linked to the schema, but called from the same file using the model.
与选项 2 类似,这允许您创建一个直接链接到架构的函数,但使用模型从同一文件调用。
model.js
模型.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);
}
}
});
}
Call from file
从文件调用
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);
}
});
回答by Nik Denisov
In addition to already posted examples, here is another approach using express-async-wrapand asynchronous functions (ES2017).
除了已经发布的示例之外,这里还有另一种使用express-async-wrap和异步函数(ES2017)的方法。
Router
路由器
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,
}
Resources
资源
回答by Dale Annin
If you're searching by an unique index, then using UserModel.count may actually be better for you than UserModel.findOne due to it returning the whole document (ie doing a read) instead of returning just an int.
如果您按唯一索引进行搜索,那么使用 UserModel.count 实际上可能比 UserModel.findOne 更适合您,因为它返回整个文档(即读取)而不是仅返回一个 int。
回答by Lucas Breitembach
check with one query if email or phoneNumber already exists in DB
如果电子邮件或电话号码已存在于数据库中,请检查一个查询
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' })
}
}

