Skip to content
84 changes: 84 additions & 0 deletions spec/ParseQuery.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,90 @@ describe('Parse.Query testing', () => {
});
});

it('can order on an object string field', function (done) {
const testSet = [
{ sortField: { value: "Z" } },
{ sortField: { value: "A" } },
{ sortField: { value: "M" } },
];

const objects = testSet.map(e => new Parse.Object('Test', e));
Parse.Object.saveAll(objects)
.then(() => new Parse.Query('Test').addDescending('sortField.value').first())
.then((result) => {
expect(result.get('sortField').value).toBe("Z");
return new Parse.Query('Test').addAscending('sortField.value').first()
})
.then((result) => {
expect(result.get('sortField').value).toBe("A");
done();
})
.catch(done.fail);
});

it('can order on an object string field (level 2)', function (done) {
const testSet = [
{ sortField: { value: { field: "Z" } } },
{ sortField: { value: { field: "A" } } },
{ sortField: { value: { field: "M" } } },
];

const objects = testSet.map(e => new Parse.Object('Test', e));
Parse.Object.saveAll(objects)
.then(() => new Parse.Query('Test').addDescending('sortField.value.field').first())
.then((result) => {
expect(result.get('sortField').value.field).toBe("Z");
return new Parse.Query('Test').addAscending('sortField.value.field').first()
})
.then((result) => {
expect(result.get('sortField').value.field).toBe("A");
done();
})
.catch(done.fail);
});

it('can order on an object number field', function (done) {
const testSet = [
{ sortField: { value: 10 } },
{ sortField: { value: 1 } },
{ sortField: { value: 5 } },
];

const objects = testSet.map(e => new Parse.Object('Test', e));
Parse.Object.saveAll(objects)
.then(() => new Parse.Query('Test').addDescending('sortField.value').first())
.then((result) => {
expect(result.get('sortField').value).toBe(10);
return new Parse.Query('Test').addAscending('sortField.value').first()
})
.then((result) => {
expect(result.get('sortField').value).toBe(1);
done();
})
.catch(done.fail);
});

it('can order on an object number field (level 2)', function (done) {
const testSet = [
{ sortField: { value: { field: 10 } } },
{ sortField: { value: { field: 1 } } },
{ sortField: { value: { field: 5 } } },
];

const objects = testSet.map(e => new Parse.Object('Test', e));
Parse.Object.saveAll(objects)
.then(() => new Parse.Query('Test').addDescending('sortField.value.field').first())
.then((result) => {
expect(result.get('sortField').value.field).toBe(10);
return new Parse.Query('Test').addAscending('sortField.value.field').first()
})
.then((result) => {
expect(result.get('sortField').value.field).toBe(1);
done();
})
.catch(done.fail);
});

it("order by ascending number then descending string", function(done) {
const strings = ["a", "b", "c", "d"];
const makeBoxedNumber = function(num, i) {
Expand Down
5 changes: 3 additions & 2 deletions src/Adapters/Storage/Postgres/PostgresStorageAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -1397,11 +1397,12 @@ export class PostgresStorageAdapter implements StorageAdapter {
if (sort) {
const sortCopy: any = sort;
const sorting = Object.keys(sort).map((key) => {
const transformKey = transformDotFieldToComponents(key).join('->');
// Using $idx pattern gives: non-integer constant in ORDER BY
if (sortCopy[key] === 1) {
return `"${key}" ASC`;
return `${transformKey} ASC`;
}
return `"${key}" DESC`;
return `${transformKey} DESC`;
}).join();
sortPattern = sort !== undefined && Object.keys(sort).length > 0 ? `ORDER BY ${sorting}` : '';
}
Expand Down
17 changes: 14 additions & 3 deletions src/Controllers/DatabaseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,16 @@ const untransformObjectACL = ({_rperm, _wperm, ...output}) => {
return output;
}

/**
* When querying, the fieldName may be compound, extract the base fieldName
* `temperature.celsius` becomes `temperature`
* @param {string} fieldName that may be a compound field name
* @returns {string} the basename of the field
*/
const getBaseFieldName = (fieldName: string): string => {
return fieldName.split('.')[0]
}

const relationSchema = { fields: { relatedId: { type: 'String' }, owningId: { type: 'String' } } };

class DatabaseController {
Expand Down Expand Up @@ -411,8 +421,8 @@ class DatabaseController {
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name for update: ${fieldName}`);
}
fieldName = fieldName.split('.')[0];
if (!SchemaController.fieldNameIsValid(fieldName) && !isSpecialUpdateKey(fieldName)) {
const baseFieldName = getBaseFieldName(fieldName);
if (!SchemaController.fieldNameIsValid(baseFieldName) && !isSpecialUpdateKey(baseFieldName)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name for update: ${fieldName}`);
}
});
Expand Down Expand Up @@ -900,7 +910,8 @@ class DatabaseController {
if (fieldName.match(/^authData\.([a-zA-Z0-9_]+)\.id$/)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Cannot sort by ${fieldName}`);
}
if (!SchemaController.fieldNameIsValid(fieldName)) {
const baseFieldName = getBaseFieldName(fieldName);
if (!SchemaController.fieldNameIsValid(baseFieldName)) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, `Invalid field name: ${fieldName}.`);
}
});
Expand Down