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

Commit 37fa3f8

Browse files
benco1967imlucas
authored andcommitted
jsonPath improvments (#11)
* 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
1 parent b8fffea commit 37fa3f8

File tree

3 files changed

+192
-44
lines changed

3 files changed

+192
-44
lines changed

index.js

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,68 @@
1-
var toDot = require('jsonpath-to-dot');
1+
function toDot(path) {
2+
return path.replace(/^\//, '').replace(/\//g, '.').replace(/~1/g, '/').replace(/~0/g, '~');
3+
}
24

35
module.exports = function(patches){
46
var update = {};
57
patches.map(function(p){
6-
if(p.op === 'add'){
8+
switch(p.op) {
9+
case 'add':
710
var path = toDot(p.path),
811
parts = path.split('.');
912

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

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

15-
if (!isNaN($position)) {
16-
if (update.$push[key]) {
17-
if (!isNaN(update.$push[key].$position)) {
18-
$position = update.$push[key].$position;
19-
delete update.$push[key].$position;
20-
}
21-
22-
if (!update.$push[key].$each) {
23-
update.$push[key] = {
24-
$each: [
25-
update.$push[key]
26-
]
27-
};
28-
}
29-
30-
update.$push[key].$each.push(p.value);
31-
update.$push[key].$position = $position;
32-
} else {
20+
if ($position !== null) {
21+
if (update.$push[key] === undefined) {
3322
update.$push[key] = {
3423
$each: [p.value],
3524
$position: $position
3625
};
26+
} else {
27+
if (update.$push[key] === null || update.$push[key].$position === undefined) {
28+
throw new Error("Unsupported Operation! can't use add op with mixed positions");
29+
}
30+
var posDiff = $position - update.$push[key].$position;
31+
if (posDiff > update.$push[key].$each.length) {
32+
throw new Error("Unsupported Operation! can use add op only with contiguous positions");
33+
}
34+
update.$push[key].$each.splice(posDiff, 0, p.value);
35+
update.$push[key].$position = Math.min($position, update.$push[key].$position);
3736
}
38-
} else {
39-
if (update.$push[key]) {
40-
if (!update.$push[key].$each) {
37+
} else if(addToEnd) {
38+
if (update.$push[key] === undefined) {
39+
update.$push[key] = p.value;
40+
} else {
41+
if (update.$push[key] === null || update.$push[key].$each === undefined) {
4142
update.$push[key] = {
4243
$each: [update.$push[key]]
4344
};
4445
}
45-
update.$push[path].$each.push(p.value);
46-
} else {
47-
update.$push[path] = p.value;
46+
if (update.$push[key].$position !== undefined) {
47+
throw new Error("Unsupported Operation! can't use add op with mixed positions");
48+
}
49+
update.$push[key].$each.push(p.value);
4850
}
51+
} else {
52+
throw new Error("Unsupported Operation! can't use add op without position");
4953
}
50-
}
51-
else if(p.op === 'remove'){
52-
if(!update.$unset) update.$unset = {};
54+
break;
55+
case 'remove':
56+
update.$unset = update.$unset || {};
5357
update.$unset[toDot(p.path)] = 1;
54-
}
55-
else if(p.op === 'replace'){
56-
if(!update.$set) update.$set = {};
58+
break;
59+
case 'replace':
60+
update.$set = update.$set || {};
5761
update.$set[toDot(p.path)] = p.value;
58-
}
59-
else if(p.op !== 'test') {
62+
break;
63+
case 'test':
64+
break;
65+
default:
6066
throw new Error('Unsupported Operation! op = ' + p.op);
6167
}
6268
});

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
"scripts": {
2020
"test": "mocha"
2121
},
22-
"dependencies": {
23-
"jsonpath-to-dot": "~0.2.0"
24-
},
22+
"dependencies": {},
2523
"devDependencies": {
2624
"chai": "^3.5.0",
2725
"mocha": "~3.1.2"

test/index.test.js

Lines changed: 149 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ describe('jsonpatch to mongodb', function() {
77
it('should work with single add', function() {
88
var patches = [{
99
op: 'add',
10-
path: '/name',
10+
path: '/name/-',
1111
value: 'dave'
1212
}];
1313

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

23+
it('should work with escaped characters', function() {
24+
var patches = [{
25+
op: 'replace',
26+
path: '/foo~1bar~0',
27+
value: 'dave'
28+
}];
29+
30+
var expected = {
31+
$set: {
32+
"foo/bar~": 'dave'
33+
}
34+
};
35+
36+
assert.deepEqual(toMongodb(patches), expected);
37+
});
38+
2339
it('should work with array set', function() {
2440
var patches = [{
2541
op: 'add',
@@ -50,13 +66,18 @@ describe('jsonpatch to mongodb', function() {
5066
op: 'add',
5167
path: '/name/2',
5268
value: 'bob'
69+
}, {
70+
op: 'add',
71+
path: '/name/2',
72+
value: 'john'
5373
}];
5474

5575
var expected = {
5676
$push: {
5777
name: {
5878
$each: [
5979
'dave',
80+
'john',
6081
'bob'
6182
],
6283
$position: 1
@@ -67,18 +88,42 @@ describe('jsonpatch to mongodb', function() {
6788
assert.deepEqual(toMongodb(patches), expected);
6889
});
6990

91+
it('should work with multiple adds in reverse position', function() {
92+
var patches = [{
93+
op: 'add',
94+
path: '/name/1',
95+
value: 'dave'
96+
},{
97+
op: 'add',
98+
path: '/name/1',
99+
value: 'bob'
100+
},{
101+
op: 'add',
102+
path: '/name/1',
103+
value: 'john'
104+
}];
105+
106+
var expected = {
107+
$push: {
108+
name: {$each: ['john', 'bob', 'dave'], $position: 1}
109+
}
110+
};
111+
112+
assert.deepEqual(toMongodb(patches), expected);
113+
});
114+
70115
it('should work with multiple adds', function() {
71116
var patches = [{
72117
op: 'add',
73-
path: '/name',
118+
path: '/name/-',
74119
value: 'dave'
75120
},{
76121
op: 'add',
77-
path: '/name',
122+
path: '/name/-',
78123
value: 'bob'
79124
},{
80125
op: 'add',
81-
path: '/name',
126+
path: '/name/-',
82127
value: 'john'
83128
}];
84129

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

139+
it('should work with multiple adds with some null at the end', function() {
140+
var patches = [{
141+
op: 'add',
142+
path: '/name/-',
143+
value: null
144+
},{
145+
op: 'add',
146+
path: '/name/-',
147+
value: 'bob'
148+
},{
149+
op: 'add',
150+
path: '/name/-',
151+
value: null
152+
}];
153+
154+
var expected = {
155+
$push: {
156+
name: {$each: [null, 'bob', null]}
157+
}
158+
};
159+
160+
assert.deepEqual(toMongodb(patches), expected);
161+
});
162+
163+
it('should work with multiple adds with some null and position', function() {
164+
var patches = [{
165+
op: 'add',
166+
path: '/name/1',
167+
value: null
168+
},{
169+
op: 'add',
170+
path: '/name/1',
171+
value: 'bob'
172+
},{
173+
op: 'add',
174+
path: '/name/1',
175+
value: null
176+
}];
177+
178+
var expected = {
179+
$push: {
180+
name: {$each: [null, 'bob', null], $position: 1}
181+
}
182+
};
183+
184+
assert.deepEqual(toMongodb(patches), expected);
185+
});
186+
94187
it('should work with remove', function() {
95188
var patches = [{
96189
op: 'remove',
@@ -135,6 +228,58 @@ describe('jsonpatch to mongodb', function() {
135228
assert.deepEqual(toMongodb(patches), expected);
136229
});
137230

231+
it('blow up on adds with non contiguous positions', function() {
232+
var patches = [{
233+
op: 'add',
234+
path: '/name/1',
235+
value: 'bob'
236+
},{
237+
op: 'add',
238+
path: '/name/3',
239+
value: 'john'
240+
}];
241+
242+
chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can use add op only with contiguous positions");
243+
});
244+
245+
it('blow up on adds with mixed position 1', function() {
246+
var patches = [{
247+
op: 'add',
248+
path: '/name/1',
249+
value: 'bob'
250+
},{
251+
op: 'add',
252+
path: '/name/-',
253+
value: 'john'
254+
}];
255+
256+
chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can't use add op with mixed positions");
257+
});
258+
259+
it('blow up on adds with mixed position 2', function() {
260+
var patches = [{
261+
op: 'add',
262+
path: '/name/-',
263+
value: 'bob'
264+
},{
265+
op: 'add',
266+
path: '/name/1',
267+
value: 'john'
268+
}];
269+
270+
chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can't use add op with mixed positions");
271+
});
272+
273+
it('should blow up on add without position', function() {
274+
var patches = [{
275+
op: 'add',
276+
path: '/name',
277+
value: 'dave'
278+
}];
279+
280+
chai.expect(function(){toMongodb(patches)}).to.throw("Unsupported Operation! can't use add op without position");
281+
});
282+
138283
it('should blow up on move', function() {
139284
var patches = [{
140285
op: 'move',
@@ -143,7 +288,6 @@ describe('jsonpatch to mongodb', function() {
143288
}];
144289

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

149293

0 commit comments

Comments
 (0)