diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index ce3034229c..260a48f217 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -4575,6 +4575,71 @@ describe('Parse.Query testing', () => { }); }); + it('should not throw error with undefined dot notation when using matchesKeyInQuery', async () => { + const group = new Parse.Object('Group', { name: 'Group #1' }); + await group.save(); + + const role1 = new Parse.Object('Role', { + name: 'Role #1', + type: 'x', + belongsTo: group, + }); + + const role2 = new Parse.Object('Role', { + name: 'Role #2', + type: 'y', + belongsTo: undefined, + }); + await Parse.Object.saveAll([role1, role2]); + + const rolesOfTypeX = new Parse.Query('Role'); + rolesOfTypeX.equalTo('type', 'x'); + + const groupsWithRoleX = new Parse.Query('Group'); + groupsWithRoleX.matchesKeyInQuery( + 'objectId', + 'belongsTo.objectId', + rolesOfTypeX + ); + + const results = await groupsWithRoleX.find(); + equal(results.length, 1); + equal(results[0].get('name'), group.get('name')); + }); + + it('should not throw error with undefined dot notation when using doesNotMatchKeyInQuery', async () => { + const group1 = new Parse.Object('Group', { name: 'Group #1' }); + const group2 = new Parse.Object('Group', { name: 'Group #2' }); + await Parse.Object.saveAll([group1, group2]); + + const role1 = new Parse.Object('Role', { + name: 'Role #1', + type: 'x', + belongsTo: group1, + }); + + const role2 = new Parse.Object('Role', { + name: 'Role #2', + type: 'y', + belongsTo: undefined, + }); + await Parse.Object.saveAll([role1, role2]); + + const rolesOfTypeX = new Parse.Query('Role'); + rolesOfTypeX.equalTo('type', 'x'); + + const groupsWithRoleX = new Parse.Query('Group'); + groupsWithRoleX.doesNotMatchKeyInQuery( + 'objectId', + 'belongsTo.objectId', + rolesOfTypeX + ); + + const results = await groupsWithRoleX.find(); + equal(results.length, 1); + equal(results[0].get('name'), group2.get('name')); + }); + it('withJSON supports geoWithin.centerSphere', done => { const inbound = new Parse.GeoPoint(1.5, 1.5); const onbound = new Parse.GeoPoint(10, 10); diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index 791be6c520..4fc955bd30 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -442,7 +442,7 @@ const buildWhereClause = ({ schema, query, index }): WhereClause => { const inPatterns = []; values.push(fieldName); baseArray.forEach((listElem, listIndex) => { - if (listElem !== null) { + if (listElem != null) { values.push(listElem); inPatterns.push(`$${index + 1 + listIndex}`); } diff --git a/src/RestQuery.js b/src/RestQuery.js index 0ff3c63f23..90cd535fa7 100644 --- a/src/RestQuery.js +++ b/src/RestQuery.js @@ -455,10 +455,18 @@ RestQuery.prototype.replaceNotInQuery = function() { }); }; +// Used to get the deepest object from json using dot notation. +const getDeepestObjectFromKey = (json, key, idx, src) => { + if (key in json) { + return json[key]; + } + src.splice(1); // Exit Early +}; + const transformSelect = (selectObject, key, objects) => { var values = []; for (var result of objects) { - values.push(key.split('.').reduce((o, i) => o[i], result)); + values.push(key.split('.').reduce(getDeepestObjectFromKey, result)); } delete selectObject['$select']; if (Array.isArray(selectObject['$in'])) { @@ -523,7 +531,7 @@ RestQuery.prototype.replaceSelect = function() { const transformDontSelect = (dontSelectObject, key, objects) => { var values = []; for (var result of objects) { - values.push(key.split('.').reduce((o, i) => o[i], result)); + values.push(key.split('.').reduce(getDeepestObjectFromKey, result)); } delete dontSelectObject['$dontSelect']; if (Array.isArray(dontSelectObject['$nin'])) {