@@ -2,7 +2,7 @@ const util = require('./util.js')
2
2
3
3
// These are the simple operators.
4
4
// Note that "is distinct from" needs to be used to ensure nulls are returned as expected, see https://modern-sql.com/feature/is-distinct-from
5
- var ops = {
5
+ const OPS = {
6
6
$eq : '=' ,
7
7
$gt : '>' ,
8
8
$gte : '>=' ,
@@ -11,7 +11,7 @@ var ops = {
11
11
$ne : ' IS DISTINCT FROM ' ,
12
12
}
13
13
14
- var otherOps = {
14
+ const OTHER_OPS = {
15
15
$all : true , $in : true , $nin : true , $not : true , $or : true , $and : true , $elemMatch : true , $regex : true , $type : true , $size : true , $exists : true , $mod : true , $text : true
16
16
}
17
17
@@ -47,6 +47,7 @@ function createElementOrArrayQuery(path, op, value, parent, arrayPathStr) {
47
47
const safeArray = `jsonb_typeof(${ text } )='array' AND`
48
48
49
49
let arrayQuery = ''
50
+ const specialKeys = getSpecialKeys ( path , value , true )
50
51
if ( typeof value === 'object' && ! Array . isArray ( value ) && value !== null ) {
51
52
if ( typeof value [ '$size' ] !== 'undefined' ) {
52
53
// size does not support array element based matching
@@ -63,6 +64,9 @@ function createElementOrArrayQuery(path, op, value, parent, arrayPathStr) {
63
64
const sub = convert ( innerPath , subquery , [ ] , false )
64
65
return `EXISTS (SELECT * FROM jsonb_array_elements(${ text } ) WHERE ${ safeArray } ${ sub } )`
65
66
} ) . join ( ' AND ' ) + ')'
67
+ } else if ( specialKeys . length === 0 ) {
68
+ const sub = convert ( innerPath , value , [ ] , true )
69
+ arrayQuery = `EXISTS (SELECT * FROM jsonb_array_elements(${ text } ) WHERE ${ safeArray } ${ sub } )`
66
70
} else {
67
71
const params = value
68
72
arrayQuery = '(' + Object . keys ( params ) . map ( function ( subKey ) {
@@ -172,7 +176,7 @@ function convertOp(path, op, value, parent, arrayPaths) {
172
176
return `${ op == '$ne' ? 'NOT ' : '' } ${ head } @> ` + util . pathToObject ( [ ...tail , value ] )
173
177
} else {
174
178
var text = util . pathToText ( path , typeof value == 'string' )
175
- return text + ops [ op ] + util . quote ( value )
179
+ return text + OPS [ op ] + util . quote ( value )
176
180
}
177
181
}
178
182
case '$type' : {
@@ -209,6 +213,12 @@ function convertOp(path, op, value, parent, arrayPaths) {
209
213
}
210
214
}
211
215
216
+ function getSpecialKeys ( path , query , forceExact ) {
217
+ return Object . keys ( query ) . filter ( function ( key ) {
218
+ return ( path . length === 1 && ! forceExact ) || key in OPS || key in OTHER_OPS
219
+ } )
220
+ }
221
+
212
222
/**
213
223
* Convert a filter expression to the corresponding PostgreSQL text.
214
224
* @param path {Array} The current path
@@ -234,9 +244,7 @@ var convert = function (path, query, arrayPaths, forceExact=false) {
234
244
if ( Object . keys ( query ) . length === 0 ) {
235
245
return 'TRUE'
236
246
}
237
- var specialKeys = Object . keys ( query ) . filter ( function ( key ) {
238
- return ( path . length === 1 && ! forceExact ) || key in ops || key in otherOps
239
- } )
247
+ const specialKeys = getSpecialKeys ( path , query , forceExact )
240
248
switch ( specialKeys . length ) {
241
249
case 0 : {
242
250
const text = util . pathToText ( path , typeof query == 'string' )
0 commit comments