@cumulus/db 19.1.0 → 19.2.0-alpha.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/dist/index.d.ts +11 -4
- package/dist/index.js +21 -6
- package/dist/lib/QuerySearchClient.d.ts +0 -2
- package/dist/lib/QuerySearchClient.js +0 -2
- package/dist/lib/collection.d.ts +8 -0
- package/dist/lib/collection.js +36 -1
- package/dist/lib/execution.d.ts +14 -12
- package/dist/lib/execution.js +23 -27
- package/dist/lib/granule.d.ts +13 -17
- package/dist/lib/granule.js +22 -24
- package/dist/migrations/20240814185217_create_reconciliation_reports_table.d.ts +4 -0
- package/dist/migrations/20240814185217_create_reconciliation_reports_table.js +40 -0
- package/dist/models/file.d.ts +4 -0
- package/dist/models/file.js +8 -0
- package/dist/models/reconciliation_report.d.ts +10 -0
- package/dist/models/reconciliation_report.js +25 -0
- package/dist/search/AsyncOperationSearch.d.ts +32 -0
- package/dist/search/AsyncOperationSearch.js +55 -0
- package/dist/search/BaseSearch.d.ts +8 -2
- package/dist/search/BaseSearch.js +40 -13
- package/dist/search/CollectionSearch.d.ts +14 -21
- package/dist/search/CollectionSearch.js +50 -52
- package/dist/search/ExecutionSearch.d.ts +4 -4
- package/dist/search/ExecutionSearch.js +7 -8
- package/dist/search/GranuleSearch.d.ts +14 -6
- package/dist/search/GranuleSearch.js +56 -5
- package/dist/search/PdrSearch.d.ts +50 -0
- package/dist/search/PdrSearch.js +100 -0
- package/dist/search/ProviderSearch.d.ts +32 -0
- package/dist/search/ProviderSearch.js +57 -0
- package/dist/search/ReconciliationReportSearch.d.ts +42 -0
- package/dist/search/ReconciliationReportSearch.js +72 -0
- package/dist/search/RuleSearch.d.ts +49 -0
- package/dist/search/RuleSearch.js +95 -0
- package/dist/search/StatsSearch.d.ts +0 -1
- package/dist/search/StatsSearch.js +3 -3
- package/dist/search/field-mapping.js +105 -1
- package/dist/search/queries.js +4 -2
- package/dist/tables.d.ts +1 -0
- package/dist/tables.js +1 -0
- package/dist/test-utils.d.ts +3 -17
- package/dist/test-utils.js +10 -1
- package/dist/translate/async_operations.js +2 -1
- package/dist/translate/pdr.d.ts +19 -0
- package/dist/translate/pdr.js +36 -19
- package/dist/translate/reconciliation_reports.d.ts +17 -0
- package/dist/translate/reconciliation_reports.js +36 -0
- package/dist/translate/rules.d.ts +3 -0
- package/dist/translate/rules.js +16 -12
- package/dist/types/reconciliation_report.d.ts +29 -0
- package/dist/types/reconciliation_report.js +3 -0
- package/dist/types/search.d.ts +3 -2
- package/package.json +8 -8
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AsyncOperationSearch = void 0;
|
|
7
|
+
const pick_1 = __importDefault(require("lodash/pick"));
|
|
8
|
+
const logger_1 = __importDefault(require("@cumulus/logger"));
|
|
9
|
+
const BaseSearch_1 = require("./BaseSearch");
|
|
10
|
+
const async_operations_1 = require("../translate/async_operations");
|
|
11
|
+
const log = new logger_1.default({ sender: '@cumulus/db/AsyncOperationSearch' });
|
|
12
|
+
/**
|
|
13
|
+
* Class to build and execute db search query for asyncOperation
|
|
14
|
+
*/
|
|
15
|
+
class AsyncOperationSearch extends BaseSearch_1.BaseSearch {
|
|
16
|
+
constructor(event) {
|
|
17
|
+
super(event, 'asyncOperation');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Build queries for infix and prefix
|
|
21
|
+
*
|
|
22
|
+
* @param params
|
|
23
|
+
* @param params.countQuery - query builder for getting count
|
|
24
|
+
* @param params.searchQuery - query builder for search
|
|
25
|
+
* @param [params.dbQueryParameters] - db query parameters
|
|
26
|
+
*/
|
|
27
|
+
buildInfixPrefixQuery(params) {
|
|
28
|
+
const { countQuery, searchQuery, dbQueryParameters } = params;
|
|
29
|
+
const { infix, prefix } = dbQueryParameters ?? this.dbQueryParameters;
|
|
30
|
+
if (infix) {
|
|
31
|
+
[countQuery, searchQuery].forEach((query) => query.whereRaw(`${this.tableName}.id::text like ?`, `%${infix}%`));
|
|
32
|
+
}
|
|
33
|
+
if (prefix) {
|
|
34
|
+
[countQuery, searchQuery].forEach((query) => query.whereRaw(`${this.tableName}.id::text like ?`, `${prefix}%`));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Translate postgres records to api records
|
|
39
|
+
*
|
|
40
|
+
* @param pgRecords - postgres records returned from query
|
|
41
|
+
* @returns translated api records
|
|
42
|
+
*/
|
|
43
|
+
translatePostgresRecordsToApiRecords(pgRecords) {
|
|
44
|
+
log.debug(`translatePostgresRecordsToApiRecords number of records ${pgRecords.length} `);
|
|
45
|
+
const { fields } = this.dbQueryParameters;
|
|
46
|
+
const apiRecords = pgRecords.map((item) => {
|
|
47
|
+
const pgAsyncOperation = item;
|
|
48
|
+
const apiRecord = (0, async_operations_1.translatePostgresAsyncOperationToApiAsyncOperation)(pgAsyncOperation);
|
|
49
|
+
return fields ? (0, pick_1.default)(apiRecord, fields) : apiRecord;
|
|
50
|
+
});
|
|
51
|
+
return apiRecords;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.AsyncOperationSearch = AsyncOperationSearch;
|
|
55
|
+
//# sourceMappingURL=AsyncOperationSearch.js.map
|
|
@@ -7,7 +7,7 @@ export declare const typeToTable: {
|
|
|
7
7
|
/**
|
|
8
8
|
* Class to build and execute db search query
|
|
9
9
|
*/
|
|
10
|
-
declare class BaseSearch {
|
|
10
|
+
declare abstract class BaseSearch {
|
|
11
11
|
readonly type: string;
|
|
12
12
|
readonly tableName: string;
|
|
13
13
|
readonly queryStringParameters: QueryStringParameters;
|
|
@@ -19,6 +19,12 @@ declare class BaseSearch {
|
|
|
19
19
|
* @returns whether collection search is needed
|
|
20
20
|
*/
|
|
21
21
|
protected searchCollection(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* check if joined executions table search is needed
|
|
24
|
+
*
|
|
25
|
+
* @returns whether execution search is needed
|
|
26
|
+
*/
|
|
27
|
+
protected searchExecution(): boolean;
|
|
22
28
|
/**
|
|
23
29
|
* check if joined pdrs table search is needed
|
|
24
30
|
*
|
|
@@ -58,7 +64,7 @@ declare class BaseSearch {
|
|
|
58
64
|
* Build basic query
|
|
59
65
|
*
|
|
60
66
|
* @param knex - DB client
|
|
61
|
-
* @
|
|
67
|
+
* @returns queries for getting count and search result
|
|
62
68
|
*/
|
|
63
69
|
protected buildBasicQuery(knex: Knex): {
|
|
64
70
|
countQuery?: Knex.QueryBuilder;
|
|
@@ -19,6 +19,7 @@ exports.typeToTable = {
|
|
|
19
19
|
pdr: tables_1.TableNames.pdrs,
|
|
20
20
|
provider: tables_1.TableNames.providers,
|
|
21
21
|
rule: tables_1.TableNames.rules,
|
|
22
|
+
reconciliationReport: tables_1.TableNames.reconciliationReports,
|
|
22
23
|
};
|
|
23
24
|
/**
|
|
24
25
|
* Class to build and execute db search query
|
|
@@ -46,6 +47,15 @@ class BaseSearch {
|
|
|
46
47
|
|| terms?.collectionName
|
|
47
48
|
|| terms?.collectionVersion);
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* check if joined executions table search is needed
|
|
52
|
+
*
|
|
53
|
+
* @returns whether execution search is needed
|
|
54
|
+
*/
|
|
55
|
+
searchExecution() {
|
|
56
|
+
const { not, term, terms } = this.dbQueryParameters;
|
|
57
|
+
return !!(not?.executionArn || term?.executionArn || terms?.executionArn);
|
|
58
|
+
}
|
|
49
59
|
/**
|
|
50
60
|
* check if joined pdrs table search is needed
|
|
51
61
|
*
|
|
@@ -113,11 +123,14 @@ class BaseSearch {
|
|
|
113
123
|
* Build basic query
|
|
114
124
|
*
|
|
115
125
|
* @param knex - DB client
|
|
116
|
-
* @
|
|
126
|
+
* @returns queries for getting count and search result
|
|
117
127
|
*/
|
|
118
128
|
buildBasicQuery(knex) {
|
|
119
|
-
|
|
120
|
-
|
|
129
|
+
const countQuery = knex(this.tableName)
|
|
130
|
+
.count('*');
|
|
131
|
+
const searchQuery = knex(this.tableName)
|
|
132
|
+
.select(`${this.tableName}.*`);
|
|
133
|
+
return { countQuery, searchQuery };
|
|
121
134
|
}
|
|
122
135
|
/**
|
|
123
136
|
* Build queries for infix and prefix
|
|
@@ -150,6 +163,9 @@ class BaseSearch {
|
|
|
150
163
|
case 'collectionVersion':
|
|
151
164
|
[countQuery, searchQuery].forEach((query) => query?.[queryMethod](`${this.tableName}.collection_cumulus_id`));
|
|
152
165
|
break;
|
|
166
|
+
case 'executionArn':
|
|
167
|
+
[countQuery, searchQuery].forEach((query) => query?.[queryMethod](`${this.tableName}.execution_cumulus_id`));
|
|
168
|
+
break;
|
|
153
169
|
case 'providerName':
|
|
154
170
|
[countQuery, searchQuery].forEach((query) => query?.[queryMethod](`${this.tableName}.provider_cumulus_id`));
|
|
155
171
|
break;
|
|
@@ -185,13 +201,12 @@ class BaseSearch {
|
|
|
185
201
|
const { countQuery, searchQuery, dbQueryParameters } = params;
|
|
186
202
|
const { range = {} } = dbQueryParameters ?? this.dbQueryParameters;
|
|
187
203
|
Object.entries(range).forEach(([name, rangeValues]) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
searchQuery.where(`${this.tableName}.${name}`, '>=',
|
|
204
|
+
const { gte, lte } = rangeValues;
|
|
205
|
+
if (gte) {
|
|
206
|
+
[countQuery, searchQuery].forEach((query) => query?.where(`${this.tableName}.${name}`, '>=', gte));
|
|
191
207
|
}
|
|
192
|
-
if (
|
|
193
|
-
countQuery?.where(`${this.tableName}.${name}`, '<=',
|
|
194
|
-
searchQuery.where(`${this.tableName}.${name}`, '<=', rangeValues.lte);
|
|
208
|
+
if (lte) {
|
|
209
|
+
[countQuery, searchQuery].forEach((query) => query?.where(`${this.tableName}.${name}`, '<=', lte));
|
|
195
210
|
}
|
|
196
211
|
});
|
|
197
212
|
}
|
|
@@ -215,6 +230,9 @@ class BaseSearch {
|
|
|
215
230
|
case 'collectionVersion':
|
|
216
231
|
[countQuery, searchQuery].forEach((query) => query?.where(`${collectionsTable}.version`, value));
|
|
217
232
|
break;
|
|
233
|
+
case 'executionArn':
|
|
234
|
+
[countQuery, searchQuery].forEach((query) => query?.where(`${executionsTable}.arn`, value));
|
|
235
|
+
break;
|
|
218
236
|
case 'providerName':
|
|
219
237
|
[countQuery, searchQuery].forEach((query) => query?.where(`${providersTable}.name`, value));
|
|
220
238
|
break;
|
|
@@ -223,7 +241,7 @@ class BaseSearch {
|
|
|
223
241
|
break;
|
|
224
242
|
case 'error.Error':
|
|
225
243
|
[countQuery, searchQuery]
|
|
226
|
-
.forEach((query) => query?.whereRaw(`${this.tableName}.error->>'Error' =
|
|
244
|
+
.forEach((query) => value && query?.whereRaw(`${this.tableName}.error->>'Error' = ?`, value));
|
|
227
245
|
break;
|
|
228
246
|
case 'asyncOperationId':
|
|
229
247
|
[countQuery, searchQuery].forEach((query) => query?.where(`${asyncOperationsTable}.id`, value));
|
|
@@ -265,6 +283,9 @@ class BaseSearch {
|
|
|
265
283
|
}
|
|
266
284
|
Object.entries((0, omit_1.default)(terms, ['collectionName', 'collectionVersion'])).forEach(([name, value]) => {
|
|
267
285
|
switch (name) {
|
|
286
|
+
case 'executionArn':
|
|
287
|
+
[countQuery, searchQuery].forEach((query) => query?.whereIn(`${executionsTable}.arn`, value));
|
|
288
|
+
break;
|
|
268
289
|
case 'providerName':
|
|
269
290
|
[countQuery, searchQuery].forEach((query) => query?.whereIn(`${providersTable}.name`, value));
|
|
270
291
|
break;
|
|
@@ -273,7 +294,7 @@ class BaseSearch {
|
|
|
273
294
|
break;
|
|
274
295
|
case 'error.Error':
|
|
275
296
|
[countQuery, searchQuery]
|
|
276
|
-
.forEach((query) => query?.whereRaw(`${this.tableName}.error->>'Error' in (
|
|
297
|
+
.forEach((query) => query?.whereRaw(`${this.tableName}.error->>'Error' in (${value.map(() => '?').join(',')})`, [...value]));
|
|
277
298
|
break;
|
|
278
299
|
case 'asyncOperationId':
|
|
279
300
|
[countQuery, searchQuery].forEach((query) => query?.whereIn(`${asyncOperationsTable}.id`, value));
|
|
@@ -308,6 +329,9 @@ class BaseSearch {
|
|
|
308
329
|
}
|
|
309
330
|
Object.entries((0, omit_1.default)(term, ['collectionName', 'collectionVersion'])).forEach(([name, value]) => {
|
|
310
331
|
switch (name) {
|
|
332
|
+
case 'executionArn':
|
|
333
|
+
[countQuery, searchQuery].forEach((query) => query?.whereNot(`${executionsTable}.arn`, value));
|
|
334
|
+
break;
|
|
311
335
|
case 'providerName':
|
|
312
336
|
[countQuery, searchQuery].forEach((query) => query?.whereNot(`${providersTable}.name`, value));
|
|
313
337
|
break;
|
|
@@ -321,7 +345,7 @@ class BaseSearch {
|
|
|
321
345
|
[countQuery, searchQuery].forEach((query) => query?.whereNot(`${executionsTable}_parent.arn`, value));
|
|
322
346
|
break;
|
|
323
347
|
case 'error.Error':
|
|
324
|
-
[countQuery, searchQuery].forEach((query) => query?.whereRaw(`${this.tableName}.error->>'Error' !=
|
|
348
|
+
[countQuery, searchQuery].forEach((query) => value && query?.whereRaw(`${this.tableName}.error->>'Error' != ?`, value));
|
|
325
349
|
break;
|
|
326
350
|
default:
|
|
327
351
|
[countQuery, searchQuery].forEach((query) => query?.whereNot(`${this.tableName}.${name}`, value));
|
|
@@ -343,6 +367,9 @@ class BaseSearch {
|
|
|
343
367
|
if (key.column.startsWith('error')) {
|
|
344
368
|
searchQuery.orderByRaw(`${this.tableName}.error ->> 'Error' ${key.order}`);
|
|
345
369
|
}
|
|
370
|
+
else if (dbQueryParameters?.collate) {
|
|
371
|
+
searchQuery.orderByRaw(`${key} collate \"${dbQueryParameters.collate}\"`);
|
|
372
|
+
}
|
|
346
373
|
else {
|
|
347
374
|
searchQuery.orderBy([key]);
|
|
348
375
|
}
|
|
@@ -369,7 +396,7 @@ class BaseSearch {
|
|
|
369
396
|
*/
|
|
370
397
|
async getEstimatedRowcount(params) {
|
|
371
398
|
const { knex, tableName = this.tableName } = params;
|
|
372
|
-
const query = knex.raw(
|
|
399
|
+
const query = knex.raw('EXPLAIN (FORMAT JSON) select * from ??', tableName);
|
|
373
400
|
log.debug(`Estimating the row count ${query.toSQL().sql}`);
|
|
374
401
|
const countResult = await query;
|
|
375
402
|
const countPath = 'rows[0]["QUERY PLAN"][0].Plan["Plan Rows"]';
|
|
@@ -20,16 +20,6 @@ export declare class CollectionSearch extends BaseSearch {
|
|
|
20
20
|
readonly active: boolean;
|
|
21
21
|
readonly includeStats: boolean;
|
|
22
22
|
constructor(event: QueryEvent);
|
|
23
|
-
/**
|
|
24
|
-
* Build basic query
|
|
25
|
-
*
|
|
26
|
-
* @param knex - DB client
|
|
27
|
-
* @returns queries for getting count and search result
|
|
28
|
-
*/
|
|
29
|
-
protected buildBasicQuery(knex: Knex): {
|
|
30
|
-
countQuery: Knex.QueryBuilder;
|
|
31
|
-
searchQuery: Knex.QueryBuilder;
|
|
32
|
-
};
|
|
33
23
|
/**
|
|
34
24
|
* Build queries for infix and prefix
|
|
35
25
|
*
|
|
@@ -44,20 +34,23 @@ export declare class CollectionSearch extends BaseSearch {
|
|
|
44
34
|
dbQueryParameters?: DbQueryParameters;
|
|
45
35
|
}): void;
|
|
46
36
|
/**
|
|
47
|
-
* Build
|
|
37
|
+
* Build subquery for active collections
|
|
38
|
+
* The subquery will search granules
|
|
48
39
|
*
|
|
49
|
-
* @param
|
|
50
|
-
* @
|
|
51
|
-
* @param [params.countQuery] - query builder for getting count
|
|
52
|
-
* @param params.searchQuery - query builder for search
|
|
53
|
-
* @param [params.dbQueryParameters] - db query parameters
|
|
40
|
+
* @param knex - db client
|
|
41
|
+
* @returns granule query
|
|
54
42
|
*/
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
43
|
+
private buildSubQueryForActiveCollections;
|
|
44
|
+
/**
|
|
45
|
+
* Build the search query
|
|
46
|
+
*
|
|
47
|
+
* @param knex - DB client
|
|
48
|
+
* @returns queries for getting count and search result
|
|
49
|
+
*/
|
|
50
|
+
protected buildSearch(knex: Knex): {
|
|
51
|
+
countQuery?: Knex.QueryBuilder;
|
|
58
52
|
searchQuery: Knex.QueryBuilder;
|
|
59
|
-
|
|
60
|
-
}): void;
|
|
53
|
+
};
|
|
61
54
|
/**
|
|
62
55
|
* Executes stats query to get granules' status aggregation
|
|
63
56
|
*
|
|
@@ -4,12 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.CollectionSearch = void 0;
|
|
7
|
+
const omitBy_1 = __importDefault(require("lodash/omitBy"));
|
|
7
8
|
const pick_1 = __importDefault(require("lodash/pick"));
|
|
8
9
|
const logger_1 = __importDefault(require("@cumulus/logger"));
|
|
9
10
|
const BaseSearch_1 = require("./BaseSearch");
|
|
11
|
+
const queries_1 = require("./queries");
|
|
12
|
+
const GranuleSearch_1 = require("./GranuleSearch");
|
|
10
13
|
const collections_1 = require("../translate/collections");
|
|
11
14
|
const tables_1 = require("../tables");
|
|
12
15
|
const log = new logger_1.default({ sender: '@cumulus/db/CollectionSearch' });
|
|
16
|
+
const granuleFields = ['createdAt', 'granuleId', 'timestamp', 'updatedAt'];
|
|
17
|
+
const isGranuleField = (_value, key) => granuleFields.includes(key.split('__')[0]);
|
|
13
18
|
/**
|
|
14
19
|
* Class to build and execute db search query for collections
|
|
15
20
|
*/
|
|
@@ -19,19 +24,10 @@ class CollectionSearch extends BaseSearch_1.BaseSearch {
|
|
|
19
24
|
super({ queryStringParameters }, 'collection');
|
|
20
25
|
this.active = (active === 'true');
|
|
21
26
|
this.includeStats = (includeStats === 'true');
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
* @param knex - DB client
|
|
27
|
-
* @returns queries for getting count and search result
|
|
28
|
-
*/
|
|
29
|
-
buildBasicQuery(knex) {
|
|
30
|
-
const countQuery = knex(this.tableName)
|
|
31
|
-
.count('*');
|
|
32
|
-
const searchQuery = knex(this.tableName)
|
|
33
|
-
.select(`${this.tableName}.*`);
|
|
34
|
-
return { countQuery, searchQuery };
|
|
27
|
+
// for active collection search, omit the fields which are for searching granules
|
|
28
|
+
if (this.active) {
|
|
29
|
+
this.dbQueryParameters = (0, queries_1.convertQueryStringToDbQueryParameters)(this.type, (0, omitBy_1.default)(this.queryStringParameters, isGranuleField));
|
|
30
|
+
}
|
|
35
31
|
}
|
|
36
32
|
/**
|
|
37
33
|
* Build queries for infix and prefix
|
|
@@ -48,38 +44,43 @@ class CollectionSearch extends BaseSearch_1.BaseSearch {
|
|
|
48
44
|
[countQuery, searchQuery].forEach((query) => query.whereLike(`${this.tableName}.name`, `%${infix}%`));
|
|
49
45
|
}
|
|
50
46
|
if (prefix) {
|
|
51
|
-
[countQuery, searchQuery].forEach((query) => query.whereLike(`${this.tableName}.name`,
|
|
47
|
+
[countQuery, searchQuery].forEach((query) => query.whereLike(`${this.tableName}.name`, `${prefix}%`));
|
|
52
48
|
}
|
|
53
49
|
}
|
|
54
50
|
/**
|
|
55
|
-
* Build
|
|
51
|
+
* Build subquery for active collections
|
|
52
|
+
* The subquery will search granules
|
|
56
53
|
*
|
|
57
|
-
* @param
|
|
58
|
-
* @
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
* @param knex - db client
|
|
55
|
+
* @returns granule query
|
|
56
|
+
*/
|
|
57
|
+
buildSubQueryForActiveCollections(knex) {
|
|
58
|
+
const granulesTable = tables_1.TableNames.granules;
|
|
59
|
+
const granuleSearch = new GranuleSearch_1.GranuleSearch({ queryStringParameters: this.queryStringParameters });
|
|
60
|
+
const { countQuery: subQuery } = granuleSearch.buildSearchForActiveCollections(knex);
|
|
61
|
+
subQuery
|
|
62
|
+
.clear('select')
|
|
63
|
+
.select(1)
|
|
64
|
+
.where(`${granulesTable}.collection_cumulus_id`, knex.raw(`${this.tableName}.cumulus_id`))
|
|
65
|
+
.limit(1);
|
|
66
|
+
return subQuery;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build the search query
|
|
70
|
+
*
|
|
71
|
+
* @param knex - DB client
|
|
72
|
+
* @returns queries for getting count and search result
|
|
62
73
|
*/
|
|
63
|
-
|
|
74
|
+
buildSearch(knex) {
|
|
75
|
+
const queries = super.buildSearch(knex);
|
|
64
76
|
if (!this.active) {
|
|
65
|
-
|
|
66
|
-
return;
|
|
77
|
+
return queries;
|
|
67
78
|
}
|
|
68
|
-
const
|
|
69
|
-
const {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Object.entries(range).forEach(([name, rangeValues]) => {
|
|
74
|
-
if (rangeValues.gte) {
|
|
75
|
-
subQuery.where(`${granulesTable}.${name}`, '>=', rangeValues.gte);
|
|
76
|
-
}
|
|
77
|
-
if (rangeValues.lte) {
|
|
78
|
-
subQuery.where(`${granulesTable}.${name}`, '<=', rangeValues.lte);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
subQuery.limit(1);
|
|
82
|
-
[countQuery, searchQuery].forEach((query) => query.whereExists(subQuery));
|
|
79
|
+
const subQuery = this.buildSubQueryForActiveCollections(knex);
|
|
80
|
+
const { countQuery, searchQuery } = queries;
|
|
81
|
+
[countQuery, searchQuery].forEach((query) => query?.whereExists(subQuery));
|
|
82
|
+
log.debug(`buildSearch returns countQuery: ${countQuery?.toSQL().sql}, searchQuery: ${searchQuery.toSQL().sql}`);
|
|
83
|
+
return { countQuery, searchQuery };
|
|
83
84
|
}
|
|
84
85
|
/**
|
|
85
86
|
* Executes stats query to get granules' status aggregation
|
|
@@ -90,21 +91,19 @@ class CollectionSearch extends BaseSearch_1.BaseSearch {
|
|
|
90
91
|
*/
|
|
91
92
|
async retrieveGranuleStats(collectionCumulusIds, knex) {
|
|
92
93
|
const granulesTable = tables_1.TableNames.granules;
|
|
93
|
-
|
|
94
|
+
let statsQuery = knex(granulesTable);
|
|
95
|
+
if (this.active) {
|
|
96
|
+
const granuleSearch = new GranuleSearch_1.GranuleSearch({
|
|
97
|
+
queryStringParameters: this.queryStringParameters,
|
|
98
|
+
});
|
|
99
|
+
const { countQuery } = granuleSearch.buildSearchForActiveCollections(knex);
|
|
100
|
+
statsQuery = countQuery.clear('select');
|
|
101
|
+
}
|
|
102
|
+
statsQuery
|
|
94
103
|
.select(`${granulesTable}.collection_cumulus_id`, `${granulesTable}.status`)
|
|
95
104
|
.count('*')
|
|
96
105
|
.groupBy(`${granulesTable}.collection_cumulus_id`, `${granulesTable}.status`)
|
|
97
106
|
.whereIn(`${granulesTable}.collection_cumulus_id`, collectionCumulusIds);
|
|
98
|
-
if (this.active) {
|
|
99
|
-
Object.entries(this.dbQueryParameters?.range ?? {}).forEach(([name, rangeValues]) => {
|
|
100
|
-
if (rangeValues.gte) {
|
|
101
|
-
statsQuery.where(`${granulesTable}.${name}`, '>=', rangeValues.gte);
|
|
102
|
-
}
|
|
103
|
-
if (rangeValues.lte) {
|
|
104
|
-
statsQuery.where(`${granulesTable}.${name}`, '<=', rangeValues.lte);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
107
|
log.debug(`retrieveGranuleStats statsQuery: ${statsQuery?.toSQL().sql}`);
|
|
109
108
|
const results = await statsQuery;
|
|
110
109
|
const reduced = results.reduce((acc, record) => {
|
|
@@ -133,6 +132,7 @@ class CollectionSearch extends BaseSearch_1.BaseSearch {
|
|
|
133
132
|
*/
|
|
134
133
|
async translatePostgresRecordsToApiRecords(pgRecords, knex) {
|
|
135
134
|
log.debug(`translatePostgresRecordsToApiRecords number of records ${pgRecords.length} `);
|
|
135
|
+
const { fields } = this.dbQueryParameters;
|
|
136
136
|
let statsRecords;
|
|
137
137
|
const cumulusIds = pgRecords.map((record) => record.cumulus_id);
|
|
138
138
|
if (this.includeStats) {
|
|
@@ -140,9 +140,7 @@ class CollectionSearch extends BaseSearch_1.BaseSearch {
|
|
|
140
140
|
}
|
|
141
141
|
const apiRecords = pgRecords.map((record) => {
|
|
142
142
|
const apiRecord = (0, collections_1.translatePostgresCollectionToApiCollection)(record);
|
|
143
|
-
const apiRecordFinal =
|
|
144
|
-
? (0, pick_1.default)(apiRecord, this.dbQueryParameters.fields)
|
|
145
|
-
: apiRecord;
|
|
143
|
+
const apiRecordFinal = fields ? (0, pick_1.default)(apiRecord, fields) : apiRecord;
|
|
146
144
|
if (statsRecords) {
|
|
147
145
|
apiRecordFinal.stats = statsRecords[record.cumulus_id] ? statsRecords[record.cumulus_id]
|
|
148
146
|
: {
|
|
@@ -16,15 +16,15 @@ interface ExecutionRecord extends BaseRecord, PostgresExecutionRecord {
|
|
|
16
16
|
export declare class ExecutionSearch extends BaseSearch {
|
|
17
17
|
constructor(event: QueryEvent);
|
|
18
18
|
/**
|
|
19
|
-
* check if joined
|
|
19
|
+
* check if joined async_operations table search is needed
|
|
20
20
|
*
|
|
21
|
-
* @returns whether
|
|
21
|
+
* @returns whether async_operations search is needed
|
|
22
22
|
*/
|
|
23
23
|
protected searchAsync(): boolean;
|
|
24
24
|
/**
|
|
25
|
-
* check if joined
|
|
25
|
+
* check if joined parent execution table search is needed
|
|
26
26
|
*
|
|
27
|
-
* @returns whether
|
|
27
|
+
* @returns whether parent execution search is needed
|
|
28
28
|
*/
|
|
29
29
|
protected searchParent(): boolean;
|
|
30
30
|
/**
|
|
@@ -24,18 +24,18 @@ class ExecutionSearch extends BaseSearch_1.BaseSearch {
|
|
|
24
24
|
super(event, 'execution');
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* check if joined
|
|
27
|
+
* check if joined async_operations table search is needed
|
|
28
28
|
*
|
|
29
|
-
* @returns whether
|
|
29
|
+
* @returns whether async_operations search is needed
|
|
30
30
|
*/
|
|
31
31
|
searchAsync() {
|
|
32
32
|
const { not, term, terms } = this.dbQueryParameters;
|
|
33
33
|
return (!!(not?.asyncOperationId || term?.asyncOperationId || terms?.asyncOperationId));
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
* check if joined
|
|
36
|
+
* check if joined parent execution table search is needed
|
|
37
37
|
*
|
|
38
|
-
* @returns whether
|
|
38
|
+
* @returns whether parent execution search is needed
|
|
39
39
|
*/
|
|
40
40
|
searchParent() {
|
|
41
41
|
const { not, term, terms } = this.dbQueryParameters;
|
|
@@ -101,7 +101,7 @@ class ExecutionSearch extends BaseSearch_1.BaseSearch {
|
|
|
101
101
|
[countQuery, searchQuery].forEach((query) => query.whereLike(`${this.tableName}.arn`, `%${infix}%`));
|
|
102
102
|
}
|
|
103
103
|
if (prefix) {
|
|
104
|
-
[countQuery, searchQuery].forEach((query) => query.whereLike(`${this.tableName}.arn`,
|
|
104
|
+
[countQuery, searchQuery].forEach((query) => query.whereLike(`${this.tableName}.arn`, `${prefix}%`));
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
@@ -112,6 +112,7 @@ class ExecutionSearch extends BaseSearch_1.BaseSearch {
|
|
|
112
112
|
*/
|
|
113
113
|
translatePostgresRecordsToApiRecords(pgRecords) {
|
|
114
114
|
log.debug(`translatePostgresRecordsToApiRecords number of records ${pgRecords.length} `);
|
|
115
|
+
const { fields } = this.dbQueryParameters;
|
|
115
116
|
const apiRecords = pgRecords.map((executionRecord) => {
|
|
116
117
|
const { collectionName, collectionVersion, asyncOperationId, parentArn } = executionRecord;
|
|
117
118
|
const collectionId = collectionName && collectionVersion
|
|
@@ -122,9 +123,7 @@ class ExecutionSearch extends BaseSearch_1.BaseSearch {
|
|
|
122
123
|
asyncOperationId,
|
|
123
124
|
parentArn,
|
|
124
125
|
});
|
|
125
|
-
return
|
|
126
|
-
? (0, pick_1.default)(apiRecord, this.dbQueryParameters.fields)
|
|
127
|
-
: apiRecord;
|
|
126
|
+
return fields ? (0, pick_1.default)(apiRecord, fields) : apiRecord;
|
|
128
127
|
});
|
|
129
128
|
return apiRecords;
|
|
130
129
|
}
|
|
@@ -5,14 +5,9 @@ import { BaseSearch } from './BaseSearch';
|
|
|
5
5
|
import { DbQueryParameters, QueryEvent } from '../types/search';
|
|
6
6
|
import { PostgresGranuleRecord } from '../types/granule';
|
|
7
7
|
interface GranuleRecord extends BaseRecord, PostgresGranuleRecord {
|
|
8
|
-
cumulus_id: number;
|
|
9
|
-
updated_at: Date;
|
|
10
|
-
collection_cumulus_id: number;
|
|
11
8
|
collectionName: string;
|
|
12
9
|
collectionVersion: string;
|
|
13
|
-
pdr_cumulus_id: number;
|
|
14
10
|
pdrName?: string;
|
|
15
|
-
provider_cumulus_id?: number;
|
|
16
11
|
providerName?: string;
|
|
17
12
|
}
|
|
18
13
|
/**
|
|
@@ -43,13 +38,26 @@ export declare class GranuleSearch extends BaseSearch {
|
|
|
43
38
|
searchQuery: Knex.QueryBuilder;
|
|
44
39
|
dbQueryParameters?: DbQueryParameters;
|
|
45
40
|
}): void;
|
|
41
|
+
/**
|
|
42
|
+
* Build the search query for active collections.
|
|
43
|
+
* If time params are specified the query will search granules that have been updated
|
|
44
|
+
* in that time frame. If granuleId or providerId are provided, it will filter those as well.
|
|
45
|
+
*
|
|
46
|
+
* @param knex - DB client
|
|
47
|
+
* @returns queries for getting count and search result
|
|
48
|
+
*/
|
|
49
|
+
buildSearchForActiveCollections(knex: Knex): {
|
|
50
|
+
countQuery: Knex.QueryBuilder;
|
|
51
|
+
searchQuery: Knex.QueryBuilder;
|
|
52
|
+
};
|
|
46
53
|
/**
|
|
47
54
|
* Translate postgres records to api records
|
|
48
55
|
*
|
|
49
56
|
* @param pgRecords - postgres records returned from query
|
|
57
|
+
* @param knex - DB client
|
|
50
58
|
* @returns translated api records
|
|
51
59
|
*/
|
|
52
|
-
protected translatePostgresRecordsToApiRecords(pgRecords: GranuleRecord[]): Partial<ApiGranuleRecord>[]
|
|
60
|
+
protected translatePostgresRecordsToApiRecords(pgRecords: GranuleRecord[], knex: Knex): Promise<Partial<ApiGranuleRecord>[]>;
|
|
53
61
|
}
|
|
54
62
|
export {};
|
|
55
63
|
//# sourceMappingURL=GranuleSearch.d.ts.map
|
|
@@ -10,6 +10,8 @@ const logger_1 = __importDefault(require("@cumulus/logger"));
|
|
|
10
10
|
const BaseSearch_1 = require("./BaseSearch");
|
|
11
11
|
const granules_1 = require("../translate/granules");
|
|
12
12
|
const tables_1 = require("../tables");
|
|
13
|
+
const file_1 = require("../models/file");
|
|
14
|
+
const execution_1 = require("../lib/execution");
|
|
13
15
|
const log = new logger_1.default({ sender: '@cumulus/db/GranuleSearch' });
|
|
14
16
|
/**
|
|
15
17
|
* Class to build and execute db search query for granules
|
|
@@ -78,14 +80,56 @@ class GranuleSearch extends BaseSearch_1.BaseSearch {
|
|
|
78
80
|
[countQuery, searchQuery].forEach((query) => query.whereLike(`${this.tableName}.granule_id`, `${prefix}%`));
|
|
79
81
|
}
|
|
80
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Build the search query for active collections.
|
|
85
|
+
* If time params are specified the query will search granules that have been updated
|
|
86
|
+
* in that time frame. If granuleId or providerId are provided, it will filter those as well.
|
|
87
|
+
*
|
|
88
|
+
* @param knex - DB client
|
|
89
|
+
* @returns queries for getting count and search result
|
|
90
|
+
*/
|
|
91
|
+
buildSearchForActiveCollections(knex) {
|
|
92
|
+
const { countQuery, searchQuery } = this.buildBasicQuery(knex);
|
|
93
|
+
this.buildTermQuery({ countQuery, searchQuery });
|
|
94
|
+
this.buildTermsQuery({ countQuery, searchQuery });
|
|
95
|
+
this.buildRangeQuery({ knex, countQuery, searchQuery });
|
|
96
|
+
log.debug(`buildSearchForActiveCollections returns countQuery: ${countQuery?.toSQL().sql}, searchQuery: ${searchQuery.toSQL().sql}`);
|
|
97
|
+
return { countQuery, searchQuery };
|
|
98
|
+
}
|
|
81
99
|
/**
|
|
82
100
|
* Translate postgres records to api records
|
|
83
101
|
*
|
|
84
102
|
* @param pgRecords - postgres records returned from query
|
|
103
|
+
* @param knex - DB client
|
|
85
104
|
* @returns translated api records
|
|
86
105
|
*/
|
|
87
|
-
translatePostgresRecordsToApiRecords(pgRecords) {
|
|
106
|
+
async translatePostgresRecordsToApiRecords(pgRecords, knex) {
|
|
88
107
|
log.debug(`translatePostgresRecordsToApiRecords number of records ${pgRecords.length} `);
|
|
108
|
+
const { fields, includeFullRecord } = this.dbQueryParameters;
|
|
109
|
+
const fileMapping = {};
|
|
110
|
+
const executionMapping = {};
|
|
111
|
+
const cumulusIds = pgRecords.map((record) => record.cumulus_id);
|
|
112
|
+
if (includeFullRecord) {
|
|
113
|
+
//get Files
|
|
114
|
+
const fileModel = new file_1.FilePgModel();
|
|
115
|
+
const files = await fileModel.searchByGranuleCumulusIds(knex, cumulusIds);
|
|
116
|
+
files.forEach((file) => {
|
|
117
|
+
if (!(file.granule_cumulus_id in fileMapping)) {
|
|
118
|
+
fileMapping[file.granule_cumulus_id] = [];
|
|
119
|
+
}
|
|
120
|
+
fileMapping[file.granule_cumulus_id].push(file);
|
|
121
|
+
});
|
|
122
|
+
//get Executions
|
|
123
|
+
const executions = await (0, execution_1.getExecutionInfoByGranuleCumulusIds)({
|
|
124
|
+
knexOrTransaction: knex,
|
|
125
|
+
granuleCumulusIds: cumulusIds,
|
|
126
|
+
});
|
|
127
|
+
executions.forEach((execution) => {
|
|
128
|
+
if (!(execution.granule_cumulus_id in executionMapping)) {
|
|
129
|
+
executionMapping[execution.granule_cumulus_id] = execution;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
89
133
|
const apiRecords = pgRecords.map((item) => {
|
|
90
134
|
const granulePgRecord = item;
|
|
91
135
|
const collectionPgRecord = {
|
|
@@ -93,14 +137,21 @@ class GranuleSearch extends BaseSearch_1.BaseSearch {
|
|
|
93
137
|
name: item.collectionName,
|
|
94
138
|
version: item.collectionVersion,
|
|
95
139
|
};
|
|
140
|
+
const executionUrls = executionMapping[item.cumulus_id]?.url
|
|
141
|
+
? [{ url: executionMapping[item.cumulus_id].url }]
|
|
142
|
+
: [];
|
|
96
143
|
const pdr = item.pdrName ? { name: item.pdrName } : undefined;
|
|
97
144
|
const providerPgRecord = item.providerName ? { name: item.providerName } : undefined;
|
|
145
|
+
const fileRecords = fileMapping[granulePgRecord.cumulus_id] || [];
|
|
98
146
|
const apiRecord = (0, granules_1.translatePostgresGranuleToApiGranuleWithoutDbQuery)({
|
|
99
|
-
granulePgRecord,
|
|
147
|
+
granulePgRecord,
|
|
148
|
+
collectionPgRecord,
|
|
149
|
+
pdr,
|
|
150
|
+
providerPgRecord,
|
|
151
|
+
files: fileRecords,
|
|
152
|
+
executionUrls,
|
|
100
153
|
});
|
|
101
|
-
return
|
|
102
|
-
? (0, pick_1.default)(apiRecord, this.dbQueryParameters.fields)
|
|
103
|
-
: apiRecord;
|
|
154
|
+
return fields ? (0, pick_1.default)(apiRecord, fields) : apiRecord;
|
|
104
155
|
});
|
|
105
156
|
return apiRecords;
|
|
106
157
|
}
|