@yarkivaev/source-to-sink 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/postgresSink.js +21 -5
- package/test/test_postgresSink.js +7 -0
package/package.json
CHANGED
package/src/postgresSink.js
CHANGED
|
@@ -5,7 +5,8 @@ import pg from 'pg';
|
|
|
5
5
|
*
|
|
6
6
|
* Creates a pg.Pool internally and implements the Sink
|
|
7
7
|
* interface for use with batch collectors. Supports optional
|
|
8
|
-
* conflict resolution via ON CONFLICT DO NOTHING
|
|
8
|
+
* conflict resolution via ON CONFLICT DO NOTHING or
|
|
9
|
+
* ON CONFLICT DO UPDATE SET.
|
|
9
10
|
*
|
|
10
11
|
* @example
|
|
11
12
|
* const sink = postgresSink('postgresql://localhost:5432/db', 'metrics', ['ts', 'value']);
|
|
@@ -20,9 +21,26 @@ import pg from 'pg';
|
|
|
20
21
|
* @param {string} table - Target table name
|
|
21
22
|
* @param {Array<string>} columns - Column names for insertion
|
|
22
23
|
* @param {object} [options] - Optional configuration
|
|
23
|
-
* @param {Array<string>} [options.conflict] - Columns for ON CONFLICT
|
|
24
|
+
* @param {Array<string>} [options.conflict] - Columns for ON CONFLICT clause
|
|
25
|
+
* @param {Array<string>} [options.update] - Columns for DO UPDATE SET clause
|
|
24
26
|
* @returns {object} Sink with write(records) method
|
|
25
27
|
*/
|
|
28
|
+
/**
|
|
29
|
+
* Builds the ON CONFLICT SQL suffix from options.
|
|
30
|
+
*
|
|
31
|
+
* @param {object} options - Sink options with conflict and update arrays
|
|
32
|
+
* @returns {string} SQL suffix or empty string
|
|
33
|
+
*/
|
|
34
|
+
function buildSuffix(options) {
|
|
35
|
+
if (!Array.isArray(options.conflict) || options.conflict.length === 0) return '';
|
|
36
|
+
const cols = options.conflict.join(', ');
|
|
37
|
+
if (Array.isArray(options.update) && options.update.length > 0) {
|
|
38
|
+
const sets = options.update.map(c => `${c} = EXCLUDED.${c}`).join(', ');
|
|
39
|
+
return ` ON CONFLICT (${cols}) DO UPDATE SET ${sets}`;
|
|
40
|
+
}
|
|
41
|
+
return ` ON CONFLICT (${cols}) DO NOTHING`;
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
export default function postgresSink(url, table, columns, options = {}) {
|
|
27
45
|
if (typeof url !== 'string' || url.length === 0) {
|
|
28
46
|
throw new Error('URL must be a non-empty string');
|
|
@@ -34,9 +52,7 @@ export default function postgresSink(url, table, columns, options = {}) {
|
|
|
34
52
|
throw new Error('Columns must be a non-empty array');
|
|
35
53
|
}
|
|
36
54
|
const pool = new pg.Pool({ connectionString: url });
|
|
37
|
-
const suffix =
|
|
38
|
-
? ` ON CONFLICT (${options.conflict.join(', ')}) DO NOTHING`
|
|
39
|
-
: '';
|
|
55
|
+
const suffix = buildSuffix(options);
|
|
40
56
|
return {
|
|
41
57
|
/**
|
|
42
58
|
* Writes records to PostgreSQL table.
|
|
@@ -73,4 +73,11 @@ describe('postgresSink', () => {
|
|
|
73
73
|
['ts', 'value'], {});
|
|
74
74
|
assert.strictEqual(typeof sink.write, 'function', 'Should have write method without conflict key');
|
|
75
75
|
});
|
|
76
|
+
|
|
77
|
+
it('returns sink with write method when conflict and update options are provided', () => {
|
|
78
|
+
const sink = postgresSink('postgresql://localhost:5432/db', 'segments',
|
|
79
|
+
['machine', 'name', 'start_time', 'end_time', 'duration'],
|
|
80
|
+
{ conflict: ['machine', 'start_time'], update: ['name', 'end_time', 'duration'] });
|
|
81
|
+
assert.strictEqual(typeof sink.write, 'function', 'Should have write method with update option');
|
|
82
|
+
});
|
|
76
83
|
});
|