web-dev-qa-db-ja.com

更新中にそのデータがデータベースに既に存在するかどうかを確認する方法(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);
    });

  });
});
24
Miguel Lorenzo

通常は mongoose validation を使用できますが、非同期の結果(既存の名前に対するdbクエリ)が必要であり、バリデーターはpromiseをサポートしていないため(私が知る限り)、独自の関数を作成する必要がありますコールバックを渡します。以下に例を示します。

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

使用される@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');
8
John Linhart

少ないコードでこれを実現する別の方法を次に示します。

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

既に投稿された例に加えて、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

2
Nik Denisov

一意のインデックスで検索する場合、UserModel.countを使用する方が、intだけを返すのではなく、ドキュメント全体を返す(読み取りを行う)ため、実際にはUserModel.findOneよりも優れている場合があります。

1
Dale Annin

この古いソリューションに陥る人のために。より良い方法があります mongooseドキュメントから

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