Fix paginated order by with composite fields (#7187)
## Context
Cursor is modifying the where object but does not handle properly
composite fields. I'm introducing field metadata as a source of truth to
fix this issue.
RAW_JSON for example (as a sub-field type) should be ignored in a lt/gt,
probably other field types as well.
## Before
```typescript
[
{
emails: {
lt: {
primaryEmail: "brenda.brown@example.com",
additionalEmails: null,
},
},
},
{
emails: {
eq: {
primaryEmail: "brenda.brown@example.com",
additionalEmails: null,
},
},
position: {
gt: 877,
},
},
{
emails: {
eq: {
primaryEmail: "brenda.brown@example.com",
additionalEmails: null,
},
},
position: {
eq: 877,
},
id: {
gt: "fe43c45d-7560-4eb1-8fd3-c48fd0a4dcd4",
},
},
]
```
## After
```typescript
[
{
emails: {
primaryEmail: {
lt: "brenda.brown@example.com",
},
},
},
{
emails: {
primaryEmail: {
eq: "brenda.brown@example.com",
},
},
position: {
gt: 877,
},
},
{
emails: {
primaryEmail: {
eq: "brenda.brown@example.com",
},
},
position: {
eq: 877,
},
id: {
gt: "fe43c45d-7560-4eb1-8fd3-c48fd0a4dcd4",
},
},
]
```
This commit is contained in:
@ -118,6 +118,7 @@ export class GraphqlQueryFindManyResolverService {
|
|||||||
const cursorArgFilter = computeCursorArgFilter(
|
const cursorArgFilter = computeCursorArgFilter(
|
||||||
cursor,
|
cursor,
|
||||||
orderByWithIdCondition,
|
orderByWithIdCondition,
|
||||||
|
objectMetadata.fields,
|
||||||
isForwardPagination,
|
isForwardPagination,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -8,10 +8,15 @@ import {
|
|||||||
GraphqlQueryRunnerException,
|
GraphqlQueryRunnerException,
|
||||||
GraphqlQueryRunnerExceptionCode,
|
GraphqlQueryRunnerExceptionCode,
|
||||||
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
} from 'src/engine/api/graphql/graphql-query-runner/errors/graphql-query-runner.exception';
|
||||||
|
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||||
|
import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
|
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||||
|
import { FieldMetadataMap } from 'src/engine/metadata-modules/utils/generate-object-metadata-map.util';
|
||||||
|
|
||||||
export const computeCursorArgFilter = (
|
export const computeCursorArgFilter = (
|
||||||
cursor: Record<string, any>,
|
cursor: Record<string, any>,
|
||||||
orderBy: RecordOrderBy,
|
orderBy: RecordOrderBy,
|
||||||
|
fieldMetadataMap: FieldMetadataMap,
|
||||||
isForwardPagination = true,
|
isForwardPagination = true,
|
||||||
): RecordFilter[] => {
|
): RecordFilter[] => {
|
||||||
const cursorKeys = Object.keys(cursor ?? {});
|
const cursorKeys = Object.keys(cursor ?? {});
|
||||||
@ -31,9 +36,12 @@ export const computeCursorArgFilter = (
|
|||||||
) {
|
) {
|
||||||
whereCondition = {
|
whereCondition = {
|
||||||
...whereCondition,
|
...whereCondition,
|
||||||
[cursorKeys[subConditionIndex]]: {
|
...buildWhereCondition(
|
||||||
eq: cursorValues[subConditionIndex],
|
cursorKeys[subConditionIndex],
|
||||||
},
|
cursorValues[subConditionIndex],
|
||||||
|
fieldMetadataMap,
|
||||||
|
'eq',
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +68,66 @@ export const computeCursorArgFilter = (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...whereCondition,
|
...whereCondition,
|
||||||
...{ [key]: { [operator]: value } },
|
...buildWhereCondition(key, value, fieldMetadataMap, operator),
|
||||||
} as RecordFilter;
|
} as RecordFilter;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const buildWhereCondition = (
|
||||||
|
key: string,
|
||||||
|
value: any,
|
||||||
|
fieldMetadataMap: FieldMetadataMap,
|
||||||
|
operator: string,
|
||||||
|
): Record<string, any> => {
|
||||||
|
const fieldMetadata = fieldMetadataMap[key];
|
||||||
|
|
||||||
|
if (!fieldMetadata) {
|
||||||
|
throw new GraphqlQueryRunnerException(
|
||||||
|
`Field metadata not found for key: ${key}`,
|
||||||
|
GraphqlQueryRunnerExceptionCode.INVALID_CURSOR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCompositeFieldMetadataType(fieldMetadata.type)) {
|
||||||
|
return buildCompositeWhereCondition(
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
fieldMetadata.type,
|
||||||
|
operator,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { [key]: { [operator]: value } };
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildCompositeWhereCondition = (
|
||||||
|
key: string,
|
||||||
|
value: any,
|
||||||
|
fieldType: FieldMetadataType,
|
||||||
|
operator: string,
|
||||||
|
): Record<string, any> => {
|
||||||
|
const compositeType = compositeTypeDefinitions.get(fieldType);
|
||||||
|
|
||||||
|
if (!compositeType) {
|
||||||
|
throw new GraphqlQueryRunnerException(
|
||||||
|
`Composite type definition not found for type: ${fieldType}`,
|
||||||
|
GraphqlQueryRunnerExceptionCode.INVALID_CURSOR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: Record<string, any> = {};
|
||||||
|
|
||||||
|
compositeType.properties.forEach((property) => {
|
||||||
|
if (
|
||||||
|
property.type !== FieldMetadataType.RAW_JSON &&
|
||||||
|
value[property.name] !== undefined
|
||||||
|
) {
|
||||||
|
result[key] = {
|
||||||
|
...result[key],
|
||||||
|
[property.name]: { [operator]: value[property.name] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user