Skip to content

Commit eb48407

Browse files
authored
Add sort parameter to named query config (#3157)
* centralise common named query execution into NamedQueryService Add method for iterating named query result pages. * add named query sorting parameter There is no sort order specified by default. The format in namedQuery.js is the same as one of the sails ORM formats. * add sort order to namedQuery so it is tested
1 parent 04abc43 commit eb48407

File tree

6 files changed

+106
-66
lines changed

6 files changed

+106
-66
lines changed

config/namedQuery.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module.exports.namedQuery = {
1313
'metadata.title': null,
1414
'dateCreated': null
1515
},
16+
sort: [{'lastSaveDate': 'DESC'}],
1617
queryParams: {
1718
'title': {
1819
type: 'string',

core/src/model/storage/NamedQueryModel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ export class NamedQueryModel {
99
collectionName?: string;
1010
resultObjectMapping?: string;
1111
brandIdFieldPath?: string;
12-
12+
sort?: string;
1313
}

typescript/api/controllers/webservice/ReportController.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,8 @@ export module Controllers {
8787
return this.apiFail(req, res, 400, new APIErrorResponse("Rows must not be greater than 100"));
8888
}
8989
let namedQueryConfig = sails.config.namedQuery[queryName];
90-
91-
let configMongoQuery = namedQueryConfig.mongoQuery;
92-
let collectionName = _.get(namedQueryConfig, 'collectionName', '');
93-
let resultObjectMapping = _.get(namedQueryConfig, 'resultObjectMapping', {});
94-
let brandIdFieldPath = _.get(namedQueryConfig, 'brandIdFieldPath', '');
95-
let mongoQuery = _.clone(configMongoQuery);
96-
let queryParams = namedQueryConfig.queryParams;
9790
let paramMap = _.clone(req.query);
98-
let response = await NamedQueryService.performNamedQuery(brandIdFieldPath,resultObjectMapping,collectionName,mongoQuery,queryParams,paramMap,brand,start,rows)
91+
let response = await NamedQueryService.performNamedQueryFromConfig(namedQueryConfig, paramMap, brand, start, rows);
9992
sails.log.verbose(`NamedQueryService response: ${JSON.stringify(response)}`);
10093
return this.apiRespond(req, res, response, 200)
10194
} catch (error) {

typescript/api/services/NamedQueryService.ts

Lines changed: 98 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ export module Services {
4646

4747
protected _exportedMethods: any = [
4848
"bootstrap",
49+
"getNamedQueryConfig",
4950
"performNamedQuery",
50-
"getNamedQueryConfig"
51+
"performNamedQueryFromConfig",
52+
"performNamedQueryFromConfigResults",
5153
];
5254

5355
public async bootstrap (defBrand) {
@@ -83,7 +85,8 @@ export module Services {
8385
queryParams: JSON.stringify(config.queryParams),
8486
collectionName: config.collectionName,
8587
resultObjectMapping: JSON.stringify(config.resultObjectMapping),
86-
brandIdFieldPath: config.brandIdFieldPath
88+
brandIdFieldPath: config.brandIdFieldPath,
89+
sort: (config.sort !== undefined && Array.isArray(config.sort) && config.sort.length > 0) ? JSON.stringify(config.sort) : "",
8790
}));
8891
}
8992

@@ -95,47 +98,47 @@ export module Services {
9598
return new NamedQueryConfig(nQDBEntry)
9699
}
97100

98-
async performNamedQuery(brandIdFieldPath, resultObjectMapping, collectionName, mongoQuery, queryParams, paramMap, brand, start, rows, user=undefined):Promise<ListAPIResponse<Object>> {
99-
101+
async performNamedQuery(brandIdFieldPath, resultObjectMapping, collectionName, mongoQuery, queryParams, paramMap, brand, start, rows, user = undefined, sort: NamedQuerySortConfig | undefined = undefined): Promise<ListAPIResponse<Object>> {
102+
const criteriaMeta = {enableExperimentalDeepTargets: true};
100103
this.setParamsInQuery(mongoQuery, queryParams, paramMap);
101104

102105
let that = this;
103-
106+
107+
// Add branding
104108
if(brandIdFieldPath != '') {
105109
mongoQuery[brandIdFieldPath] = brand.id;
106110
}
107-
sails.log.debug("Mongo query to be executed");
108-
sails.log.debug(mongoQuery);
109-
111+
sails.log.debug("Mongo query to be executed", mongoQuery);
112+
113+
// Get the total count of matching records
110114
let totalItems = 0;
111115
if(collectionName == 'user') {
112-
totalItems = await User.count(mongoQuery).meta({
113-
enableExperimentalDeepTargets: true
114-
});
116+
totalItems = await User.count(mongoQuery).meta(criteriaMeta);
115117
} else {
116-
totalItems = await Record.count(mongoQuery).meta({
117-
enableExperimentalDeepTargets: true
118-
});
118+
totalItems = await Record.count(mongoQuery).meta(criteriaMeta);
119+
}
120+
121+
// Build query criteria
122+
const criteria = {
123+
where: mongoQuery,
124+
skip: start,
125+
limit: rows
126+
};
127+
128+
// Add sorting
129+
if (sort !== undefined && Array.isArray(sort) && (sort?.length ?? 0) > 0) {
130+
// e.g. [{ name: 'ASC'}, { age: 'DESC' }]
131+
criteria['sort'] = sort;
119132
}
120133

134+
// Run query
135+
sails.log.debug("Mongo query criteria", criteria);
121136
let results = [];
122137
if (totalItems > 0) {
123138
if(collectionName == 'user') {
124-
results = await User.find({
125-
where: mongoQuery,
126-
skip: start,
127-
limit: rows
128-
}).meta({
129-
enableExperimentalDeepTargets: true
130-
});
139+
results = await User.find(criteria).meta(criteriaMeta);
131140
} else {
132-
results = await Record.find({
133-
where: mongoQuery,
134-
skip: start,
135-
limit: rows
136-
}).meta({
137-
enableExperimentalDeepTargets: true
138-
});
141+
results = await Record.find(criteria).meta(criteriaMeta);
139142
}
140143
}
141144

@@ -299,6 +302,69 @@ export module Services {
299302
return _.get(variables, templateOrPath);
300303
}
301304

305+
public async performNamedQueryFromConfig(config: NamedQueryConfig, paramMap, brand, start, rows, user?) {
306+
sails.log.debug("performNamedQueryFromConfig with parameters", {
307+
config: config,
308+
paramMap: paramMap,
309+
brand: brand,
310+
start: start,
311+
rows: rows,
312+
user: user
313+
});
314+
const collectionName = _.get(config, 'collectionName', '') ?? '';
315+
const resultObjectMapping = _.get(config, 'resultObjectMapping', {}) ?? {};
316+
const brandIdFieldPath = _.get(config, 'brandIdFieldPath', '') ?? '';
317+
const mongoQuery = _.clone(_.get(config, 'mongoQuery', {}) ?? {});
318+
const queryParams = _.get(config, 'queryParams', {}) ?? {};
319+
const sort = _.get(config, 'sort', []) ?? [];
320+
return await this.performNamedQuery(brandIdFieldPath, resultObjectMapping, collectionName, mongoQuery, queryParams, paramMap, brand, start, rows, user, sort);
321+
}
322+
323+
public async performNamedQueryFromConfigResults(config: NamedQueryConfig, paramMap: Record<string, string>, brand, queryName: string, start: number = 0,rows: number = 30, maxRecords: number = 100, user = undefined) {
324+
const records = [];
325+
let requestCount = 0;
326+
sails.log.debug(`All named query results: start query with name '${queryName}' brand ${JSON.stringify(brand)} start ${start} rows ${rows} paramMap ${JSON.stringify(paramMap)}`);
327+
328+
while (true) {
329+
// Keep going while there are more results.
330+
331+
const response = await this.performNamedQueryFromConfig(config, paramMap, brand, start, rows, user);
332+
requestCount += 1;
333+
334+
if (!response) {
335+
// stop if there is no response
336+
sails.log.warn(`All named query results: invalid query response for '${queryName}'`);
337+
break;
338+
}
339+
340+
// add the new records to the collected records
341+
sails.log.debug(`All named query results: add results for '${queryName}': start ${start} rows ${rows} new results ${response.records.length} summary ${JSON.stringify(response.summary)}`);
342+
for (const record of response.records) {
343+
records.push(record);
344+
}
345+
346+
const currentRetrievedCount = start + rows;
347+
if (response.summary.numFound <= currentRetrievedCount) {
348+
// stop if the total count is less than or equal to the number of records retrieved so far
349+
sails.log.debug(`All named query results: reached end of results for '${queryName}': start ${start} rows ${rows} total results ${records.length}`);
350+
break;
351+
}
352+
353+
// update the start point
354+
start = currentRetrievedCount;
355+
356+
// Check the number of records and fail if it is more than maxRecords.
357+
if (records.length > maxRecords){
358+
sails.log.warn(`All named query results: returning early before finished with ${records.length} results for '${queryName}' from ${requestCount} requests because the number of records is more than max records ${maxRecords}`);
359+
}
360+
361+
// continue the while loop
362+
}
363+
364+
sails.log.log(`All named query results: returning ${records.length} results for '${queryName}' from ${requestCount} requests`);
365+
return records;
366+
}
367+
302368
}
303369
}
304370
module.exports = new Services.NamedQueryService().exports();
@@ -319,6 +385,8 @@ enum NamedQueryFormatOptions {
319385
ISODate = 'ISODate'
320386
}
321387

388+
type NamedQuerySortConfig = Record<string, "ASC" | "DESC">[];
389+
322390
class QueryParameterDefinition {
323391
required:boolean
324392
type:DataType
@@ -342,6 +410,7 @@ export class NamedQueryConfig {
342410
collectionName: string;
343411
resultObjectMapping: any;
344412
brandIdFieldPath: string;
413+
sort: NamedQuerySortConfig | undefined;
345414

346415
constructor(values:any) {
347416
this.name = values.name;
@@ -355,6 +424,7 @@ export class NamedQueryConfig {
355424
this.collectionName = values.collectionName;
356425
this.resultObjectMapping = JSON.parse(values.resultObjectMapping);
357426
this.brandIdFieldPath = values.brandIdFieldPath;
427+
this.sort = (values.sort !== undefined && values.sort.length > 0) ? JSON.parse(values.sort) : [];
358428
}
359429
}
360430

typescript/api/services/ReportsService.ts

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -160,16 +160,8 @@ export module Services {
160160
if (report.reportSource == ReportSource.database) {
161161

162162
let namedQueryConfig = await NamedQueryService.getNamedQueryConfig(brand, report.databaseQuery.queryName)
163-
164-
let configMongoQuery = namedQueryConfig.mongoQuery;
165-
let collectionName = _.get(namedQueryConfig, 'collectionName', '');
166-
let resultObjectMapping = _.get(namedQueryConfig, 'resultObjectMapping', {});
167-
let brandIdFieldPath = _.get(namedQueryConfig, 'brandIdFieldPath', '');
168-
let mongoQuery = _.clone(configMongoQuery);
169-
let queryParams = namedQueryConfig.queryParams;
170163
let paramMap = this.buildNamedQueryParamMap(req, report)
171-
172-
let dbResult = await NamedQueryService.performNamedQuery(brandIdFieldPath, resultObjectMapping, collectionName, mongoQuery, queryParams, paramMap, brand, start, rows);
164+
let dbResult = await NamedQueryService.performNamedQueryFromConfig(namedQueryConfig, paramMap, brand, start, rows);
173165
return this.getTranslateDatabaseResultToReportResult(dbResult, report);
174166
} else {
175167
let url = this.buildSolrParams(brand, req, report, start, rows, 'json');
@@ -282,17 +274,9 @@ export module Services {
282274
let result: ReportResult = null
283275
if (report.reportSource == ReportSource.database) {
284276

285-
let namedQueryConfig = await NamedQueryService.getNamedQueryConfig(brand, report.databaseQuery.queryName)
286-
287-
let configMongoQuery = namedQueryConfig.mongoQuery;
288-
let collectionName = _.get(namedQueryConfig, 'collectionName', '');
289-
let resultObjectMapping = _.get(namedQueryConfig, 'resultObjectMapping', {});
290-
let brandIdFieldPath = _.get(namedQueryConfig, 'brandIdFieldPath', '');
291-
let mongoQuery = _.clone(configMongoQuery);
292-
let queryParams = namedQueryConfig.queryParams;
293-
let paramMap = this.buildNamedQueryParamMap(req, report)
294-
295-
let dbResult = await NamedQueryService.performNamedQuery(brandIdFieldPath, resultObjectMapping, collectionName, mongoQuery, queryParams, paramMap, brand, start, rows);
277+
let namedQueryConfig = await NamedQueryService.getNamedQueryConfig(brand, report.databaseQuery.queryName);
278+
let paramMap = this.buildNamedQueryParamMap(req, report);
279+
let dbResult = await NamedQueryService.performNamedQueryFromConfig(namedQueryConfig, paramMap, brand, start, rows);
296280
result = this.getTranslateDatabaseResultToReportResult(dbResult, report);
297281
} else {
298282
var url = this.buildSolrParams(brand, req, report, start, rows, 'json');

typescript/api/services/VocabService.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,8 @@ export module Services {
152152
if (queryConfig.querySource == 'database') {
153153

154154
let namedQueryConfig = await NamedQueryService.getNamedQueryConfig(brand, queryConfig.databaseQuery.queryName);
155-
156-
let configMongoQuery = namedQueryConfig.mongoQuery;
157-
let collectionName = _.get(namedQueryConfig, 'collectionName', '');
158-
let resultObjectMapping = _.get(namedQueryConfig, 'resultObjectMapping', {});
159-
let brandIdFieldPath = _.get(namedQueryConfig, 'brandIdFieldPath', '');
160-
let mongoQuery = _.clone(configMongoQuery);
161-
let queryParams = namedQueryConfig.queryParams;
162155
let paramMap = this.buildNamedQueryParamMap(queryConfig, searchString, user);
163-
164-
let dbResults = await NamedQueryService.performNamedQuery(brandIdFieldPath, resultObjectMapping, collectionName, mongoQuery, queryParams, paramMap, brand, start, rows);
156+
let dbResults = await NamedQueryService.performNamedQueryFromConfig(namedQueryConfig, paramMap, brand, start, rows);
165157
if(queryConfig.resultObjectMapping) {
166158
return this.getResultObjectMappings(dbResults,queryConfig);
167159
} else {

0 commit comments

Comments
 (0)