From d4da486d042754e639bf0ec6ec256d641621a538 Mon Sep 17 00:00:00 2001 From: Rahul Lanjewar Date: Tue, 29 Apr 2025 18:59:59 +0530 Subject: [PATCH 01/10] fix: failing test cases --- spec/MongoStorageAdapter.spec.js | 71 ++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index b026fc0961..d6b9fcedcb 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -390,6 +390,77 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined); }); + + /** + * If we use equalTo to comparse the nested pointer it works + * But it does not work with contained in or matchesQuery + */ + it('Parse query works with nested objects if equal to is used', 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('Parse query works when containedIn is used', 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('Parse query works when matchesQuery is used which in turn uses contained in', 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); + }) + it_only_mongodb_version('<5.1 || >=6')('should use index for caseInsensitive query', async () => { const user = new Parse.User(); user.set('username', 'Bugs'); From ec45ba0e558cb98213f289410015962a4d98dd3d Mon Sep 17 00:00:00 2001 From: Rahul Lanjewar Date: Tue, 29 Apr 2025 19:02:48 +0530 Subject: [PATCH 02/10] fix: actual fix --- src/Adapters/Storage/Mongo/MongoTransform.js | 91 ++++++++++---------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 336d9affc9..9a0ab9780a 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, key, count = false) { const inArray = field && field.type && field.type === 'Array'; + // Check wether the given key has `.` + const isNestedKey = key.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) { @@ -668,10 +671,10 @@ function transformConstraint(constraint, field, count = false) { // This is a hack so that: // $regex is handled before $options // $nearSphere is handled before $maxDistance - var keys = Object.keys(constraint).sort().reverse(); + var constraintKeys = Object.keys(constraint).sort().reverse(); var answer = {}; - for (var key of keys) { - switch (key) { + for (var constraintKey of constraintKeys) { + switch (constraintKey) { case '$lt': case '$lte': case '$gt': @@ -679,7 +682,7 @@ function transformConstraint(constraint, field, count = false) { case '$exists': case '$ne': case '$eq': { - const val = constraint[key]; + const val = constraint[constraintKey]; if (val && typeof val === 'object' && val.$relativeTime) { if (field && field.type !== 'Date') { throw new Parse.Error( @@ -688,7 +691,7 @@ function transformConstraint(constraint, field, count = false) { ); } - switch (key) { + switch (constraintKey) { case '$exists': case '$ne': case '$eq': @@ -700,28 +703,28 @@ function transformConstraint(constraint, field, count = false) { const parserResult = Utils.relativeTimeToDate(val.$relativeTime); if (parserResult.status === 'success') { - answer[key] = parserResult.result; + answer[constraintKey] = parserResult.result; break; } log.info('Error while parsing relative date', parserResult); throw new Parse.Error( Parse.Error.INVALID_JSON, - `bad $relativeTime (${key}) value. ${parserResult.info}` + `bad $relativeTime (${constraintKey}) value. ${parserResult.info}` ); } - answer[key] = transformer(val); + answer[constraintKey] = transformer(val); break; } case '$in': case '$nin': { - const arr = constraint[key]; + const arr = constraint[constraintKey]; if (!(arr instanceof Array)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value'); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + constraintKey + ' value'); } - answer[key] = _.flatMap(arr, value => { + answer[constraintKey] = _.flatMap(arr, value => { return (atom => { if (Array.isArray(atom)) { return value.map(transformer); @@ -733,13 +736,13 @@ function transformConstraint(constraint, field, count = false) { break; } case '$all': { - const arr = constraint[key]; + const arr = constraint[constraintKey]; if (!(arr instanceof Array)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value'); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + constraintKey + ' value'); } - answer[key] = arr.map(transformInteriorAtom); + answer[constraintKey] = arr.map(transformInteriorAtom); - const values = answer[key]; + const values = answer[constraintKey]; if (isAnyValueRegex(values) && !isAllValuesRegexOrNone(values)) { throw new Parse.Error( Parse.Error.INVALID_JSON, @@ -750,15 +753,15 @@ function transformConstraint(constraint, field, count = false) { break; } case '$regex': - var s = constraint[key]; + var s = constraint[constraintKey]; if (typeof s !== 'string') { throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad regex: ' + s); } - answer[key] = s; + answer[constraintKey] = s; break; case '$containedBy': { - const arr = constraint[key]; + const arr = constraint[constraintKey]; if (!(arr instanceof Array)) { throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $containedBy: should be an array`); } @@ -768,25 +771,25 @@ function transformConstraint(constraint, field, count = false) { break; } case '$options': - answer[key] = constraint[key]; + answer[constraintKey] = constraint[constraintKey]; break; case '$text': { - const search = constraint[key].$search; + const search = constraint[constraintKey].$search; if (typeof search !== 'object') { throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $search, should be object`); } if (!search.$term || typeof search.$term !== 'string') { throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $term, should be string`); } else { - answer[key] = { + answer[constraintKey] = { $search: search.$term, }; } if (search.$language && typeof search.$language !== 'string') { throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $language, should be string`); } else if (search.$language) { - answer[key].$language = search.$language; + answer[constraintKey].$language = search.$language; } if (search.$caseSensitive && typeof search.$caseSensitive !== 'boolean') { throw new Parse.Error( @@ -794,7 +797,7 @@ function transformConstraint(constraint, field, count = false) { `bad $text: $caseSensitive, should be boolean` ); } else if (search.$caseSensitive) { - answer[key].$caseSensitive = search.$caseSensitive; + answer[constraintKey].$caseSensitive = search.$caseSensitive; } if (search.$diacriticSensitive && typeof search.$diacriticSensitive !== 'boolean') { throw new Parse.Error( @@ -802,18 +805,18 @@ function transformConstraint(constraint, field, count = false) { `bad $text: $diacriticSensitive, should be boolean` ); } else if (search.$diacriticSensitive) { - answer[key].$diacriticSensitive = search.$diacriticSensitive; + answer[constraintKey].$diacriticSensitive = search.$diacriticSensitive; } break; } case '$nearSphere': { - const point = constraint[key]; + const point = constraint[constraintKey]; if (count) { answer.$geoWithin = { $centerSphere: [[point.longitude, point.latitude], constraint.$maxDistance], }; } else { - answer[key] = [point.longitude, point.latitude]; + answer[constraintKey] = [point.longitude, point.latitude]; } break; } @@ -821,34 +824,34 @@ function transformConstraint(constraint, field, count = false) { if (count) { break; } - answer[key] = constraint[key]; + answer[constraintKey] = constraint[constraintKey]; break; } // The SDKs don't seem to use these but they are documented in the // REST API docs. case '$maxDistanceInRadians': - answer['$maxDistance'] = constraint[key]; + answer['$maxDistance'] = constraint[constraintKey]; break; case '$maxDistanceInMiles': - answer['$maxDistance'] = constraint[key] / 3959; + answer['$maxDistance'] = constraint[constraintKey] / 3959; break; case '$maxDistanceInKilometers': - answer['$maxDistance'] = constraint[key] / 6371; + answer['$maxDistance'] = constraint[constraintKey] / 6371; break; case '$select': case '$dontSelect': throw new Parse.Error( Parse.Error.COMMAND_UNAVAILABLE, - 'the ' + key + ' constraint is not supported yet' + 'the ' + constraintKey + ' constraint is not supported yet' ); case '$within': - var box = constraint[key]['$box']; + var box = constraint[constraintKey]['$box']; if (!box || box.length != 2) { throw new Parse.Error(Parse.Error.INVALID_JSON, 'malformatted $within arg'); } - answer[key] = { + answer[constraintKey] = { $box: [ [box[0].longitude, box[0].latitude], [box[1].longitude, box[1].latitude], @@ -857,8 +860,8 @@ function transformConstraint(constraint, field, count = false) { break; case '$geoWithin': { - const polygon = constraint[key]['$polygon']; - const centerSphere = constraint[key]['$centerSphere']; + const polygon = constraint[constraintKey]['$polygon']; + const centerSphere = constraint[constraintKey]['$centerSphere']; if (polygon !== undefined) { let points; if (typeof polygon === 'object' && polygon.__type === 'Polygon') { @@ -895,7 +898,7 @@ function transformConstraint(constraint, field, count = false) { } return [point.longitude, point.latitude]; }); - answer[key] = { + answer[constraintKey] = { $polygon: points, }; } else if (centerSphere !== undefined) { @@ -924,14 +927,14 @@ function transformConstraint(constraint, field, count = false) { 'bad $geoWithin value; $centerSphere distance invalid' ); } - answer[key] = { + answer[constraintKey] = { $centerSphere: [[point.longitude, point.latitude], distance], }; } break; } case '$geoIntersects': { - const point = constraint[key]['$point']; + const point = constraint[constraintKey]['$point']; if (!GeoPointCoder.isValidJSON(point)) { throw new Parse.Error( Parse.Error.INVALID_JSON, @@ -940,7 +943,7 @@ function transformConstraint(constraint, field, count = false) { } else { Parse.GeoPoint._validate(point.latitude, point.longitude); } - answer[key] = { + answer[constraintKey] = { $geometry: { type: 'Point', coordinates: [point.longitude, point.latitude], @@ -949,8 +952,8 @@ function transformConstraint(constraint, field, count = false) { break; } default: - if (key.match(/^\$+/)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad constraint: ' + key); + if (constraintKey.match(/^\$+/)) { + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad constraint: ' + constraintKey); } return CannotTransform; } From be419ed83107f26a66fec91d506a56963ac1d6b1 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 30 Apr 2025 00:26:59 +0100 Subject: [PATCH 03/10] rename tests Signed-off-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- spec/MongoStorageAdapter.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index d6b9fcedcb..730f8bede4 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -395,7 +395,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { * If we use equalTo to comparse the nested pointer it works * But it does not work with contained in or matchesQuery */ - it('Parse query works with nested objects if equal to is used', async () => { + it('queries nested objects using equalTo', async () => { const child = new Parse.Object('Child') child.set('key','value') await child.save(); @@ -439,7 +439,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(query1.length).toEqual(1); }) - it('Parse query works when matchesQuery is used which in turn uses contained in', async () => { + it('queries nested objects using matchesQuery', async () => { const child = new Parse.Object('Child') child.set('key','value') await child.save(); From 649507def43ac883a16109b4105545e9be700e64 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 30 Apr 2025 00:27:43 +0100 Subject: [PATCH 04/10] rename test Signed-off-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- spec/MongoStorageAdapter.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index 730f8bede4..62f072097b 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -417,7 +417,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(query1.length).toEqual(1); }) - it('Parse query works when containedIn is used', async () => { + it('queries nested objects using containedIn', async () => { const child = new Parse.Object('Child') child.set('key','value') await child.save(); From 628a9a72ffe78754550ff28d16ace325071b63ed Mon Sep 17 00:00:00 2001 From: Rahul Lanjewar Date: Wed, 30 Apr 2025 13:32:12 +0530 Subject: [PATCH 05/10] fix: test --- spec/ParseQuery.spec.js | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index e6f3b1e08a..fe0a18183e 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -5306,4 +5306,76 @@ describe('Parse.Query testing', () => { expect(score).toEqual([1]); }, { useMasterKey: true }); }); + + describe_only_db('mongo')('check if containedIn and matchesQuery works with nested keys', async () => { + /** + * If we use equalTo to compare the nested pointer it works + * But it does not work with contained in or matchesQuery + */ + it('Parse query works with nested objects if equal to is used', 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('Parse query works when containedIn is used', 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('Parse query works when matchesQuery is used which in turn uses contained in', 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); + }); + }); }); From 7f9fc03cfd43ff2b704b2f8912d73050352e4409 Mon Sep 17 00:00:00 2001 From: Rahul Lanjewar Date: Wed, 30 Apr 2025 13:34:34 +0530 Subject: [PATCH 06/10] fix: remove unwanted changes --- src/Adapters/Storage/Mongo/MongoTransform.js | 86 ++++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 9a0ab9780a..f78c972bdc 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -651,10 +651,10 @@ 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, key, 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 = key.indexOf('.') > -1; + const isNestedKey = queryKey.indexOf('.') > -1; if (typeof constraint !== 'object' || !constraint) { return CannotTransform; } @@ -671,10 +671,10 @@ function transformConstraint(constraint, field, key, count = false) { // This is a hack so that: // $regex is handled before $options // $nearSphere is handled before $maxDistance - var constraintKeys = Object.keys(constraint).sort().reverse(); + var keys = Object.keys(constraint).sort().reverse(); var answer = {}; - for (var constraintKey of constraintKeys) { - switch (constraintKey) { + for (var key of keys) { + switch (key) { case '$lt': case '$lte': case '$gt': @@ -682,7 +682,7 @@ function transformConstraint(constraint, field, key, count = false) { case '$exists': case '$ne': case '$eq': { - const val = constraint[constraintKey]; + const val = constraint[key]; if (val && typeof val === 'object' && val.$relativeTime) { if (field && field.type !== 'Date') { throw new Parse.Error( @@ -691,7 +691,7 @@ function transformConstraint(constraint, field, key, count = false) { ); } - switch (constraintKey) { + switch (key) { case '$exists': case '$ne': case '$eq': @@ -703,28 +703,28 @@ function transformConstraint(constraint, field, key, count = false) { const parserResult = Utils.relativeTimeToDate(val.$relativeTime); if (parserResult.status === 'success') { - answer[constraintKey] = parserResult.result; + answer[key] = parserResult.result; break; } log.info('Error while parsing relative date', parserResult); throw new Parse.Error( Parse.Error.INVALID_JSON, - `bad $relativeTime (${constraintKey}) value. ${parserResult.info}` + `bad $relativeTime (${key}) value. ${parserResult.info}` ); } - answer[constraintKey] = transformer(val); + answer[key] = transformer(val); break; } case '$in': case '$nin': { - const arr = constraint[constraintKey]; + const arr = constraint[key]; if (!(arr instanceof Array)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + constraintKey + ' value'); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value'); } - answer[constraintKey] = _.flatMap(arr, value => { + answer[key] = _.flatMap(arr, value => { return (atom => { if (Array.isArray(atom)) { return value.map(transformer); @@ -736,13 +736,13 @@ function transformConstraint(constraint, field, key, count = false) { break; } case '$all': { - const arr = constraint[constraintKey]; + const arr = constraint[key]; if (!(arr instanceof Array)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + constraintKey + ' value'); + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value'); } - answer[constraintKey] = arr.map(transformInteriorAtom); + answer[key] = arr.map(transformInteriorAtom); - const values = answer[constraintKey]; + const values = answer[key]; if (isAnyValueRegex(values) && !isAllValuesRegexOrNone(values)) { throw new Parse.Error( Parse.Error.INVALID_JSON, @@ -753,15 +753,15 @@ function transformConstraint(constraint, field, key, count = false) { break; } case '$regex': - var s = constraint[constraintKey]; + var s = constraint[key]; if (typeof s !== 'string') { throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad regex: ' + s); } - answer[constraintKey] = s; + answer[key] = s; break; case '$containedBy': { - const arr = constraint[constraintKey]; + const arr = constraint[key]; if (!(arr instanceof Array)) { throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $containedBy: should be an array`); } @@ -771,25 +771,25 @@ function transformConstraint(constraint, field, key, count = false) { break; } case '$options': - answer[constraintKey] = constraint[constraintKey]; + answer[key] = constraint[key]; break; case '$text': { - const search = constraint[constraintKey].$search; + const search = constraint[key].$search; if (typeof search !== 'object') { throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $search, should be object`); } if (!search.$term || typeof search.$term !== 'string') { throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $term, should be string`); } else { - answer[constraintKey] = { + answer[key] = { $search: search.$term, }; } if (search.$language && typeof search.$language !== 'string') { throw new Parse.Error(Parse.Error.INVALID_JSON, `bad $text: $language, should be string`); } else if (search.$language) { - answer[constraintKey].$language = search.$language; + answer[key].$language = search.$language; } if (search.$caseSensitive && typeof search.$caseSensitive !== 'boolean') { throw new Parse.Error( @@ -797,7 +797,7 @@ function transformConstraint(constraint, field, key, count = false) { `bad $text: $caseSensitive, should be boolean` ); } else if (search.$caseSensitive) { - answer[constraintKey].$caseSensitive = search.$caseSensitive; + answer[key].$caseSensitive = search.$caseSensitive; } if (search.$diacriticSensitive && typeof search.$diacriticSensitive !== 'boolean') { throw new Parse.Error( @@ -805,18 +805,18 @@ function transformConstraint(constraint, field, key, count = false) { `bad $text: $diacriticSensitive, should be boolean` ); } else if (search.$diacriticSensitive) { - answer[constraintKey].$diacriticSensitive = search.$diacriticSensitive; + answer[key].$diacriticSensitive = search.$diacriticSensitive; } break; } case '$nearSphere': { - const point = constraint[constraintKey]; + const point = constraint[key]; if (count) { answer.$geoWithin = { $centerSphere: [[point.longitude, point.latitude], constraint.$maxDistance], }; } else { - answer[constraintKey] = [point.longitude, point.latitude]; + answer[key] = [point.longitude, point.latitude]; } break; } @@ -824,34 +824,34 @@ function transformConstraint(constraint, field, key, count = false) { if (count) { break; } - answer[constraintKey] = constraint[constraintKey]; + answer[key] = constraint[key]; break; } // The SDKs don't seem to use these but they are documented in the // REST API docs. case '$maxDistanceInRadians': - answer['$maxDistance'] = constraint[constraintKey]; + answer['$maxDistance'] = constraint[key]; break; case '$maxDistanceInMiles': - answer['$maxDistance'] = constraint[constraintKey] / 3959; + answer['$maxDistance'] = constraint[key] / 3959; break; case '$maxDistanceInKilometers': - answer['$maxDistance'] = constraint[constraintKey] / 6371; + answer['$maxDistance'] = constraint[key] / 6371; break; case '$select': case '$dontSelect': throw new Parse.Error( Parse.Error.COMMAND_UNAVAILABLE, - 'the ' + constraintKey + ' constraint is not supported yet' + 'the ' + key + ' constraint is not supported yet' ); case '$within': - var box = constraint[constraintKey]['$box']; + var box = constraint[key]['$box']; if (!box || box.length != 2) { throw new Parse.Error(Parse.Error.INVALID_JSON, 'malformatted $within arg'); } - answer[constraintKey] = { + answer[key] = { $box: [ [box[0].longitude, box[0].latitude], [box[1].longitude, box[1].latitude], @@ -860,8 +860,8 @@ function transformConstraint(constraint, field, key, count = false) { break; case '$geoWithin': { - const polygon = constraint[constraintKey]['$polygon']; - const centerSphere = constraint[constraintKey]['$centerSphere']; + const polygon = constraint[key]['$polygon']; + const centerSphere = constraint[key]['$centerSphere']; if (polygon !== undefined) { let points; if (typeof polygon === 'object' && polygon.__type === 'Polygon') { @@ -898,7 +898,7 @@ function transformConstraint(constraint, field, key, count = false) { } return [point.longitude, point.latitude]; }); - answer[constraintKey] = { + answer[key] = { $polygon: points, }; } else if (centerSphere !== undefined) { @@ -927,14 +927,14 @@ function transformConstraint(constraint, field, key, count = false) { 'bad $geoWithin value; $centerSphere distance invalid' ); } - answer[constraintKey] = { + answer[key] = { $centerSphere: [[point.longitude, point.latitude], distance], }; } break; } case '$geoIntersects': { - const point = constraint[constraintKey]['$point']; + const point = constraint[key]['$point']; if (!GeoPointCoder.isValidJSON(point)) { throw new Parse.Error( Parse.Error.INVALID_JSON, @@ -943,7 +943,7 @@ function transformConstraint(constraint, field, key, count = false) { } else { Parse.GeoPoint._validate(point.latitude, point.longitude); } - answer[constraintKey] = { + answer[key] = { $geometry: { type: 'Point', coordinates: [point.longitude, point.latitude], @@ -952,8 +952,8 @@ function transformConstraint(constraint, field, key, count = false) { break; } default: - if (constraintKey.match(/^\$+/)) { - throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad constraint: ' + constraintKey); + if (key.match(/^\$+/)) { + throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad constraint: ' + key); } return CannotTransform; } From 89f875031d093c82c8c4e88137b3394dc268acf2 Mon Sep 17 00:00:00 2001 From: Rahul Lanjewar Date: Wed, 30 Apr 2025 13:35:33 +0530 Subject: [PATCH 07/10] fix: remove changes from mongo storage spec --- spec/MongoStorageAdapter.spec.js | 71 -------------------------------- 1 file changed, 71 deletions(-) diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index 62f072097b..b026fc0961 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -390,77 +390,6 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined); }); - - /** - * If we use equalTo to comparse the nested pointer it works - * But it does not work with contained in or matchesQuery - */ - it('queries nested objects 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 objects 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 objects 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); - }) - it_only_mongodb_version('<5.1 || >=6')('should use index for caseInsensitive query', async () => { const user = new Parse.User(); user.set('username', 'Bugs'); From ccf5a60caae8efebc1f87513ac5985897bb4d26d Mon Sep 17 00:00:00 2001 From: Rahul Lanjewar Date: Wed, 30 Apr 2025 16:19:34 +0530 Subject: [PATCH 08/10] fix: remove async --- spec/ParseQuery.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index fe0a18183e..18201ff4d4 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -5307,7 +5307,7 @@ describe('Parse.Query testing', () => { }, { useMasterKey: true }); }); - describe_only_db('mongo')('check if containedIn and matchesQuery works with nested keys', async () => { + describe_only_db('mongo')('check if containedIn and matchesQuery works with nested keys', () => { /** * If we use equalTo to compare the nested pointer it works * But it does not work with contained in or matchesQuery From fc67ed74b507beaf911f9c2064adb4cbcde2f458 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Fri, 2 May 2025 16:42:19 +0100 Subject: [PATCH 09/10] simplify test names Signed-off-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- spec/ParseQuery.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index 18201ff4d4..f635479d99 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -5307,12 +5307,12 @@ describe('Parse.Query testing', () => { }, { useMasterKey: true }); }); - describe_only_db('mongo')('check if containedIn and matchesQuery works with nested keys', () => { + describe_only_db('mongo')('query nested keys', () => { /** * If we use equalTo to compare the nested pointer it works * But it does not work with contained in or matchesQuery */ - it('Parse query works with nested objects if equal to is used', async () => { + it('queries nested key using equalTo', async () => { const child = new Parse.Object('Child'); child.set('key', 'value'); await child.save(); @@ -5334,7 +5334,7 @@ describe('Parse.Query testing', () => { expect(query1.length).toEqual(1); }); - it('Parse query works when containedIn is used', async () => { + it('queries nested key using containedIn', async () => { const child = new Parse.Object('Child'); child.set('key', 'value'); await child.save(); @@ -5356,7 +5356,7 @@ describe('Parse.Query testing', () => { expect(query1.length).toEqual(1); }); - it('Parse query works when matchesQuery is used which in turn uses contained in', async () => { + it('queries nested key using matchesQuery', async () => { const child = new Parse.Object('Child'); child.set('key', 'value'); await child.save(); From e7836b75895d030f29fd40eb591dde94adfa700b Mon Sep 17 00:00:00 2001 From: Rahul Lanjewar <63550998+RahulLanjewar93@users.noreply.github.com> Date: Fri, 2 May 2025 21:15:47 +0530 Subject: [PATCH 10/10] remove comment since it explains the bug Signed-off-by: Rahul Lanjewar <63550998+RahulLanjewar93@users.noreply.github.com> --- spec/ParseQuery.spec.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index f635479d99..a8ed838d23 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -5308,10 +5308,6 @@ describe('Parse.Query testing', () => { }); describe_only_db('mongo')('query nested keys', () => { - /** - * If we use equalTo to compare the nested pointer it works - * But it does not work with contained in or matchesQuery - */ it('queries nested key using equalTo', async () => { const child = new Parse.Object('Child'); child.set('key', 'value');