Skip to content

Commit 7f5a15d

Browse files
authored
fix: graphQL query ignores condition equalTo with value false (#8032)
1 parent 6e68656 commit 7f5a15d

File tree

3 files changed

+154
-16
lines changed

3 files changed

+154
-16
lines changed

spec/ParseGraphQLServer.spec.js

+124
Original file line numberDiff line numberDiff line change
@@ -9516,6 +9516,130 @@ describe('ParseGraphQLServer', () => {
95169516
}
95179517
});
95189518

9519+
it('should support where argument on object field that contains false boolean value or 0 number value', async () => {
9520+
try {
9521+
const someObjectFieldValue1 = {
9522+
foo: { bar: true, baz: 100 },
9523+
};
9524+
9525+
const someObjectFieldValue2 = {
9526+
foo: { bar: false, baz: 0 },
9527+
};
9528+
9529+
const object1 = new Parse.Object('SomeClass');
9530+
await object1.save({
9531+
someObjectField: someObjectFieldValue1,
9532+
});
9533+
const object2 = new Parse.Object('SomeClass');
9534+
await object2.save({
9535+
someObjectField: someObjectFieldValue2,
9536+
});
9537+
9538+
const whereToObject1 = {
9539+
someObjectField: {
9540+
equalTo: { key: 'foo.bar', value: true },
9541+
notEqualTo: { key: 'foo.baz', value: 0 },
9542+
},
9543+
};
9544+
const whereToObject2 = {
9545+
someObjectField: {
9546+
notEqualTo: { key: 'foo.bar', value: true },
9547+
equalTo: { key: 'foo.baz', value: 0 },
9548+
},
9549+
};
9550+
9551+
const whereToAll = {
9552+
someObjectField: {
9553+
lessThan: { key: 'foo.baz', value: 101 },
9554+
},
9555+
};
9556+
9557+
const whereToNone = {
9558+
someObjectField: {
9559+
notEqualTo: { key: 'foo.bar', value: true },
9560+
equalTo: { key: 'foo.baz', value: 1 },
9561+
},
9562+
};
9563+
9564+
const queryResult = await apolloClient.query({
9565+
query: gql`
9566+
query GetSomeObject(
9567+
$id1: ID!
9568+
$id2: ID!
9569+
$whereToObject1: SomeClassWhereInput
9570+
$whereToObject2: SomeClassWhereInput
9571+
$whereToAll: SomeClassWhereInput
9572+
$whereToNone: SomeClassWhereInput
9573+
) {
9574+
obj1: someClass(id: $id1) {
9575+
id
9576+
someObjectField
9577+
}
9578+
obj2: someClass(id: $id2) {
9579+
id
9580+
someObjectField
9581+
}
9582+
onlyObj1: someClasses(where: $whereToObject1) {
9583+
edges {
9584+
node {
9585+
id
9586+
someObjectField
9587+
}
9588+
}
9589+
}
9590+
onlyObj2: someClasses(where: $whereToObject2) {
9591+
edges {
9592+
node {
9593+
id
9594+
someObjectField
9595+
}
9596+
}
9597+
}
9598+
all: someClasses(where: $whereToAll) {
9599+
edges {
9600+
node {
9601+
id
9602+
someObjectField
9603+
}
9604+
}
9605+
}
9606+
none: someClasses(where: $whereToNone) {
9607+
edges {
9608+
node {
9609+
id
9610+
someObjectField
9611+
}
9612+
}
9613+
}
9614+
}
9615+
`,
9616+
variables: {
9617+
id1: object1.id,
9618+
id2: object2.id,
9619+
whereToObject1,
9620+
whereToObject2,
9621+
whereToAll,
9622+
whereToNone,
9623+
},
9624+
});
9625+
9626+
const { obj1, obj2, onlyObj1, onlyObj2, all, none } = queryResult.data;
9627+
9628+
expect(obj1.someObjectField).toEqual(someObjectFieldValue1);
9629+
expect(obj2.someObjectField).toEqual(someObjectFieldValue2);
9630+
9631+
// Checks class query results
9632+
expect(onlyObj1.edges.length).toEqual(1);
9633+
expect(onlyObj1.edges[0].node.someObjectField).toEqual(someObjectFieldValue1);
9634+
expect(onlyObj2.edges.length).toEqual(1);
9635+
expect(onlyObj2.edges[0].node.someObjectField).toEqual(someObjectFieldValue2);
9636+
expect(all.edges.length).toEqual(2);
9637+
expect(none.edges.length).toEqual(0);
9638+
} catch (e) {
9639+
handleError(e);
9640+
}
9641+
});
9642+
95199643
it('should support object composed queries', async () => {
95209644
try {
95219645
const someObjectFieldValue1 = {

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

+29-15
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,22 @@ const toPostgresValue = value => {
9191
return value;
9292
};
9393

94+
const toPostgresValueCastType = value => {
95+
const postgresValue = toPostgresValue(value);
96+
let castType;
97+
switch (typeof postgresValue) {
98+
case 'number':
99+
castType = 'double precision';
100+
break;
101+
case 'boolean':
102+
castType = 'boolean';
103+
break;
104+
default:
105+
castType = undefined;
106+
}
107+
return castType;
108+
};
109+
94110
const transformValue = value => {
95111
if (typeof value === 'object' && value.__type === 'Pointer') {
96112
return value.objectId;
@@ -369,9 +385,12 @@ const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClaus
369385
);
370386
} else {
371387
if (fieldName.indexOf('.') >= 0) {
372-
const constraintFieldName = transformDotField(fieldName);
388+
const castType = toPostgresValueCastType(fieldValue.$ne);
389+
const constraintFieldName = castType
390+
? `CAST ((${transformDotField(fieldName)}) AS ${castType})`
391+
: transformDotField(fieldName);
373392
patterns.push(
374-
`(${constraintFieldName} <> $${index} OR ${constraintFieldName} IS NULL)`
393+
`(${constraintFieldName} <> $${index + 1} OR ${constraintFieldName} IS NULL)`
375394
);
376395
} else if (typeof fieldValue.$ne === 'object' && fieldValue.$ne.$relativeTime) {
377396
throw new Parse.Error(
@@ -401,8 +420,12 @@ const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClaus
401420
index += 1;
402421
} else {
403422
if (fieldName.indexOf('.') >= 0) {
423+
const castType = toPostgresValueCastType(fieldValue.$eq);
424+
const constraintFieldName = castType
425+
? `CAST ((${transformDotField(fieldName)}) AS ${castType})`
426+
: transformDotField(fieldName);
404427
values.push(fieldValue.$eq);
405-
patterns.push(`${transformDotField(fieldName)} = $${index++}`);
428+
patterns.push(`${constraintFieldName} = $${index++}`);
406429
} else if (typeof fieldValue.$eq === 'object' && fieldValue.$eq.$relativeTime) {
407430
throw new Parse.Error(
408431
Parse.Error.INVALID_JSON,
@@ -771,20 +794,11 @@ const buildWhereClause = ({ schema, query, index, caseInsensitive }): WhereClaus
771794
Object.keys(ParseToPosgresComparator).forEach(cmp => {
772795
if (fieldValue[cmp] || fieldValue[cmp] === 0) {
773796
const pgComparator = ParseToPosgresComparator[cmp];
774-
let postgresValue = toPostgresValue(fieldValue[cmp]);
775797
let constraintFieldName;
798+
let postgresValue = toPostgresValue(fieldValue[cmp]);
799+
776800
if (fieldName.indexOf('.') >= 0) {
777-
let castType;
778-
switch (typeof postgresValue) {
779-
case 'number':
780-
castType = 'double precision';
781-
break;
782-
case 'boolean':
783-
castType = 'boolean';
784-
break;
785-
default:
786-
castType = undefined;
787-
}
801+
const castType = toPostgresValueCastType(fieldValue[cmp]);
788802
constraintFieldName = castType
789803
? `CAST ((${transformDotField(fieldName)}) AS ${castType})`
790804
: transformDotField(fieldName);

src/GraphQL/transformers/query.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ const transformQueryConstraintInputToParse = (
108108
* }
109109
* }
110110
*/
111-
if (fieldValue.key && fieldValue.value && parentConstraints && parentFieldName) {
111+
if (fieldValue.key && fieldValue.value !== undefined && parentConstraints && parentFieldName) {
112112
delete parentConstraints[parentFieldName];
113113
parentConstraints[`${parentFieldName}.${fieldValue.key}`] = {
114114
...parentConstraints[`${parentFieldName}.${fieldValue.key}`],

0 commit comments

Comments
 (0)