Skip to content
This repository has been archived by the owner on Dec 20, 2023. It is now read-only.

Commit

Permalink
jsonPath improvments (#11)
Browse files Browse the repository at this point in the history
* use of a swith instead of if else, faster and more readable

* use same initialization pattern for every nodes

* remove useless code

* according to the rfc6901 the path for array should has a valid position index or '-', this last one means to add to the end of the array

* add is not possible without position

rfc6901 says that add op is not possible without position index or '-'

* add/$push improvments

added items must be contiguous, if we add a item in the first position
and in the third position, we can't do it in only one $push => throws
error
add op can't mixe position index and '-', as we don't know the length of
the array where the itmes will be pushed, we can't mixed index positions
and '-' position (added to end) => throws error

* handle the escaped characters in jsonpath

~0 <=> ~ and ~1 <=> / were forgotten
no more dependency to jsonpath-to-dot
  • Loading branch information
benco1967 authored and imlucas committed Jan 14, 2020
1 parent b8fffea commit 37fa3f8
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 44 deletions.
78 changes: 42 additions & 36 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,68 @@
var toDot = require('jsonpath-to-dot');
function toDot(path) {
return path.replace(/^\//, '').replace(/\//g, '.').replace(/~1/g, '/').replace(/~0/g, '~');
}

module.exports = function(patches){
var update = {};
patches.map(function(p){
if(p.op === 'add'){
switch(p.op) {
case 'add':
var path = toDot(p.path),
parts = path.split('.');

var key = parts[0];
var $position = parts[1] && parseInt(parts[1], 10);
var positionPart = parts.length > 1 && parts[parts.length - 1];
var addToEnd = positionPart === '-';
var key = parts.slice(0, -1).join('.');
var $position = positionPart && parseInt(positionPart, 10) || null;

update.$push = update.$push || {};

if (!isNaN($position)) {
if (update.$push[key]) {
if (!isNaN(update.$push[key].$position)) {
$position = update.$push[key].$position;
delete update.$push[key].$position;
}

if (!update.$push[key].$each) {
update.$push[key] = {
$each: [
update.$push[key]
]
};
}

update.$push[key].$each.push(p.value);
update.$push[key].$position = $position;
} else {
if ($position !== null) {
if (update.$push[key] === undefined) {
update.$push[key] = {
$each: [p.value],
$position: $position
};
} else {
if (update.$push[key] === null || update.$push[key].$position === undefined) {
throw new Error("Unsupported Operation! can't use add op with mixed positions");
}
var posDiff = $position - update.$push[key].$position;
if (posDiff > update.$push[key].$each.length) {
throw new Error("Unsupported Operation! can use add op only with contiguous positions");
}
update.$push[key].$each.splice(posDiff, 0, p.value);
update.$push[key].$position = Math.min($position, update.$push[key].$position);
}
} else {
if (update.$push[key]) {
if (!update.$push[key].$each) {
} else if(addToEnd) {
if (update.$push[key] === undefined) {
update.$push[key] = p.value;
} else {
if (update.$push[key] === null || update.$push[key].$each === undefined) {
update.$push[key] = {
$each: [update.$push[key]]
};
}
update.$push[path].$each.push(p.value);
} else {
update.$push[path] = p.value;
if (update.$push[key].$position !== undefined) {
throw new Error("Unsupported Operation! can't use add op with mixed positions");
}
update.$push[key].$each.push(p.value);
}
} else {
throw new Error("Unsupported Operation! can't use add op without position");
}
}
else if(p.op === 'remove'){
if(!update.$unset) update.$unset = {};
break;
case 'remove':
update.$unset = update.$unset || {};
update.$unset[toDot(p.path)] = 1;
}
else if(p.op === 'replace'){
if(!update.$set) update.$set = {};
break;
case 'replace':
update.$set = update.$set || {};
update.$set[toDot(p.path)] = p.value;
}
else if(p.op !== 'test') {
break;
case 'test':
break;
default:
throw new Error('Unsupported Operation! op = ' + p.op);
}
});
Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@
"scripts": {
"test": "mocha"
},
"dependencies": {
"jsonpath-to-dot": "~0.2.0"
},
"dependencies": {},
"devDependencies": {
"chai": "^3.5.0",
"mocha": "~3.1.2"
Expand Down
154 changes: 149 additions & 5 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('jsonpatch to mongodb', function() {
it('should work with single add', function() {
var patches = [{
op: 'add',
path: '/name',
path: '/name/-',
value: 'dave'
}];

Expand All @@ -20,6 +20,22 @@ describe('jsonpatch to mongodb', function() {
assert.deepEqual(toMongodb(patches), expected);
});

it('should work with escaped characters', function() {
var patches = [{
op: 'replace',
path: '/foo~1bar~0',
value: 'dave'
}];

var expected = {
$set: {
"foo/bar~": 'dave'
}
};

assert.deepEqual(toMongodb(patches), expected);
});

it('should work with array set', function() {
var patches = [{
op: 'add',
Expand Down Expand Up @@ -50,13 +66,18 @@ describe('jsonpatch to mongodb', function() {
op: 'add',
path: '/name/2',
value: 'bob'
}, {
op: 'add',
path: '/name/2',
value: 'john'
}];

var expected = {
$push: {
name: {
$each: [
'dave',
'john',
'bob'
],
$position: 1
Expand All @@ -67,18 +88,42 @@ describe('jsonpatch to mongodb', function() {
assert.deepEqual(toMongodb(patches), expected);
});

it('should work with multiple adds in reverse position', function() {
var patches = [{
op: 'add',
path: '/name/1',
value: 'dave'
},{
op: 'add',
path: '/name/1',
value: 'bob'
},{
op: 'add',
path: '/name/1',
value: 'john'
}];

var expected = {
$push: {
name: {$each: ['john', 'bob', 'dave'], $position: 1}
}
};

assert.deepEqual(toMongodb(patches), expected);
});

it('should work with multiple adds', function() {
var patches = [{
op: 'add',
path: '/name',
path: '/name/-',
value: 'dave'
},{
op: 'add',
path: '/name',
path: '/name/-',
value: 'bob'
},{
op: 'add',
path: '/name',
path: '/name/-',
value: 'john'
}];

Expand All @@ -91,6 +136,54 @@ describe('jsonpatch to mongodb', function() {
assert.deepEqual(toMongodb(patches), expected);
});

it('should work with multiple adds with some null at the end', function() {
var patches = [{
op: 'add',
path: '/name/-',
value: null
},{
op: 'add',
path: '/name/-',
value: 'bob'
},{
op: 'add',
path: '/name/-',
value: null
}];

var expected = {
$push: {
name: {$each: [null, 'bob', null]}
}
};

assert.deepEqual(toMongodb(patches), expected);
});

it('should work with multiple adds with some null and position', function() {
var patches = [{
op: 'add',
path: '/name/1',
value: null
},{
op: 'add',
path: '/name/1',
value: 'bob'
},{
op: 'add',
path: '/name/1',
value: null
}];

var expected = {
$push: {
name: {$each: [null, 'bob', null], $position: 1}
}
};

assert.deepEqual(toMongodb(patches), expected);
});

it('should work with remove', function() {
var patches = [{
op: 'remove',
Expand Down Expand Up @@ -135,6 +228,58 @@ describe('jsonpatch to mongodb', function() {
assert.deepEqual(toMongodb(patches), expected);
});

it('blow up on adds with non contiguous positions', function() {
var patches = [{
op: 'add',
path: '/name/1',
value: 'bob'
},{
op: 'add',
path: '/name/3',
value: 'john'
}];

chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can use add op only with contiguous positions");
});

it('blow up on adds with mixed position 1', function() {
var patches = [{
op: 'add',
path: '/name/1',
value: 'bob'
},{
op: 'add',
path: '/name/-',
value: 'john'
}];

chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can't use add op with mixed positions");
});

it('blow up on adds with mixed position 2', function() {
var patches = [{
op: 'add',
path: '/name/-',
value: 'bob'
},{
op: 'add',
path: '/name/1',
value: 'john'
}];

chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can't use add op with mixed positions");
});

it('should blow up on add without position', function() {
var patches = [{
op: 'add',
path: '/name',
value: 'dave'
}];

chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can't use add op without position");
});

it('should blow up on move', function() {
var patches = [{
op: 'move',
Expand All @@ -143,7 +288,6 @@ describe('jsonpatch to mongodb', function() {
}];

chai.expect(function(){toMongodb(patches)}).to.throw('Unsupported Operation! op = move');

});


Expand Down

0 comments on commit 37fa3f8

Please sign in to comment.