【问题标题】:skip update columns with pg-promise使用 pg-promise 跳过更新列
【发布时间】:2017-04-03 11:59:51
【问题描述】:

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

以下是PUT语句的代码:

    //UPDATE a single record
function updateRecord(req, res, next) {
    db.none('update generic1 SET string1=$1,' +
                               'string2=$2,' +
                               'string3=$3,' +
                               'string4=$4,' +
                               'string5=$5,' +
                               'string6=$6,' +
                               'integer1=$7,' +
                               'integer2=$8,' +
                               'integer3=$9,' +
                               'date1=$10,' +
                               'date2=$11,' +
                               'date3=$12,' +
                               'currency1=$13,' +
                               'currency2=$14' +
            'WHERE id = $15',
            [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 动词吗?

【问题讨论】:

    标签: node.js pg-promise


    【解决方案1】:

    我是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

    【讨论】:

    • @adam.sellers 查看最新版本的更新;)
    • @adam.sellers 添加了另一个更新 - 选项 emptyUpdate ;)
    【解决方案2】:

    替代解决方案:

    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=$1, password=$2 WHERE id=$3'
        // values = ['foo', 'bar', req.params.id]
    

    【讨论】:

      【解决方案3】:

      谢谢@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);
              });
      }
      

      【讨论】:

      • 由于您没有提供属性def - 当属性不存在时的默认值,在这些情况下您将得到c.value 等于undefined,因此请检查!c.exists不需要;)
      猜你喜欢
      • 2017-11-29
      • 2021-07-03
      • 2021-09-27
      • 2020-06-12
      • 2018-04-15
      • 1970-01-01
      • 2019-01-18
      • 2020-02-12
      • 2021-06-16
      相关资源
      最近更新 更多