使用 pg-promise 跳过更新列

skip update columns with pg-promise

我在使用 Postgres 的 pg-promise 的节点上安装了 API,这很好用,但我正在考虑如何修改 PUT 语句以更好地处理输入中的 NULLS。

PUT语句的代码如下:

    //UPDATE a single record
function updateRecord(req, res, next) {
    db.none('update generic1 SET string1=,' +
                               'string2=,' +
                               'string3=,' +
                               'string4=,' +
                               'string5=,' +
                               'string6=,' +
                               'integer1=,' +
                               'integer2=,' +
                               'integer3=,' +
                               'date1=,' +
                               'date2=,' +
                               'date3=,' +
                               'currency1=,' +
                               'currency2=' +
            'WHERE id = ',
            [req.body.string1,
             req.body.string2,
             req.body.string3,
             req.body.string4,
             req.body.string5,
             req.body.string6,
             parseInt(req.body.integer1),
             parseInt(req.body.integer2),
             parseInt(req.body.integer3),
             req.body.date1,
             req.body.date2,
             req.body.date3,
             parseInt(req.body.currency1),
             parseInt(req.body.currency2),
             parseInt(req.params.id)])
        .then(function(){
            res.status(200)
                .json({
                    'status': 'success',
                    'message': 'updated one record'
                });
        })
        .catch(function(err){
            return next(err);
        });
}

现在这条语句有效,但如果我将 NULLS 传递给下一次更新,它也会删除现有值。例如,如果我只想更新 string1 和 date2,我必须发送整个 json 对象,否则所有其他值都将设置为 NULL。

有没有更好的方法来处理这个问题?我应该改用 PATCH 动词吗??

我是 pg-promise 的作者 ;)

var pgp = require('pg-promise')({
    capSQL: true // capitalize all generated SQL
});

// generic way to skip NULL/undefined values for strings:
function str(col) {
    return {
        name: col,
        skip: function () {
            var val = this[col];
            return val === null || val === undefined;
        }
    };
}

// generic way to skip NULL/undefined values for integers,
// while parsing the type correctly:
function int(col) {
    return {
        name: col,
        skip: function () {
            var val = this[col];
            return val === null || val === undefined;
        },
        init: function () {
            return parseInt(this[col]);
        }
    };
}

// Creating a reusable ColumnSet for all updates:
var csGeneric = new pgp.helpers.ColumnSet([
    str('string1'), str('string2'), str('string3'), str('string4'), str('string5'),
    str('string6'), int('integer1'), int('integer2'), int('integer3'),
    str('date1'), str('date2'), str('date3')
], {table: 'generic1'});

// Your new request handler:
function updateRecord(req, res, next) {

    var update = pgp.helpers.update(req.body, csGeneric) + ' WHERE id = ' +
        parseInt(req.params.id);

    db.none(update)
        .then(function () {
            res.status(200)
                .json({
                    'status': 'success',
                    'message': 'updated one record'
                });
        })
        .catch(function (err) {
            return next(err);
        });
}

查看 helpers 命名空间 ;)


或者,您可以对每一列进行自己的验证,然后相应地生成一个 UPDATE 查询,尽管它不会那么优雅 ;)

更新

请注意 initskip 的参数化方式在库的 5.4.0 版本中发生了变化,请参阅 the release notes

从5.4.0版本开始,您可以将代码简化为:

// generic way to skip NULL/undefined values for strings:
function str(column) {
    return {
        name: column,
        skip: c => c.value === null || c.value === undefined
    };
}

// generic way to skip NULL/undefined values for integers,
// while parsing the type correctly:
function int(column) {
    return {
        name: column,
        skip: c => c.value === null || c.value === undefined,
        init: c => +c.value
    };
}

如果你想跳过根本没有传入的属性,因此甚至不存在于对象中,那么代替这个:

skip: c => c.value === null || c.value === undefined

你可以这样做:

skip: c => !c.exists

更新

库的版本 5.6.7 对此进行了进一步改进 - 选项 emptyUpdate,指定时表示方法返回的值,而不是抛出 Cannot generate an UPDATE without any columns。有关详细信息,请参阅 helpers.update

另请参阅:ColumnConfig

谢谢@vitaly-t!更快更干净,总是好的结果:)

作为参考,我还包含了使用上述帮助器的插入语句。

    //firstly create a function that skips the nulls for strings
function str(column) {
    return {
        name: column,
        skip: c => c.value === null || c.value === undefined || !c.exists
    };
}

//now a function that skips nulls for integers, while parsing type
function int(column) {
    return {
        name: column,
        skip: c => c.value === null || c.value === undefined || !c.exists,
        init: c => +c.value
    };
}

//creating a column set for all updates
var usefulColumSet = new pgp.helpers.ColumnSet([
    str('string1'), str('string2'), str('string3'), str('string4'), str('string5'),
    str('string6'), int('integer1'), int('integer2'), int('integer3'),
    str('date1'), str('date2'), str('date3'), int('currency1'), int('currency2')
], {table: 'generic1'});

//*********************CREATE a single record*************************
function createRecord(req, res, next) {
    var insert = pgp.helpers.insert(req.body, usefulColumSet);

    db.none(insert)
        .then(function(){
            res.status(200)
                .json({
                    status: 'success',
                    message: 'Inserted one record successully'
                });
        })
        .catch(function(err){
            return next(err);
        });
}


//************************UPDATE a single record*************
function updateRecord(req, res, next) {
    var update = pgp.helpers.update(req.body, usefulColumSet) + ' WHERE id = ' + parseInt(req.params.id);

    db.none(update)
        .then(function() {
            res.status(200)
                .json({
                    status: 200,
                    message: 'updated a single record cleanly'
                });
        })
        .catch(function(err) {
            return next(err);
        });
}

备选方案:

function updateFoo(req, res){ 
    let {name, email, password} = req.body; 
    // Ex: req.body = { name: 'foo', password: 'bar' }
    let data = { name, email, password }; // {name: 'foo', email:undefined, password:'bar}
    //Remove ONLY undefined keys from data
    Object.keys(data).forEach( key => { if(data[key] === undefined) delete data[key] });

    let query = 'UPDATE foo SET';

    let i = 1;
    Object.keys(data).forEach( key => { query += ` ${key}=$${index},`; i++; })
    query = query.slice(0, -1) // Remove exceeding comma
    query += ` WHERE id=$${i}`;

    let values = Object.values(data); // ['foo', 'bar']
    values.push(req.params.id);

    // .....
    // query = 'UPDATE foo SET  name=, password= WHERE id='
    // values = ['foo', 'bar', req.params.id]