@toxplanet/pegasus-sdk 1.1.20 → 1.1.21
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/lib/chemicals.js +274 -224
- package/lib/connection.js +12 -35
- package/lib/db/index.js +20 -12
- package/package.json +1 -2
- package/lib/db/schema.js +0 -27
package/lib/chemicals.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
const { logError, logInfo } = require('@toxplanet/tphelper/logging');
|
|
2
|
-
const { getDrizzle, schema } = require('./db');
|
|
3
|
-
const { eq, sql, and, inArray, arrayContains } = require('drizzle-orm');
|
|
4
2
|
const { SQSClient, SendMessageCommand } = require('@aws-sdk/client-sqs');
|
|
5
3
|
|
|
6
4
|
const SEARCH_BOOST_EXACT_PRIMARY = 100;
|
|
@@ -14,16 +12,66 @@ function escapeLikePattern(value) {
|
|
|
14
12
|
return value.replace(/[%_\\]/g, '\\$&');
|
|
15
13
|
}
|
|
16
14
|
|
|
15
|
+
function parsePostgresArray(str) {
|
|
16
|
+
if (!str || str === '{}') return [];
|
|
17
|
+
const trimmed = str.slice(1, -1);
|
|
18
|
+
if (!trimmed) return [];
|
|
19
|
+
const result = [];
|
|
20
|
+
let current = '';
|
|
21
|
+
let inQuotes = false;
|
|
22
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
23
|
+
const char = trimmed[i];
|
|
24
|
+
if (char === '"') {
|
|
25
|
+
if (inQuotes && trimmed[i + 1] === '"') {
|
|
26
|
+
current += '"';
|
|
27
|
+
i++;
|
|
28
|
+
} else {
|
|
29
|
+
inQuotes = !inQuotes;
|
|
30
|
+
}
|
|
31
|
+
} else if (char === ',' && !inQuotes) {
|
|
32
|
+
result.push(current);
|
|
33
|
+
current = '';
|
|
34
|
+
} else {
|
|
35
|
+
current += char;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (current) result.push(current);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
17
42
|
class ChemicalsService {
|
|
18
43
|
constructor(connection) {
|
|
19
44
|
this.connection = connection;
|
|
20
|
-
this.db = null;
|
|
21
45
|
this.sqsClient = null;
|
|
22
46
|
}
|
|
23
47
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
48
|
+
_parsePostgresArray(str) {
|
|
49
|
+
return parsePostgresArray(str);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_toPostgresArray(arr) {
|
|
53
|
+
if (!Array.isArray(arr) || arr.length === 0) return '{}';
|
|
54
|
+
return '{' + arr.map(s => `"${String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`).join(',') + '}';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
_serializeDate(d) {
|
|
58
|
+
return d instanceof Date ? d.toISOString() : (d || new Date().toISOString());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
_mapChemicalRow(row) {
|
|
62
|
+
if (!row) return null;
|
|
63
|
+
return {
|
|
64
|
+
chemicalId: row.chemical_id,
|
|
65
|
+
sourceId: row.source_id,
|
|
66
|
+
chemicalName: row.chemical_name,
|
|
67
|
+
chemicalMeta: row.chemical_meta ? JSON.parse(row.chemical_meta) : null,
|
|
68
|
+
chemicalIdentifiers: row.chemical_identifiers ? JSON.parse(row.chemical_identifiers) : null,
|
|
69
|
+
chemicalSynonyms: this._parsePostgresArray(row.chemical_synonyms),
|
|
70
|
+
chemicalCategories: this._parsePostgresArray(row.chemical_categories),
|
|
71
|
+
createdAt: row.created_at,
|
|
72
|
+
updatedAt: row.updated_at,
|
|
73
|
+
importedAt: row.imported_at
|
|
74
|
+
};
|
|
27
75
|
}
|
|
28
76
|
|
|
29
77
|
async sendSqlWriteFailure({ sql, parameters, error, retryCount, failedAt }) {
|
|
@@ -71,32 +119,43 @@ class ChemicalsService {
|
|
|
71
119
|
}
|
|
72
120
|
|
|
73
121
|
_buildChemicalUpsertSql(chemical) {
|
|
74
|
-
const
|
|
122
|
+
const upsertSql = [
|
|
75
123
|
'INSERT INTO chemicals (source_id, chemical_name, chemical_meta, chemical_identifiers, chemical_synonyms, chemical_categories, created_at, updated_at)',
|
|
76
|
-
'VALUES (:source_id, :chemical_name, :chemical_meta::jsonb, :chemical_identifiers::jsonb, :chemical_synonyms, :chemical_categories, :created_at, :updated_at)',
|
|
124
|
+
'VALUES (:source_id, :chemical_name, :chemical_meta::jsonb, :chemical_identifiers::jsonb, :chemical_synonyms::text[], :chemical_categories::text[], :created_at, :updated_at)',
|
|
77
125
|
'ON CONFLICT (source_id) DO UPDATE SET',
|
|
78
|
-
' chemical_name =
|
|
79
|
-
' chemical_meta =
|
|
80
|
-
' chemical_identifiers =
|
|
81
|
-
' chemical_synonyms =
|
|
82
|
-
' chemical_categories =
|
|
83
|
-
' updated_at =
|
|
126
|
+
' chemical_name = EXCLUDED.chemical_name,',
|
|
127
|
+
' chemical_meta = EXCLUDED.chemical_meta,',
|
|
128
|
+
' chemical_identifiers = EXCLUDED.chemical_identifiers,',
|
|
129
|
+
' chemical_synonyms = EXCLUDED.chemical_synonyms,',
|
|
130
|
+
' chemical_categories = EXCLUDED.chemical_categories,',
|
|
131
|
+
' updated_at = EXCLUDED.updated_at',
|
|
132
|
+
'RETURNING chemical_id, source_id'
|
|
84
133
|
].join('\n');
|
|
85
134
|
|
|
86
|
-
const serializeDate = (d) => d instanceof Date ? d.toISOString() : d;
|
|
87
|
-
|
|
88
135
|
const parameters = [
|
|
89
136
|
{ name: 'source_id', value: { stringValue: chemical.sourceId } },
|
|
90
137
|
{ name: 'chemical_name', value: { stringValue: chemical.chemicalName } },
|
|
91
|
-
{ name: 'chemical_meta', value: { stringValue: JSON.stringify(chemical.chemicalMeta ?? {}) } },
|
|
92
|
-
{ name: 'chemical_identifiers', value: { stringValue: JSON.stringify(chemical.chemicalIdentifiers ?? {}) } },
|
|
93
|
-
{ name: 'chemical_synonyms', value: { stringValue:
|
|
94
|
-
{ name: 'chemical_categories', value: { stringValue:
|
|
95
|
-
{ name: 'created_at', value: { stringValue:
|
|
96
|
-
{ name: 'updated_at', value: { stringValue:
|
|
138
|
+
{ name: 'chemical_meta', value: { stringValue: JSON.stringify(chemical.chemicalMeta ?? {}) }, typeHint: 'JSON' },
|
|
139
|
+
{ name: 'chemical_identifiers', value: { stringValue: JSON.stringify(chemical.chemicalIdentifiers ?? {}) }, typeHint: 'JSON' },
|
|
140
|
+
{ name: 'chemical_synonyms', value: { stringValue: this._toPostgresArray(chemical.chemicalSynonyms ?? []) } },
|
|
141
|
+
{ name: 'chemical_categories', value: { stringValue: this._toPostgresArray(chemical.chemicalCategories ?? []) } },
|
|
142
|
+
{ name: 'created_at', value: { stringValue: this._serializeDate(chemical.createdAt) }, typeHint: 'TIMESTAMP' },
|
|
143
|
+
{ name: 'updated_at', value: { stringValue: this._serializeDate(chemical.updatedAt) }, typeHint: 'TIMESTAMP' }
|
|
97
144
|
];
|
|
98
145
|
|
|
99
|
-
return { sql, parameters };
|
|
146
|
+
return { sql: upsertSql, parameters };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async _executeChemicalUpsert(chemical) {
|
|
150
|
+
await this.connection.ensureConnected();
|
|
151
|
+
const { sql, parameters } = this._buildChemicalUpsertSql(chemical);
|
|
152
|
+
const queryResult = await this.connection.query(sql, parameters);
|
|
153
|
+
const row = queryResult.rows?.[0];
|
|
154
|
+
if (!row) return null;
|
|
155
|
+
return {
|
|
156
|
+
chemicalId: row.chemical_id,
|
|
157
|
+
sourceId: row.source_id
|
|
158
|
+
};
|
|
100
159
|
}
|
|
101
160
|
|
|
102
161
|
_buildDebugSql(chemical) {
|
|
@@ -173,24 +232,8 @@ class ChemicalsService {
|
|
|
173
232
|
logInfo('pegasus-sdk', `[bulkIndexFielded] DEBUG SQL for document ${i}:\n${this._buildDebugSql(chemical)}`);
|
|
174
233
|
|
|
175
234
|
const attemptUpsert = async () => {
|
|
176
|
-
const
|
|
177
|
-
return
|
|
178
|
-
.values(chemical)
|
|
179
|
-
.onConflictDoUpdate({
|
|
180
|
-
target: schema.chemicals.sourceId,
|
|
181
|
-
set: {
|
|
182
|
-
chemicalName: chemical.chemicalName,
|
|
183
|
-
chemicalMeta: chemical.chemicalMeta,
|
|
184
|
-
chemicalIdentifiers: chemical.chemicalIdentifiers,
|
|
185
|
-
chemicalSynonyms: chemical.chemicalSynonyms,
|
|
186
|
-
chemicalCategories: chemical.chemicalCategories,
|
|
187
|
-
updatedAt: new Date()
|
|
188
|
-
}
|
|
189
|
-
})
|
|
190
|
-
.returning({
|
|
191
|
-
chemicalId: schema.chemicals.chemicalId,
|
|
192
|
-
sourceId: schema.chemicals.sourceId
|
|
193
|
-
});
|
|
235
|
+
const result = await this._executeChemicalUpsert(chemical);
|
|
236
|
+
return result ? [result] : [];
|
|
194
237
|
};
|
|
195
238
|
|
|
196
239
|
let lastError = null;
|
|
@@ -281,25 +324,36 @@ class ChemicalsService {
|
|
|
281
324
|
|
|
282
325
|
async createChemical(chemical) {
|
|
283
326
|
try {
|
|
284
|
-
|
|
327
|
+
await this.connection.ensureConnected();
|
|
285
328
|
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
.
|
|
301
|
-
|
|
302
|
-
|
|
329
|
+
const columns = ['source_id', 'chemical_name', 'chemical_meta', 'chemical_identifiers', 'chemical_synonyms', 'chemical_categories', 'created_at', 'updated_at'];
|
|
330
|
+
const values = [':source_id', ':chemical_name', ':chemical_meta::jsonb', ':chemical_identifiers::jsonb', ':chemical_synonyms::text[]', ':chemical_categories::text[]', ':created_at', ':updated_at'];
|
|
331
|
+
const params = [
|
|
332
|
+
{ name: 'source_id', value: { stringValue: chemical.source_id } },
|
|
333
|
+
{ name: 'chemical_name', value: { stringValue: chemical.chemical_name } },
|
|
334
|
+
{ name: 'chemical_meta', value: { stringValue: JSON.stringify(chemical.chemical_meta || {}) }, typeHint: 'JSON' },
|
|
335
|
+
{ name: 'chemical_identifiers', value: { stringValue: JSON.stringify(chemical.chemical_identifiers || {}) }, typeHint: 'JSON' },
|
|
336
|
+
{ name: 'chemical_synonyms', value: { stringValue: this._toPostgresArray(chemical.chemical_synonyms || []) } },
|
|
337
|
+
{ name: 'chemical_categories', value: { stringValue: this._toPostgresArray(chemical.chemical_categories || []) } },
|
|
338
|
+
{ name: 'created_at', value: { stringValue: this._serializeDate(chemical.created_at || new Date()) }, typeHint: 'TIMESTAMP' },
|
|
339
|
+
{ name: 'updated_at', value: { stringValue: this._serializeDate(chemical.updated_at || new Date()) }, typeHint: 'TIMESTAMP' }
|
|
340
|
+
];
|
|
341
|
+
|
|
342
|
+
if (chemical.imported_at) {
|
|
343
|
+
columns.push('imported_at');
|
|
344
|
+
values.push(':imported_at');
|
|
345
|
+
params.push({ name: 'imported_at', value: { stringValue: this._serializeDate(chemical.imported_at) }, typeHint: 'TIMESTAMP' });
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (chemical.chemical_id) {
|
|
349
|
+
columns.push('chemical_id');
|
|
350
|
+
values.push(':chemical_id');
|
|
351
|
+
params.push({ name: 'chemical_id', value: { stringValue: chemical.chemical_id } });
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const sql = `INSERT INTO chemicals (${columns.join(', ')}) VALUES (${values.join(', ')}) RETURNING *`;
|
|
355
|
+
const result = await this.connection.query(sql, params);
|
|
356
|
+
return this._mapChemicalRow(result.rows?.[0]);
|
|
303
357
|
} catch (error) {
|
|
304
358
|
logError('pegasus-sdk', 'ChemicalsService', 'createChemical', error);
|
|
305
359
|
throw error;
|
|
@@ -308,23 +362,40 @@ class ChemicalsService {
|
|
|
308
362
|
|
|
309
363
|
async updateChemical(chemicalId, updates) {
|
|
310
364
|
try {
|
|
311
|
-
|
|
365
|
+
await this.connection.ensureConnected();
|
|
366
|
+
|
|
367
|
+
const setClauses = [];
|
|
368
|
+
const params = [];
|
|
369
|
+
|
|
370
|
+
if (updates.chemical_name) {
|
|
371
|
+
setClauses.push('chemical_name = :chemical_name');
|
|
372
|
+
params.push({ name: 'chemical_name', value: { stringValue: updates.chemical_name } });
|
|
373
|
+
}
|
|
374
|
+
if (updates.chemical_meta) {
|
|
375
|
+
setClauses.push('chemical_meta = :chemical_meta::jsonb');
|
|
376
|
+
params.push({ name: 'chemical_meta', value: { stringValue: JSON.stringify(updates.chemical_meta) }, typeHint: 'JSON' });
|
|
377
|
+
}
|
|
378
|
+
if (updates.chemical_identifiers) {
|
|
379
|
+
setClauses.push('chemical_identifiers = :chemical_identifiers::jsonb');
|
|
380
|
+
params.push({ name: 'chemical_identifiers', value: { stringValue: JSON.stringify(updates.chemical_identifiers) }, typeHint: 'JSON' });
|
|
381
|
+
}
|
|
382
|
+
if (updates.chemical_synonyms) {
|
|
383
|
+
setClauses.push('chemical_synonyms = :chemical_synonyms::text[]');
|
|
384
|
+
params.push({ name: 'chemical_synonyms', value: { stringValue: this._toPostgresArray(updates.chemical_synonyms) } });
|
|
385
|
+
}
|
|
386
|
+
if (updates.chemical_categories) {
|
|
387
|
+
setClauses.push('chemical_categories = :chemical_categories::text[]');
|
|
388
|
+
params.push({ name: 'chemical_categories', value: { stringValue: this._toPostgresArray(updates.chemical_categories) } });
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
setClauses.push('updated_at = :updated_at');
|
|
392
|
+
params.push({ name: 'updated_at', value: { stringValue: this._serializeDate(new Date()) }, typeHint: 'TIMESTAMP' });
|
|
312
393
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
if (updates.chemical_categories) updateData.chemicalCategories = updates.chemical_categories;
|
|
319
|
-
updateData.updatedAt = new Date();
|
|
320
|
-
|
|
321
|
-
const [result] = await db
|
|
322
|
-
.update(schema.chemicals)
|
|
323
|
-
.set(updateData)
|
|
324
|
-
.where(eq(schema.chemicals.chemicalId, chemicalId))
|
|
325
|
-
.returning();
|
|
326
|
-
|
|
327
|
-
return result || null;
|
|
394
|
+
params.push({ name: 'id', value: { stringValue: chemicalId } });
|
|
395
|
+
|
|
396
|
+
const sql = `UPDATE chemicals SET ${setClauses.join(', ')} WHERE chemical_id = :id::uuid RETURNING *`;
|
|
397
|
+
const result = await this.connection.query(sql, params);
|
|
398
|
+
return this._mapChemicalRow(result.rows?.[0]) || null;
|
|
328
399
|
} catch (error) {
|
|
329
400
|
logError('pegasus-sdk', 'ChemicalsService', 'updateChemical', error);
|
|
330
401
|
throw error;
|
|
@@ -333,14 +404,12 @@ class ChemicalsService {
|
|
|
333
404
|
|
|
334
405
|
async deleteChemical(chemicalId) {
|
|
335
406
|
try {
|
|
336
|
-
|
|
407
|
+
await this.connection.ensureConnected();
|
|
337
408
|
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return deleted || null;
|
|
409
|
+
const sql = 'DELETE FROM chemicals WHERE chemical_id = :id::uuid RETURNING *';
|
|
410
|
+
const params = [{ name: 'id', value: { stringValue: chemicalId } }];
|
|
411
|
+
const result = await this.connection.query(sql, params);
|
|
412
|
+
return this._mapChemicalRow(result.rows?.[0]) || null;
|
|
344
413
|
} catch (error) {
|
|
345
414
|
logError('pegasus-sdk', 'ChemicalsService', 'deleteChemical', error);
|
|
346
415
|
throw error;
|
|
@@ -349,14 +418,12 @@ class ChemicalsService {
|
|
|
349
418
|
|
|
350
419
|
async deleteBySourceId(sourceId) {
|
|
351
420
|
try {
|
|
352
|
-
|
|
421
|
+
await this.connection.ensureConnected();
|
|
353
422
|
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return deleted || null;
|
|
423
|
+
const sql = 'DELETE FROM chemicals WHERE source_id = :source_id RETURNING *';
|
|
424
|
+
const params = [{ name: 'source_id', value: { stringValue: sourceId } }];
|
|
425
|
+
const result = await this.connection.query(sql, params);
|
|
426
|
+
return this._mapChemicalRow(result.rows?.[0]) || null;
|
|
360
427
|
} catch (error) {
|
|
361
428
|
logError('pegasus-sdk', 'ChemicalsService', 'deleteBySourceId', error);
|
|
362
429
|
throw error;
|
|
@@ -365,13 +432,12 @@ class ChemicalsService {
|
|
|
365
432
|
|
|
366
433
|
async deleteCollection(collectionName) {
|
|
367
434
|
try {
|
|
368
|
-
|
|
435
|
+
await this.connection.ensureConnected();
|
|
369
436
|
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
437
|
+
const sql = 'DELETE FROM chemicals WHERE :collection_name = ANY(chemical_categories) RETURNING *';
|
|
438
|
+
const params = [{ name: 'collection_name', value: { stringValue: collectionName } }];
|
|
439
|
+
const result = await this.connection.query(sql, params);
|
|
440
|
+
const deleted = result.rows.map(row => this._mapChemicalRow(row));
|
|
375
441
|
return { deletedCount: deleted.length, deleted };
|
|
376
442
|
} catch (error) {
|
|
377
443
|
logError('pegasus-sdk', 'ChemicalsService', 'deleteCollection', error);
|
|
@@ -381,20 +447,20 @@ class ChemicalsService {
|
|
|
381
447
|
|
|
382
448
|
async updateCollectionProperty(collectionName, propertyPath, newValue) {
|
|
383
449
|
try {
|
|
384
|
-
|
|
450
|
+
await this.connection.ensureConnected();
|
|
451
|
+
|
|
385
452
|
const pathArray = propertyPath.split('.');
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
.
|
|
390
|
-
.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
return { updatedCount: results.length, updated: results };
|
|
453
|
+
const sql = 'UPDATE chemicals SET chemical_meta = jsonb_set(chemical_meta, :path::text[], :value::jsonb), updated_at = :updated_at WHERE :collection_name = ANY(chemical_categories) RETURNING *';
|
|
454
|
+
const params = [
|
|
455
|
+
{ name: 'path', value: { stringValue: JSON.stringify(pathArray) }, typeHint: 'JSON' },
|
|
456
|
+
{ name: 'value', value: { stringValue: JSON.stringify(newValue) }, typeHint: 'JSON' },
|
|
457
|
+
{ name: 'updated_at', value: { stringValue: this._serializeDate(new Date()) }, typeHint: 'TIMESTAMP' },
|
|
458
|
+
{ name: 'collection_name', value: { stringValue: collectionName } }
|
|
459
|
+
];
|
|
460
|
+
|
|
461
|
+
const result = await this.connection.query(sql, params);
|
|
462
|
+
const updated = result.rows.map(row => this._mapChemicalRow(row));
|
|
463
|
+
return { updatedCount: updated.length, updated };
|
|
398
464
|
} catch (error) {
|
|
399
465
|
logError('pegasus-sdk', 'ChemicalsService', 'updateCollectionProperty', error);
|
|
400
466
|
throw error;
|
|
@@ -403,31 +469,38 @@ class ChemicalsService {
|
|
|
403
469
|
|
|
404
470
|
async bulkUpdateProperty(filter, propertyPath, newValue) {
|
|
405
471
|
try {
|
|
406
|
-
|
|
472
|
+
await this.connection.ensureConnected();
|
|
407
473
|
|
|
408
|
-
let
|
|
474
|
+
let whereClause = '1=1';
|
|
475
|
+
const params = [];
|
|
409
476
|
|
|
410
477
|
if (filter.chemicalIds && filter.chemicalIds.length > 0) {
|
|
411
|
-
|
|
478
|
+
const ids = filter.chemicalIds.map((id, i) => `:cid_${i}`).join(',');
|
|
479
|
+
whereClause = `chemical_id = ANY(ARRAY[${ids}]::uuid[])`;
|
|
480
|
+
filter.chemicalIds.forEach((id, i) => {
|
|
481
|
+
params.push({ name: `cid_${i}`, value: { stringValue: id } });
|
|
482
|
+
});
|
|
412
483
|
} else if (filter.sourceIds && filter.sourceIds.length > 0) {
|
|
413
|
-
|
|
484
|
+
const ids = filter.sourceIds.map((id, i) => `:sid_${i}`).join(',');
|
|
485
|
+
whereClause = `source_id = ANY(ARRAY[${ids}]::text[])`;
|
|
486
|
+
filter.sourceIds.forEach((id, i) => {
|
|
487
|
+
params.push({ name: `sid_${i}`, value: { stringValue: id } });
|
|
488
|
+
});
|
|
414
489
|
} else if (filter.category) {
|
|
415
|
-
|
|
490
|
+
whereClause = ':category = ANY(chemical_categories)';
|
|
491
|
+
params.push({ name: 'category', value: { stringValue: filter.category } });
|
|
416
492
|
}
|
|
417
493
|
|
|
418
494
|
const pathArray = propertyPath.split('.');
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
.returning();
|
|
429
|
-
|
|
430
|
-
return { updatedCount: results.length, updated: results };
|
|
495
|
+
const sql = `UPDATE chemicals SET chemical_meta = jsonb_set(COALESCE(chemical_meta, '{}'), :path::text[], :value::jsonb), updated_at = :updated_at WHERE ${whereClause} RETURNING *`;
|
|
496
|
+
|
|
497
|
+
params.push({ name: 'path', value: { stringValue: JSON.stringify(pathArray) }, typeHint: 'JSON' });
|
|
498
|
+
params.push({ name: 'value', value: { stringValue: JSON.stringify(newValue) }, typeHint: 'JSON' });
|
|
499
|
+
params.push({ name: 'updated_at', value: { stringValue: this._serializeDate(new Date()) }, typeHint: 'TIMESTAMP' });
|
|
500
|
+
|
|
501
|
+
const result = await this.connection.query(sql, params);
|
|
502
|
+
const updated = result.rows.map(row => this._mapChemicalRow(row));
|
|
503
|
+
return { updatedCount: updated.length, updated };
|
|
431
504
|
} catch (error) {
|
|
432
505
|
logError('pegasus-sdk', 'ChemicalsService', 'bulkUpdateProperty', error);
|
|
433
506
|
throw error;
|
|
@@ -436,15 +509,12 @@ class ChemicalsService {
|
|
|
436
509
|
|
|
437
510
|
async getChemicalById(chemicalId) {
|
|
438
511
|
try {
|
|
439
|
-
|
|
512
|
+
await this.connection.ensureConnected();
|
|
440
513
|
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
.limit(1);
|
|
446
|
-
|
|
447
|
-
return result || null;
|
|
514
|
+
const sql = 'SELECT * FROM chemicals WHERE chemical_id = :id::uuid LIMIT 1';
|
|
515
|
+
const params = [{ name: 'id', value: { stringValue: chemicalId } }];
|
|
516
|
+
const result = await this.connection.query(sql, params);
|
|
517
|
+
return this._mapChemicalRow(result.rows?.[0]) || null;
|
|
448
518
|
} catch (error) {
|
|
449
519
|
logError('pegasus-sdk', 'ChemicalsService', 'getChemicalById', error);
|
|
450
520
|
throw error;
|
|
@@ -453,15 +523,12 @@ class ChemicalsService {
|
|
|
453
523
|
|
|
454
524
|
async getChemicalBySourceId(sourceId) {
|
|
455
525
|
try {
|
|
456
|
-
|
|
526
|
+
await this.connection.ensureConnected();
|
|
457
527
|
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
.limit(1);
|
|
463
|
-
|
|
464
|
-
return result || null;
|
|
528
|
+
const sql = 'SELECT * FROM chemicals WHERE source_id = :source_id LIMIT 1';
|
|
529
|
+
const params = [{ name: 'source_id', value: { stringValue: sourceId } }];
|
|
530
|
+
const result = await this.connection.query(sql, params);
|
|
531
|
+
return this._mapChemicalRow(result.rows?.[0]) || null;
|
|
465
532
|
} catch (error) {
|
|
466
533
|
logError('pegasus-sdk', 'ChemicalsService', 'getChemicalBySourceId', error);
|
|
467
534
|
throw error;
|
|
@@ -470,14 +537,12 @@ class ChemicalsService {
|
|
|
470
537
|
|
|
471
538
|
async getChemicalsByCAS(casNumber) {
|
|
472
539
|
try {
|
|
473
|
-
|
|
540
|
+
await this.connection.ensureConnected();
|
|
474
541
|
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
return results;
|
|
542
|
+
const sql = "SELECT * FROM chemicals WHERE chemical_identifiers->>'CAS' = :cas OR chemical_identifiers->'CAS' ? :cas";
|
|
543
|
+
const params = [{ name: 'cas', value: { stringValue: casNumber } }];
|
|
544
|
+
const result = await this.connection.query(sql, params);
|
|
545
|
+
return result.rows.map(row => this._mapChemicalRow(row));
|
|
481
546
|
} catch (error) {
|
|
482
547
|
logError('pegasus-sdk', 'ChemicalsService', 'getChemicalsByCAS', error);
|
|
483
548
|
throw error;
|
|
@@ -490,14 +555,12 @@ class ChemicalsService {
|
|
|
490
555
|
throw new Error(`Invalid identifier type: ${identifierType}`);
|
|
491
556
|
}
|
|
492
557
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
return results;
|
|
558
|
+
await this.connection.ensureConnected();
|
|
559
|
+
|
|
560
|
+
const sql = `SELECT * FROM chemicals WHERE chemical_identifiers->>'${identifierType}' = :value OR chemical_identifiers->'${identifierType}' ? :value`;
|
|
561
|
+
const params = [{ name: 'value', value: { stringValue: identifierValue } }];
|
|
562
|
+
const result = await this.connection.query(sql, params);
|
|
563
|
+
return result.rows.map(row => this._mapChemicalRow(row));
|
|
501
564
|
} catch (error) {
|
|
502
565
|
logError('pegasus-sdk', 'ChemicalsService', 'getChemicalsByIdentifier', error);
|
|
503
566
|
throw error;
|
|
@@ -506,14 +569,12 @@ class ChemicalsService {
|
|
|
506
569
|
|
|
507
570
|
async countByCollection(collectionName) {
|
|
508
571
|
try {
|
|
509
|
-
|
|
572
|
+
await this.connection.ensureConnected();
|
|
510
573
|
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
return { count: result[0].count };
|
|
574
|
+
const sql = 'SELECT count(*)::int AS count FROM chemicals WHERE :collection_name = ANY(chemical_categories)';
|
|
575
|
+
const params = [{ name: 'collection_name', value: { stringValue: collectionName } }];
|
|
576
|
+
const result = await this.connection.query(sql, params);
|
|
577
|
+
return { count: result.rows[0]?.count ?? 0 };
|
|
517
578
|
} catch (error) {
|
|
518
579
|
logError('pegasus-sdk', 'ChemicalsService', 'countByCollection', error);
|
|
519
580
|
throw error;
|
|
@@ -522,15 +583,13 @@ class ChemicalsService {
|
|
|
522
583
|
|
|
523
584
|
async countByIdentifier(identifierValue) {
|
|
524
585
|
try {
|
|
525
|
-
|
|
586
|
+
await this.connection.ensureConnected();
|
|
526
587
|
|
|
527
588
|
const searchPattern = `%${escapeLikePattern(identifierValue)}%`;
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
return { count: result[0].count };
|
|
589
|
+
const sql = 'SELECT count(*)::int AS count FROM chemicals WHERE chemical_identifiers::text LIKE :pattern';
|
|
590
|
+
const params = [{ name: 'pattern', value: { stringValue: searchPattern } }];
|
|
591
|
+
const result = await this.connection.query(sql, params);
|
|
592
|
+
return { count: result.rows[0]?.count ?? 0 };
|
|
534
593
|
} catch (error) {
|
|
535
594
|
logError('pegasus-sdk', 'ChemicalsService', 'countByIdentifier', error);
|
|
536
595
|
throw error;
|
|
@@ -539,14 +598,12 @@ class ChemicalsService {
|
|
|
539
598
|
|
|
540
599
|
async countByCAS(casNumber) {
|
|
541
600
|
try {
|
|
542
|
-
|
|
601
|
+
await this.connection.ensureConnected();
|
|
543
602
|
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
return { count: result[0].count };
|
|
603
|
+
const sql = "SELECT count(*)::int AS count FROM chemicals WHERE chemical_identifiers->>'CAS' = :cas OR chemical_identifiers->'CAS' ? :cas";
|
|
604
|
+
const params = [{ name: 'cas', value: { stringValue: casNumber } }];
|
|
605
|
+
const result = await this.connection.query(sql, params);
|
|
606
|
+
return { count: result.rows[0]?.count ?? 0 };
|
|
550
607
|
} catch (error) {
|
|
551
608
|
logError('pegasus-sdk', 'ChemicalsService', 'countByCAS', error);
|
|
552
609
|
throw error;
|
|
@@ -555,13 +612,11 @@ class ChemicalsService {
|
|
|
555
612
|
|
|
556
613
|
async getTotalSynonymCount() {
|
|
557
614
|
try {
|
|
558
|
-
|
|
615
|
+
await this.connection.ensureConnected();
|
|
559
616
|
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
return { count: result[0].count || 0 };
|
|
617
|
+
const sql = 'SELECT sum(array_length(chemical_synonyms, 1))::int AS count FROM chemicals';
|
|
618
|
+
const result = await this.connection.query(sql, []);
|
|
619
|
+
return { count: result.rows[0]?.count || 0 };
|
|
565
620
|
} catch (error) {
|
|
566
621
|
logError('pegasus-sdk', 'ChemicalsService', 'getTotalSynonymCount', error);
|
|
567
622
|
throw error;
|
|
@@ -570,14 +625,12 @@ class ChemicalsService {
|
|
|
570
625
|
|
|
571
626
|
async getSynonymCount(synonymTerm) {
|
|
572
627
|
try {
|
|
573
|
-
|
|
628
|
+
await this.connection.ensureConnected();
|
|
574
629
|
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
return { count: result[0].count };
|
|
630
|
+
const sql = 'SELECT count(*)::int AS count FROM chemicals WHERE :term = ANY(chemical_synonyms)';
|
|
631
|
+
const params = [{ name: 'term', value: { stringValue: synonymTerm } }];
|
|
632
|
+
const result = await this.connection.query(sql, params);
|
|
633
|
+
return { count: result.rows[0]?.count ?? 0 };
|
|
581
634
|
} catch (error) {
|
|
582
635
|
logError('pegasus-sdk', 'ChemicalsService', 'getSynonymCount', error);
|
|
583
636
|
throw error;
|
|
@@ -586,19 +639,18 @@ class ChemicalsService {
|
|
|
586
639
|
|
|
587
640
|
async convertIdentifier(fromIdentifier, toIdentifierType) {
|
|
588
641
|
try {
|
|
589
|
-
|
|
642
|
+
await this.connection.ensureConnected();
|
|
590
643
|
|
|
591
644
|
const searchPattern = `%${escapeLikePattern(fromIdentifier)}%`;
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
.where(sql`${schema.chemicals.chemicalIdentifiers}::text LIKE ${searchPattern}`);
|
|
645
|
+
const sql = 'SELECT * FROM chemicals WHERE chemical_identifiers::text LIKE :pattern LIMIT 1';
|
|
646
|
+
const params = [{ name: 'pattern', value: { stringValue: searchPattern } }];
|
|
647
|
+
const result = await this.connection.query(sql, params);
|
|
596
648
|
|
|
597
|
-
if (
|
|
649
|
+
if (result.rows.length === 0) {
|
|
598
650
|
return null;
|
|
599
651
|
}
|
|
600
652
|
|
|
601
|
-
const chemical =
|
|
653
|
+
const chemical = this._mapChemicalRow(result.rows[0]);
|
|
602
654
|
const identifiers = chemical.chemicalIdentifiers || {};
|
|
603
655
|
const toIdentifier = identifiers[toIdentifierType];
|
|
604
656
|
|
|
@@ -734,11 +786,10 @@ class ChemicalsService {
|
|
|
734
786
|
|
|
735
787
|
async countAll() {
|
|
736
788
|
try {
|
|
737
|
-
|
|
738
|
-
const
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
return { count: result[0].count };
|
|
789
|
+
await this.connection.ensureConnected();
|
|
790
|
+
const sql = 'SELECT count(*)::int AS count FROM chemicals';
|
|
791
|
+
const result = await this.connection.query(sql, []);
|
|
792
|
+
return { count: result.rows[0]?.count ?? 0 };
|
|
742
793
|
} catch (error) {
|
|
743
794
|
logError('pegasus-sdk', 'ChemicalsService', 'countAll', error);
|
|
744
795
|
throw error;
|
|
@@ -747,28 +798,28 @@ class ChemicalsService {
|
|
|
747
798
|
|
|
748
799
|
async findChemicalsWithoutDocuments(collectionName, searchTerm, pageSize = 100) {
|
|
749
800
|
try {
|
|
750
|
-
|
|
801
|
+
await this.connection.ensureConnected();
|
|
751
802
|
|
|
752
|
-
let
|
|
803
|
+
let whereFragments = [];
|
|
804
|
+
const params = [];
|
|
753
805
|
|
|
754
806
|
if (collectionName) {
|
|
755
|
-
|
|
807
|
+
whereFragments.push(':collection_name = ANY(chemical_categories)');
|
|
808
|
+
params.push({ name: 'collection_name', value: { stringValue: collectionName } });
|
|
756
809
|
}
|
|
757
810
|
|
|
758
811
|
if (searchTerm) {
|
|
759
812
|
const searchPattern = `%${escapeLikePattern(searchTerm)}%`;
|
|
760
|
-
|
|
813
|
+
whereFragments.push('chemical_name ILIKE :search_term');
|
|
814
|
+
params.push({ name: 'search_term', value: { stringValue: searchPattern } });
|
|
761
815
|
}
|
|
762
816
|
|
|
763
|
-
const whereClause =
|
|
817
|
+
const whereClause = whereFragments.length > 0 ? 'WHERE ' + whereFragments.join(' AND ') : '';
|
|
818
|
+
params.push({ name: 'page_size', value: { longValue: pageSize } });
|
|
764
819
|
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
.where(whereClause)
|
|
769
|
-
.limit(pageSize);
|
|
770
|
-
|
|
771
|
-
return results;
|
|
820
|
+
const sql = `SELECT * FROM chemicals ${whereClause} LIMIT :page_size`;
|
|
821
|
+
const result = await this.connection.query(sql, params);
|
|
822
|
+
return result.rows.map(row => this._mapChemicalRow(row));
|
|
772
823
|
} catch (error) {
|
|
773
824
|
logError('pegasus-sdk', 'ChemicalsService', 'findChemicalsWithoutDocuments', error);
|
|
774
825
|
throw error;
|
|
@@ -777,18 +828,17 @@ class ChemicalsService {
|
|
|
777
828
|
|
|
778
829
|
async countChemicalsWithoutDocuments(collectionName) {
|
|
779
830
|
try {
|
|
780
|
-
|
|
831
|
+
await this.connection.ensureConnected();
|
|
781
832
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
833
|
+
let sql = 'SELECT count(*)::int AS count FROM chemicals';
|
|
834
|
+
const params = [];
|
|
835
|
+
if (collectionName) {
|
|
836
|
+
sql += ' WHERE :collection_name = ANY(chemical_categories)';
|
|
837
|
+
params.push({ name: 'collection_name', value: { stringValue: collectionName } });
|
|
838
|
+
}
|
|
785
839
|
|
|
786
|
-
const result = await
|
|
787
|
-
|
|
788
|
-
.from(schema.chemicals)
|
|
789
|
-
.where(whereClause);
|
|
790
|
-
|
|
791
|
-
return { count: result[0].count };
|
|
840
|
+
const result = await this.connection.query(sql, params);
|
|
841
|
+
return { count: result.rows[0]?.count ?? 0 };
|
|
792
842
|
} catch (error) {
|
|
793
843
|
logError('pegasus-sdk', 'ChemicalsService', 'countChemicalsWithoutDocuments', error);
|
|
794
844
|
throw error;
|
package/lib/connection.js
CHANGED
|
@@ -2,7 +2,7 @@ const { Client: OpenSearchClient } = require('@opensearch-project/opensearch');
|
|
|
2
2
|
const { RDSDataClient, ExecuteStatementCommand } = require('@aws-sdk/client-rds-data');
|
|
3
3
|
const { AwsSigv4Signer } = require('@opensearch-project/opensearch/aws');
|
|
4
4
|
const { fromNodeProviderChain } = require('@aws-sdk/credential-providers');
|
|
5
|
-
const {
|
|
5
|
+
const { mapRecords } = require('./db');
|
|
6
6
|
const { loadConfig } = require('../config');
|
|
7
7
|
const { logInfo, logError } = require('@toxplanet/tphelper/logging');
|
|
8
8
|
|
|
@@ -20,7 +20,6 @@ class PegasusConnection {
|
|
|
20
20
|
this.databaseName = this.config.database?.name;
|
|
21
21
|
|
|
22
22
|
this.rdsDataClient = null;
|
|
23
|
-
this.db = null;
|
|
24
23
|
this.osClient = null;
|
|
25
24
|
this.isConnected = false;
|
|
26
25
|
}
|
|
@@ -32,12 +31,6 @@ class PegasusConnection {
|
|
|
32
31
|
|
|
33
32
|
this.rdsDataClient = new RDSDataClient({ region: this.region });
|
|
34
33
|
|
|
35
|
-
this.db = getDrizzle(this.rdsDataClient, {
|
|
36
|
-
resourceArn: this.clusterArn,
|
|
37
|
-
secretArn: this.secretArn,
|
|
38
|
-
database: this.databaseName
|
|
39
|
-
});
|
|
40
|
-
|
|
41
34
|
logInfo('pegasus-sdk', 'RDS Data API client initialized');
|
|
42
35
|
|
|
43
36
|
try {
|
|
@@ -78,7 +71,7 @@ class PegasusConnection {
|
|
|
78
71
|
* @returns {Promise<boolean>} true if a connect happened, false if already connected.
|
|
79
72
|
*/
|
|
80
73
|
async ensureConnected() {
|
|
81
|
-
if (!this.isConnected
|
|
74
|
+
if (!this.isConnected) {
|
|
82
75
|
await this.connect();
|
|
83
76
|
return true;
|
|
84
77
|
}
|
|
@@ -91,7 +84,6 @@ class PegasusConnection {
|
|
|
91
84
|
*/
|
|
92
85
|
async reconnect() {
|
|
93
86
|
logInfo('pegasus-sdk', 'Reconnecting RDS Data API client');
|
|
94
|
-
this.db = null;
|
|
95
87
|
this.isConnected = false;
|
|
96
88
|
await this.connect();
|
|
97
89
|
}
|
|
@@ -108,20 +100,12 @@ class PegasusConnection {
|
|
|
108
100
|
return;
|
|
109
101
|
}
|
|
110
102
|
|
|
111
|
-
this.db = null;
|
|
112
103
|
this.rdsDataClient = null;
|
|
113
104
|
this.osClient = null;
|
|
114
105
|
this.isConnected = false;
|
|
115
106
|
logInfo('pegasus-sdk', 'RDS Data API client disconnected');
|
|
116
107
|
}
|
|
117
108
|
|
|
118
|
-
getPostgresClient() {
|
|
119
|
-
if (!this.db) {
|
|
120
|
-
throw new Error('RDS Data API not initialized. Call connect() first.');
|
|
121
|
-
}
|
|
122
|
-
return this.db;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
109
|
getOpenSearchClient() {
|
|
126
110
|
if (!this.osClient) {
|
|
127
111
|
throw new Error('OpenSearch connection not established. Call connect() first or provide openSearchEndpoint.');
|
|
@@ -135,7 +119,7 @@ class PegasusConnection {
|
|
|
135
119
|
|
|
136
120
|
async testConnection() {
|
|
137
121
|
try {
|
|
138
|
-
if (!this.
|
|
122
|
+
if (!this.rdsDataClient) {
|
|
139
123
|
throw new Error('RDS Data API not initialized');
|
|
140
124
|
}
|
|
141
125
|
|
|
@@ -143,16 +127,18 @@ class PegasusConnection {
|
|
|
143
127
|
resourceArn: this.clusterArn,
|
|
144
128
|
secretArn: this.secretArn,
|
|
145
129
|
database: this.databaseName,
|
|
146
|
-
sql: 'SELECT NOW() as current_time, version() as pg_version'
|
|
130
|
+
sql: 'SELECT NOW() as current_time, version() as pg_version',
|
|
131
|
+
includeResultMetadata: true
|
|
147
132
|
});
|
|
148
133
|
|
|
149
134
|
const result = await this.rdsDataClient.send(command);
|
|
150
|
-
const
|
|
135
|
+
const rows = mapRecords(result.records, result.columnMetadata);
|
|
136
|
+
const row = rows?.[0];
|
|
151
137
|
|
|
152
138
|
const pgStatus = {
|
|
153
139
|
connected: true,
|
|
154
|
-
timestamp: row?.
|
|
155
|
-
version: row?.
|
|
140
|
+
timestamp: row?.current_time,
|
|
141
|
+
version: row?.pg_version
|
|
156
142
|
};
|
|
157
143
|
|
|
158
144
|
let osStatus = null;
|
|
@@ -205,7 +191,8 @@ class PegasusConnection {
|
|
|
205
191
|
secretArn: this.secretArn,
|
|
206
192
|
database: this.databaseName,
|
|
207
193
|
sql,
|
|
208
|
-
parameters: params || []
|
|
194
|
+
parameters: params || [],
|
|
195
|
+
includeResultMetadata: true
|
|
209
196
|
});
|
|
210
197
|
|
|
211
198
|
const result = await this.rdsDataClient.send(command);
|
|
@@ -213,19 +200,9 @@ class PegasusConnection {
|
|
|
213
200
|
|
|
214
201
|
return {
|
|
215
202
|
rowCount: result.numberOfRecordsUpdated || result.records?.length || 0,
|
|
216
|
-
rows: result.records
|
|
217
|
-
command: result
|
|
203
|
+
rows: mapRecords(result.records, result.columnMetadata)
|
|
218
204
|
};
|
|
219
205
|
}
|
|
220
|
-
|
|
221
|
-
async transaction(callback) {
|
|
222
|
-
if (!this.db) {
|
|
223
|
-
throw new Error('RDS Data API not initialized. Call connect() first.');
|
|
224
|
-
}
|
|
225
|
-
return await this.db.transaction(async (trx) => {
|
|
226
|
-
return await callback(trx);
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
206
|
}
|
|
230
207
|
|
|
231
208
|
module.exports = PegasusConnection;
|
package/lib/db/index.js
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
function getFieldValue(field) {
|
|
2
|
+
if (!field || field.isNull) return null;
|
|
3
|
+
if ('stringValue' in field) return field.stringValue;
|
|
4
|
+
if ('longValue' in field) return field.longValue;
|
|
5
|
+
if ('doubleValue' in field) return field.doubleValue;
|
|
6
|
+
if ('booleanValue' in field) return field.booleanValue;
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
+
function mapRecord(record, columnMetadata) {
|
|
11
|
+
const obj = {};
|
|
12
|
+
columnMetadata.forEach((col, i) => {
|
|
13
|
+
obj[col.name] = getFieldValue(record[i]);
|
|
14
|
+
});
|
|
15
|
+
return obj;
|
|
16
|
+
}
|
|
10
17
|
|
|
11
|
-
function
|
|
12
|
-
return
|
|
18
|
+
function mapRecords(records = [], columnMetadata = []) {
|
|
19
|
+
return records.map(r => mapRecord(r, columnMetadata));
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
module.exports = {
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
getFieldValue,
|
|
24
|
+
mapRecord,
|
|
25
|
+
mapRecords
|
|
18
26
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toxplanet/pegasus-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.21",
|
|
4
4
|
"description": "SDK for migrating chemical data to Pegasus PostgreSQL + OpenSearch architecture with Elasticsearch client compatibility",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@toxplanet/tphelper": "1.2.8",
|
|
28
|
-
"drizzle-orm": "^0.30.0",
|
|
29
28
|
"@opensearch-project/opensearch": "^2.5.0",
|
|
30
29
|
"@aws-sdk/client-rds-data": "^3.490.0",
|
|
31
30
|
"@aws-sdk/client-sqs": "^3.490.0",
|
package/lib/db/schema.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const { pgTable, uuid, text, jsonb, timestamp, index } = require('drizzle-orm/pg-core');
|
|
2
|
-
const { sql } = require('drizzle-orm');
|
|
3
|
-
|
|
4
|
-
const chemicals = pgTable('chemicals', {
|
|
5
|
-
chemicalId: uuid('chemical_id').defaultRandom().primaryKey(),
|
|
6
|
-
sourceId: text('source_id').notNull().unique(),
|
|
7
|
-
chemicalName: text('chemical_name').notNull(),
|
|
8
|
-
chemicalMeta: jsonb('chemical_meta'),
|
|
9
|
-
chemicalIdentifiers: jsonb('chemical_identifiers'),
|
|
10
|
-
chemicalSynonyms: text('chemical_synonyms').array(),
|
|
11
|
-
chemicalCategories: text('chemical_categories').array(),
|
|
12
|
-
createdAt: timestamp('created_at', { withTimezone: true }).notNull(),
|
|
13
|
-
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull(),
|
|
14
|
-
importedAt: timestamp('imported_at', { withTimezone: true }).defaultNow()
|
|
15
|
-
}, (table) => {
|
|
16
|
-
return {
|
|
17
|
-
nameIdx: index('idx_chemicals_name').on(table.chemicalName),
|
|
18
|
-
createdAtIdx: index('idx_chemicals_created_at').on(table.createdAt),
|
|
19
|
-
updatedAtIdx: index('idx_chemicals_updated_at').on(table.updatedAt),
|
|
20
|
-
identifiersGinIdx: index('idx_chemicals_identifiers_gin').on(table.chemicalIdentifiers),
|
|
21
|
-
synonymsGinIdx: index('idx_chemicals_synonyms_gin').on(table.chemicalSynonyms)
|
|
22
|
-
};
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
module.exports = {
|
|
26
|
-
chemicals
|
|
27
|
-
};
|