Skip to content

Commit 4da26f2

Browse files
committed
Adds support for AddUnique and Remove
1 parent 8c33182 commit 4da26f2

File tree

3 files changed

+67
-17
lines changed

3 files changed

+67
-17
lines changed

spec/ParseAPI.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ it_exclude_dbs(['postgres'])('ensure that if you try to sign up a user with a un
817817
});
818818
});
819819

820-
it_exclude_dbs(['postgres'])('should return the updated fields on PUT', done => {
820+
it('should return the updated fields on PUT', done => {
821821
let obj = new Parse.Object('GameScore');
822822
obj.save({a:'hello', c: 1, d: ['1'], e:['1'], f:['1','2']}).then(( ) => {
823823
var headers = {

spec/ParseObject.spec.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -625,15 +625,21 @@ describe('Parse.Object testing', () => {
625625
var query = new Parse.Query('X');
626626
return query.get(x1.id);
627627
}).then((x3) => {
628-
expect(x3.get('stuff')).toEqual([1, {'hello': 'world'}, {'foo': 'bar'}, {'bar': 'baz'}]);
628+
let stuff = x3.get('stuff');
629+
let target = [1, {'hello': 'world'}, {'foo': 'bar'}, {'bar': 'baz'}];
630+
expect(stuff.length).toEqual(target.length);
631+
let found = 0;
632+
for (let thing in target) {
633+
for (let st in stuff) {
634+
if (st == thing) {
635+
found++;
636+
}
637+
}
638+
}
639+
expect(found).toBe(target.length);
629640
done();
630641
}, (error) => {
631-
on_db('mongo', () => {
632-
jfail(error);
633-
});
634-
on_db('postgres', () => {
635-
expect(error.message).toEqual("Postgres does not support AddUnique operator.");
636-
});
642+
jfail(error);
637643
done();
638644
});
639645
});
@@ -654,6 +660,7 @@ describe('Parse.Object testing', () => {
654660
expect(x3.get('stuff')).toEqual([1, {'foo': 'bar'}]);
655661
done();
656662
}, (error) => {
663+
console.error(error);
657664
on_db('mongo', () => {
658665
jfail(error);
659666
});

src/Adapters/Storage/Postgres/PostgresStorageAdapter.js

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -624,8 +624,16 @@ export class PostgresStorageAdapter {
624624
});
625625

626626
columnsArray = columnsArray.concat(Object.keys(geoPoints));
627-
let initialValues = valuesArray.map((val, index) => `$${index + 2 + columnsArray.length}${(['_rperm','_wperm'].includes(columnsArray[index])) ? '::text[]' : ''}`);
628-
627+
let initialValues = valuesArray.map((val, index) => {
628+
let termination = '';
629+
let fieldName = columnsArray[index];
630+
if (['_rperm','_wperm'].includes(fieldName)) {
631+
termination = '::text[]';
632+
} else if (schema.fields[fieldName] && schema.fields[fieldName].type === 'Array') {
633+
termination = '::jsonb';
634+
}
635+
return `$${index + 2 + columnsArray.length}${termination}`;
636+
});
629637
let geoPointsInjects = Object.keys(geoPoints).map((key, idx) => {
630638
let value = geoPoints[key];
631639
valuesArray.push(value.latitude, value.longitude);
@@ -725,17 +733,21 @@ export class PostgresStorageAdapter {
725733
values.push(fieldName, fieldValue.amount);
726734
index += 2;
727735
} else if (fieldValue.__op === 'Add') {
728-
updatePatterns.push(`$${index}:name = COALESCE($${index}:name, '[]'::jsonb) || $${index + 1}`);
736+
updatePatterns.push(`$${index}:name = COALESCE($${index}:name, '[]'::jsonb) || array_to_json($${index + 1})::jsonb`);
729737
values.push(fieldName, fieldValue.objects);
730738
index += 2;
731739
} else if (fieldValue.__op === 'Delete') {
732740
updatePatterns.push(`$${index}:name = $${index + 1}`)
733741
values.push(fieldName, null);
734742
index += 2;
735743
} else if (fieldValue.__op === 'Remove') {
736-
return Promise.reject(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Postgres does not support Remove operator.'));
744+
updatePatterns.push(`$${index}:name = array_remove(COALESCE($${index}:name, '[]'::jsonb), $${index + 1}::jsonb)`)
745+
values.push(fieldName, JSON.stringify(fieldValue.objects));
746+
index += 2;
737747
} else if (fieldValue.__op === 'AddUnique') {
738-
return Promise.reject(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Postgres does not support AddUnique operator.'));
748+
updatePatterns.push(`$${index}:name = array_add_unique(COALESCE($${index}:name, '[]'::jsonb), $${index + 1}::jsonb)`);
749+
values.push(fieldName, JSON.stringify(fieldValue.objects));
750+
index += 2;
739751
} else if (fieldName === 'updatedAt') { //TODO: stop special casing this. It should check for __type === 'Date' and use .iso
740752
updatePatterns.push(`$${index}:name = $${index + 1}`)
741753
values.push(fieldName, fieldValue);
@@ -958,11 +970,18 @@ export class PostgresStorageAdapter {
958970
throw err;
959971
});
960972
});
961-
962973
return Promise.all(promises).then(() => {
963-
return this._client.any(json_object_set_key).catch((err) => {
964-
console.error(err);
965-
})
974+
return Promise.all([
975+
this._client.any(json_object_set_key).catch((err) => {
976+
console.error(err);
977+
}),
978+
this._client.any(array_add_unique).catch((err) => {
979+
console.error(err);
980+
}),
981+
this._client.any(array_remove).catch((err) => {
982+
console.error(err);
983+
})
984+
]);
966985
}).then(() => {
967986
debug(`initialzationDone in ${new Date().getTime() - now}`);
968987
})
@@ -992,5 +1011,29 @@ SELECT concat(\'{\', string_agg(to_json("key") || \':\' || "value", \',\'), \'}\
9921011
SELECT "key_to_set", to_json("value_to_set")::jsonb) AS "fields"\
9931012
$function$;'
9941013

1014+
const array_add_unique = `CREATE OR REPLACE FUNCTION "array_add_unique"(
1015+
"array" jsonb,
1016+
"values" jsonb
1017+
)
1018+
RETURNS jsonb
1019+
LANGUAGE sql
1020+
IMMUTABLE
1021+
STRICT
1022+
AS $function$
1023+
SELECT to_jsonb(ARRAY(SELECT DISTINCT jsonb_array_elements("values" || "array")))
1024+
$function$;`;
1025+
1026+
const array_remove = `CREATE OR REPLACE FUNCTION "array_remove"(\
1027+
"array" jsonb,\
1028+
"values" jsonb\
1029+
)\
1030+
RETURNS jsonb \
1031+
LANGUAGE sql \
1032+
IMMUTABLE \
1033+
STRICT \
1034+
AS $function$ \
1035+
SELECT to_jsonb(ARRAY(SELECT * FROM jsonb_array_elements("array") as elt WHERE elt NOT IN (SELECT * FROM (SELECT jsonb_array_elements("values")) AS sub))) \
1036+
$function$;`;
1037+
9951038
export default PostgresStorageAdapter;
9961039
module.exports = PostgresStorageAdapter; // Required for tests

0 commit comments

Comments
 (0)