Sails.js中的数据库调用beforeCreate()模型钩子导致验证错误

时间:2017-09-21 12:34:51

标签: mongodb sails.js waterline sails-mongo

我有一个名为“products”的Sails.js模型,它是一个MongoDB集合。在模型中,我有一个beforeCreate()钩子,它以'YYMMDD-count`格式生成一个独特的productId,如'170921-00001',其中count是当天创建的记录号。这就是我的模型:

module.exports = {

    attributes: {
        name: { type: 'string', required: true },
        productId: { type: 'string', unique: true }
    },


    beforeCreate: function(values, next) {

        var moment = require('moment');

        // Generate the productId
        Products.count({
            createdAt: {
                '>=': moment().format('YYYY-MM-DD'),
                '<': moment(moment().format('YYYY-MM-DD')).add(1, 'days').format('YYYY-MM-DD')
            }
        }).exec(function(error, productCount) {

            if (error) {
                throw error;
            }

            var count = '',
                totalPlaces = 5 - productCount.toString().length;
            while (totalPlaces > 0) {
                count = count + '0';
                totalPlaces--;
            }

            values.productId = moment().format('YYMMDD') + '-' + (count + productCount);

            next();
        });
    }
};

问题是,当我尝试在单个数据库调用中将多个产品插入集合时,这个中断。我收到以下错误:

debug: Error (E_VALIDATION) :: 1 attribute is invalid
MongoError: E11000 duplicate key error index: my-app.products.$productId_1 dup key: { : "170921-00002" }
    at Function.MongoError.create (/Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb-core/lib/error.js:31:11)
    at toError (/Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb/lib/utils.js:114:22)
    at /Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb/lib/collection.js:658:23
    at handleCallback (/Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb/lib/utils.js:95:56)
    at resultHandler (/Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb/lib/bulk/ordered.js:421:14)
    at /Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb-core/lib/connection/pool.js:455:18
    at /Users/Nag/Code/my-app/web-service/node_modules/async-listener/glue.js:188:31
    at _combinedTickCallback (internal/process/next_tick.js:73:7)
    at process._tickDomainCallback (internal/process/next_tick.js:128:9)
    at process.fallback (/Users/Nag/Code/my-app/web-service/node_modules/async-listener/index.js:529:15)

Invalid attributes sent to undefined:
 • productId
   • A record with that `productId` already exists (`170921-00002`).

当我插入单个记录时,它工作正常,但是当我插入多个记录时,第一个记录被插入,但每个后续记录都会产生该错误。是因为在钩子计算productId之前,即使它已经完成插入前一条记录,文档也会被插入?我该如何解决这个问题?

3 个答案:

答案 0 :(得分:0)

使用Waterline解决这个问题非常困难。如果productId的这个约定还没有嵌入到你的代码中,那么最明智的做法可能是在它构建太多之前进行更改。

我在我的代码中遇到了类似的问题,但是使用的模型我从来没有在一次调用中创建多个 - 为了防止巧合的“碰撞”我只使用了try catch在创建并重新运行catch块中的创建之前。

但这是另一种解决方案......也许可以在Product.js文件中存储实时日数:

var day = new Date();
var daycount = 0;

module.exports = {

    attributes: {
        name: { type: 'string', required: true },
        productId: { type: 'string', unique: true }
    },
    beforeCreate: function(values, next) {
        var moment = require('moment');
        // Generate the productId
        Products.count({
            createdAt: {
                '>=': moment().format('YYYY-MM-DD'),
                '<': moment(moment().format('YYYY-MM-DD')).add(1, 'days').format('YYYY-MM-DD')
            }
        }).exec(function(error, productCount) {
            if (error) {
                throw error;
            }

            // stop here to check your day and count variables...
            var today = new Date();
            if (productCount === 0) { // none created yet today
                day = today;
                daycount = 0;
            } else if (daycount === 0) { // must have started the app with records in db
                daycount = productCount;
            }
            if (day < today && day.getDate() < today.getDate()) { // new day
                day = today;
                daycount = 0;
            }
            productCount = Math.max(productCount, daycount++); // notice the increment
            // now proceed as before...

            var count = '',
                totalPlaces = 5 - productCount.toString().length;
            while (totalPlaces > 0) {
                count = count + '0';
                totalPlaces--;
            }
            values.productId = moment().format('YYMMDD') + '-' + (count + productCount);
            next();
        });
    }
};

即使许多产品创建调用在添加到数据库之前都已运行beforeCreate(因此获得相同的productCount),daycountday变量应该在记忆中保存,并逐一更新。

即使这样有效,但感觉不稳定 - 我会认真考虑重组你的数据,而不是采用这种方法!

答案 1 :(得分:0)

当您向api发送数据时,您是否在确定产品名称?

name: { type: 'string', required: true } <= Here, the name is required from client to api.

答案 2 :(得分:0)

您应该在更新方法之前创建唯一值:

  

values.productId = moment()。format('YYMMDD')+' - '+(count + productCount);

您的代码的这一行可能是:

  

values.productId = moment()。format('YYMMDD')+' - '+(count + productCount)+ Math.random();

它始终生成唯一ID。