使用 node-postgres 更新数据的更简单方法?
Posted
技术标签:
【中文标题】使用 node-postgres 更新数据的更简单方法?【英文标题】:Easier way to update data with node-postgres? 【发布时间】:2014-03-12 15:54:13 【问题描述】:我正在使用极好的插件 node-postgres,https://github.com/brianc/node-postgres
我有这个更新休息电话。我的表中有大约 30 列。有没有更简单的方法来更新这些?
/*
Post /api/project/products/:pr_id HTTP/1.1
*/
exports.updateProduct = function(req, res)
pg.connect(cs, function(err, client, done)
var query = "UPDATE products SET pr_title = ($1), pr_usercode = ($2) WHERE pr_id=($3)";
client.query(query, [req.body.pr_title, req.body.pr_usercode, req.params.pr_id], function(err, result)
if (handleErr(err, done)) return;
done();
sendResponse(res, result.rows[0]);
)
);
;
我这里只有三列。当我写完所有 30 列时,它会变得凌乱且难以维护。必须是一种只用简单的一行更新 req.body 中所有列的方式吗?
有什么想法吗?
【问题讨论】:
【参考方案1】:你总是可以推出这样的功能:
function updateProductByID (id, cols)
// Setup static beginning of query
var query = ['UPDATE products'];
query.push('SET');
// Create another array storing each set command
// and assigning a number value for parameterized query
var set = [];
Object.keys(cols).forEach(function (key, i)
set.push(key + ' = ($' + (i + 1) + ')');
);
query.push(set.join(', '));
// Add the WHERE statement to look up by id
query.push('WHERE pr_id = ' + id );
// Return a complete query string
return query.join(' ');
然后这样使用它:
/*
Post /api/project/products/:pr_id HTTP/1.1
*/
exports.updateProduct = function(req, res)
pg.connect(cs, function(err, client, done)
// Setup the query
var query = updateProductByID(req.params.pr_id, req.body);
// Turn req.body into an array of values
var colValues = Object.keys(req.body).map(function (key)
return req.body[key];
);
client.query(query, colValues, function(err, result)
if (handleErr(err, done)) return;
done();
sendResponse(res, result.rows[0]);
);
);
;
或者,如果你需要一个 ORM,因为你会像上面那样做很多事情,你应该检查像 Knex.js 这样的模块
【讨论】:
请记住updateProductByID
中的id
应该是参数化值。不使用i + 1
,而是使用i + 2
,然后将产品ID值unshift为colValues
,以确保不会通过插入恶意ID进行SQL注入。
过滤器函数返回布尔表达式,而不是值。您应该将“过滤器”替换为“地图”,这是返回值的正确函数
@NirO。好消息——这是用伪代码编写的,并没有实际执行。会更新。
updateProductById
函数只是构建一个字符串。函数名有误导性
@anolan23 这不是生产代码——命名很难。【参考方案2】:
已经给出了很好的答案,但是恕我直言,在一方面还不够好,它们都缺乏良好的抽象性。我将尝试使用node-postgres
提供更抽象的方式来更新postgres
中的数据。
遵循官方文档总是好的做法,以下代码结构取自node-postgres,您可以随意扩展它:
这是我的,这是您与数据库交互的地方
const Pool = require("pg");
const connection = require("./connection.json");
const pool = new Pool(connection);
const insert, select, remove, update = require("./helpers");
/**
* The main mechanism to avoid SQL Injection is by escaping the input parameters.
* Any good SQL library should have a way to achieve this.
* PG library allows you to do this by placeholders `($1, $2)`
*/
module.exports =
query: (text, params, callback) =>
const start = Date.now();
return pool.query(text, params, (err, res) =>
const duration = Date.now() - start;
console.log("executed query", text, duration, rows: res.rowCount );
callback(err, res);
);
,
getClient: callback =>
pool.connect((err, client, done) =>
const query = client.query;
// monkey patch the query method to keep track of the last query executed
client.query = (...args) =>
client.lastQuery = args;
return query.apply(client, args);
;
// set a timeout of 5 seconds, after which we will log this client's last query
const timeout = setTimeout(() =>
console.error("A client has been checked out for more than 5 seconds!");
console.error(
`The last executed query on this client was: $client.lastQuery`
);
, 5000);
const release = err =>
// call the actual 'done' method, returning this client to the pool
done(err);
// clear our timeout
clearTimeout(timeout);
// set the query method back to its old un-monkey-patched version
client.query = query;
;
callback(err, client, release);
);
,
/**
* Updates data
*
* entity: table name, e.g, users
* conditions: id: "some-unique-user-id", ...
* fields: list of desired columns to update username: "Joe", ...
*/
updateOne: async (entity, conditions, fields) =>
if (!entity) throw new Error("no entity table specified");
if (Utils.isObjEmpty(conditions))
throw new Error("no conditions specified");
let resp;
const text, values = update(entity, conditions, fields);
try
rs = await pool.query(text, values);
resp = rs.rows[0];
catch (err)
console.error(err);
throw err;
return resp;
,
createOne: async (entity, data) =>
,
deleteOne: async (entity, conditions, data) =>
,
findAll: async (entity, conditions, fields) =>
,
// ... other methods
;
这里是 CRUD 操作的辅助方法,它们会准备查询 带有准备值的文本:
/**
* tableName: `users`
* conditions: id: 'joe-unique-id', ...
* data: username: 'Joe', age: 28, status: 'active', ...
*
* "UPDATE users SET field_1 = $1, field_2 = $2, field_3 = $3, ... ( WHERE ...) RETURNING *";
*/
exports.update = (tableName, conditions = , data = ) =>
const dKeys = Object.keys(data);
const dataTuples = dKeys.map((k, index) => `$k = $$index + 1`);
const updates = dataTuples.join(", ");
const len = Object.keys(data).length;
let text = `UPDATE $tableName SET $updates `;
if (!Utils.isObjEmpty(conditions))
const keys = Object.keys(conditions);
const condTuples = keys.map((k, index) => `$k = $$index + 1 + len `);
const condPlaceholders = condTuples.join(" AND ");
text += ` WHERE $condPlaceholders RETURNING *`;
const values = [];
Object.keys(data).forEach(key =>
values.push(data[key]);
);
Object.keys(conditions).forEach(key =>
values.push(conditions[key]);
);
return text, values ;
;
exports.select = (tableName, conditions = , data = ["*"]) => ...
exports.insert = (tableName, conditions = ) => ...
exports.remove = (tableName, conditions = , data = []) => ...
最后,您可以在路由处理程序中使用它而不会造成混乱 你的代码库:
const db = require("../db");
/**
*
*/
exports.updateUser = async (req, res) =>
try
console.log("[PUT] api/v1/users");
const fields =
name: req.body.name,
description: req.body.description,
info: req.body.info
;
const userId = req.params.id;
const conditions = id: userId ;
const updatedUser = await db.updateOne("users", conditions, fields);
if (updatedUser)
console.log(`team $updatedUser.name updated successfully`);
return res.json(updatedUser);
res.status(404).json( msg: "Bad request" );
catch (err)
console.error(err);
res.status(500).send( msg: "Server error" );
;
方便的实用程序:
const Utils = ;
Utils.isObject = x => x !== null && typeof x === "object";
Utils.isObjEmpty = obj => Utils.isObject(obj) && Object.keys(obj).length === 0;
【讨论】:
很好的答案。你能解释一下$k = $$index + 1
是如何不受sql 注入影响的吗?如果发送的请求带有一个带有恶意 sql 键的对象怎么办?
@Chano,它发生在抽象层并返回 text
和 values
作为辅助方法的结果,但最终它们最终成为 pool.query(text, values)
的参数,此方法 @ 987654332@ 由pg
提供,负责所有清理和其他可能的恶意内容【参考方案3】:
我喜欢使用knexjs,它适用于 postgre。它也是一种有趣的 javascript 编写查询的方式(没有那些讨厌的 SQL 字符串操作)。
以这个方法为例,它存储了一些联系信息。该联系信息的 JSON 模式在其他地方定义(在我验证时也很有用)。结果是代码生成的查询,其中仅包含传入的列。
function saveContactInfo( inputs, callback )
var setObj = ;
for( var property in inputs.contact )
//assumes properties are same as DB columns, otherwise need to use some string-mapping lookup.
setObj[ property ] = inputs.contact[property];
setObj[ "LastModified" ] = new Date();
var query = knex( "tblContact" ).update( setObj ).where( "contactId", inputs.contact.contactId );
//log.debug("contactDao.saveContactInfo: " + query.toString());
query.exec( function(err, results )
if(err) return callback(err);
//Return from DB is usually an array, so return the object, not the array.
callback( null, results[0] );
);
Knexjs 也有一些漂亮的 postgre-only 选项(如果我没有使用 mysql,这对我很有用)
【讨论】:
答案也很好。我批准了另一个,因为他的解决方案没有插件。但我一定会尝试 knexjs。好像很方便Thx!【参考方案4】:我的简单例子:
async update(objectToSave)
const dbID = objectToSave.id;
const args = Object.values(objectToSave);
const keys = Object.keys(objectToSave).join(',');
const argKeys = Object.keys(objectToSave).map((obj,index) =>
return "$"+(index+1)
).join(',');
const query = "UPDATE table SET ("+keys+") = ("+argKeys+") WHERE id = "+dbID;
try
const res = await client.query(query, args)
return true;
catch (err)
console.log(err.stack)
return false;
【讨论】:
【参考方案5】:创建插入查询
exports.createInsertQuery = (tablename, obj) =>
let insert = 'insert into ' + tablename;
let keys = Object.keys(obj);
let dollar = keys.map(function (item, idx) return '$' + (idx + 1); );
let values = Object.keys(obj).map(function (k) return obj[k]; );
return
query: insert + '(' + keys + ')' + ' values(' + dollar + ')',
params: values
用法
let data = firstname : 'hie' , lastname : 'jack', age : 4
let yo = createInsertQuery('user',data)
client.query(yo.query, yo.params ,(err,res) =>
console.log(res)
)
同样,您可以创建 update 、 delete 查询
【讨论】:
以上是关于使用 node-postgres 更新数据的更简单方法?的主要内容,如果未能解决你的问题,请参考以下文章
将 Async/Await 与 node-postgres 一起使用
为啥我不能使用 node-postgres 从数据库中删除?