Comparing version 0.0.5 to 0.0.6
@@ -33,2 +33,4 @@ 'use strict'; | ||
var _scopeTools = require('./scopeTools'); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
@@ -73,2 +75,41 @@ | ||
// Slices an input. If the input is an array of scopes: slices each scope individually. If the | ||
// input is a scope with an array: slices the scope | ||
function slice(input, start, end) { | ||
// if (Array.isArray(input)) { | ||
// return input.map(scope => scope.slice(start, end)) | ||
// } | ||
if (Array.isArray(input)) { | ||
return input.slice(start, end); | ||
} | ||
// Input is a single scope | ||
return input.slice(start, end); | ||
} | ||
function first(input, offset) { | ||
// if (Array.isArray(input)) { | ||
// return input.map(scope => scope.first(offset)) | ||
// } | ||
if (Array.isArray(input)) { | ||
return input[offset]; | ||
} | ||
// Input is a single scope | ||
return input.first(offset); | ||
} | ||
function shallowFlatten(value) { | ||
if (!Array.isArray(value)) { | ||
return value; | ||
} | ||
const result = []; | ||
value.forEach(item => { | ||
if (Array.isArray(item)) { | ||
result.push(...item); | ||
} else { | ||
result.push(item); | ||
} | ||
}); | ||
return result; | ||
} | ||
module.exports = class Executor { | ||
@@ -215,3 +256,5 @@ | ||
if (lhs.value === null || lhs.value === undefined || rhs.value === null || rhs.value === undefined) { | ||
const lhsValue = (0, _scopeTools.asPlainValue)(lhs); | ||
const rhsValue = (0, _scopeTools.asPlainValue)(rhs); | ||
if (lhsValue === null || lhsValue === undefined || rhsValue === null || rhsValue === undefined) { | ||
return scope.clone({ | ||
@@ -221,6 +264,6 @@ value: null | ||
} | ||
if (Array.isArray(rhs.value)) { | ||
for (let i = 0; i < rhs.value.length; i++) { | ||
const candidate = rhs.value[i]; | ||
if (lhs.value == candidate) { | ||
if (Array.isArray(rhsValue)) { | ||
for (let i = 0; i < rhsValue.length; i++) { | ||
const candidate = rhsValue[i]; | ||
if (lhsValue == candidate) { | ||
return scope.clone({ | ||
@@ -231,3 +274,3 @@ value: true | ||
if (candidate instanceof types.Path) { | ||
if (candidate.contains(lhs.value)) { | ||
if (candidate.contains(lhsValue)) { | ||
return scope.clone({ | ||
@@ -243,5 +286,6 @@ value: true | ||
} | ||
if (rhs.value instanceof types.Path) { | ||
if (rhsValue instanceof types.Path) { | ||
(0, _debug2.default)('path containment', lhsValue, rhsValue); | ||
return scope.clone({ | ||
value: rhs.value.contains(lhs.value) | ||
value: rhsValue.contains(lhsValue) | ||
}); | ||
@@ -262,3 +306,3 @@ } | ||
value: elements.map(function (element) { | ||
return element.value; | ||
return (0, _scopeTools.asPlainValue)(element); | ||
}) | ||
@@ -280,4 +324,6 @@ }); | ||
return _this8.exec(objOp.value, scope).then(function (resultScope) { | ||
const value = (0, _scopeTools.asPlainValue)(resultScope); | ||
(0, _debug2.default)(`objOp assignment [${objOp.name}] = ${value}`, value); | ||
return { | ||
[objOp.name]: resultScope.value | ||
[objOp.name]: value | ||
}; | ||
@@ -313,16 +359,6 @@ }); | ||
return _asyncToGenerator(function* () { | ||
let arrayValue = Array.isArray(lhs.value) ? lhs.value : [lhs.value]; | ||
const result = arrayValue.map(function (item) { | ||
const scope = lhs.clone({ | ||
value: item | ||
}); | ||
return _this9.exec(operation, scope); | ||
}); | ||
return Promise.all(result).then(function (result) { | ||
return lhs.clone({ | ||
value: result.map(function (scope) { | ||
return scope.value; | ||
}) | ||
}); | ||
}); | ||
let input = Array.isArray(lhs) ? lhs : [lhs]; | ||
return Promise.all(input.map(function (item) { | ||
return _this9.exec(operation, item); | ||
})); | ||
})(); | ||
@@ -335,15 +371,42 @@ } | ||
return _asyncToGenerator(function* () { | ||
let arrayValue = Array.isArray(lhs.value) ? lhs.value : [lhs.value]; | ||
const filterPromises = arrayValue.map(function (item) { | ||
const scope = lhs.clone({ | ||
value: item | ||
}); | ||
return _this10.exec(operation, scope); | ||
let input; | ||
if (Array.isArray(lhs)) { | ||
(0, _debug2.default)("LHS is array"); | ||
// If lhs is an array of scopes, we perform flattening on each scope before proceeding | ||
input = lhs; | ||
(0, _debug2.default)('flatten', lhs); | ||
input = lhs.reduce(function (sum, item) { | ||
if (Array.isArray(item.value)) { | ||
return sum.concat(item.value.map(function (element) { | ||
return item.clone({ | ||
value: element | ||
}); | ||
})); | ||
} | ||
return sum.concat(item); | ||
}, []); | ||
(0, _debug2.default)('flattened to', input); | ||
} else { | ||
// If lhs is a single array value, explode it to an array of scopes | ||
if (Array.isArray(lhs.value)) { | ||
(0, _debug2.default)("LHS is single array value (explode)"); | ||
input = lhs.value.map(function (item) { | ||
return lhs.clone({ | ||
value: item | ||
}); | ||
}); | ||
} else { | ||
// If lhs is a single non-array value, wrap it in an array | ||
(0, _debug2.default)("LHS is single non-array value (wrap)"); | ||
input = [lhs]; | ||
} | ||
} | ||
(0, _debug2.default)('mapped lhs for filter, lhs:', lhs, 'input:', input); | ||
const filterPromises = input.map(function (item) { | ||
return _this10.exec(operation, item); | ||
}); | ||
return Promise.all(filterPromises).then(function (filterValues) { | ||
(0, _debug2.default)('filter result:', filterValues); | ||
return lhs.clone({ | ||
value: arrayValue.filter(function (element, index) { | ||
return filterValues[index].value === true; | ||
}) | ||
return input.filter(function (element, index) { | ||
return filterValues[index].value === true; | ||
}); | ||
@@ -358,30 +421,29 @@ }); | ||
return _asyncToGenerator(function* () { | ||
const inputIsArray = Array.isArray(lhs.value); | ||
let arrayValue = inputIsArray ? lhs.value : [lhs.value]; | ||
const inputIsArray = Array.isArray(lhs); | ||
let input = inputIsArray ? lhs : [lhs]; | ||
let result; | ||
switch (operation.op) { | ||
case 'subscript': | ||
(0, _debug2.default)('subscript:', operation); | ||
result = lhs.slice(operation.start, operation.end); | ||
if (operation.first) { | ||
result = lhs.clone({ | ||
value: result.value[0], | ||
start: null | ||
}); | ||
(0, _debug2.default)('first()', lhs, operation.start); | ||
(0, _debug2.default)('=>', first(lhs, operation.start)); | ||
return first(lhs, operation.start); | ||
} else { | ||
(0, _debug2.default)('slice()', lhs, operation.start, operation.end); | ||
(0, _debug2.default)('=>', slice(lhs, operation.start, operation.end)); | ||
return slice(lhs, operation.start, operation.end); | ||
} | ||
break; | ||
case 'ordering': | ||
result = (0, _sort2.default)(lhs, operation.terms); | ||
return (0, _sort2.default)(lhs, operation.terms); | ||
break; | ||
case 'filter': | ||
result = _this11.pipeFilter(operation, lhs); | ||
return _this11.pipeFilter(operation, lhs); | ||
break; | ||
default: | ||
(0, _debug2.default)('mapping:', lhs); | ||
result = _this11.pipeMap(operation, lhs).then(function (result) { | ||
(0, _debug2.default)('mapped:', result); | ||
return result; | ||
}); | ||
result = _this11.pipeMap(operation, lhs); | ||
break; | ||
} | ||
(0, _debug2.default)('input was array:', inputIsArray, result); | ||
if (inputIsArray) { | ||
@@ -391,8 +453,8 @@ return result; | ||
if (!result.then) { | ||
(0, _debug2.default)("Un-arrayifying plain value", result); | ||
return Array.isArray(result) ? result[0] : result; | ||
} | ||
return result.then(function (arrayResult) { | ||
return arrayResult.clone({ | ||
value: arrayResult.value[0] | ||
}); | ||
(0, _debug2.default)("Un-arrayifying promised value", arrayResult); | ||
return arrayResult[0]; | ||
}); | ||
@@ -416,2 +478,3 @@ })(); | ||
let nextOp = ops.shift(); | ||
(0, _debug2.default)('nextOp:', nextOp); | ||
current = yield _this12.pipe(nextOp, current); | ||
@@ -418,0 +481,0 @@ } |
@@ -18,2 +18,4 @@ 'use strict'; | ||
var _scopeTools = require('./scopeTools'); | ||
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } | ||
@@ -40,4 +42,5 @@ | ||
function count(_, input) { | ||
if (Array.isArray(input.value)) { | ||
return input.value.length; | ||
const value = (0, _scopeTools.asPlainValue)(input); | ||
if (Array.isArray(value)) { | ||
return value.length; | ||
} | ||
@@ -48,15 +51,16 @@ return input.value !== null && input.value !== undefined ? 1 : 0; | ||
function length(_, input) { | ||
if (input.value === null || input.value === undefined) { | ||
const value = (0, _scopeTools.asPlainValue)(input); | ||
if (value === null || value === undefined) { | ||
return null; | ||
} | ||
if (Array.isArray(input.value)) { | ||
return input.value.length; | ||
if (Array.isArray(value)) { | ||
return value.length; | ||
} | ||
if (typeof input.value === 'string') { | ||
return input.value.length; | ||
if (typeof value === 'string') { | ||
return value.length; | ||
} | ||
if (value && typeof input.value === 'object') { | ||
return Object.keys(input.value).length; | ||
if (value && typeof value === 'object') { | ||
return Object.keys(value).length; | ||
} | ||
return input.value !== null && input.value !== undefined ? 1 : 0; | ||
return value !== null && value !== undefined ? 1 : 0; | ||
} | ||
@@ -63,0 +67,0 @@ |
@@ -66,2 +66,15 @@ 'use strict'; | ||
first(offset) { | ||
if (!Array.isArray(this.value)) { | ||
return this.clone({ | ||
value: null | ||
}); | ||
} | ||
const collectionStart = this.start || 0; | ||
return this.clone({ | ||
value: this.value.slice(offset + collectionStart)[0], | ||
start: 0 | ||
}); | ||
} | ||
// Returns true if the provided document references any of the ids | ||
@@ -212,2 +225,6 @@ doesReference(ids) { | ||
inspect() { | ||
return `Scope<${JSON.stringify(this.value)}>`; | ||
} | ||
} | ||
@@ -214,0 +231,0 @@ exports.default = Scope; // A small function to extract all refs in a document as an array |
@@ -52,4 +52,4 @@ 'use strict'; | ||
function sort(scope, terms) { | ||
(0, _debug2.default)("sort()", scope, terms); | ||
function sortScopes(scopes, terms) { | ||
(0, _debug2.default)("sort()", scopes, terms); | ||
terms = terms.slice(); | ||
@@ -59,9 +59,6 @@ // Map each object into terms sorted by cardinality | ||
// becomes [[1, "a"], [2, "b"]] | ||
const arrayValue = Array.isArray(scope.value) ? scope.value : [scope.value]; | ||
const itemWithTerms = arrayValue.map(item => ({ | ||
rawValue: item, | ||
const itemWithTerms = scopes.map(scope => ({ | ||
scope: scope, | ||
terms: terms.map(term => { | ||
return new _Scope2.default({ | ||
value: item | ||
}).resolveAccessor(term.expression.path).value; | ||
return scope.resolveAccessor(term.expression.path).value; | ||
}) | ||
@@ -82,6 +79,35 @@ })); | ||
return scope.clone({ | ||
value: sorted.map(item => item.rawValue) | ||
}); | ||
(0, _debug2.default)('sorted', sorted); | ||
return sorted.map(item => item.scope); | ||
} | ||
function sort(input, terms) { | ||
if (Array.isArray(input)) { | ||
// Input is array of scopes | ||
(0, _debug2.default)("Sorting as scopes"); | ||
return sortScopes(input, terms); | ||
} | ||
if (Array.isArray(input.value)) { | ||
(0, _debug2.default)("Sorting array by unboxing"); | ||
// Input is scoped array | ||
const scoped = input.value.map(item => { | ||
return input.clone({ | ||
value: item | ||
}); | ||
}); | ||
const sorted = sortScopes(scoped, terms); | ||
return sorted; | ||
// Should probably rebox output so that `*|order(ordinal).ordinal` was illegal. Should only | ||
// be [] that performs unboxing. But we have tests documenting the non-reboxing behavior, so | ||
// we'll keep it for now. If we re-box you would have to do this instead: `*|order(ordinal)[].ordinal` | ||
// // Re-box sorted output | ||
// return input.clone({ | ||
// value: sorted.map(scope => scope.value) | ||
// }) | ||
} | ||
// Item is a plain value, just pass it through | ||
(0, _debug2.default)("Plain value, sorting by pass-through, but in an array of scopes"); | ||
return [input]; | ||
} | ||
//# sourceMappingURL=sort.js.map |
@@ -47,6 +47,6 @@ 'use strict'; | ||
} | ||
if (operation.path.length == 2) { | ||
const lastOpType = operation.path[1].op; | ||
if (lastOpType == 'filter' || lastOpType == 'subscript') { | ||
return determineUnambiguousAlias(operation.path[0]); | ||
if (operation.operations.length == 2) { | ||
const lastOpType = operation.operations[1].op; | ||
if (lastOpType == 'filter' || lastOpType == 'subscript' || lastOpType == 'object') { | ||
return determineUnambiguousAlias(operation.operations[0]); | ||
} | ||
@@ -53,0 +53,0 @@ } |
@@ -19,2 +19,4 @@ 'use strict'; | ||
var _scopeTools = require('../exec/scopeTools'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -46,3 +48,2 @@ | ||
const ast = (0, _parse2.default)(source, params); | ||
(0, _debug2.default)(JSON.stringify(ast)); | ||
@@ -52,18 +53,2 @@ const globalFilterAst = globalFilter ? (0, _parse2.default)(globalFilter, {}) : null; | ||
const operations = (0, _plan2.default)(ast, globalFilterAst); | ||
(0, _debug2.default)(JSON.stringify(operations, null, 2)); | ||
// fetcher = (filter) => { | ||
// debug("fetch:", filter) | ||
// return new Promise((resolve, reject) => { | ||
// resolve([ | ||
// { | ||
// a: 1, | ||
// b: "one" | ||
// }, | ||
// { | ||
// a: 2, | ||
// b: "two" | ||
// } | ||
// ]) | ||
// }) | ||
// } | ||
const resultScope = yield (0, _exec2.default)({ | ||
@@ -74,3 +59,3 @@ operations, | ||
return resultScope.value; | ||
return (0, _scopeTools.asPlainValue)(resultScope); | ||
}); | ||
@@ -77,0 +62,0 @@ |
^ = parent, source = parent siblings | ||
^._ref == _id | ||
@@ -22,5 +23,26 @@ _id in $[]._ref | ||
ref[]-> | ||
ref[]|(*[^._ref == _id][0]) | ||
global(ref[]) | ||
ref-> | ||
*[ref._ref == _id][0] | ||
*{ | ||
authors[]-> | ||
} | ||
*{ | ||
"authors": authors[] | *[^._ref == _id][0] | ||
} | ||
*{ | ||
"authors": *[_id in ^.authors[]._ref] | ||
} | ||
ref[]->title | ||
map(ref[], (*[_id in source[].ref[]._ref])[_id == ^._ref][0].title) | ||
ref[] | (*[_id == ^._ref][0].title) | ||
map(ref[]._ref, *[_id in ^^[].ref[]._ref] |
@@ -0,1 +1,3 @@ | ||
* [ ] Refactor boxing. Make general container type? Need to keep track of start-index for collections even after unboxing | ||
* [ ] Exec pipelines in constraint expressions | ||
@@ -14,1 +16,3 @@ * [ ] Joins | ||
* [ ] Generalize source caching to cache all constant subexprs | ||
{ | ||
"name": "groq", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "A GROQ runtime", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -8,2 +8,3 @@ import Scope from './Scope' | ||
import match from './match' | ||
import { asPlainValue } from './scopeTools' | ||
@@ -43,2 +44,42 @@ const DEFAULT_LIMIT = 100 | ||
// Slices an input. If the input is an array of scopes: slices each scope individually. If the | ||
// input is a scope with an array: slices the scope | ||
function slice(input, start, end) { | ||
// if (Array.isArray(input)) { | ||
// return input.map(scope => scope.slice(start, end)) | ||
// } | ||
if (Array.isArray(input)) { | ||
return input.slice(start, end) | ||
} | ||
// Input is a single scope | ||
return input.slice(start, end) | ||
} | ||
function first(input, offset) { | ||
// if (Array.isArray(input)) { | ||
// return input.map(scope => scope.first(offset)) | ||
// } | ||
if (Array.isArray(input)) { | ||
return input[offset] | ||
} | ||
// Input is a single scope | ||
return input.first(offset) | ||
} | ||
function shallowFlatten(value) { | ||
if (!Array.isArray(value)) { | ||
return value | ||
} | ||
const result = [] | ||
value.forEach(item => { | ||
if (Array.isArray(item)) { | ||
result.push(...item) | ||
} else { | ||
result.push(item) | ||
} | ||
}) | ||
return result | ||
} | ||
module.exports = class Executor { | ||
@@ -152,3 +193,5 @@ | ||
]) | ||
if (lhs.value === null || lhs.value === undefined || rhs.value === null || rhs.value === undefined) { | ||
const lhsValue = asPlainValue(lhs) | ||
const rhsValue = asPlainValue(rhs) | ||
if (lhsValue === null || lhsValue === undefined || rhsValue === null || rhsValue === undefined) { | ||
return scope.clone({ | ||
@@ -158,6 +201,6 @@ value: null | ||
} | ||
if (Array.isArray(rhs.value)) { | ||
for (let i = 0; i < rhs.value.length; i++) { | ||
const candidate = rhs.value[i] | ||
if (lhs.value == candidate) { | ||
if (Array.isArray(rhsValue)) { | ||
for (let i = 0; i < rhsValue.length; i++) { | ||
const candidate = rhsValue[i] | ||
if (lhsValue == candidate) { | ||
return scope.clone({ | ||
@@ -168,3 +211,3 @@ value: true | ||
if (candidate instanceof types.Path) { | ||
if (candidate.contains(lhs.value)) { | ||
if (candidate.contains(lhsValue)) { | ||
return scope.clone({ | ||
@@ -179,7 +222,7 @@ value: true | ||
}) | ||
} | ||
if (rhs.value instanceof types.Path) { | ||
if (rhsValue instanceof types.Path) { | ||
debug('path containment', lhsValue, rhsValue) | ||
return scope.clone({ | ||
value: rhs.value.contains(lhs.value) | ||
value: rhsValue.contains(lhsValue) | ||
}) | ||
@@ -194,3 +237,3 @@ } | ||
return scope.clone({ | ||
value: elements.map(element => element.value) | ||
value: elements.map(element => asPlainValue(element)) | ||
}) | ||
@@ -206,4 +249,6 @@ }) | ||
return this.exec(objOp.value, scope).then(resultScope => { | ||
const value = asPlainValue(resultScope) | ||
debug(`objOp assignment [${objOp.name}] = ${value}`, value) | ||
return { | ||
[objOp.name]: resultScope.value | ||
[objOp.name]: value | ||
} | ||
@@ -230,23 +275,40 @@ }) | ||
async pipeMap(operation, lhs) { | ||
let arrayValue = Array.isArray(lhs.value) ? lhs.value : [lhs.value] | ||
const result = arrayValue.map(item => { | ||
const scope = lhs.clone({ | ||
value: item | ||
}) | ||
return this.exec(operation, scope) | ||
}) | ||
return Promise.all(result).then(result => { | ||
return lhs.clone({ | ||
value: result.map(scope => scope.value) | ||
}) | ||
}) | ||
let input = Array.isArray(lhs) ? lhs : [lhs] | ||
return Promise.all(input.map(item => { | ||
return this.exec(operation, item) | ||
})) | ||
} | ||
async pipeFilter(operation, lhs) { | ||
let arrayValue = Array.isArray(lhs.value) ? lhs.value : [lhs.value] | ||
const filterPromises = arrayValue.map(item => { | ||
const scope = lhs.clone({ | ||
value: item | ||
}) | ||
return this.exec(operation, scope) | ||
let input | ||
if (Array.isArray(lhs)) { | ||
debug("LHS is array") | ||
// If lhs is an array of scopes, we perform flattening on each scope before proceeding | ||
input = lhs | ||
debug('flatten', lhs) | ||
input = lhs.reduce((sum, item) => { | ||
if (Array.isArray(item.value)) { | ||
return sum.concat(item.value.map(element => item.clone({ | ||
value: element | ||
}))) | ||
} | ||
return sum.concat(item) | ||
}, []) | ||
debug('flattened to', input) | ||
} else { | ||
// If lhs is a single array value, explode it to an array of scopes | ||
if (Array.isArray(lhs.value)) { | ||
debug("LHS is single array value (explode)") | ||
input = lhs.value.map(item => lhs.clone({ | ||
value: item | ||
})) | ||
} else { | ||
// If lhs is a single non-array value, wrap it in an array | ||
debug("LHS is single non-array value (wrap)") | ||
input = [lhs] | ||
} | ||
} | ||
debug('mapped lhs for filter, lhs:', lhs, 'input:', input) | ||
const filterPromises = input.map(item => { | ||
return this.exec(operation, item) | ||
}) | ||
@@ -256,5 +318,3 @@ return Promise.all(filterPromises).then( | ||
debug('filter result:', filterValues) | ||
return lhs.clone({ | ||
value: arrayValue.filter(((element, index) => filterValues[index].value === true)) | ||
}) | ||
return input.filter(((element, index) => filterValues[index].value === true)) | ||
} | ||
@@ -265,30 +325,29 @@ ) | ||
async pipe(operation, lhs) { | ||
const inputIsArray = Array.isArray(lhs.value) | ||
let arrayValue = inputIsArray ? lhs.value : [lhs.value] | ||
const inputIsArray = Array.isArray(lhs) | ||
let input = inputIsArray ? lhs : [lhs] | ||
let result | ||
switch (operation.op) { | ||
case 'subscript': | ||
debug('subscript:', operation) | ||
result = lhs.slice(operation.start, operation.end) | ||
if (operation.first) { | ||
result = lhs.clone({ | ||
value: result.value[0], | ||
start: null | ||
}) | ||
debug('first()', lhs, operation.start) | ||
debug('=>', first(lhs, operation.start)) | ||
return first(lhs, operation.start) | ||
} else { | ||
debug('slice()', lhs, operation.start, operation.end) | ||
debug('=>', slice(lhs, operation.start, operation.end)) | ||
return slice(lhs, operation.start, operation.end) | ||
} | ||
break | ||
case 'ordering': | ||
result = sort(lhs, operation.terms) | ||
return sort(lhs, operation.terms) | ||
break | ||
case 'filter': | ||
result = this.pipeFilter(operation, lhs) | ||
return this.pipeFilter(operation, lhs) | ||
break | ||
default: | ||
debug('mapping:', lhs) | ||
result = this.pipeMap(operation, lhs).then(result => { | ||
debug('mapped:', result) | ||
return result | ||
}) | ||
result = this.pipeMap(operation, lhs) | ||
break | ||
} | ||
debug('input was array:', inputIsArray, result) | ||
if (inputIsArray) { | ||
@@ -298,8 +357,8 @@ return result | ||
if (!result.then) { | ||
debug("Un-arrayifying plain value", result) | ||
return Array.isArray(result) ? result[0] : result | ||
} | ||
return result.then(arrayResult => { | ||
return arrayResult.clone({ | ||
value: arrayResult.value[0] | ||
}) | ||
debug("Un-arrayifying promised value", arrayResult) | ||
return arrayResult[0] | ||
}) | ||
@@ -319,2 +378,3 @@ } | ||
let nextOp = ops.shift() | ||
debug('nextOp:', nextOp) | ||
current = await this.pipe(nextOp, current) | ||
@@ -321,0 +381,0 @@ } |
import * as types from './types' | ||
import { asPlainValue } from './scopeTools' | ||
@@ -22,4 +23,5 @@ export function path(_, spec) { | ||
export function count(_, input) { | ||
if (Array.isArray(input.value)) { | ||
return input.value.length | ||
const value = asPlainValue(input) | ||
if (Array.isArray(value)) { | ||
return value.length | ||
} | ||
@@ -30,15 +32,16 @@ return (input.value !== null && input.value !== undefined) ? 1 : 0 | ||
export function length(_, input) { | ||
if (input.value === null || input.value === undefined) { | ||
const value = asPlainValue(input) | ||
if (value === null || value === undefined) { | ||
return null | ||
} | ||
if (Array.isArray(input.value)) { | ||
return input.value.length | ||
if (Array.isArray(value)) { | ||
return value.length | ||
} | ||
if (typeof input.value === 'string') { | ||
return input.value.length | ||
if (typeof value === 'string') { | ||
return value.length | ||
} | ||
if (value && typeof input.value === 'object') { | ||
return Object.keys(input.value).length | ||
if (value && typeof value === 'object') { | ||
return Object.keys(value).length | ||
} | ||
return (input.value !== null && input.value !== undefined) ? 1 : 0 | ||
return (value !== null && value !== undefined) ? 1 : 0 | ||
} | ||
@@ -45,0 +48,0 @@ |
@@ -50,2 +50,15 @@ import debug from '../debug' | ||
first(offset) { | ||
if (!Array.isArray(this.value)) { | ||
return this.clone({ | ||
value: null | ||
}) | ||
} | ||
const collectionStart = this.start || 0 | ||
return this.clone({ | ||
value: this.value.slice(offset + collectionStart)[0], | ||
start: 0 | ||
}) | ||
} | ||
// Returns true if the provided document references any of the ids | ||
@@ -198,2 +211,6 @@ doesReference(ids) { | ||
inspect() { | ||
return `Scope<${JSON.stringify(this.value)}>` | ||
} | ||
} | ||
@@ -200,0 +217,0 @@ // A small function to extract all refs in a document as an array |
@@ -39,4 +39,4 @@ import Scope from './Scope' | ||
export default function sort(scope, terms) { | ||
debug("sort()", scope, terms) | ||
function sortScopes(scopes, terms) { | ||
debug("sort()", scopes, terms) | ||
terms = terms.slice() | ||
@@ -46,9 +46,6 @@ // Map each object into terms sorted by cardinality | ||
// becomes [[1, "a"], [2, "b"]] | ||
const arrayValue = Array.isArray(scope.value) ? scope.value : [scope.value] | ||
const itemWithTerms = arrayValue.map(item => ({ | ||
rawValue: item, | ||
const itemWithTerms = scopes.map(scope => ({ | ||
scope: scope, | ||
terms: terms.map(term => { | ||
return (new Scope({ | ||
value: item | ||
})).resolveAccessor(term.expression.path).value | ||
return scope.resolveAccessor(term.expression.path).value | ||
}) | ||
@@ -69,5 +66,35 @@ })) | ||
return scope.clone({ | ||
value: sorted.map(item => item.rawValue) | ||
}) | ||
} | ||
debug('sorted', sorted) | ||
return sorted.map(item => item.scope) | ||
} | ||
export default function sort(input, terms) { | ||
if (Array.isArray(input)) { | ||
// Input is array of scopes | ||
debug("Sorting as scopes") | ||
return sortScopes(input, terms) | ||
} | ||
if (Array.isArray(input.value)) { | ||
debug("Sorting array by unboxing") | ||
// Input is scoped array | ||
const scoped = input.value.map(item => { | ||
return input.clone({ | ||
value: item | ||
}) | ||
}) | ||
const sorted = sortScopes(scoped, terms) | ||
return sorted | ||
// Should probably rebox output so that `*|order(ordinal).ordinal` was illegal. Should only | ||
// be [] that performs unboxing. But we have tests documenting the non-reboxing behavior, so | ||
// we'll keep it for now. If we re-box you would have to do this instead: `*|order(ordinal)[].ordinal` | ||
// // Re-box sorted output | ||
// return input.clone({ | ||
// value: sorted.map(scope => scope.value) | ||
// }) | ||
} | ||
// Item is a plain value, just pass it through | ||
debug("Plain value, sorting by pass-through, but in an array of scopes") | ||
return [input] | ||
} | ||
@@ -34,6 +34,6 @@ import { Assignment } from './operations' | ||
} | ||
if (operation.path.length == 2) { | ||
const lastOpType = operation.path[1].op | ||
if (lastOpType == 'filter' || lastOpType == 'subscript') { | ||
return determineUnambiguousAlias(operation.path[0]) | ||
if (operation.operations.length == 2) { | ||
const lastOpType = operation.operations[1].op | ||
if (lastOpType == 'filter' || lastOpType == 'subscript' || lastOpType == 'object') { | ||
return determineUnambiguousAlias(operation.operations[0]) | ||
} | ||
@@ -40,0 +40,0 @@ } |
@@ -5,2 +5,3 @@ import parse from '../parse' | ||
import debug from '../debug' | ||
import { asPlainValue } from '../exec/scopeTools' | ||
@@ -24,3 +25,2 @@ // Entry point for query engine. Accepts a GROQ-query + a fetcher to retrieve | ||
const ast = parse(source, params) | ||
debug(JSON.stringify(ast)) | ||
@@ -30,18 +30,2 @@ const globalFilterAst = globalFilter ? parse(globalFilter, {}) : null | ||
const operations = plan(ast, globalFilterAst) | ||
debug(JSON.stringify(operations, null, 2)) | ||
// fetcher = (filter) => { | ||
// debug("fetch:", filter) | ||
// return new Promise((resolve, reject) => { | ||
// resolve([ | ||
// { | ||
// a: 1, | ||
// b: "one" | ||
// }, | ||
// { | ||
// a: 2, | ||
// b: "two" | ||
// } | ||
// ]) | ||
// }) | ||
// } | ||
const resultScope = await exec({ | ||
@@ -52,3 +36,3 @@ operations, | ||
return resultScope.value | ||
return asPlainValue(resultScope) | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4004065
127
65474