diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index e6f3b1e08a..a8ed838d23 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -5306,4 +5306,72 @@ describe('Parse.Query testing', () => { expect(score).toEqual([1]); }, { useMasterKey: true }); }); + + describe_only_db('mongo')('query nested keys', () => { + it('queries nested key using equalTo', async () => { + const child = new Parse.Object('Child'); + child.set('key', 'value'); + await child.save(); + + const parent = new Parse.Object('Parent'); + parent.set('some', { + nested: { + key: { + child, + }, + }, + }); + await parent.save(); + + const query1 = await new Parse.Query('Parent') + .equalTo('some.nested.key.child', child) + .find(); + + expect(query1.length).toEqual(1); + }); + + it('queries nested key using containedIn', async () => { + const child = new Parse.Object('Child'); + child.set('key', 'value'); + await child.save(); + + const parent = new Parse.Object('Parent'); + parent.set('some', { + nested: { + key: { + child, + }, + }, + }); + await parent.save(); + + const query1 = await new Parse.Query('Parent') + .containedIn('some.nested.key.child', [child]) + .find(); + + expect(query1.length).toEqual(1); + }); + + it('queries nested key using matchesQuery', async () => { + const child = new Parse.Object('Child'); + child.set('key', 'value'); + await child.save(); + + const parent = new Parse.Object('Parent'); + parent.set('some', { + nested: { + key: { + child, + }, + }, + }); + await parent.save(); + + const query1 = await new Parse.Query('Parent') + .matchesQuery('some.nested.key.child', new Parse.Query('Child').equalTo('key', 'value')) + .find(); + + expect(query1.length).toEqual(1); + }); + }); }); diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 336d9affc9..f78c972bdc 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -327,7 +327,7 @@ function transformQueryKeyValue(className, key, value, schema, count = false) { } // Handle query constraints - const transformedConstraint = transformConstraint(value, field, count); + const transformedConstraint = transformConstraint(value, field, key, count); if (transformedConstraint !== CannotTransform) { if (transformedConstraint.$text) { return { key: '$text', value: transformedConstraint.$text }; @@ -651,12 +651,15 @@ function transformTopLevelAtom(atom, field) { // If it is not a valid constraint but it could be a valid something // else, return CannotTransform. // inArray is whether this is an array field. -function transformConstraint(constraint, field, count = false) { +function transformConstraint(constraint, field, queryKey, count = false) { const inArray = field && field.type && field.type === 'Array'; + // Check wether the given key has `.` + const isNestedKey = queryKey.indexOf('.') > -1; if (typeof constraint !== 'object' || !constraint) { return CannotTransform; } - const transformFunction = inArray ? transformInteriorAtom : transformTopLevelAtom; + // For inArray or nested key, we need to transform the interior atom + const transformFunction = (inArray || isNestedKey) ? transformInteriorAtom : transformTopLevelAtom; const transformer = atom => { const result = transformFunction(atom, field); if (result === CannotTransform) {