Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feat/OORT-636-sorting-by-multipl…
Browse files Browse the repository at this point in the history
…e-fields' into next-oort
  • Loading branch information
matheus-relief committed Apr 17, 2024
2 parents b2c0356 + ef287bd commit 44df744
Show file tree
Hide file tree
Showing 8 changed files with 345 additions and 110 deletions.
213 changes: 213 additions & 0 deletions migrations/1691027528104-update-sort-structure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import { Dashboard, Form, Resource } from '@models';
import { startDatabaseForMigration } from '../src/utils/migrations/database.helper';
import { logger } from '@services/logger.service';
import { isEqual } from 'lodash';

/** Updates layouts to work with new sort structure */
const updateLayouts = async () => {
logger.info('Updating sort structure, looking for layouts to update...');
const updates: any = [];
const resources = await Resource.find({});

for (const resource of resources) {
const updatedSorts = {};
const layouts = resource.layouts ?? [];
for (let i = 0; i < layouts.length; i++) {
const layout = layouts[i];
const sort = layout?.query?.sort;
if (!sort || Array.isArray(sort)) continue;
updatedSorts[`layouts.${i}.query.sort`] = sort.field ? [sort] : [];
}
if (Object.keys(updatedSorts).length > 0) {
updates.push({
updateOne: {
filter: { _id: resource._id },
update: {
$set: updatedSorts,
},
},
});
}
}

if (updates.length > 0) {
logger.info(`Updating ${updates.length} resources...`);
await Resource.bulkWrite(updates);
} else logger.info('No resources to update.');
};

/** Updates dashboards to work with new sort structure */
const updateDashboards = async () => {
logger.info('Updating sort structure, looking for dashboards to update...');
const updates: any = [];
const dashboards = await Dashboard.find({});

for (const dashboard of dashboards) {
const updatedSorts = {};
const widgets = dashboard.structure ?? [];
for (let i = 0; i < widgets.length; i++) {
const widget = widgets[i];
if (!widget || widget.component !== 'grid') continue;
const floatingButtons = widget.settings?.floatingButtons ?? [];
for (let j = 0; j < floatingButtons.length; j++) {
const floatingButton = floatingButtons[j];
const sort = floatingButton?.targetFormQuery?.sort;
if (!sort || Array.isArray(sort)) continue;
updatedSorts[
`structure.${i}.settings.floatingButtons.${j}.targetFormQuery.sort`
] = sort.field ? [sort] : [];
}
}

if (Object.keys(updatedSorts).length > 0)
updates.push({
updateOne: {
filter: { _id: dashboard._id },
update: {
$set: updatedSorts,
},
},
});
}

if (updates.length > 0) {
logger.info(`Updating ${updates.length} dashboards...`);
await Dashboard.bulkWrite(updates);
} else logger.info('No dashboards to update.');
};

/**
* Gets the updated form structure, accounting for multi-sort
*
* @param form Form to get updated structure of
* @returns Updated form structure
*/
const updateFormStructure = (form: Form) => {
const updateSort = (elements: any) => {
elements.forEach((question: any) => {
if (question.elements) {
// If question is a panel type that has sub-questions
updateSort(question.elements);
} else if (question.templateElements) {
// If question is a paneldynamic type that has sub-questions
updateSort(question.templateElements);
} else if (question.gridFieldsSettings) {
const sort = question.gridFieldsSettings.sort;
if (!sort || Array.isArray(sort)) return;
question.gridFieldsSettings.sort = sort.field ? [sort] : [];
}
});
};

const structure = JSON.parse(form.structure ?? '{"pages": []}');
structure.pages.forEach((p) => {
if (p.elements) updateSort(p.elements);
});

return JSON.stringify(structure);
};

