From 752f0e9143a82fabf1d45e2a966093be1dad8c6c Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Wed, 7 Dec 2016 20:03:40 -0500 Subject: [PATCH] Fixes #3195 (#3201) * Adds error reproduction * Fix transform in order to accept nested array results in $in/$nin The error originated by the nesting of 2 array in $in [["..."]], using _.flatMap with those will guarantee at the lower level that the query is properly resolved * nits * _.flatMap the $in/$nin values --- spec/ParseQuery.spec.js | 29 ++++++++++++++++ src/Adapters/Storage/Mongo/MongoTransform.js | 33 +++++++++++-------- .../Postgres/PostgresStorageAdapter.js | 5 +-- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index b0cc1802..45e522e7 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -2467,6 +2467,35 @@ describe('Parse.Query testing', () => { }); }); + it("should match a key in an array (#3195)", function(done) { + var AuthorObject = Parse.Object.extend("Author"); + var GroupObject = Parse.Object.extend("Group"); + var PostObject = Parse.Object.extend("Post"); + + return new AuthorObject().save().then((user) => { + const post = new PostObject({ + author: user + }); + + const group = new GroupObject({ + members: [user], + }); + + return Parse.Promise.when(post.save(), group.save()); + }).then((p) => { + return new Parse.Query(PostObject) + .matchesKeyInQuery("author", "members", new Parse.Query(GroupObject)) + .find() + .then((r) => { + expect(r.length).toEqual(1); + if (r.length > 0) { + expect(r[0].id).toEqual(p.id); + } + done(); + }, done.fail); + }); + }); + it('should find objects with array of pointers', (done) => { var objects = []; while(objects.length != 5) { diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 6b1fe1ce..1c6e7d2b 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -223,8 +223,9 @@ function transformQueryKeyValue(className, key, value, schema) { } // Handle query constraints - if (transformConstraint(value, expectedTypeIsArray) !== CannotTransform) { - return {key, value: transformConstraint(value, expectedTypeIsArray)}; + const transformedConstraint = transformConstraint(value, expectedTypeIsArray); + if (transformedConstraint !== CannotTransform) { + return {key, value: transformedConstraint}; } if (expectedTypeIsArray && !(value instanceof Array)) { @@ -508,7 +509,14 @@ function transformConstraint(constraint, inArray) { if (typeof constraint !== 'object' || !constraint) { return CannotTransform; } - + const transformFunction = inArray ? transformInteriorAtom : transformTopLevelAtom; + const transformer = (atom) => { + const result = transformFunction(atom); + if (result === CannotTransform) { + throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${JSON.stringify(atom)}`); + } + return result; + } // keys is the constraints in reverse alphabetical order. // This is a hack so that: // $regex is handled before $options @@ -524,10 +532,7 @@ function transformConstraint(constraint, inArray) { case '$exists': case '$ne': case '$eq': - answer[key] = inArray ? transformInteriorAtom(constraint[key]) : transformTopLevelAtom(constraint[key]); - if (answer[key] === CannotTransform) { - throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${constraint[key]}`); - } + answer[key] = transformer(constraint[key]); break; case '$in': @@ -536,12 +541,14 @@ function transformConstraint(constraint, inArray) { if (!(arr instanceof Array)) { throw new Parse.Error(Parse.Error.INVALID_JSON, 'bad ' + key + ' value'); } - answer[key] = arr.map(value => { - const result = inArray ? transformInteriorAtom(value) : transformTopLevelAtom(value); - if (result === CannotTransform) { - throw new Parse.Error(Parse.Error.INVALID_JSON, `bad atom: ${value}`); - } - return result; + answer[key] = _.flatMap(arr, value => { + return ((atom) => { + if (Array.isArray(atom)) { + return value.map(transformer); + } else { + return transformer(atom); + } + })(value); }); break; } diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index 194c6938..40a2ee1b 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -1,5 +1,6 @@ import { createClient } from './PostgresClient'; import Parse from 'parse/node'; +import _ from 'lodash'; const PostgresRelationDoesNotExistError = '42P01'; const PostgresDuplicateRelationError = '42P07'; @@ -296,10 +297,10 @@ const buildWhereClause = ({ schema, query, index }) => { } } if (fieldValue.$in) { - createConstraint(fieldValue.$in, false); + createConstraint(_.flatMap(fieldValue.$in, elt => elt), false); } if (fieldValue.$nin) { - createConstraint(fieldValue.$nin, true); + createConstraint(_.flatMap(fieldValue.$nin, elt => elt), true); } }