Support pointer in distinct query (#4471)

* Support pointer in distinct query

* extract transform pointer string
This commit is contained in:
Diamond Lewis
2017-12-29 21:32:40 -06:00
committed by GitHub
parent 7d773a5d8a
commit 6143988a82
4 changed files with 66 additions and 16 deletions

View File

@@ -392,6 +392,23 @@ describe('Parse.Query Aggregate testing', () => {
}).catch(done.fail);
});
it('distinct pointer', (done) => {
const pointer1 = new TestObject();
const pointer2 = new TestObject();
const obj1 = new TestObject({ pointer: pointer1 });
const obj2 = new TestObject({ pointer: pointer2 });
const obj3 = new TestObject({ pointer: pointer1 });
Parse.Object.saveAll([pointer1, pointer2, obj1, obj2, obj3]).then(() => {
const query = new Parse.Query(TestObject);
return query.distinct('pointer');
}).then((results) => {
expect(results.length).toEqual(2);
expect(results.some(result => result.objectId === pointer1.id)).toEqual(true);
expect(results.some(result => result.objectId === pointer2.id)).toEqual(true);
done();
});
});
it('distinct class does not exist return empty', (done) => {
const options = Object.assign({}, masterKeyOptions, {
body: { distinct: 'unknown' }

View File

@@ -10,6 +10,7 @@ import {
transformKey,
transformWhere,
transformUpdate,
transformPointerString,
} from './MongoTransform';
import Parse from 'parse/node';
import _ from 'lodash';
@@ -483,9 +484,19 @@ export class MongoStorageAdapter {
distinct(className, schema, query, fieldName) {
schema = convertParseSchemaToMongoSchema(schema);
const isPointerField = schema.fields[fieldName] && schema.fields[fieldName].type === 'Pointer';
if (isPointerField) {
fieldName = `_p_${fieldName}`
}
return this._adaptiveCollection(className)
.then(collection => collection.distinct(fieldName, transformWhere(className, query, schema)))
.then(objects => objects.map(object => mongoObjectToParseObject(className, object, schema)));
.then(objects => objects.map(object => {
if (isPointerField) {
const field = fieldName.substring(3);
return transformPointerString(schema, field, object);
}
return mongoObjectToParseObject(className, object, schema);
}));
}
aggregate(className, schema, pipeline, readPreference) {

View File

@@ -1014,6 +1014,18 @@ const nestedMongoObjectToNestedParseObject = mongoObject => {
}
}
const transformPointerString = (schema, field, pointerString) => {
const objData = pointerString.split('$');
if (objData[0] !== schema.fields[field].targetClass) {
throw 'pointer to incorrect className';
}
return {
__type: 'Pointer',
className: objData[0],
objectId: objData[1]
};
}
// Converts from a mongo-format object to a REST-format object.
// Does not strip out anything based on a lack of authentication.
const mongoObjectToParseObject = (className, mongoObject, schema) => {
@@ -1126,15 +1138,7 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => {
if (mongoObject[key] === null) {
break;
}
var objData = mongoObject[key].split('$');
if (objData[0] !== schema.fields[newKey].targetClass) {
throw 'pointer to incorrect className';
}
restObject[newKey] = {
__type: 'Pointer',
className: objData[0],
objectId: objData[1]
};
restObject[newKey] = transformPointerString(schema, newKey, mongoObject[key]);
break;
} else if (key[0] == '_' && key != '__type') {
throw ('bad key in untransform: ' + key);
@@ -1345,4 +1349,5 @@ module.exports = {
mongoObjectToParseObject,
relativeTimeToDate,
transformConstraint,
transformPointerString,
};

View File

@@ -6,6 +6,7 @@ import sql from './sql';
const PostgresRelationDoesNotExistError = '42P01';
const PostgresDuplicateRelationError = '42P07';
const PostgresDuplicateColumnError = '42701';
const PostgresMissingColumnError = '42703';
const PostgresDuplicateObjectError = '42710';
const PostgresUniqueIndexViolationError = '23505';
const PostgresTransactionAbortedError = '25P02';
@@ -1438,21 +1439,37 @@ export class PostgresStorageAdapter {
const isArrayField = schema.fields
&& schema.fields[fieldName]
&& schema.fields[fieldName].type === 'Array';
const isPointerField = schema.fields
&& schema.fields[fieldName]
&& schema.fields[fieldName].type === 'Pointer';
const values = [field, column, className];
const where = buildWhereClause({ schema, query, index: 4 });
values.push(...where.values);
const wherePattern = where.pattern.length > 0 ? `WHERE ${where.pattern}` : '';
let qs = `SELECT DISTINCT ON ($1:raw) $2:raw FROM $3:name ${wherePattern}`;
if (isArrayField) {
qs = `SELECT distinct jsonb_array_elements($1:raw) as $2:raw FROM $3:name ${wherePattern}`;
}
const transformer = isArrayField ? 'jsonb_array_elements' : 'ON';
const qs = `SELECT DISTINCT ${transformer}($1:raw) $2:raw FROM $3:name ${wherePattern}`;
debug(qs, values);
return this._client.any(qs, values)
.catch(() => [])
.catch((error) => {
if (error.code === PostgresMissingColumnError) {
return [];
}
throw error;
})
.then((results) => {
if (fieldName.indexOf('.') === -1) {
return results.map(object => object[field]);
results = results.filter((object) => object[field] !== null);
return results.map(object => {
if (!isPointerField) {
return object[field];
}
return {
__type: 'Pointer',
className: schema.fields[fieldName].targetClass,
objectId: object[field]
};
});
}
const child = fieldName.split('.')[1];
return results.map(object => object[column][child]);