Skip to content

Commit

Permalink
fix(json-api-nestjs): try to fix condition
Browse files Browse the repository at this point in the history
- incorrect condition for relation: user?photo.userId=1
- add suuport
several condition to one field: user?date<2022-12-12&date>2022-12-12
  • Loading branch information
klerick committed Apr 24, 2023
1 parent c8752fa commit 478e0cd
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,23 @@ export async function getAll<T>(

const { target, relation } = filter;

const expressionObjectForRelation = relation
? this.UtilsMethode.applyQueryFilterRelation(
builder,
relation,
this.repository.metadata
)
: [];

const expressionObject = [
...(target
? this.UtilsMethode.applyQueryFiltersTarget(
builder,
target,
this.repository.metadata
)
: []),
...(relation
? this.UtilsMethode.applyQueryFilterRelation(
builder,
relation,
this.repository.metadata
)
builder,
target,
this.repository.metadata
)
: []),
...expressionObjectForRelation,
];
const expressionObjectLength = expressionObject.length;
const includeLength = include ? include.length : 0;
Expand Down Expand Up @@ -168,10 +170,24 @@ export async function getAll<T>(
.setParameters(builder.getParameters())
.getRawMany<T>();

const result = await resultBuilderQuery
const resultBuilder = resultBuilderQuery
.select([...fieldsSelect])
.whereInIds(resultIds.map((i) => i[`${countAlias}_${primaryColumn}`]))
.getRawMany();
.whereInIds(resultIds.map((i) => i[`${countAlias}_${primaryColumn}`]));

for (let i = 0; i < expressionObjectForRelation.length; i++) {
const { expression, params, selectInclude } =
expressionObjectForRelation[i];
if (selectInclude) {
resultBuilder.leftJoin(
`${preparedResourceName}.${selectInclude}`,
selectInclude
);
}
resultBuilder.andWhere(expression);
resultBuilder.setParameters(params ? { [params.name]: params.val } : {});
}

const result = await resultBuilder.getRawMany();

const callQuery = Date.now() - startTime;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ describe('Utils methode test', () => {
isActive: { [FilterOperand.gte]: 'test' },
lastName: { [FilterOperand.in]: ['test', 'test'] },
createdAt: { [FilterOperand.like]: 'test' },
updatedAt: { [FilterOperand.lt]: 'test' },
updatedAt: { [FilterOperand.lt]: 'test', [FilterOperand.gt]: 'test' },
id: { [FilterOperand.lte]: 'test' },
},
};
Expand Down Expand Up @@ -306,30 +306,34 @@ describe('Utils methode test', () => {
);

const params = Object.keys(filter.manager);
let increment = 0;
for (let i = 0; i < params.length; i++) {
const alias = 'manager';
const paramsName = UtilsMethode.getParamName(
`${alias}.${params[i]}`,
i
);
const operand = Object.keys(filter.manager[params[i]])[0];
const checkExpression = OperandsMap[operand].replace(
'EXPRESSION',
paramsName
);
expect(expression[i].expression).toBe(
`${alias}.${params[i]} ${checkExpression}`
);
expect(expression[i].params.name).toBe(paramsName);
if (operand === FilterOperand.like) {
expect(expression[i].params.val).toBe(
`%${filter.manager[params[i]][operand]}%`
for (const operand of Object.keys(filter.manager[params[i]])){
const paramsName = UtilsMethode.getParamName(
`${alias}.${params[i]}`,
increment
);
} else {
expect(expression[i].params.val).toBe(
filter.manager[params[i]][operand]
const checkExpression = OperandsMap[operand].replace(
'EXPRESSION',
paramsName
);
expect(expression[increment].expression).toBe(
`${alias}.${params[i]} ${checkExpression}`
);
expect(expression[increment].params.name).toBe(paramsName);
if (operand === FilterOperand.like) {
expect(expression[increment].params.val).toBe(
`%${filter.manager[params[i]][operand]}%`
);
} else {
expect(expression[increment].params.val).toBe(
filter.manager[params[i]][operand]
);
}
increment++
}

expect(expression[i].selectInclude).toBe('manager');
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export class UtilsMethode {
resultExpression.push({
expression: `${preparedResourceName}.${field.toString()} ${OperandMapForNull[
operand
].replace(
].replace(
'EXPRESSION',
UtilsMethode.getParamName(
`${preparedResourceName}.${field}`,
Expand All @@ -158,7 +158,7 @@ export class UtilsMethode {
resultExpression.push({
expression: `${preparedResourceName}.${field.toString()} ${OperandsMap[
operand
].replace(
].replace(
'EXPRESSION',
UtilsMethode.getParamName(`${preparedResourceName}.${field}`, i)
)}`,
Expand Down Expand Up @@ -203,110 +203,118 @@ export class UtilsMethode {
const resourceName = snakeToCamel(name);
let relationFieldProperty: keyof Filter<T>['relation'][typeof relationProperty];
for (relationFieldProperty in filter[relationProperty]) {
const operand = Object.keys(
for (const operand of Object.keys(
filter[relationProperty][relationFieldProperty]
).pop();
const value =
operand === FilterOperand.like
? `%${filter[relationProperty][relationFieldProperty][operand]}%`
: filter[relationProperty][relationFieldProperty][operand];

const currentOperandMap =
value.toString().toLocaleLowerCase() === 'null'
? OperandMapForNull
: OperandsMap;
const paramsField =
value.toString().toLocaleLowerCase() === 'null'
? null
: UtilsMethode.getParamName(
)) {
const value =
operand === FilterOperand.like
? `%${filter[relationProperty][relationFieldProperty][operand]}%`
: filter[relationProperty][relationFieldProperty][operand];
const currentOperandMap =
value.toString().toLocaleLowerCase() === 'null'
? OperandMapForNull
: OperandsMap;
const paramsField =
value.toString().toLocaleLowerCase() === 'null'
? null
: UtilsMethode.getParamName(
`${relationProperty}.${relationFieldProperty.toString()}`,
i
);
switch (relation.relationType) {
case 'many-to-many': {
const { inverseJoinColumns, joinColumns } =
relation.isManyToManyOwner ? relation : relation.inverseRelation;
const relationProps = relation.isManyToManyOwner
? relation
: relation.inverseRelation;
const { joinTableName } = relationProps;
const { databaseName: queryJoinPropsName } =
relation.isManyToManyOwner
? inverseJoinColumns[0]
: joinColumns[0];
const { databaseName: selectJoinPropsName } =
relation.isManyToManyOwner
? joinColumns[0]
: inverseJoinColumns[0];
const onQuery = `${joinTableName}.${queryJoinPropsName} = ${relationProperty}.${primaryColumn}`;
const selectQuery = `${joinTableName}.${selectJoinPropsName}`;

const query = builder
.subQuery()
.select(selectQuery)
.from(joinTableName, joinTableName)
.leftJoin(resourceName, relationProperty, onQuery)
.where(
`${relationProperty}.${relationFieldProperty.toString()} ${currentOperandMap[
operand
].replace('EXPRESSION', paramsField)}`
)
.getQuery();
resultExpression.push({
expression: `${preparedResourceName}.id IN ${query}`,
params:
paramsField === null
? null
: {
switch (relation.relationType) {
case 'many-to-many': {
const { inverseJoinColumns, joinColumns } =
relation.isManyToManyOwner
? relation
: relation.inverseRelation;
const relationProps = relation.isManyToManyOwner
? relation
: relation.inverseRelation;
const { joinTableName } = relationProps;
const { databaseName: queryJoinPropsName } =
relation.isManyToManyOwner
? inverseJoinColumns[0]
: joinColumns[0];
const { databaseName: selectJoinPropsName } =
relation.isManyToManyOwner
? joinColumns[0]
: inverseJoinColumns[0];
const onQuery = `${joinTableName}.${queryJoinPropsName} = ${relationProperty}.${primaryColumn}`;
const selectQuery = `${joinTableName}.${selectJoinPropsName}`;

const query = builder
.subQuery()
.select(selectQuery)
.from(joinTableName, joinTableName)
.leftJoin(resourceName, relationProperty, onQuery)
.where(
`${relationProperty}.${relationFieldProperty.toString()} ${currentOperandMap[
operand
].replace('EXPRESSION', paramsField)}`
)
.getQuery();
resultExpression.push({
expression: `${preparedResourceName}.id IN ${query}`,
params:
paramsField === null
? null
: {
val: value,
name: paramsField,
},
});
});

break;
}
case 'one-to-many': {
const query = builder
.subQuery()
.select(`${resourceName}.${inverseSidePropertyPath}`)
.from(target, resourceName)
.where(
`${resourceName}.${relationFieldProperty.toString()} ${currentOperandMap[
break;
}
case 'one-to-many': {
if (paramsField !== null) {
resultExpression.push({
expression: `${relationProperty}.${relationFieldProperty.toString()} ${currentOperandMap[
operand
].replace('EXPRESSION', paramsField)}`,
params: {
val: value,
name: paramsField,
},
selectInclude: relationProperty,
});
break;
}
const query = builder
.subQuery()
.select(`${resourceName}.${inverseSidePropertyPath}`)
.from(target, resourceName)
.where(
`${resourceName}.${relationFieldProperty.toString()} ${currentOperandMap[
operand
].replace('EXPRESSION', paramsField)}`
)
.getQuery();
resultExpression.push({
expression: `${preparedResourceName}.id IN ${query}`,
params: null,
});
break;
}
default:
resultExpression.push({
expression: `${relationProperty}.${relationFieldProperty.toString()} ${currentOperandMap[
operand
].replace('EXPRESSION', paramsField)}`
)
.getQuery();

resultExpression.push({
expression: `${preparedResourceName}.id IN ${query}`,
params:
paramsField === null
? null
: {
].replace('EXPRESSION', paramsField)}`,
params:
paramsField === null
? null
: {
val: value,
name: paramsField,
},
});
break;
selectInclude: relationProperty,
});
break;
}
default:
resultExpression.push({
expression: `${relationProperty}.${relationFieldProperty.toString()} ${currentOperandMap[
operand
].replace('EXPRESSION', paramsField)}`,
params:
paramsField === null
? null
: {
val: value,
name: paramsField,
},
selectInclude: relationProperty,
});
break;
}

i++;
i++;
}
}
}
return resultExpression;
Expand Down Expand Up @@ -380,8 +388,8 @@ export class UtilsMethode {
) {
const detail = isArray
? `Resource '${relationsTypeName}' with ids '${idsToAdd.join(
','
)}' does not exist`
','
)}' does not exist`
: `Resource '${relationsTypeName}' with id '${idsToAdd[0]}' does not exist`;
throw new NotFoundException({
detail,
Expand Down

0 comments on commit 478e0cd

Please sign in to comment.