@toxplanet/pegasus-sdk 1.0.2 → 1.1.1
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/index.js +43 -43
- package/lib/chemicals.js +293 -293
- package/lib/connection.js +223 -223
- package/lib/documents.js +94 -94
- package/lib/search.js +336 -336
- package/lib/sync.js +43 -43
- package/lib/utils.js +49 -49
- package/package.json +48 -48
package/index.js
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
const PegasusConnection = require('./lib/connection');
|
|
2
|
-
const ChemicalsService = require('./lib/chemicals');
|
|
3
|
-
const DocumentsService = require('./lib/documents');
|
|
4
|
-
const SearchService = require('./lib/search');
|
|
5
|
-
const SyncService = require('./lib/sync');
|
|
6
|
-
const UtilsService = require('./lib/utils');
|
|
7
|
-
const ElasticsearchService = require('./lib/elasticsearch');
|
|
8
|
-
|
|
9
|
-
class PegasusSDK {
|
|
10
|
-
constructor(config) {
|
|
11
|
-
this.connection = new PegasusConnection(config);
|
|
12
|
-
this.elasticsearch = new ElasticsearchService(this.connection);
|
|
13
|
-
this.chemicals = new ChemicalsService(this.connection);
|
|
14
|
-
this.documents = new DocumentsService(this.connection);
|
|
15
|
-
this.search = new SearchService(this.connection);
|
|
16
|
-
this.sync = new SyncService(this.connection);
|
|
17
|
-
this.utils = new UtilsService(this.connection);
|
|
18
|
-
|
|
19
|
-
this.chemicals.registerElasticsearchHandlers(this.elasticsearch);
|
|
20
|
-
this.documents.registerElasticsearchHandlers(this.elasticsearch);
|
|
21
|
-
this.search.registerElasticsearchHandlers(this.elasticsearch);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async connect() {
|
|
25
|
-
return this.connection.connect();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async disconnect() {
|
|
29
|
-
return this.connection.disconnect();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async healthCheck() {
|
|
33
|
-
return this.connection.testConnection();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
module.exports = PegasusSDK;
|
|
38
|
-
module.exports.PegasusConnection = PegasusConnection;
|
|
39
|
-
module.exports.ChemicalsService = ChemicalsService;
|
|
40
|
-
module.exports.DocumentsService = DocumentsService;
|
|
41
|
-
module.exports.SearchService = SearchService;
|
|
42
|
-
module.exports.SyncService = SyncService;
|
|
43
|
-
module.exports.UtilsService = UtilsService;
|
|
1
|
+
const PegasusConnection = require('./lib/connection');
|
|
2
|
+
const ChemicalsService = require('./lib/chemicals');
|
|
3
|
+
const DocumentsService = require('./lib/documents');
|
|
4
|
+
const SearchService = require('./lib/search');
|
|
5
|
+
const SyncService = require('./lib/sync');
|
|
6
|
+
const UtilsService = require('./lib/utils');
|
|
7
|
+
const ElasticsearchService = require('./lib/elasticsearch');
|
|
8
|
+
|
|
9
|
+
class PegasusSDK {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.connection = new PegasusConnection(config);
|
|
12
|
+
this.elasticsearch = new ElasticsearchService(this.connection);
|
|
13
|
+
this.chemicals = new ChemicalsService(this.connection);
|
|
14
|
+
this.documents = new DocumentsService(this.connection);
|
|
15
|
+
this.search = new SearchService(this.connection);
|
|
16
|
+
this.sync = new SyncService(this.connection);
|
|
17
|
+
this.utils = new UtilsService(this.connection);
|
|
18
|
+
|
|
19
|
+
this.chemicals.registerElasticsearchHandlers(this.elasticsearch);
|
|
20
|
+
this.documents.registerElasticsearchHandlers(this.elasticsearch);
|
|
21
|
+
this.search.registerElasticsearchHandlers(this.elasticsearch);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async connect() {
|
|
25
|
+
return this.connection.connect();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async disconnect() {
|
|
29
|
+
return this.connection.disconnect();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async healthCheck() {
|
|
33
|
+
return this.connection.testConnection();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = PegasusSDK;
|
|
38
|
+
module.exports.PegasusConnection = PegasusConnection;
|
|
39
|
+
module.exports.ChemicalsService = ChemicalsService;
|
|
40
|
+
module.exports.DocumentsService = DocumentsService;
|
|
41
|
+
module.exports.SearchService = SearchService;
|
|
42
|
+
module.exports.SyncService = SyncService;
|
|
43
|
+
module.exports.UtilsService = UtilsService;
|
|
44
44
|
module.exports.ElasticsearchService = ElasticsearchService;
|
package/lib/chemicals.js
CHANGED
|
@@ -1,293 +1,293 @@
|
|
|
1
|
-
const { logError } = require('@toxplanet/tphelper/logging');
|
|
2
|
-
const { getDrizzle, schema } = require('./db');
|
|
3
|
-
const { eq } = require('drizzle-orm');
|
|
4
|
-
|
|
5
|
-
class ChemicalsService {
|
|
6
|
-
constructor(connection) {
|
|
7
|
-
this.connection = connection;
|
|
8
|
-
this.db = null;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
getDb() {
|
|
12
|
-
if (!this.db) {
|
|
13
|
-
this.db = getDrizzle(this.connection.pgPool);
|
|
14
|
-
}
|
|
15
|
-
return this.db;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async bulkIndexFielded(documents) {}
|
|
19
|
-
|
|
20
|
-
async bulkIndexFulltext(documents) {}
|
|
21
|
-
|
|
22
|
-
async bulkIndexSubstances(substances) {}
|
|
23
|
-
|
|
24
|
-
async createChemical(chemical) {
|
|
25
|
-
try {
|
|
26
|
-
const db = this.getDb();
|
|
27
|
-
|
|
28
|
-
const [result] = await db
|
|
29
|
-
.insert(schema.chemicals)
|
|
30
|
-
.values({
|
|
31
|
-
sourceId: chemical.source_id,
|
|
32
|
-
chemicalName: chemical.chemical_name,
|
|
33
|
-
chemicalMeta: chemical.chemical_meta,
|
|
34
|
-
chemicalIdentifiers: chemical.chemical_identifiers,
|
|
35
|
-
chemicalSynonyms: chemical.chemical_synonyms,
|
|
36
|
-
chemicalCategories: chemical.chemical_categories,
|
|
37
|
-
createdAt: chemical.created_at || new Date(),
|
|
38
|
-
updatedAt: chemical.updated_at || new Date(),
|
|
39
|
-
...(chemical.imported_at && { importedAt: chemical.imported_at }),
|
|
40
|
-
...(chemical.chemical_id && { chemicalId: chemical.chemical_id })
|
|
41
|
-
})
|
|
42
|
-
.returning();
|
|
43
|
-
|
|
44
|
-
return result;
|
|
45
|
-
} catch (error) {
|
|
46
|
-
logError('pegasus-sdk', 'ChemicalsService', 'createChemical', error);
|
|
47
|
-
throw error;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async updateChemical(chemicalId, updates) {}
|
|
52
|
-
|
|
53
|
-
async deleteChemical(chemicalId) {
|
|
54
|
-
try {
|
|
55
|
-
const db = this.getDb();
|
|
56
|
-
|
|
57
|
-
const [deleted] = await db
|
|
58
|
-
.delete(schema.chemicals)
|
|
59
|
-
.where(eq(schema.chemicals.chemicalId, chemicalId))
|
|
60
|
-
.returning();
|
|
61
|
-
|
|
62
|
-
return deleted || null;
|
|
63
|
-
} catch (error) {
|
|
64
|
-
logError('pegasus-sdk', 'ChemicalsService', 'deleteChemical', error);
|
|
65
|
-
throw error;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async deleteBySourceId(sourceId) {}
|
|
70
|
-
|
|
71
|
-
async deleteCollection(collectionName) {}
|
|
72
|
-
|
|
73
|
-
async updateCollectionProperty(collectionName, propertyPath, newValue) {}
|
|
74
|
-
|
|
75
|
-
async bulkUpdateProperty(filter, propertyPath, newValue) {}
|
|
76
|
-
|
|
77
|
-
async getChemicalById(chemicalId) {
|
|
78
|
-
try {
|
|
79
|
-
const db = this.getDb();
|
|
80
|
-
|
|
81
|
-
const [result] = await db
|
|
82
|
-
.select()
|
|
83
|
-
.from(schema.chemicals)
|
|
84
|
-
.where(eq(schema.chemicals.chemicalId, chemicalId))
|
|
85
|
-
.limit(1);
|
|
86
|
-
|
|
87
|
-
return result || null;
|
|
88
|
-
} catch (error) {
|
|
89
|
-
logError('pegasus-sdk', 'ChemicalsService', 'getChemicalById', error);
|
|
90
|
-
throw error;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async getChemicalBySourceId(sourceId) {}
|
|
95
|
-
|
|
96
|
-
async getChemicalsByCAS(casNumber) {}
|
|
97
|
-
|
|
98
|
-
async getChemicalsByIdentifier(identifierType, identifierValue) {}
|
|
99
|
-
|
|
100
|
-
async countByCollection(collectionName) {}
|
|
101
|
-
|
|
102
|
-
async countByIdentifier(identifierValue) {}
|
|
103
|
-
|
|
104
|
-
async countByCAS(casNumber) {}
|
|
105
|
-
|
|
106
|
-
async getTotalSynonymCount() {}
|
|
107
|
-
|
|
108
|
-
async getSynonymCount(synonymTerm) {}
|
|
109
|
-
|
|
110
|
-
async convertIdentifier(fromIdentifier, toIdentifierType) {}
|
|
111
|
-
|
|
112
|
-
async convertIdentifiersBatch(fromIdentifiers, toIdentifierType) {}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Search for chemicals by name using OpenSearch
|
|
116
|
-
* @param {string} searchTerm - Name to search for
|
|
117
|
-
* @param {number} limit - Maximum number of results (default: 10)
|
|
118
|
-
* @returns {Promise<Object>} Search results
|
|
119
|
-
*/
|
|
120
|
-
async searchByName(searchTerm, limit = 10) {
|
|
121
|
-
if (!searchTerm) {
|
|
122
|
-
return { results: [] };
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
const opensearchClient = this.connection.getOpenSearchClient();
|
|
127
|
-
const indexName = this.connection.getOpenSearchIndex();
|
|
128
|
-
|
|
129
|
-
const response = await opensearchClient.search({
|
|
130
|
-
index: indexName,
|
|
131
|
-
body: {
|
|
132
|
-
size: limit,
|
|
133
|
-
query: {
|
|
134
|
-
bool: {
|
|
135
|
-
should: [
|
|
136
|
-
// Prioritize exact name matches
|
|
137
|
-
{ term: { 'chemical_name.keyword': { value: searchTerm, boost: 100, case_insensitive: true } } },
|
|
138
|
-
// Then prefix matches
|
|
139
|
-
{ prefix: { 'chemical_name.keyword': { value: searchTerm, boost: 50, case_insensitive: true } } },
|
|
140
|
-
// Include synonym matches as secondary
|
|
141
|
-
{ term: { 'synonyms.keyword': { value: searchTerm, boost: 30, case_insensitive: true } } },
|
|
142
|
-
{ prefix: { 'synonyms.keyword': { value: searchTerm, boost: 10, case_insensitive: true } } }
|
|
143
|
-
],
|
|
144
|
-
minimum_should_match: 1
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
_source: ['postgres_id', 'chemical_name', 'cas_numbers', 'identifier_values', 'synonyms']
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const hits = response.body?.hits?.hits || [];
|
|
152
|
-
const results = hits.map((hit) => ({
|
|
153
|
-
id: hit._source.postgres_id,
|
|
154
|
-
name: hit._source.chemical_name,
|
|
155
|
-
cas: hit._source.cas_numbers || [],
|
|
156
|
-
identifiers: hit._source.identifier_values || [],
|
|
157
|
-
synonyms: hit._source.synonyms || [],
|
|
158
|
-
score: hit._score
|
|
159
|
-
}));
|
|
160
|
-
|
|
161
|
-
return { results };
|
|
162
|
-
} catch (error) {
|
|
163
|
-
logError('pegasus-sdk', 'ChemicalsService', 'searchByName', error);
|
|
164
|
-
throw error;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Search for chemicals by synonym using OpenSearch
|
|
170
|
-
* @param {string} synonymTerm - Synonym to search for
|
|
171
|
-
* @param {number} limit - Maximum number of results (default: 10)
|
|
172
|
-
* @returns {Promise<Object>} Search results
|
|
173
|
-
*/
|
|
174
|
-
async searchBySynonym(synonymTerm, limit = 10) {
|
|
175
|
-
if (!synonymTerm) {
|
|
176
|
-
return { results: [] };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
const opensearchClient = this.connection.getOpenSearchClient();
|
|
181
|
-
const indexName = this.connection.getOpenSearchIndex();
|
|
182
|
-
|
|
183
|
-
const response = await opensearchClient.search({
|
|
184
|
-
index: indexName,
|
|
185
|
-
body: {
|
|
186
|
-
size: limit,
|
|
187
|
-
query: {
|
|
188
|
-
bool: {
|
|
189
|
-
should: [
|
|
190
|
-
// Prioritize exact synonym matches
|
|
191
|
-
{ term: { 'synonyms.keyword': { value: synonymTerm, boost: 100, case_insensitive: true } } },
|
|
192
|
-
// Then prefix matches
|
|
193
|
-
{ prefix: { 'synonyms.keyword': { value: synonymTerm, boost: 50, case_insensitive: true } } },
|
|
194
|
-
// Include name matches as secondary
|
|
195
|
-
{ term: { 'chemical_name.keyword': { value: synonymTerm, boost: 30, case_insensitive: true } } },
|
|
196
|
-
{ prefix: { 'chemical_name.keyword': { value: synonymTerm, boost: 10, case_insensitive: true } } }
|
|
197
|
-
],
|
|
198
|
-
minimum_should_match: 1
|
|
199
|
-
}
|
|
200
|
-
},
|
|
201
|
-
_source: ['postgres_id', 'chemical_name', 'cas_numbers', 'identifier_values', 'synonyms']
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
const hits = response.body?.hits?.hits || [];
|
|
206
|
-
const results = hits.map((hit) => ({
|
|
207
|
-
id: hit._source.postgres_id,
|
|
208
|
-
name: hit._source.chemical_name,
|
|
209
|
-
cas: hit._source.cas_numbers || [],
|
|
210
|
-
identifiers: hit._source.identifier_values || [],
|
|
211
|
-
synonyms: hit._source.synonyms || [],
|
|
212
|
-
score: hit._score
|
|
213
|
-
}));
|
|
214
|
-
|
|
215
|
-
return { results };
|
|
216
|
-
} catch (error) {
|
|
217
|
-
logError('pegasus-sdk', 'ChemicalsService', 'searchBySynonym', error);
|
|
218
|
-
throw error;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async countAll() {
|
|
223
|
-
try {
|
|
224
|
-
const db = this.getDb();
|
|
225
|
-
const result = await db.select({ count: sql`count(*)::int` }).from(schema.chemicals);
|
|
226
|
-
return { count: result[0].count };
|
|
227
|
-
} catch (error) {
|
|
228
|
-
logError('pegasus-sdk', 'ChemicalsService', 'countAll', error);
|
|
229
|
-
throw error;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
async findChemicalsWithoutDocuments(collectionName, searchTerm, pageSize) {}
|
|
234
|
-
|
|
235
|
-
async countChemicalsWithoutDocuments(collectionName) {}
|
|
236
|
-
|
|
237
|
-
registerElasticsearchHandlers(elasticsearchService) {
|
|
238
|
-
const indexPatterns = this.connection.config.indexRoutes?.chemicals || ['chemicals*'];
|
|
239
|
-
|
|
240
|
-
indexPatterns.forEach(pattern => {
|
|
241
|
-
elasticsearchService.registerIndexRoute(pattern, {
|
|
242
|
-
|
|
243
|
-
const chemical = params.body;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
module.exports = ChemicalsService;
|
|
1
|
+
const { logError } = require('@toxplanet/tphelper/logging');
|
|
2
|
+
const { getDrizzle, schema } = require('./db');
|
|
3
|
+
const { eq } = require('drizzle-orm');
|
|
4
|
+
|
|
5
|
+
class ChemicalsService {
|
|
6
|
+
constructor(connection) {
|
|
7
|
+
this.connection = connection;
|
|
8
|
+
this.db = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
getDb() {
|
|
12
|
+
if (!this.db) {
|
|
13
|
+
this.db = getDrizzle(this.connection.pgPool);
|
|
14
|
+
}
|
|
15
|
+
return this.db;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async bulkIndexFielded(documents) {}
|
|
19
|
+
|
|
20
|
+
async bulkIndexFulltext(documents) {}
|
|
21
|
+
|
|
22
|
+
async bulkIndexSubstances(substances) {}
|
|
23
|
+
|
|
24
|
+
async createChemical(chemical) {
|
|
25
|
+
try {
|
|
26
|
+
const db = this.getDb();
|
|
27
|
+
|
|
28
|
+
const [result] = await db
|
|
29
|
+
.insert(schema.chemicals)
|
|
30
|
+
.values({
|
|
31
|
+
sourceId: chemical.source_id,
|
|
32
|
+
chemicalName: chemical.chemical_name,
|
|
33
|
+
chemicalMeta: chemical.chemical_meta,
|
|
34
|
+
chemicalIdentifiers: chemical.chemical_identifiers,
|
|
35
|
+
chemicalSynonyms: chemical.chemical_synonyms,
|
|
36
|
+
chemicalCategories: chemical.chemical_categories,
|
|
37
|
+
createdAt: chemical.created_at || new Date(),
|
|
38
|
+
updatedAt: chemical.updated_at || new Date(),
|
|
39
|
+
...(chemical.imported_at && { importedAt: chemical.imported_at }),
|
|
40
|
+
...(chemical.chemical_id && { chemicalId: chemical.chemical_id })
|
|
41
|
+
})
|
|
42
|
+
.returning();
|
|
43
|
+
|
|
44
|
+
return result;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
logError('pegasus-sdk', 'ChemicalsService', 'createChemical', error);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async updateChemical(chemicalId, updates) {}
|
|
52
|
+
|
|
53
|
+
async deleteChemical(chemicalId) {
|
|
54
|
+
try {
|
|
55
|
+
const db = this.getDb();
|
|
56
|
+
|
|
57
|
+
const [deleted] = await db
|
|
58
|
+
.delete(schema.chemicals)
|
|
59
|
+
.where(eq(schema.chemicals.chemicalId, chemicalId))
|
|
60
|
+
.returning();
|
|
61
|
+
|
|
62
|
+
return deleted || null;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
logError('pegasus-sdk', 'ChemicalsService', 'deleteChemical', error);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async deleteBySourceId(sourceId) {}
|
|
70
|
+
|
|
71
|
+
async deleteCollection(collectionName) {}
|
|
72
|
+
|
|
73
|
+
async updateCollectionProperty(collectionName, propertyPath, newValue) {}
|
|
74
|
+
|
|
75
|
+
async bulkUpdateProperty(filter, propertyPath, newValue) {}
|
|
76
|
+
|
|
77
|
+
async getChemicalById(chemicalId) {
|
|
78
|
+
try {
|
|
79
|
+
const db = this.getDb();
|
|
80
|
+
|
|
81
|
+
const [result] = await db
|
|
82
|
+
.select()
|
|
83
|
+
.from(schema.chemicals)
|
|
84
|
+
.where(eq(schema.chemicals.chemicalId, chemicalId))
|
|
85
|
+
.limit(1);
|
|
86
|
+
|
|
87
|
+
return result || null;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
logError('pegasus-sdk', 'ChemicalsService', 'getChemicalById', error);
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async getChemicalBySourceId(sourceId) {}
|
|
95
|
+
|
|
96
|
+
async getChemicalsByCAS(casNumber) {}
|
|
97
|
+
|
|
98
|
+
async getChemicalsByIdentifier(identifierType, identifierValue) {}
|
|
99
|
+
|
|
100
|
+
async countByCollection(collectionName) {}
|
|
101
|
+
|
|
102
|
+
async countByIdentifier(identifierValue) {}
|
|
103
|
+
|
|
104
|
+
async countByCAS(casNumber) {}
|
|
105
|
+
|
|
106
|
+
async getTotalSynonymCount() {}
|
|
107
|
+
|
|
108
|
+
async getSynonymCount(synonymTerm) {}
|
|
109
|
+
|
|
110
|
+
async convertIdentifier(fromIdentifier, toIdentifierType) {}
|
|
111
|
+
|
|
112
|
+
async convertIdentifiersBatch(fromIdentifiers, toIdentifierType) {}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Search for chemicals by name using OpenSearch
|
|
116
|
+
* @param {string} searchTerm - Name to search for
|
|
117
|
+
* @param {number} limit - Maximum number of results (default: 10)
|
|
118
|
+
* @returns {Promise<Object>} Search results
|
|
119
|
+
*/
|
|
120
|
+
async searchByName(searchTerm, limit = 10) {
|
|
121
|
+
if (!searchTerm) {
|
|
122
|
+
return { results: [] };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const opensearchClient = this.connection.getOpenSearchClient();
|
|
127
|
+
const indexName = this.connection.getOpenSearchIndex();
|
|
128
|
+
|
|
129
|
+
const response = await opensearchClient.search({
|
|
130
|
+
index: indexName,
|
|
131
|
+
body: {
|
|
132
|
+
size: limit,
|
|
133
|
+
query: {
|
|
134
|
+
bool: {
|
|
135
|
+
should: [
|
|
136
|
+
// Prioritize exact name matches
|
|
137
|
+
{ term: { 'chemical_name.keyword': { value: searchTerm, boost: 100, case_insensitive: true } } },
|
|
138
|
+
// Then prefix matches
|
|
139
|
+
{ prefix: { 'chemical_name.keyword': { value: searchTerm, boost: 50, case_insensitive: true } } },
|
|
140
|
+
// Include synonym matches as secondary
|
|
141
|
+
{ term: { 'synonyms.keyword': { value: searchTerm, boost: 30, case_insensitive: true } } },
|
|
142
|
+
{ prefix: { 'synonyms.keyword': { value: searchTerm, boost: 10, case_insensitive: true } } }
|
|
143
|
+
],
|
|
144
|
+
minimum_should_match: 1
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
_source: ['postgres_id', 'chemical_name', 'cas_numbers', 'identifier_values', 'synonyms']
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const hits = response.body?.hits?.hits || [];
|
|
152
|
+
const results = hits.map((hit) => ({
|
|
153
|
+
id: hit._source.postgres_id,
|
|
154
|
+
name: hit._source.chemical_name,
|
|
155
|
+
cas: hit._source.cas_numbers || [],
|
|
156
|
+
identifiers: hit._source.identifier_values || [],
|
|
157
|
+
synonyms: hit._source.synonyms || [],
|
|
158
|
+
score: hit._score
|
|
159
|
+
}));
|
|
160
|
+
|
|
161
|
+
return { results };
|
|
162
|
+
} catch (error) {
|
|
163
|
+
logError('pegasus-sdk', 'ChemicalsService', 'searchByName', error);
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Search for chemicals by synonym using OpenSearch
|
|
170
|
+
* @param {string} synonymTerm - Synonym to search for
|
|
171
|
+
* @param {number} limit - Maximum number of results (default: 10)
|
|
172
|
+
* @returns {Promise<Object>} Search results
|
|
173
|
+
*/
|
|
174
|
+
async searchBySynonym(synonymTerm, limit = 10) {
|
|
175
|
+
if (!synonymTerm) {
|
|
176
|
+
return { results: [] };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const opensearchClient = this.connection.getOpenSearchClient();
|
|
181
|
+
const indexName = this.connection.getOpenSearchIndex();
|
|
182
|
+
|
|
183
|
+
const response = await opensearchClient.search({
|
|
184
|
+
index: indexName,
|
|
185
|
+
body: {
|
|
186
|
+
size: limit,
|
|
187
|
+
query: {
|
|
188
|
+
bool: {
|
|
189
|
+
should: [
|
|
190
|
+
// Prioritize exact synonym matches
|
|
191
|
+
{ term: { 'synonyms.keyword': { value: synonymTerm, boost: 100, case_insensitive: true } } },
|
|
192
|
+
// Then prefix matches
|
|
193
|
+
{ prefix: { 'synonyms.keyword': { value: synonymTerm, boost: 50, case_insensitive: true } } },
|
|
194
|
+
// Include name matches as secondary
|
|
195
|
+
{ term: { 'chemical_name.keyword': { value: synonymTerm, boost: 30, case_insensitive: true } } },
|
|
196
|
+
{ prefix: { 'chemical_name.keyword': { value: synonymTerm, boost: 10, case_insensitive: true } } }
|
|
197
|
+
],
|
|
198
|
+
minimum_should_match: 1
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
_source: ['postgres_id', 'chemical_name', 'cas_numbers', 'identifier_values', 'synonyms']
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const hits = response.body?.hits?.hits || [];
|
|
206
|
+
const results = hits.map((hit) => ({
|
|
207
|
+
id: hit._source.postgres_id,
|
|
208
|
+
name: hit._source.chemical_name,
|
|
209
|
+
cas: hit._source.cas_numbers || [],
|
|
210
|
+
identifiers: hit._source.identifier_values || [],
|
|
211
|
+
synonyms: hit._source.synonyms || [],
|
|
212
|
+
score: hit._score
|
|
213
|
+
}));
|
|
214
|
+
|
|
215
|
+
return { results };
|
|
216
|
+
} catch (error) {
|
|
217
|
+
logError('pegasus-sdk', 'ChemicalsService', 'searchBySynonym', error);
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async countAll() {
|
|
223
|
+
try {
|
|
224
|
+
const db = this.getDb();
|
|
225
|
+
const result = await db.select({ count: sql`count(*)::int` }).from(schema.chemicals);
|
|
226
|
+
return { count: result[0].count };
|
|
227
|
+
} catch (error) {
|
|
228
|
+
logError('pegasus-sdk', 'ChemicalsService', 'countAll', error);
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async findChemicalsWithoutDocuments(collectionName, searchTerm, pageSize) {}
|
|
234
|
+
|
|
235
|
+
async countChemicalsWithoutDocuments(collectionName) {}
|
|
236
|
+
|
|
237
|
+
registerElasticsearchHandlers(elasticsearchService) {
|
|
238
|
+
const indexPatterns = this.connection.config.indexRoutes?.chemicals || ['chemicals*'];
|
|
239
|
+
|
|
240
|
+
indexPatterns.forEach(pattern => {
|
|
241
|
+
elasticsearchService.registerIndexRoute(pattern, {
|
|
242
|
+
index: async (params) => {
|
|
243
|
+
const chemical = params.body;
|
|
244
|
+
return await this.createChemical(chemical);
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
bulk: async (params) => {
|
|
248
|
+
const operations = params.body || params.operations;
|
|
249
|
+
const documents = [];
|
|
250
|
+
|
|
251
|
+
for (let i = 0; i < operations.length; i += 2) {
|
|
252
|
+
const action = operations[i];
|
|
253
|
+
const document = operations[i + 1];
|
|
254
|
+
|
|
255
|
+
if (action.index || action.create) {
|
|
256
|
+
documents.push(document);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return await this.bulkIndexFielded(documents);
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
get: async (params) => {
|
|
264
|
+
return await this.getChemicalById(params.id);
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
update: async (params) => {
|
|
268
|
+
return await this.updateChemical(params.id, params.body);
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
delete: async (params) => {
|
|
272
|
+
return await this.deleteChemical(params.id);
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
search: async (params) => {
|
|
276
|
+
const query = params.body?.query;
|
|
277
|
+
const searchTerm = query?.match?.chemical_name ||
|
|
278
|
+
query?.term?.chemical_name ||
|
|
279
|
+
query?.query_string?.query || '';
|
|
280
|
+
const limit = params.body?.size || 10;
|
|
281
|
+
|
|
282
|
+
return await this.searchByName(searchTerm, limit);
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
count: async (params) => {
|
|
286
|
+
return await this.countAll();
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = ChemicalsService;
|