/** Updates forms to work with new sort structure */
const updateForms = async () => {
logger.info('Updating sort structure, looking for forms to update...');
const updates: any = [];
const forms = await Form.find({});

for (const form of forms) {
const updatedSorts: Record<string, any> = {};
const fields = form.fields ?? [];
for (let i = 0; i < fields.length; i++) {
const field = fields[i];
const sort = field.gridFieldsSettings?.sort;
if (!sort || Array.isArray(sort)) continue;
updatedSorts[`fields.${i}.gridFieldsSettings.sort`] = sort.field
? [sort]
: [];
}

const updatedStructure = updateFormStructure(form);
if (
!isEqual(JSON.parse(form.structure ?? '{}'), JSON.parse(updatedStructure))
)
updatedSorts.structure = updatedStructure;

if (Object.keys(updatedSorts).length > 0) {
updates.push({
updateOne: {
filter: { _id: form._id },
update: {
$set: updatedSorts,
},
},
});
}
}

if (updates.length > 0) {
logger.info(`Updating ${updates.length} forms...`);
await Form.bulkWrite(updates);
} else logger.info('No forms to update.');
};

/** Updates resources to work with new sort structure */
const updateResources = async () => {
logger.info('Updating sort structure, looking for resources to update...');
const updates: any = [];
const resources = await Resource.find({});

for (const resource of resources) {
const updatedSorts = {};
const fields = resource.fields ?? [];
for (let i = 0; i < fields.length; i++) {
const field = fields[i];
const sort = field.gridFieldsSettings?.sort;
if (!sort || Array.isArray(sort)) continue;
updatedSorts[`fields.${i}.gridFieldsSettings.sort`] = sort.field
? [sort]
: [];
}

if (Object.keys(updatedSorts).length > 0)
updates.push({
updateOne: {
filter: { _id: resource._id },
update: {
$set: updatedSorts,
},
},
});
}

if (updates.length > 0) {
logger.info(`Updating ${updates.length} resources...`);
await Resource.bulkWrite(updates);
} else logger.info('No resources to update.');
};

/** Updates data to be compatible with multi-sorting */
export const up = async () => {
await startDatabaseForMigration();

// Migrate layouts
await updateLayouts();

// Migrate dashboards
await updateDashboards();

// Migrate forms
await updateForms();

// Migrate resources
await updateResources();
};

