@graffy/pg 0.16.10-alpha.2 → 0.16.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs +151 -151
- package/index.mjs +151 -151
- package/package.json +2 -2
package/index.cjs
CHANGED
|
@@ -66,149 +66,6 @@ const empty = raw("");
|
|
|
66
66
|
function sql(strings, ...values) {
|
|
67
67
|
return new Sql(strings, values);
|
|
68
68
|
}
|
|
69
|
-
const valid = {
|
|
70
|
-
$eq: true,
|
|
71
|
-
$lt: true,
|
|
72
|
-
$gt: true,
|
|
73
|
-
$lte: true,
|
|
74
|
-
$gte: true,
|
|
75
|
-
$re: true,
|
|
76
|
-
$ire: true,
|
|
77
|
-
$text: true,
|
|
78
|
-
$and: true,
|
|
79
|
-
$or: true,
|
|
80
|
-
$any: true,
|
|
81
|
-
$all: true,
|
|
82
|
-
$has: true,
|
|
83
|
-
$cts: true,
|
|
84
|
-
$ctd: true,
|
|
85
|
-
$keycts: true,
|
|
86
|
-
$keyctd: true
|
|
87
|
-
};
|
|
88
|
-
const inverse = {
|
|
89
|
-
$eq: "$neq",
|
|
90
|
-
$neq: "$eq",
|
|
91
|
-
$in: "$nin",
|
|
92
|
-
$nin: "$in",
|
|
93
|
-
$lt: "$gte",
|
|
94
|
-
$gte: "$lt",
|
|
95
|
-
$gt: "$lte",
|
|
96
|
-
$lte: "$gt"
|
|
97
|
-
};
|
|
98
|
-
function getAst(filter) {
|
|
99
|
-
return simplify(construct(filter));
|
|
100
|
-
}
|
|
101
|
-
function isValidSubQuery(node) {
|
|
102
|
-
if (!node || typeof node !== "object")
|
|
103
|
-
return false;
|
|
104
|
-
const keys = Object.keys(node);
|
|
105
|
-
for (const key of keys) {
|
|
106
|
-
if (key[0] === "$" && !["$and", "$or", "$not"].includes(key))
|
|
107
|
-
return false;
|
|
108
|
-
if (key[0] !== "$")
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
for (const key in node) {
|
|
112
|
-
if (!isValidSubQuery(node[key]))
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
function construct(node, prop, op) {
|
|
118
|
-
if (!node || typeof node !== "object" || prop && op) {
|
|
119
|
-
if (op && prop)
|
|
120
|
-
return [op, prop, node];
|
|
121
|
-
if (prop)
|
|
122
|
-
return ["$eq", prop, node];
|
|
123
|
-
throw Error(`pgast.expected_prop_before:${JSON.stringify(node)}`);
|
|
124
|
-
}
|
|
125
|
-
if (Array.isArray(node)) {
|
|
126
|
-
return ["$or", node.map((item) => construct(item, prop, op))];
|
|
127
|
-
}
|
|
128
|
-
if (prop && isValidSubQuery(node)) {
|
|
129
|
-
return ["$sub", prop, construct(node)];
|
|
130
|
-
}
|
|
131
|
-
return [
|
|
132
|
-
"$and",
|
|
133
|
-
Object.entries(node).map(([key, val]) => {
|
|
134
|
-
if (key === "$or" || key === "$and") {
|
|
135
|
-
return [key, construct(val, prop, op)[1]];
|
|
136
|
-
}
|
|
137
|
-
if (key === "$not") {
|
|
138
|
-
return [key, construct(val, prop, op)];
|
|
139
|
-
}
|
|
140
|
-
if (key[0] === "$") {
|
|
141
|
-
if (!valid[key])
|
|
142
|
-
throw Error(`pgast.invalid_op:${key}`);
|
|
143
|
-
if (op)
|
|
144
|
-
throw Error(`pgast.unexpected_op:${op} before:${key}`);
|
|
145
|
-
if (!prop)
|
|
146
|
-
throw Error(`pgast.expected_prop_before:${key}`);
|
|
147
|
-
return construct(val, prop, key);
|
|
148
|
-
}
|
|
149
|
-
return construct(val, key);
|
|
150
|
-
})
|
|
151
|
-
];
|
|
152
|
-
}
|
|
153
|
-
function simplify(node) {
|
|
154
|
-
const op = node[0];
|
|
155
|
-
if (op === "$and" || op === "$or") {
|
|
156
|
-
node[1] = node[1].map((subnode) => simplify(subnode));
|
|
157
|
-
} else if (op === "$not") {
|
|
158
|
-
node[1] = simplify(node[1]);
|
|
159
|
-
} else if (op === "$sub") {
|
|
160
|
-
node[2] = simplify(node[2]);
|
|
161
|
-
}
|
|
162
|
-
if (op === "$and") {
|
|
163
|
-
if (!node[1].length)
|
|
164
|
-
return true;
|
|
165
|
-
if (node[1].includes(false))
|
|
166
|
-
return false;
|
|
167
|
-
node[1] = node[1].filter((item) => item !== true);
|
|
168
|
-
} else if (op === "$or") {
|
|
169
|
-
if (!node[1].length)
|
|
170
|
-
return false;
|
|
171
|
-
if (node[1].includes(true))
|
|
172
|
-
return true;
|
|
173
|
-
node[1] = node[1].filter((item) => item !== false);
|
|
174
|
-
} else if (op === "$not" && typeof node[1] === "boolean") {
|
|
175
|
-
return !node[1];
|
|
176
|
-
}
|
|
177
|
-
if (op === "$or") {
|
|
178
|
-
const { eqmap, noneq, change } = node[1].reduce(
|
|
179
|
-
(acc, item) => {
|
|
180
|
-
if (item[0] !== "$eq") {
|
|
181
|
-
acc.noneq.push(item);
|
|
182
|
-
} else if (acc.eqmap[item[1]]) {
|
|
183
|
-
acc.change = true;
|
|
184
|
-
acc.eqmap[item[1]].push(item[2]);
|
|
185
|
-
return acc;
|
|
186
|
-
} else {
|
|
187
|
-
acc.eqmap[item[1]] = [item[2]];
|
|
188
|
-
}
|
|
189
|
-
return acc;
|
|
190
|
-
},
|
|
191
|
-
{ eqmap: {}, noneq: [], change: false }
|
|
192
|
-
);
|
|
193
|
-
if (change) {
|
|
194
|
-
node[1] = [
|
|
195
|
-
...noneq,
|
|
196
|
-
...Object.entries(eqmap).map(
|
|
197
|
-
([prop, val]) => val.length > 1 ? ["$in", prop, val] : ["$eq", prop, val[0]]
|
|
198
|
-
)
|
|
199
|
-
];
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
if ((op === "$and" || op === "$or") && node[1].length === 1) {
|
|
203
|
-
return node[1][0];
|
|
204
|
-
}
|
|
205
|
-
if (op === "$not") {
|
|
206
|
-
const [subop, ...subargs] = node[1];
|
|
207
|
-
const invop = inverse[subop];
|
|
208
|
-
return invop ? [invop, ...subargs] : node;
|
|
209
|
-
}
|
|
210
|
-
return node;
|
|
211
|
-
}
|
|
212
69
|
const getJsonBuildTrusted = (variadic) => {
|
|
213
70
|
const args = join(
|
|
214
71
|
Object.entries(variadic).map(([name, value]) => {
|
|
@@ -231,11 +88,11 @@ const lookup = (prop, options) => {
|
|
|
231
88
|
const { types: types2 } = options.schema;
|
|
232
89
|
if (types2[prefix] === "jsonb") {
|
|
233
90
|
return sql`"${raw(prefix)}" #> ${suffix}`;
|
|
234
|
-
}
|
|
91
|
+
}
|
|
92
|
+
if (types2[prefix] === "cube" && suffix.length === 1) {
|
|
235
93
|
return sql`"${raw(prefix)}" ~> ${parseInt(suffix[0])}`;
|
|
236
|
-
} else {
|
|
237
|
-
throw Error(`pg.cannot_lookup ${prop}`);
|
|
238
94
|
}
|
|
95
|
+
throw Error(`pg.cannot_lookup ${prop}`);
|
|
239
96
|
};
|
|
240
97
|
const lookupNumeric = (prop) => {
|
|
241
98
|
const [prefix, ...suffix] = prop.split(".");
|
|
@@ -384,6 +241,149 @@ function stripAttributes(object) {
|
|
|
384
241
|
null
|
|
385
242
|
);
|
|
386
243
|
}
|
|
244
|
+
const valid = {
|
|
245
|
+
$eq: true,
|
|
246
|
+
$lt: true,
|
|
247
|
+
$gt: true,
|
|
248
|
+
$lte: true,
|
|
249
|
+
$gte: true,
|
|
250
|
+
$re: true,
|
|
251
|
+
$ire: true,
|
|
252
|
+
$text: true,
|
|
253
|
+
$and: true,
|
|
254
|
+
$or: true,
|
|
255
|
+
$any: true,
|
|
256
|
+
$all: true,
|
|
257
|
+
$has: true,
|
|
258
|
+
$cts: true,
|
|
259
|
+
$ctd: true,
|
|
260
|
+
$keycts: true,
|
|
261
|
+
$keyctd: true
|
|
262
|
+
};
|
|
263
|
+
const inverse = {
|
|
264
|
+
$eq: "$neq",
|
|
265
|
+
$neq: "$eq",
|
|
266
|
+
$in: "$nin",
|
|
267
|
+
$nin: "$in",
|
|
268
|
+
$lt: "$gte",
|
|
269
|
+
$gte: "$lt",
|
|
270
|
+
$gt: "$lte",
|
|
271
|
+
$lte: "$gt"
|
|
272
|
+
};
|
|
273
|
+
function getAst(filter) {
|
|
274
|
+
return simplify(construct(filter));
|
|
275
|
+
}
|
|
276
|
+
function isValidSubQuery(node) {
|
|
277
|
+
if (!node || typeof node !== "object")
|
|
278
|
+
return false;
|
|
279
|
+
const keys = Object.keys(node);
|
|
280
|
+
for (const key of keys) {
|
|
281
|
+
if (key[0] === "$" && !["$and", "$or", "$not"].includes(key))
|
|
282
|
+
return false;
|
|
283
|
+
if (key[0] !== "$")
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
for (const key in node) {
|
|
287
|
+
if (!isValidSubQuery(node[key]))
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
function construct(node, prop, op) {
|
|
293
|
+
if (!node || typeof node !== "object" || prop && op) {
|
|
294
|
+
if (op && prop)
|
|
295
|
+
return [op, prop, node];
|
|
296
|
+
if (prop)
|
|
297
|
+
return ["$eq", prop, node];
|
|
298
|
+
throw Error(`pgast.expected_prop_before:${JSON.stringify(node)}`);
|
|
299
|
+
}
|
|
300
|
+
if (Array.isArray(node)) {
|
|
301
|
+
return ["$or", node.map((item) => construct(item, prop, op))];
|
|
302
|
+
}
|
|
303
|
+
if (prop && isValidSubQuery(node)) {
|
|
304
|
+
return ["$sub", prop, construct(node)];
|
|
305
|
+
}
|
|
306
|
+
return [
|
|
307
|
+
"$and",
|
|
308
|
+
Object.entries(node).map(([key, val]) => {
|
|
309
|
+
if (key === "$or" || key === "$and") {
|
|
310
|
+
return [key, construct(val, prop, op)[1]];
|
|
311
|
+
}
|
|
312
|
+
if (key === "$not") {
|
|
313
|
+
return [key, construct(val, prop, op)];
|
|
314
|
+
}
|
|
315
|
+
if (key[0] === "$") {
|
|
316
|
+
if (!valid[key])
|
|
317
|
+
throw Error(`pgast.invalid_op:${key}`);
|
|
318
|
+
if (op)
|
|
319
|
+
throw Error(`pgast.unexpected_op:${op} before:${key}`);
|
|
320
|
+
if (!prop)
|
|
321
|
+
throw Error(`pgast.expected_prop_before:${key}`);
|
|
322
|
+
return construct(val, prop, key);
|
|
323
|
+
}
|
|
324
|
+
return construct(val, key);
|
|
325
|
+
})
|
|
326
|
+
];
|
|
327
|
+
}
|
|
328
|
+
function simplify(node) {
|
|
329
|
+
const op = node[0];
|
|
330
|
+
if (op === "$and" || op === "$or") {
|
|
331
|
+
node[1] = node[1].map((subnode) => simplify(subnode));
|
|
332
|
+
} else if (op === "$not") {
|
|
333
|
+
node[1] = simplify(node[1]);
|
|
334
|
+
} else if (op === "$sub") {
|
|
335
|
+
node[2] = simplify(node[2]);
|
|
336
|
+
}
|
|
337
|
+
if (op === "$and") {
|
|
338
|
+
if (!node[1].length)
|
|
339
|
+
return true;
|
|
340
|
+
if (node[1].includes(false))
|
|
341
|
+
return false;
|
|
342
|
+
node[1] = node[1].filter((item) => item !== true);
|
|
343
|
+
} else if (op === "$or") {
|
|
344
|
+
if (!node[1].length)
|
|
345
|
+
return false;
|
|
346
|
+
if (node[1].includes(true))
|
|
347
|
+
return true;
|
|
348
|
+
node[1] = node[1].filter((item) => item !== false);
|
|
349
|
+
} else if (op === "$not" && typeof node[1] === "boolean") {
|
|
350
|
+
return !node[1];
|
|
351
|
+
}
|
|
352
|
+
if (op === "$or") {
|
|
353
|
+
const { eqmap, noneq, change } = node[1].reduce(
|
|
354
|
+
(acc, item) => {
|
|
355
|
+
if (item[0] !== "$eq") {
|
|
356
|
+
acc.noneq.push(item);
|
|
357
|
+
} else if (acc.eqmap[item[1]]) {
|
|
358
|
+
acc.change = true;
|
|
359
|
+
acc.eqmap[item[1]].push(item[2]);
|
|
360
|
+
return acc;
|
|
361
|
+
} else {
|
|
362
|
+
acc.eqmap[item[1]] = [item[2]];
|
|
363
|
+
}
|
|
364
|
+
return acc;
|
|
365
|
+
},
|
|
366
|
+
{ eqmap: {}, noneq: [], change: false }
|
|
367
|
+
);
|
|
368
|
+
if (change) {
|
|
369
|
+
node[1] = [
|
|
370
|
+
...noneq,
|
|
371
|
+
...Object.entries(eqmap).map(
|
|
372
|
+
([prop, val]) => val.length > 1 ? ["$in", prop, val] : ["$eq", prop, val[0]]
|
|
373
|
+
)
|
|
374
|
+
];
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if ((op === "$and" || op === "$or") && node[1].length === 1) {
|
|
378
|
+
return node[1][0];
|
|
379
|
+
}
|
|
380
|
+
if (op === "$not") {
|
|
381
|
+
const [subop, ...subargs] = node[1];
|
|
382
|
+
const invop = inverse[subop];
|
|
383
|
+
return invop ? [invop, ...subargs] : node;
|
|
384
|
+
}
|
|
385
|
+
return node;
|
|
386
|
+
}
|
|
387
387
|
const opSql = {
|
|
388
388
|
$and: "AND",
|
|
389
389
|
$or: "OR",
|
|
@@ -445,7 +445,8 @@ function getNodeSql(ast, options) {
|
|
|
445
445
|
ast[1].map((node) => getNodeSql(node, options)),
|
|
446
446
|
`) ${opSql[op]} (`
|
|
447
447
|
)})`;
|
|
448
|
-
}
|
|
448
|
+
}
|
|
449
|
+
if (op === "$not") {
|
|
449
450
|
return sql`${opSql[op]} (${getNodeSql(ast[1], options)})`;
|
|
450
451
|
}
|
|
451
452
|
if (op === "$sub") {
|
|
@@ -469,11 +470,10 @@ function getNodeSql(ast, options) {
|
|
|
469
470
|
sql`"${raw(prefix)}" #>> ${suffix}`
|
|
470
471
|
] : [sql`"${raw(prefix)}"`, sql`"${raw(prefix)}" #>> '{}'`];
|
|
471
472
|
return getBinarySql(lhs, "jsonb", op, ast[2], textLhs);
|
|
472
|
-
} else {
|
|
473
|
-
if (suffix.length)
|
|
474
|
-
throw Error(`pg.lookup_not_jsonb ${prefix}`);
|
|
475
|
-
return getBinarySql(sql`"${raw(prefix)}"`, types2[prefix], op, ast[2]);
|
|
476
473
|
}
|
|
474
|
+
if (suffix.length)
|
|
475
|
+
throw Error(`pg.lookup_not_jsonb ${prefix}`);
|
|
476
|
+
return getBinarySql(sql`"${raw(prefix)}"`, types2[prefix], op, ast[2]);
|
|
477
477
|
}
|
|
478
478
|
function getSql(filter, options) {
|
|
479
479
|
const ast = getAst(filter);
|
package/index.mjs
CHANGED
|
@@ -64,149 +64,6 @@ const empty = raw("");
|
|
|
64
64
|
function sql(strings, ...values) {
|
|
65
65
|
return new Sql(strings, values);
|
|
66
66
|
}
|
|
67
|
-
const valid = {
|
|
68
|
-
$eq: true,
|
|
69
|
-
$lt: true,
|
|
70
|
-
$gt: true,
|
|
71
|
-
$lte: true,
|
|
72
|
-
$gte: true,
|
|
73
|
-
$re: true,
|
|
74
|
-
$ire: true,
|
|
75
|
-
$text: true,
|
|
76
|
-
$and: true,
|
|
77
|
-
$or: true,
|
|
78
|
-
$any: true,
|
|
79
|
-
$all: true,
|
|
80
|
-
$has: true,
|
|
81
|
-
$cts: true,
|
|
82
|
-
$ctd: true,
|
|
83
|
-
$keycts: true,
|
|
84
|
-
$keyctd: true
|
|
85
|
-
};
|
|
86
|
-
const inverse = {
|
|
87
|
-
$eq: "$neq",
|
|
88
|
-
$neq: "$eq",
|
|
89
|
-
$in: "$nin",
|
|
90
|
-
$nin: "$in",
|
|
91
|
-
$lt: "$gte",
|
|
92
|
-
$gte: "$lt",
|
|
93
|
-
$gt: "$lte",
|
|
94
|
-
$lte: "$gt"
|
|
95
|
-
};
|
|
96
|
-
function getAst(filter) {
|
|
97
|
-
return simplify(construct(filter));
|
|
98
|
-
}
|
|
99
|
-
function isValidSubQuery(node) {
|
|
100
|
-
if (!node || typeof node !== "object")
|
|
101
|
-
return false;
|
|
102
|
-
const keys = Object.keys(node);
|
|
103
|
-
for (const key of keys) {
|
|
104
|
-
if (key[0] === "$" && !["$and", "$or", "$not"].includes(key))
|
|
105
|
-
return false;
|
|
106
|
-
if (key[0] !== "$")
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
109
|
-
for (const key in node) {
|
|
110
|
-
if (!isValidSubQuery(node[key]))
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
function construct(node, prop, op) {
|
|
116
|
-
if (!node || typeof node !== "object" || prop && op) {
|
|
117
|
-
if (op && prop)
|
|
118
|
-
return [op, prop, node];
|
|
119
|
-
if (prop)
|
|
120
|
-
return ["$eq", prop, node];
|
|
121
|
-
throw Error(`pgast.expected_prop_before:${JSON.stringify(node)}`);
|
|
122
|
-
}
|
|
123
|
-
if (Array.isArray(node)) {
|
|
124
|
-
return ["$or", node.map((item) => construct(item, prop, op))];
|
|
125
|
-
}
|
|
126
|
-
if (prop && isValidSubQuery(node)) {
|
|
127
|
-
return ["$sub", prop, construct(node)];
|
|
128
|
-
}
|
|
129
|
-
return [
|
|
130
|
-
"$and",
|
|
131
|
-
Object.entries(node).map(([key, val]) => {
|
|
132
|
-
if (key === "$or" || key === "$and") {
|
|
133
|
-
return [key, construct(val, prop, op)[1]];
|
|
134
|
-
}
|
|
135
|
-
if (key === "$not") {
|
|
136
|
-
return [key, construct(val, prop, op)];
|
|
137
|
-
}
|
|
138
|
-
if (key[0] === "$") {
|
|
139
|
-
if (!valid[key])
|
|
140
|
-
throw Error(`pgast.invalid_op:${key}`);
|
|
141
|
-
if (op)
|
|
142
|
-
throw Error(`pgast.unexpected_op:${op} before:${key}`);
|
|
143
|
-
if (!prop)
|
|
144
|
-
throw Error(`pgast.expected_prop_before:${key}`);
|
|
145
|
-
return construct(val, prop, key);
|
|
146
|
-
}
|
|
147
|
-
return construct(val, key);
|
|
148
|
-
})
|
|
149
|
-
];
|
|
150
|
-
}
|
|
151
|
-
function simplify(node) {
|
|
152
|
-
const op = node[0];
|
|
153
|
-
if (op === "$and" || op === "$or") {
|
|
154
|
-
node[1] = node[1].map((subnode) => simplify(subnode));
|
|
155
|
-
} else if (op === "$not") {
|
|
156
|
-
node[1] = simplify(node[1]);
|
|
157
|
-
} else if (op === "$sub") {
|
|
158
|
-
node[2] = simplify(node[2]);
|
|
159
|
-
}
|
|
160
|
-
if (op === "$and") {
|
|
161
|
-
if (!node[1].length)
|
|
162
|
-
return true;
|
|
163
|
-
if (node[1].includes(false))
|
|
164
|
-
return false;
|
|
165
|
-
node[1] = node[1].filter((item) => item !== true);
|
|
166
|
-
} else if (op === "$or") {
|
|
167
|
-
if (!node[1].length)
|
|
168
|
-
return false;
|
|
169
|
-
if (node[1].includes(true))
|
|
170
|
-
return true;
|
|
171
|
-
node[1] = node[1].filter((item) => item !== false);
|
|
172
|
-
} else if (op === "$not" && typeof node[1] === "boolean") {
|
|
173
|
-
return !node[1];
|
|
174
|
-
}
|
|
175
|
-
if (op === "$or") {
|
|
176
|
-
const { eqmap, noneq, change } = node[1].reduce(
|
|
177
|
-
(acc, item) => {
|
|
178
|
-
if (item[0] !== "$eq") {
|
|
179
|
-
acc.noneq.push(item);
|
|
180
|
-
} else if (acc.eqmap[item[1]]) {
|
|
181
|
-
acc.change = true;
|
|
182
|
-
acc.eqmap[item[1]].push(item[2]);
|
|
183
|
-
return acc;
|
|
184
|
-
} else {
|
|
185
|
-
acc.eqmap[item[1]] = [item[2]];
|
|
186
|
-
}
|
|
187
|
-
return acc;
|
|
188
|
-
},
|
|
189
|
-
{ eqmap: {}, noneq: [], change: false }
|
|
190
|
-
);
|
|
191
|
-
if (change) {
|
|
192
|
-
node[1] = [
|
|
193
|
-
...noneq,
|
|
194
|
-
...Object.entries(eqmap).map(
|
|
195
|
-
([prop, val]) => val.length > 1 ? ["$in", prop, val] : ["$eq", prop, val[0]]
|
|
196
|
-
)
|
|
197
|
-
];
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if ((op === "$and" || op === "$or") && node[1].length === 1) {
|
|
201
|
-
return node[1][0];
|
|
202
|
-
}
|
|
203
|
-
if (op === "$not") {
|
|
204
|
-
const [subop, ...subargs] = node[1];
|
|
205
|
-
const invop = inverse[subop];
|
|
206
|
-
return invop ? [invop, ...subargs] : node;
|
|
207
|
-
}
|
|
208
|
-
return node;
|
|
209
|
-
}
|
|
210
67
|
const getJsonBuildTrusted = (variadic) => {
|
|
211
68
|
const args = join(
|
|
212
69
|
Object.entries(variadic).map(([name, value]) => {
|
|
@@ -229,11 +86,11 @@ const lookup = (prop, options) => {
|
|
|
229
86
|
const { types: types2 } = options.schema;
|
|
230
87
|
if (types2[prefix] === "jsonb") {
|
|
231
88
|
return sql`"${raw(prefix)}" #> ${suffix}`;
|
|
232
|
-
}
|
|
89
|
+
}
|
|
90
|
+
if (types2[prefix] === "cube" && suffix.length === 1) {
|
|
233
91
|
return sql`"${raw(prefix)}" ~> ${parseInt(suffix[0])}`;
|
|
234
|
-
} else {
|
|
235
|
-
throw Error(`pg.cannot_lookup ${prop}`);
|
|
236
92
|
}
|
|
93
|
+
throw Error(`pg.cannot_lookup ${prop}`);
|
|
237
94
|
};
|
|
238
95
|
const lookupNumeric = (prop) => {
|
|
239
96
|
const [prefix, ...suffix] = prop.split(".");
|
|
@@ -382,6 +239,149 @@ function stripAttributes(object) {
|
|
|
382
239
|
null
|
|
383
240
|
);
|
|
384
241
|
}
|
|
242
|
+
const valid = {
|
|
243
|
+
$eq: true,
|
|
244
|
+
$lt: true,
|
|
245
|
+
$gt: true,
|
|
246
|
+
$lte: true,
|
|
247
|
+
$gte: true,
|
|
248
|
+
$re: true,
|
|
249
|
+
$ire: true,
|
|
250
|
+
$text: true,
|
|
251
|
+
$and: true,
|
|
252
|
+
$or: true,
|
|
253
|
+
$any: true,
|
|
254
|
+
$all: true,
|
|
255
|
+
$has: true,
|
|
256
|
+
$cts: true,
|
|
257
|
+
$ctd: true,
|
|
258
|
+
$keycts: true,
|
|
259
|
+
$keyctd: true
|
|
260
|
+
};
|
|
261
|
+
const inverse = {
|
|
262
|
+
$eq: "$neq",
|
|
263
|
+
$neq: "$eq",
|
|
264
|
+
$in: "$nin",
|
|
265
|
+
$nin: "$in",
|
|
266
|
+
$lt: "$gte",
|
|
267
|
+
$gte: "$lt",
|
|
268
|
+
$gt: "$lte",
|
|
269
|
+
$lte: "$gt"
|
|
270
|
+
};
|
|
271
|
+
function getAst(filter) {
|
|
272
|
+
return simplify(construct(filter));
|
|
273
|
+
}
|
|
274
|
+
function isValidSubQuery(node) {
|
|
275
|
+
if (!node || typeof node !== "object")
|
|
276
|
+
return false;
|
|
277
|
+
const keys = Object.keys(node);
|
|
278
|
+
for (const key of keys) {
|
|
279
|
+
if (key[0] === "$" && !["$and", "$or", "$not"].includes(key))
|
|
280
|
+
return false;
|
|
281
|
+
if (key[0] !== "$")
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
for (const key in node) {
|
|
285
|
+
if (!isValidSubQuery(node[key]))
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
function construct(node, prop, op) {
|
|
291
|
+
if (!node || typeof node !== "object" || prop && op) {
|
|
292
|
+
if (op && prop)
|
|
293
|
+
return [op, prop, node];
|
|
294
|
+
if (prop)
|
|
295
|
+
return ["$eq", prop, node];
|
|
296
|
+
throw Error(`pgast.expected_prop_before:${JSON.stringify(node)}`);
|
|
297
|
+
}
|
|
298
|
+
if (Array.isArray(node)) {
|
|
299
|
+
return ["$or", node.map((item) => construct(item, prop, op))];
|
|
300
|
+
}
|
|
301
|
+
if (prop && isValidSubQuery(node)) {
|
|
302
|
+
return ["$sub", prop, construct(node)];
|
|
303
|
+
}
|
|
304
|
+
return [
|
|
305
|
+
"$and",
|
|
306
|
+
Object.entries(node).map(([key, val]) => {
|
|
307
|
+
if (key === "$or" || key === "$and") {
|
|
308
|
+
return [key, construct(val, prop, op)[1]];
|
|
309
|
+
}
|
|
310
|
+
if (key === "$not") {
|
|
311
|
+
return [key, construct(val, prop, op)];
|
|
312
|
+
}
|
|
313
|
+
if (key[0] === "$") {
|
|
314
|
+
if (!valid[key])
|
|
315
|
+
throw Error(`pgast.invalid_op:${key}`);
|
|
316
|
+
if (op)
|
|
317
|
+
throw Error(`pgast.unexpected_op:${op} before:${key}`);
|
|
318
|
+
if (!prop)
|
|
319
|
+
throw Error(`pgast.expected_prop_before:${key}`);
|
|
320
|
+
return construct(val, prop, key);
|
|
321
|
+
}
|
|
322
|
+
return construct(val, key);
|
|
323
|
+
})
|
|
324
|
+
];
|
|
325
|
+
}
|
|
326
|
+
function simplify(node) {
|
|
327
|
+
const op = node[0];
|
|
328
|
+
if (op === "$and" || op === "$or") {
|
|
329
|
+
node[1] = node[1].map((subnode) => simplify(subnode));
|
|
330
|
+
} else if (op === "$not") {
|
|
331
|
+
node[1] = simplify(node[1]);
|
|
332
|
+
} else if (op === "$sub") {
|
|
333
|
+
node[2] = simplify(node[2]);
|
|
334
|
+
}
|
|
335
|
+
if (op === "$and") {
|
|
336
|
+
if (!node[1].length)
|
|
337
|
+
return true;
|
|
338
|
+
if (node[1].includes(false))
|
|
339
|
+
return false;
|
|
340
|
+
node[1] = node[1].filter((item) => item !== true);
|
|
341
|
+
} else if (op === "$or") {
|
|
342
|
+
if (!node[1].length)
|
|
343
|
+
return false;
|
|
344
|
+
if (node[1].includes(true))
|
|
345
|
+
return true;
|
|
346
|
+
node[1] = node[1].filter((item) => item !== false);
|
|
347
|
+
} else if (op === "$not" && typeof node[1] === "boolean") {
|
|
348
|
+
return !node[1];
|
|
349
|
+
}
|
|
350
|
+
if (op === "$or") {
|
|
351
|
+
const { eqmap, noneq, change } = node[1].reduce(
|
|
352
|
+
(acc, item) => {
|
|
353
|
+
if (item[0] !== "$eq") {
|
|
354
|
+
acc.noneq.push(item);
|
|
355
|
+
} else if (acc.eqmap[item[1]]) {
|
|
356
|
+
acc.change = true;
|
|
357
|
+
acc.eqmap[item[1]].push(item[2]);
|
|
358
|
+
return acc;
|
|
359
|
+
} else {
|
|
360
|
+
acc.eqmap[item[1]] = [item[2]];
|
|
361
|
+
}
|
|
362
|
+
return acc;
|
|
363
|
+
},
|
|
364
|
+
{ eqmap: {}, noneq: [], change: false }
|
|
365
|
+
);
|
|
366
|
+
if (change) {
|
|
367
|
+
node[1] = [
|
|
368
|
+
...noneq,
|
|
369
|
+
...Object.entries(eqmap).map(
|
|
370
|
+
([prop, val]) => val.length > 1 ? ["$in", prop, val] : ["$eq", prop, val[0]]
|
|
371
|
+
)
|
|
372
|
+
];
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if ((op === "$and" || op === "$or") && node[1].length === 1) {
|
|
376
|
+
return node[1][0];
|
|
377
|
+
}
|
|
378
|
+
if (op === "$not") {
|
|
379
|
+
const [subop, ...subargs] = node[1];
|
|
380
|
+
const invop = inverse[subop];
|
|
381
|
+
return invop ? [invop, ...subargs] : node;
|
|
382
|
+
}
|
|
383
|
+
return node;
|
|
384
|
+
}
|
|
385
385
|
const opSql = {
|
|
386
386
|
$and: "AND",
|
|
387
387
|
$or: "OR",
|
|
@@ -443,7 +443,8 @@ function getNodeSql(ast, options) {
|
|
|
443
443
|
ast[1].map((node) => getNodeSql(node, options)),
|
|
444
444
|
`) ${opSql[op]} (`
|
|
445
445
|
)})`;
|
|
446
|
-
}
|
|
446
|
+
}
|
|
447
|
+
if (op === "$not") {
|
|
447
448
|
return sql`${opSql[op]} (${getNodeSql(ast[1], options)})`;
|
|
448
449
|
}
|
|
449
450
|
if (op === "$sub") {
|
|
@@ -467,11 +468,10 @@ function getNodeSql(ast, options) {
|
|
|
467
468
|
sql`"${raw(prefix)}" #>> ${suffix}`
|
|
468
469
|
] : [sql`"${raw(prefix)}"`, sql`"${raw(prefix)}" #>> '{}'`];
|
|
469
470
|
return getBinarySql(lhs, "jsonb", op, ast[2], textLhs);
|
|
470
|
-
} else {
|
|
471
|
-
if (suffix.length)
|
|
472
|
-
throw Error(`pg.lookup_not_jsonb ${prefix}`);
|
|
473
|
-
return getBinarySql(sql`"${raw(prefix)}"`, types2[prefix], op, ast[2]);
|
|
474
471
|
}
|
|
472
|
+
if (suffix.length)
|
|
473
|
+
throw Error(`pg.lookup_not_jsonb ${prefix}`);
|
|
474
|
+
return getBinarySql(sql`"${raw(prefix)}"`, types2[prefix], op, ast[2]);
|
|
475
475
|
}
|
|
476
476
|
function getSql(filter, options) {
|
|
477
477
|
const ast = getAst(filter);
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graffy/pg",
|
|
3
3
|
"description": "The standard Postgres module for Graffy. Each instance this module mounts a Postgres table as a Graffy subtree.",
|
|
4
4
|
"author": "aravind (https://github.com/aravindet)",
|
|
5
|
-
"version": "0.16.
|
|
5
|
+
"version": "0.16.11",
|
|
6
6
|
"main": "./index.cjs",
|
|
7
7
|
"exports": {
|
|
8
8
|
"import": "./index.mjs",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"license": "Apache-2.0",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@graffy/common": "0.16.
|
|
19
|
+
"@graffy/common": "0.16.11",
|
|
20
20
|
"debug": "^4.3.3"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|