Skip to content

Commit add5ae8

Browse files
Vaibhav  BhallaVaibhav  Bhalla
authored andcommitted
feat:partial json update
1 parent 92a199d commit add5ae8

File tree

3 files changed

+94
-4
lines changed

3 files changed

+94
-4
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,43 @@ CustomerRepository.find({
545545
});
546546
```
547547
548+
## Partial updates of JSON fields (CONCAT)
549+
550+
By default, updating a property mapped to the PostgreSQL 'jsonb' data type will replace the entire JSON value.
551+
With this enhancement, you can now perform partial updates (merges) of jsonb columns using the PostgreSQL JSONB concatenation operator (||).
552+
553+
### How it works
554+
555+
If you set the value of a property to an object containing a special CONCAT key, the connector will:
556+
557+
- Generate an UPDATE statement using || ?::jsonb
558+
559+
- Merge the object specified in CONCAT into the existing JSONB column value, overriding fields with the same key but leaving others unchanged.
560+
561+
Assuming a model property such as this:
562+
563+
```ts
564+
@property({
565+
type: 'object',
566+
postgresql: {
567+
dataType: 'jsonb'
568+
},
569+
})
570+
address?: object;
571+
```
572+
573+
Now perform a partial update to change only the city leaving the street intact:
574+
575+
```ts
576+
await customerRepository.updateById(customerId, {
577+
address: {
578+
CONCAT: {
579+
city: 'New City'
580+
}
581+
}
582+
});
583+
```
584+
548585
## Extended operators
549586
550587
PostgreSQL supports the following PostgreSQL-specific operators:

lib/postgresql.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ PostgreSQL.prototype._buildWhere = function(model, where) {
788788
* @param {boolean} isWhereClause
789789
* @returns {*} The escaped value of DB column
790790
*/
791-
PostgreSQL.prototype.toColumnValue = function(prop, val, isWhereClause) {
791+
PostgreSQL.prototype.toColumnValue = function(prop, val, isWhereClause, fieldName) {
792792
if (val == null) {
793793
// PostgreSQL complains with NULLs in not null columns
794794
// If we have an autoincrement value, return DEFAULT instead
@@ -857,7 +857,22 @@ PostgreSQL.prototype.toColumnValue = function(prop, val, isWhereClause) {
857857
}
858858
}
859859
}
860-
860+
if (prop.postgresql && prop.postgresql.dataType === 'jsonb') {
861+
// check for any json operator for updates
862+
const jsonType = prop.postgresql.dataType;
863+
try {
864+
const rawData = JSON.parse(val);
865+
if (rawData['CONCAT']) {
866+
// If the property is a json type and partial update is enabled,
867+
return new ParameterizedSQL({
868+
sql: `${fieldName} || ?::${jsonType}`,
869+
params: [JSON.stringify(rawData['CONCAT'])],
870+
});
871+
}
872+
} catch (e) {
873+
// do nothing
874+
}
875+
}
861876
return val;
862877
};
863878

test/postgresql.test.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -848,8 +848,13 @@ describe('postgresql connector', function() {
848848
dataType: 'json',
849849
},
850850
},
851+
metadata: {
852+
type: 'object',
853+
postgresql: {
854+
dataType: 'jsonb',
855+
},
856+
},
851857
});
852-
853858
db.automigrate(function(err) {
854859
if (err) return done(err);
855860
Customer.createAll([{
@@ -871,7 +876,6 @@ describe('postgresql connector', function() {
871876
});
872877
});
873878
});
874-
875879
it('allows querying for nested json properties', function(done) {
876880
Customer.find({
877881
where: {
@@ -963,6 +967,40 @@ describe('postgresql connector', function() {
963967
done();
964968
});
965969
});
970+
it('should support partial update of json data type using CONCAT', function(done) {
971+
Customer.create({
972+
address: {
973+
city: 'Old City',
974+
street: {
975+
number: 100,
976+
name: 'Old Street',
977+
},
978+
},
979+
metadata: {
980+
extenalid: '123',
981+
isactive: true,
982+
},
983+
}, function(err, customer) {
984+
if (err) return done(err);
985+
const partialUpdate = {
986+
metadata: {
987+
CONCAT: {
988+
isactive: false,
989+
},
990+
},
991+
};
992+
993+
Customer.updateAll({id: customer.id}, partialUpdate, function(err) {
994+
if (err) return done(err);
995+
Customer.findById(customer.id, function(err, updatedCustomer) {
996+
if (err) return done(err);
997+
updatedCustomer.metadata.isactive.should.equal(false);
998+
updatedCustomer.metadata.extenalid.should.equal('123');
999+
done();
1000+
});
1001+
});
1002+
});
1003+
});
9661004
});
9671005

9681006
it('should return array of models with id column value for createAll()', function(done) {

0 commit comments

Comments
 (0)