Skip to content

Commit 859d818

Browse files
nodejhfengmk2
authored andcommitted
feat: update multiple rows (#55)
1 parent db6d596 commit 859d818

File tree

3 files changed

+412
-5
lines changed

3 files changed

+412
-5
lines changed

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,74 @@ console.log(result);
158158
changedRows: 1 }
159159
```
160160

161+
### Update multiple rows
162+
163+
- Update multiple rows with primary key: `id`
164+
165+
```js
166+
let options = [{
167+
id: 123,
168+
name: 'fengmk2',
169+
email: 'm@fengmk2.com',
170+
otherField: 'other field value',
171+
modifiedAt: db.literals.now, // `now()` on db server
172+
}, {
173+
id: 124,
174+
name: 'fengmk2_2',
175+
email: 'm@fengmk2_2.com',
176+
otherField: 'other field value 2',
177+
modifiedAt: db.literals.now, // `now()` on db server
178+
}]
179+
let result = yield db.updateRows('table-name', options);
180+
console.log(result);
181+
{ fieldCount: 0,
182+
affectedRows: 2,
183+
insertId: 0,
184+
serverStatus: 2,
185+
warningCount: 0,
186+
message: '(Rows matched: 2 Changed: 2 Warnings: 0',
187+
protocol41: true,
188+
changedRows: 2 }
189+
```
190+
191+
- Update multiple rows with `row` and `where` properties
192+
193+
194+
```js
195+
let options = [{
196+
row: {
197+
email: 'm@fengmk2.com',
198+
otherField: 'other field value',
199+
modifiedAt: db.literals.now, // `now()` on db server
200+
},
201+
where: {
202+
id: 123,
203+
name: 'fengmk2',
204+
}
205+
}, {
206+
row: {
207+
email: 'm@fengmk2_2.com',
208+
otherField: 'other field value2',
209+
modifiedAt: db.literals.now, // `now()` on db server
210+
},
211+
where: {
212+
id: 124,
213+
name: 'fengmk2_2',
214+
}
215+
}]
216+
let result = yield db.updateRows('table-name', options);
217+
console.log(result);
218+
{ fieldCount: 0,
219+
affectedRows: 2,
220+
insertId: 0,
221+
serverStatus: 2,
222+
warningCount: 0,
223+
message: '(Rows matched: 2 Changed: 2 Warnings: 0',
224+
protocol41: true,
225+
changedRows: 2 }
226+
```
227+
228+
161229
### Get
162230

163231
- Get a row
@@ -317,6 +385,7 @@ TBD
317385
- *get(table, where, options)
318386
- *insert(table, row[s], options)
319387
- *update(table, row, options)
388+
- *updateRows(table, options)
320389
- *delete(table, where)
321390
- *count(table, where)
322391

lib/operator.js

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ proto.insert = function* (table, rows, options) {
142142
};
143143

144144
proto.update = function* (table, row, options) {
145-
// TODO: support multi rows
146145
options = options || {};
147146
if (!options.columns) {
148147
options.columns = Object.keys(row);
@@ -174,6 +173,134 @@ proto.update = function* (table, row, options) {
174173
return yield this.query(sql);
175174
};
176175

176+
/**
177+
*
178+
* Update multiple rows from a table
179+
*
180+
* UPDATE `table_name` SET
181+
* `column1` CASE
182+
* WHEN condition1 THEN 'value11'
183+
* WHEN condition2 THEN 'value12'
184+
* WHEN condition3 THEN 'value13'
185+
* ELSE `column1` END,
186+
* `column2` CASE
187+
* WHEN condition1 THEN 'value21'
188+
* WHEN condition2 THEN 'value22'
189+
* WHEN condition3 THEN 'value23'
190+
* ELSE `column2` END
191+
* WHERE condition
192+
*
193+
* See MySQL Case Syntax: https://dev.mysql.com/doc/refman/5.7/en/case.html
194+
*
195+
* @param {String} table table name
196+
* @param {Array<Object>} options Object Arrays
197+
* each Object needs a primary key `id`, or each Object has `row` and `where` properties
198+
* e.g.
199+
* [{ id: 1, name: 'fengmk21' }]
200+
* or [{ row: { name: 'fengmk21' }, where: { id: 1 } }]
201+
* @return {object} update result
202+
*/
203+
proto.updateRows = function* (table, options) {
204+
if (!Array.isArray(options)) {
205+
throw new Error('Options should be array');
206+
}
207+
208+
/**
209+
* {
210+
* column: {
211+
* when: [ 'WHEN condition1 THEN ?', 'WHEN condition12 THEN ?' ],
212+
* then: [ value1, value1 ]
213+
* }
214+
* }
215+
*/
216+
const SQL_CASE = {};
217+
// e.g. { id: [], column: [] }
218+
const WHERE = {};
219+
220+
options.forEach(option => {
221+
222+
if (!option.hasOwnProperty('id') && !(option.row && option.where)) {
223+
throw new Error('Can not auto detect updateRows condition, please set option.row and option.where, or make sure option.id exists');
224+
}
225+
226+
// convert { id, column } to { row: { column }, where: { id } }
227+
if (option.hasOwnProperty('id')) {
228+
const where = { id: option.id };
229+
const row = Object.keys(option).reduce((result, key) => {
230+
if (key !== 'id') {
231+
result[key] = option[key];
232+
}
233+
return result;
234+
}, {});
235+
option = { row, where };
236+
}
237+
238+
let where = this._where(option.where);
239+
where = where.indexOf('WHERE') === -1 ? where : where.substring(where.indexOf('WHERE') + 5);
240+
for (const key in option.row) {
241+
if (!SQL_CASE[key]) {
242+
SQL_CASE[key] = { when: [], then: [] };
243+
}
244+
SQL_CASE[key].when.push(' WHEN ' + where + ' THEN ? ');
245+
SQL_CASE[key].then.push(option.row[key]);
246+
}
247+
248+
for (const key in option.where) {
249+
if (!WHERE[key]) {
250+
WHERE[key] = [];
251+
}
252+
if (WHERE[key].indexOf(option.where[key]) === -1) {
253+
WHERE[key].push(option.where[key]);
254+
}
255+
}
256+
});
257+
258+
let SQL = [ 'UPDATE ?? SET ' ];
259+
let VALUES = [ table ];
260+
261+
const TEMPLATE = [];
262+
for (const key in SQL_CASE) {
263+
let templateSql = ' ?? = CASE ';
264+
VALUES.push(key);
265+
templateSql += SQL_CASE[key].when.join(' ');
266+
VALUES = VALUES.concat(SQL_CASE[key].then);
267+
templateSql += ' ELSE ?? END ';
268+
TEMPLATE.push(templateSql);
269+
VALUES.push(key);
270+
}
271+
272+
SQL += TEMPLATE.join(' , ');
273+
SQL += this._where(WHERE);
274+
275+
/**
276+
* e.g.
277+
*
278+
* updateRows(table, [
279+
* {id: 1, name: 'fengmk21', email: 'm@fengmk21.com'},
280+
* {id: 2, name: 'fengmk22', email: 'm@fengmk22.com'},
281+
* {id: 3, name: 'fengmk23', email: 'm@fengmk23.com'},
282+
* ])
283+
*
284+
* UPDATE `ali-sdk-test-user` SET
285+
* `name` =
286+
* CASE
287+
* WHEN `id` = 1 THEN 'fengmk21'
288+
* WHEN `id` = 2 THEN 'fengmk22'
289+
* WHEN `id` = 3 THEN 'fengmk23'
290+
* ELSE `name` END,
291+
* `email` =
292+
* CASE
293+
* WHEN `id` = 1 THEN 'm@fengmk21.com'
294+
* WHEN `id` = 2 THEN 'm@fengmk22.com'
295+
* WHEN `id` = 3 THEN 'm@fengmk23.com'
296+
* ELSE `email` END
297+
* WHERE `id` IN (1, 2, 3)
298+
*/
299+
const sql = this.format(SQL, VALUES);
300+
debug('updateRows(%j, %j) \n=> %j', table, options, sql);
301+
return yield this.query(sql);
302+
};
303+
177304
proto.delete = function* (table, where) {
178305
const sql = this.format('DELETE FROM ??', [ table ]) +
179306
this._where(where);

0 commit comments

Comments
 (0)