/**
* Sample function of down migration
*
* @returns just migrate data.
*/
export const down = async () => {
/*
Code you downgrade script here!
*/
};
3 changes: 1 addition & 2 deletions src/routes/download/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,7 @@ router.get('/resource/records/:id', async (req, res) => {
* fileName: string // Name for the file
* email: boolean // Send the file by email
* query: any // Query parameters to build it
* sortField?: string
* sortOrder?: 'asc' | 'desc'
* sort?: any[] // If using sort, list of the sort fields and the order we want to apply
* }
*/
router.post('/records', async (req, res) => {
Expand Down
6 changes: 2 additions & 4 deletions src/utils/files/extractGridData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ interface GridExtractParams {
filter?: any;
format: 'csv' | 'xlsx';
query: any;
sortField?: string;
sortOrder?: 'asc' | 'desc';
sort?: any;
}

/**
Expand Down Expand Up @@ -153,8 +152,7 @@ const getRows = async (
variables: {
first: batchSize,
skip: offset,
sortField: params.sortField,
sortOrder: params.sortOrder,
sort: params.sort,
filter: params.filter,
display: true,
},
Expand Down
6 changes: 2 additions & 4 deletions src/utils/files/resourceExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ interface ExportBatchParams {
filter?: any;
format: 'csv' | 'xlsx';
query: any;
sortField?: string;
sortOrder?: 'asc' | 'desc';
sort?: any[];
resource?: string;
timeZone: string;
fileName: string;
Expand Down Expand Up @@ -223,8 +222,7 @@ export default class Exporter {
)();
set(this.req.context, 'dataSources', contextDataSources);
const sort = await getSortAggregation(
this.params.sortField,
this.params.sortOrder,
this.params.sort,
this.resource.fields,
this.req.context
);
Expand Down
10 changes: 4 additions & 6 deletions src/utils/query/queryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,11 @@ const buildMetaFields = (fields: any[]): any => {
export const buildTotalCountQuery = (query: any): any => {
if (query) {
const gqlQuery = `
query GetCustomQuery($first: Int, $skip: Int, $filter: JSON, $contextFilters: JSON, $sortField: String, $sortOrder: String, $display: Boolean) {
query GetCustomQuery($first: Int, $skip: Int, $filter: JSON, $sort: JSON, $contextFilters: JSON, $sortField: String, $sortOrder: String, $display: Boolean) {
${query.name}(
first: $first,
skip: $skip,
sortField: $sortField,
sortOrder: $sortOrder,
sort: $sort,
filter: $filter,
contextFilters: $contextFilters
display: $display
Expand All @@ -147,12 +146,11 @@ export const buildQuery = (query: any): any => {
if (query && query.fields.length > 0) {
const fields = ['canUpdate\ncanDelete\n'].concat(buildFields(query.fields));
const gqlQuery = `
query GetCustomQuery($first: Int, $skip: Int, $filter: JSON, $contextFilters: JSON, $sortField: String, $sortOrder: String, $display: Boolean, $at: Date) {
query GetCustomQuery($first: Int, $skip: Int, $filter: JSON, $sort: JSON, $contextFilters: JSON, $sortField: String, $sortOrder: String, $display: Boolean, $at: Date) {
${query.name}(
first: $first,
skip: $skip,
sortField: $sortField,
sortOrder: $sortOrder,
sort: $sort,
filter: $filter,
contextFilters: $contextFilters
display: $display
Expand Down
4 changes: 1 addition & 3 deletions src/utils/schema/introspection/getSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
GraphQLNonNull,
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
parse,
} from 'graphql';
import { pluralize } from 'inflection';
Expand Down Expand Up @@ -138,8 +137,7 @@ export const getSchema = (
first: { type: GraphQLInt },
afterCursor: { type: GraphQLID },
skip: { type: GraphQLInt },
sortField: { type: GraphQLString },
sortOrder: { type: GraphQLString },
sort: { type: GraphQLJSON },
filter: { type: GraphQLJSON },
contextFilters: { type: GraphQLJSON },
display: { type: GraphQLBoolean },
Expand Down
27 changes: 20 additions & 7 deletions src/utils/schema/resolvers/Query/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,16 @@ const sortRecords = (records: any[], sortArgs: any): void => {
}
};

/**
* Check if field is used as sort field
*
* @param sortFields array with all the sort fields and order
* @param fieldName field to check
* @returns true if field is used to sorting
*/
const isSortField = (sortFields: any[], fieldName: string): boolean =>
sortFields.includes((item: any) => item.field === fieldName);

/**
* Returns a resolver that fetches records from resources/forms
*
Expand All @@ -212,8 +222,7 @@ export default (entityName: string, fieldsByName: any, idsByName: any) =>
async (
parent,
{
sortField,
sortOrder = 'asc',
sort,
first = DEFAULT_FIRST,
skip = 0,
afterCursor,
Expand Down Expand Up @@ -247,8 +256,12 @@ export default (entityName: string, fieldsByName: any, idsByName: any) =>
} as any;
// === FILTERING ===
const usedFields = extractFilterFields(filter);
if (sortField) {
usedFields.push(sortField);
if (sort && sort.length > 0) {
sort.forEach((item: any) => {
if (item.field) {
usedFields.push(item.field);
}
});
}

// Get list of needed resources for the aggregation
Expand Down Expand Up @@ -362,7 +375,7 @@ export default (entityName: string, fieldsByName: any, idsByName: any) =>
}

// If sort field is a calculated field
if (sortField === field.name) {
if (isSortField(sort, field.name)) {
return true;
}

Expand Down Expand Up @@ -450,7 +463,7 @@ export default (entityName: string, fieldsByName: any, idsByName: any) =>
...calculatedFieldsAggregation,
{ $match: filters },
...projectAggregation,
...(await getSortAggregation(sortField, sortOrder, fields, context)),
...(await getSortAggregation(sort, fields, context)),
{
$facet: {
items: [{ $skip: skip }, { $limit: first + 1 }],
Expand Down Expand Up @@ -480,7 +493,7 @@ export default (entityName: string, fieldsByName: any, idsByName: any) =>
...linkedRecordsAggregation,
...linkedReferenceDataAggregation,
...defaultRecordAggregation,
...(await getSortAggregation(sortField, sortOrder, fields, context)),
...(await getSortAggregation(sort, fields, context)),
{ $match: { $and: [filters, cursorFilters] } },
{
$facet: {
Expand Down
Loading

0 comments on commit 44df744

Please sign in to comment.