Skip to content

Commit 3eaa028

Browse files
committed
Add basic support for project object fields in arrays
1 parent 5ee6587 commit 3eaa028

File tree

2 files changed

+43
-13
lines changed

2 files changed

+43
-13
lines changed

select.js

+31-13
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
var util = require('./util.js')
22

3-
function convertRecur(fieldName, input) {
3+
// TODO (ensure multi-level inside array projections work)
4+
function convertRecur(fieldName, input, arrayFields, prefix, prefixStrip) {
45
if (typeof input === 'string') {
5-
return util.pathToText([fieldName].concat(input.split('.')), false)
6+
return util.pathToText([fieldName].concat(input.replace(new RegExp('^' + prefixStrip), '').split('.')), false)
67
} else {
78
var entries = []
89
for (var key in input) {
910
entries.push('\'' + key + '\'')
10-
entries.push(convertRecur(fieldName, input[key]))
11+
if (!arrayFields[key]) {
12+
entries.push(convertRecur(fieldName, input[key], arrayFields[key] || {}, prefix + key + '.' , prefixStrip))
13+
} else {
14+
const obj = convertRecur('value', input[key], arrayFields[key] || {}, prefix + key + '.', prefix + key + '.')
15+
entries.push('(SELECT jsonb_agg(r) FROM (SELECT ' + obj + ' as r ' +
16+
'FROM jsonb_array_elements(data->\'arr\') as value) AS obj)')
17+
}
1118
}
1219
return 'jsonb_build_object(' + entries.join(', ') + ')'
1320
}
1421
}
1522

16-
var convert = function (fieldName, projection) {
17-
// Empty projection returns full document
18-
if (!projection) {
19-
return fieldName
20-
}
21-
//var output = '';
23+
function convertToShellDoc(projection, prefix = '') {
2224
var shellDoc = {}
2325
var removals = []
2426
Object.keys(projection).forEach(function(field) {
@@ -31,19 +33,35 @@ var convert = function (fieldName, projection) {
3133
current[key] = current[key] || {}
3234
current = current[key]
3335
} else {
34-
current[key] = field
36+
current[key] = prefix + field
3537
}
3638
}
3739
} else if (projection[field] === 0) {
3840
if (field !== '_id') {
3941
removals.push('#- ' + util.toPostgresPath(path))
4042
}
43+
} else if (typeof projection[field] === 'object' && !Array.isArray(projection[field])) {
44+
const { shellDoc: subShellDoc, removals: subRemovals } =
45+
convertToShellDoc(projection[field], prefix + field + '.')
46+
shellDoc[field] = subShellDoc
47+
removals = removals.concat(subRemovals)
4148
} else {
4249
console.error(`unexpected projection value ${projection[field]} for ${field}`)
4350
}
44-
//output += util.convertDotNotation(fieldName, field)
45-
//output +=
4651
})
52+
return { shellDoc, removals }
53+
}
54+
55+
var convert = function (fieldName, projection, arrayFields) {
56+
// Empty projection returns full document
57+
if (!projection) {
58+
return fieldName
59+
}
60+
//var output = '';
61+
let { shellDoc, removals } = convertToShellDoc(projection)
62+
63+
//output += util.convertDotNotation(fieldName, field)
64+
//output +=
4765
if (Object.keys(shellDoc).length > 0 && typeof projection['_id'] === 'undefined') {
4866
shellDoc['_id'] = '_id'
4967
}
@@ -54,7 +72,7 @@ var convert = function (fieldName, projection) {
5472
throw new Error('Projection cannot have a mix of inclusion and exclusion.')
5573
}
5674

57-
var out = Object.keys(shellDoc).length > 0 ? convertRecur(fieldName, shellDoc) : fieldName
75+
var out = Object.keys(shellDoc).length > 0 ? convertRecur(fieldName, shellDoc, arrayFields || {}, '', '') : fieldName
5876
if (removals.length) {
5977
out += ' ' + removals.join(' ')
6078
}

test/select.js

+12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ describe('select: ', function() {
2020
it('nested field', function () {
2121
assert.equal(convertSelect('data', { 'field.inner': 1 }), 'jsonb_build_object(\'field\', jsonb_build_object(\'inner\', data->\'field\'->\'inner\'), \'_id\', data->\'_id\') as data')
2222
})
23+
it('nested field alternate', function () {
24+
assert.equal(convertSelect('data', { field: { inner: 1 } }), 'jsonb_build_object(\'field\', jsonb_build_object(\'inner\', data->\'field\'->\'inner\'), \'_id\', data->\'_id\') as data')
25+
})
2326
it('multiple fields', function () {
2427
assert.equal(convertSelect('data', { a: 1, b: 1 }), 'jsonb_build_object(\'a\', data->\'a\', \'b\', data->\'b\', \'_id\', data->\'_id\') as data')
2528
})
@@ -39,4 +42,13 @@ describe('select: ', function() {
3942
assert.throws(() => convertSelect('data', { a: 1, b: 0 }), 'Projection cannot have a mix of inclusion and exclusion.')
4043
})
4144
})
45+
46+
describe('array fields', function () {
47+
it('single field', function () {
48+
assert.equal(convertSelect('data', { 'arr.color': 1 }, { arr: 1 }),
49+
'jsonb_build_object(\'arr\', (SELECT jsonb_agg(r) FROM (SELECT jsonb_build_object(' +
50+
'\'color\', value->\'color\') as r FROM jsonb_array_elements(data->\'arr\') as value)' +
51+
' AS obj), \'_id\', data->\'_id\') as data')
52+
})
53+
})
4254
})

0 commit comments

Comments
 (0)