Skip to content

Commit 57fa93b

Browse files
committed
add functions for work with json for postgres dialect
1 parent 180a2dc commit 57fa93b

File tree

8 files changed

+241
-40
lines changed

8 files changed

+241
-40
lines changed

lib/builder.js

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,6 @@ Builder.prototype._wrapPlaceholder = function(name) {
3535
return this.options.valuesPrefix + name;
3636
};
3737

38-
Builder.prototype._wrapIdentifier = function(name) {
39-
if (name !== '*' && this.options.wrappedIdentifiers &&
40-
!this.dialect.config.wrapIdentifierRegexp.test(name)) {
41-
// try to split name by dot
42-
var nameParts = name.split('.');
43-
44-
if (nameParts.length > 2) {
45-
throw new Error('Can\'t wrap identifier with name name "' + name + '"');
46-
} else if (nameParts.length === 2) {
47-
name = this._wrapIdentifier(nameParts[0]) + '.' +
48-
this._wrapIdentifier(nameParts[1]);
49-
} else {
50-
name = this.dialect.config.identifierPrefix + name +
51-
this.dialect.config.identifierSuffix;
52-
}
53-
}
54-
55-
return name;
56-
};
57-
5838
Builder.prototype._pushValue = function(value) {
5939
var valueType = typeof value;
6040

lib/dialects/base/blocks.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module.exports = function(dialect) {
2626
}
2727

2828
if (field) {
29-
field = this._wrapIdentifier(field);
29+
field = this.dialect._wrapIdentifier(field);
3030

3131
var table = params.table || params.$table;
3232
if (table) {
@@ -125,15 +125,15 @@ module.exports = function(dialect) {
125125
});
126126

127127
dialect.blocks.add('table', function(params) {
128-
return this._wrapIdentifier(params.table);
128+
return this.dialect._wrapIdentifier(params.table);
129129
});
130130

131131
dialect.blocks.add('name', function(params) {
132-
return this._wrapIdentifier(params.name);
132+
return this.dialect._wrapIdentifier(params.name);
133133
});
134134

135135
dialect.blocks.add('alias', function(params) {
136-
return 'as ' + this._wrapIdentifier(params.alias);
136+
return 'as ' + this.dialect._wrapIdentifier(params.alias);
137137
});
138138

139139
dialect.blocks.add('condition', function(params) {
@@ -255,10 +255,10 @@ module.exports = function(dialect) {
255255
if (_.isArray(group)) {
256256
var self = this;
257257
result = _(group).map(function(field) {
258-
return self._wrapIdentifier(field);
258+
return self.dialect._wrapIdentifier(field);
259259
}).join(', ');
260260
} else if (_.isString(group)) {
261-
result = this._wrapIdentifier(group);
261+
result = this.dialect._wrapIdentifier(group);
262262
}
263263

264264
if (result) {
@@ -277,19 +277,19 @@ module.exports = function(dialect) {
277277
var self = this;
278278
if (_.isArray(sort)) {
279279
result = _(sort).map(function(sortField) {
280-
return self._wrapIdentifier(sortField);
280+
return self.dialect._wrapIdentifier(sortField);
281281
}).join(', ');
282282

283283
// if sort is object -> field1 asc, field2 desc, ...
284284
} else if (_.isObject(sort)) {
285285
result = _(sort).map(function(direction, field) {
286-
return self._wrapIdentifier(field) + ' ' +
286+
return self.dialect._wrapIdentifier(field) + ' ' +
287287
(direction > 0 ? 'asc' : 'desc');
288288
}).join(', ');
289289

290290
// if sort is string -> not process
291291
} else if (_.isString(sort)) {
292-
result = this._wrapIdentifier(sort);
292+
result = this.dialect._wrapIdentifier(sort);
293293
}
294294

295295
if (result) {

lib/dialects/base/conditions.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function buildCompareCondition(builder, field, operator, value) {
1414
placeholder = builder._pushValue(value);
1515
}
1616

17-
field = builder._wrapIdentifier(field);
17+
field = builder.dialect._wrapIdentifier(field);
1818
return [field, operator, placeholder].join(' ');
1919
}
2020

@@ -34,7 +34,7 @@ function buildContainsCondition(builder, field, operator, value) {
3434
throw new Error('Invalid `' + operator + '` value type "' + (typeof value) + '"');
3535
}
3636

37-
field = builder._wrapIdentifier(field);
37+
field = builder.dialect._wrapIdentifier(field);
3838
return [field, operator, newValue].join(' ');
3939
}
4040

@@ -87,10 +87,10 @@ module.exports = function(dialect) {
8787
placeholder = this.buildBlock('field', value);
8888
} else {
8989
// $field - special operator, that not make placeholder for value
90-
placeholder = this._wrapIdentifier(value);
90+
placeholder = this.dialect._wrapIdentifier(value);
9191
}
9292

93-
return [this._wrapIdentifier(field), '=', placeholder].join(' ');
93+
return [this.dialect._wrapIdentifier(field), '=', placeholder].join(' ');
9494
});
9595

9696

@@ -114,6 +114,6 @@ module.exports = function(dialect) {
114114
var placeholder1 = this._pushValue(value[0]);
115115
var placeholder2 = this._pushValue(value[1]);
116116

117-
return [this._wrapIdentifier(field), 'between', placeholder1, 'and', placeholder2].join(' ');
117+
return [this.dialect._wrapIdentifier(field), 'between', placeholder1, 'and', placeholder2].join(' ');
118118
});
119119
};

lib/dialects/base/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,23 @@ Dialect.prototype.config = {
3636
defaultLogicalOperator: '$and',
3737
defaultModifier: '$set'
3838
};
39+
40+
Dialect.prototype._wrapIdentifier = function(name) {
41+
if (name !== '*' && this.builder.options.wrappedIdentifiers &&
42+
!this.config.wrapIdentifierRegexp.test(name)) {
43+
// try to split name by dot
44+
var nameParts = name.split('.');
45+
46+
if (nameParts.length > 2) {
47+
throw new Error('Can\'t wrap identifier with name name "' + name + '"');
48+
} else if (nameParts.length === 2) {
49+
name = this._wrapIdentifier(nameParts[0]) + '.' +
50+
this._wrapIdentifier(nameParts[1]);
51+
} else {
52+
name = this.config.identifierPrefix + name +
53+
this.config.identifierSuffix;
54+
}
55+
}
56+
57+
return name;
58+
};

lib/dialects/base/modifiers.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module.exports = function(dialect) {
99
return _(values).map(function(value, field) {
1010
var placeholder = self._pushValue(value);
1111

12-
return [self._wrapIdentifier(field), '=', placeholder].join(' ');
12+
return [self.dialect._wrapIdentifier(field), '=', placeholder].join(' ');
1313
}).join(', ');
1414
});
1515

@@ -18,7 +18,7 @@ module.exports = function(dialect) {
1818

1919
return _(values).map(function(value, field) {
2020
var placeholder = self._pushValue(value);
21-
field = self._wrapIdentifier(field);
21+
field = self.dialect._wrapIdentifier(field);
2222

2323
return [field, '=', field, '+', placeholder].join(' ');
2424
}).join(', ');
@@ -29,7 +29,7 @@ module.exports = function(dialect) {
2929

3030
return _(values).map(function(value, field) {
3131
var placeholder = self._pushValue(value);
32-
field = self._wrapIdentifier(field);
32+
field = self.dialect._wrapIdentifier(field);
3333

3434
return [field, '=', field, '-', placeholder].join(' ');
3535
}).join(', ');

lib/dialects/postgresql/conditions.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use strict';
2+
3+
var _ = require('underscore');
4+
5+
var buildInCondition = function(builder, field, operator, value) {
6+
field = builder.dialect._wrapIdentifier(field);
7+
8+
var placeholder;
9+
try {
10+
placeholder = builder.buildBlock('field', value);
11+
} catch (e) {
12+
placeholder = builder._pushValue(JSON.stringify(value));
13+
}
14+
15+
return [field, operator, placeholder].join(' ');
16+
};
17+
18+
var buildHasCondition = function(builder, field, operator, value) {
19+
field = builder.dialect._wrapIdentifier(field);
20+
21+
var placeholder;
22+
if (_(value).isArray()) {
23+
placeholder = 'array[' + _(value).map(function(item) {
24+
return builder._pushValue(item);
25+
}).join(', ') + ']';
26+
} else {
27+
placeholder = builder.buildBlock('field', value);
28+
}
29+
30+
return [field, operator, placeholder].join(' ');
31+
};
32+
33+
module.exports = function(dialect) {
34+
dialect.conditions.add('$jsonContains', function(field, operator, value) {
35+
return buildInCondition(this, field, '@>', value);
36+
});
37+
38+
dialect.conditions.add('$jsonIn', function(field, operator, value) {
39+
return buildInCondition(this, field, '<@', value);
40+
});
41+
42+
dialect.conditions.add('$jsonHas', function(field, operator, value) {
43+
field = this.dialect._wrapIdentifier(field);
44+
45+
var placeholder = value;
46+
if (_(placeholder).isObject()) {
47+
placeholder = this.buildBlock('field', placeholder);
48+
} else {
49+
placeholder = this._pushValue(value.toString());
50+
}
51+
52+
return [field, '?', placeholder].join(' ');
53+
});
54+
55+
dialect.conditions.add('$jsonHasAny', function(field, operator, value) {
56+
return buildHasCondition(this, field, '?|', value);
57+
});
58+
59+
dialect.conditions.add('$jsonHasAll', function(field, operator, value) {
60+
return buildHasCondition(this, field, '?&', value);
61+
});
62+
};

lib/dialects/postgresql/index.js

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,54 @@
11
'use strict';
22

33
var BaseDialect = require('../base');
4-
var _ = require('underscore');
5-
var util = require('util');
4+
var _ = require('underscore');
5+
var util = require('util');
6+
var conditionsInit = require('./conditions');
67

78
var Dialect = module.exports = function(builder) {
89
BaseDialect.call(this, builder);
10+
conditionsInit(this);
911
};
1012

1113
util.inherits(Dialect, BaseDialect);
1214

13-
Dialect.prototype.config = _({}).extend(BaseDialect.prototype.config);
15+
Dialect.prototype.config = _({
16+
jsonSeparatorRegexp: /->>?/g,
17+
jsonPathWrap: '\''
18+
}).extend(BaseDialect.prototype.config);
19+
20+
Dialect.prototype._wrapJsonPathPart = function(pathPart) {
21+
// check maybe it already wrapped
22+
var wrappedPart;
23+
if (pathPart.indexOf(this.config.jsonPathWrap) === -1) {
24+
wrappedPart = this.config.jsonPathWrap + pathPart +
25+
this.config.jsonPathWrap;
26+
} else {
27+
wrappedPart = pathPart;
28+
}
29+
return wrappedPart;
30+
};
31+
32+
Dialect.prototype._wrapIdentifier = function(name) {
33+
// check if this is json field
34+
var identifier;
35+
if (this.config.jsonSeparatorRegexp.test(name)) {
36+
var self =this,
37+
regExp = this.config.jsonSeparatorRegexp;
38+
// split and wrap each json path part
39+
var jsonPathArr = _(name.split(regExp)).map(function(pathPart, ind) {
40+
return ind ? (self.config.jsonPathWrap + pathPart + self.config.jsonPathWrap) :
41+
self._wrapIdentifier(pathPart);
42+
});
43+
// and concat it back
44+
var identifier = _(jsonPathArr).reduce(function(memo, pathPart) {
45+
var pathSeparator = regExp.exec(name);
46+
return memo + pathPart + (pathSeparator || '');
47+
}, '');
48+
// set regexp lastIndex to 0
49+
regExp.lastIndex = 0;
50+
} else {
51+
identifier = BaseDialect.prototype._wrapIdentifier.call(this, name);
52+
}
53+
return identifier;
54+
};

0 commit comments

Comments
 (0)