diff --git a/spec/ParseQuery.Aggregate.spec.js b/spec/ParseQuery.Aggregate.spec.js index 6b6a184a9b..7e4bc6ffbe 100644 --- a/spec/ParseQuery.Aggregate.spec.js +++ b/spec/ParseQuery.Aggregate.spec.js @@ -589,6 +589,30 @@ describe('Parse.Query Aggregate testing', () => { }).catch(done.fail); }); + it('distinct null field', (done) => { + const options = Object.assign({}, masterKeyOptions, { + body: { distinct: 'distinctField' } + }); + const user1 = new Parse.User(); + user1.setUsername('distinct_1'); + user1.setPassword('password'); + user1.set('distinctField', 'one'); + + const user2 = new Parse.User(); + user2.setUsername('distinct_2'); + user2.setPassword('password'); + user2.set('distinctField', null); + user1.signUp().then(() => { + return user2.signUp(); + }).then(() => { + return rp.get(Parse.serverURL + '/aggregate/_User', options); + }).then((resp) => { + expect(resp.results.length).toEqual(1); + expect(resp.results).toEqual(['one']); + done(); + }).catch(done.fail); + }); + it('does not return sensitive hidden properties', (done) => { const options = Object.assign({}, masterKeyOptions, { body: { diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 71d37340f9..11218f97aa 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -511,13 +511,16 @@ export class MongoStorageAdapter implements StorageAdapter { } return this._adaptiveCollection(className) .then(collection => collection.distinct(fieldName, transformWhere(className, query, schema))) - .then(objects => objects.map(object => { - if (isPointerField) { - const field = fieldName.substring(3); - return transformPointerString(schema, field, object); - } - return mongoObjectToParseObject(className, object, schema); - })); + .then(objects => { + objects = objects.filter((obj) => obj != null); + return objects.map(object => { + if (isPointerField) { + const field = fieldName.substring(3); + return transformPointerString(schema, field, object); + } + return mongoObjectToParseObject(className, object, schema); + }); + }); } aggregate(className: string, schema: any, pipeline: any, readPreference: ?string) { diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index 9b76379c55..7cfbced820 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -1464,7 +1464,8 @@ export class PostgresStorageAdapter implements StorageAdapter { debug('distinct', className, query); let field = fieldName; let column = fieldName; - if (fieldName.indexOf('.') >= 0) { + const isNested = fieldName.indexOf('.') >= 0; + if (isNested) { field = transformDotFieldToComponents(fieldName).join('->'); column = fieldName.split('.')[0]; } @@ -1480,7 +1481,10 @@ export class PostgresStorageAdapter implements StorageAdapter { const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : ''; const transformer = isArrayField ? 'jsonb_array_elements' : 'ON'; - const qs = `SELECT DISTINCT ${transformer}($1:raw) $2:raw FROM $3:name ${wherePattern}`; + let qs = `SELECT DISTINCT ${transformer}($1:name) $2:name FROM $3:name ${wherePattern}`; + if (isNested) { + qs = `SELECT DISTINCT ${transformer}($1:raw) $2:raw FROM $3:name ${wherePattern}`; + } debug(qs, values); return this._client.any(qs, values) .catch((error) => { @@ -1490,7 +1494,7 @@ export class PostgresStorageAdapter implements StorageAdapter { throw error; }) .then((results) => { - if (fieldName.indexOf('.') === -1) { + if (!isNested) { results = results.filter((object) => object[field] !== null); return results.map(object => { if (!isPointerField) {