sparqlalgebrajs
Advanced tools
Comparing version 0.6.6 to 0.7.0
import translate from './lib/sparqlAlgebra'; | ||
import * as Algebra from './lib/algebra'; | ||
import Factory from './lib/Factory'; | ||
export { translate, Algebra, Factory }; | ||
import { toSparql, toSparqlJs } from './lib/sparql'; | ||
export { translate, Algebra, Factory, toSparql, toSparqlJs }; |
@@ -9,2 +9,5 @@ "use strict"; | ||
exports.Factory = Factory_1.default; | ||
const sparql_1 = require("./lib/sparql"); | ||
exports.toSparql = sparql_1.toSparql; | ||
exports.toSparqlJs = sparql_1.toSparqlJs; | ||
//# sourceMappingURL=index.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const _ = require("lodash"); | ||
const Algebra = require("./algebra"); | ||
@@ -16,2 +15,3 @@ const SparqlGenerator = require('sparqljs').Generator; | ||
resetContext(); | ||
op = removeQuads(op); | ||
return translateOperation(op); | ||
@@ -21,3 +21,3 @@ } | ||
function flatten(s) { | ||
return Array.prototype.concat(...s); | ||
return Array.prototype.concat(...s).filter(x => x); | ||
} | ||
@@ -29,8 +29,10 @@ function resetContext() { | ||
// this allows us to differentiate between BIND and SELECT when translating EXTEND | ||
if (op.type !== types.EXTEND) | ||
if (op.type !== types.EXTEND && op.type !== types.ORDER_BY) | ||
context.project = false; | ||
switch (op.type) { | ||
case types.EXPRESSION: return translateExpression(op); | ||
case types.ASK: return translateProject(op, types.ASK); | ||
case types.BGP: return translateBgp(op); | ||
case types.CONSTRUCT: return translateConstruct(op); | ||
case types.DESCRIBE: return translateProject(op, types.DESCRIBE); | ||
case types.DISTINCT: return translateDistinct(op); | ||
@@ -45,4 +47,5 @@ case types.EXTEND: return translateExtend(op); | ||
case types.ORDER_BY: return translateOrderBy(op); | ||
case types.PATH: return translatePath(op); | ||
case types.PATTERN: return translatePattern(op); | ||
case types.PROJECT: return translateProject(op); | ||
case types.PROJECT: return translateProject(op, types.PROJECT); | ||
case types.REDUCED: return translateReduced(op); | ||
@@ -65,2 +68,15 @@ case types.SLICE: return translateSlice(op); | ||
} | ||
function translatePathComponent(path) { | ||
switch (path.type) { | ||
case types.ALT: return translateAlt(path); | ||
case types.INV: return translateInv(path); | ||
case types.LINK: return translateLink(path); | ||
case types.NPS: return translateNps(path); | ||
case types.ONE_OR_MORE_PATH: return translateOneOrMorePath(path); | ||
case types.SEQ: return translateSeq(path); | ||
case types.ZERO_OR_MORE_PATH: return translateZeroOrMorePath(path); | ||
case types.ZERO_OR_ONE_PATH: return translateZeroOrOnePath(path); | ||
} | ||
return null; | ||
} | ||
function translateTerm(term) { | ||
@@ -75,3 +91,3 @@ if (term.termType === 'BlankNode') | ||
result += '@' + lit.language; | ||
if (lit.datatype) | ||
else if (lit.datatype && lit.datatype.value !== 'http://www.w3.org/2001/XMLSchema#string') | ||
result += '^^' + lit.datatype.value; | ||
@@ -88,3 +104,3 @@ return result; | ||
function translateAggregateExpression(expr) { | ||
return { | ||
let result = { | ||
expression: translateExpression(expr.expression), | ||
@@ -95,2 +111,5 @@ type: 'aggregate', | ||
}; | ||
if (expr.separator) | ||
result.separator = expr.separator; | ||
return result; | ||
} | ||
@@ -114,3 +133,3 @@ function translateExistenceExpression(expr) { | ||
function translateOperatorExpression(expr) { | ||
return { | ||
let result = { | ||
type: 'operation', | ||
@@ -120,2 +139,5 @@ operator: expr.operator, | ||
}; | ||
if (result.operator === 'in' || result.operator === 'notin') | ||
result.args = [result.args[0]].concat([result.args.slice(1)]); | ||
return result; | ||
} | ||
@@ -131,5 +153,8 @@ function translateTermExpression(expr) { | ||
function translateBgp(op) { | ||
let patterns = op.patterns.map(translatePattern); | ||
if (patterns.length === 0) | ||
return null; | ||
return { | ||
type: 'bgp', | ||
triples: op.patterns.map(translatePattern) | ||
triples: patterns | ||
}; | ||
@@ -217,2 +242,5 @@ } | ||
function translateMinus(op) { | ||
let patterns = translateOperation(op.right); | ||
if (patterns.type === 'group') | ||
patterns = patterns.patterns; | ||
return flatten([ | ||
@@ -222,3 +250,3 @@ translateOperation(op.left), | ||
type: 'minus', | ||
patterns: translateOperation(op.right) | ||
patterns: patterns | ||
} | ||
@@ -232,2 +260,13 @@ ]); | ||
} | ||
function translatePath(op) { | ||
// TODO: quads back to graph statement | ||
return { | ||
type: 'bgp', | ||
triples: [{ | ||
subject: translateTerm(op.subject), | ||
predicate: translatePathComponent(op.predicate), | ||
object: translateTerm(op.object) | ||
}] | ||
}; | ||
} | ||
function translatePattern(op) { | ||
@@ -255,9 +294,18 @@ // TODO: quads back to graph statement | ||
} | ||
function translateProject(op) { | ||
function translateProject(op, type) { | ||
let result = { | ||
type: 'query', | ||
prefixes: {}, | ||
queryType: 'SELECT', | ||
variables: op.variables.map(translateTerm) | ||
prefixes: {} | ||
}; | ||
if (type === types.PROJECT) { | ||
result.queryType = 'SELECT'; | ||
result.variables = op.variables.map(translateTerm); | ||
} | ||
else if (type === types.ASK) { | ||
result.queryType = 'ASK'; | ||
} | ||
else if (type === types.DESCRIBE) { | ||
result.queryType = 'DESCRIBE'; | ||
result.variables = op.terms.map(translateTerm); | ||
} | ||
// backup values in case of nested queries | ||
@@ -271,3 +319,6 @@ // everything in extend, group, etc. is irrelevant for this project call | ||
context.project = true; | ||
result.where = flatten([translateOperation(op.input)]); | ||
let input = flatten([translateOperation(op.input)]); | ||
if (input.length === 1 && input[0].type === 'group') | ||
input = input[0].patterns; | ||
result.where = input; | ||
let aggregators = {}; | ||
@@ -298,19 +349,20 @@ // these can not reference each other | ||
if (context.order.length > 0) | ||
result.order = context.order.map(translateOperation).map(o => { | ||
if (typeof o === 'string') | ||
return { expression: o }; | ||
return o; | ||
result.order = context.order.map(translateOperation).map(o => ({ expression: o })); | ||
// this needs to happen after the group because it might depend on variables generated there | ||
if (result.variables) { | ||
result.variables = result.variables.map((v) => { | ||
if (extensions[v]) | ||
return { | ||
variable: v, | ||
expression: extensions[v] | ||
}; | ||
return v; | ||
}); | ||
// this needs to happen after the group because it might depend on variables generated there | ||
result.variables = result.variables.map((v) => { | ||
if (extensions[v]) | ||
return { | ||
variable: v, | ||
expression: extensions[v] | ||
}; | ||
return v; | ||
}); | ||
// if the * didn't match any variables this would be empty | ||
if (result.variables.length === 0) | ||
result.variables = ['*']; | ||
} | ||
// convert filter to 'having' if it contains an aggregator variable | ||
// could always convert, but is nicer to use filter when possible | ||
if (result.where[result.where.length - 1].type === 'filter') { | ||
if (result.where.length > 0 && result.where[result.where.length - 1].type === 'filter') { | ||
let filter = result.where[result.where.length - 1]; | ||
@@ -331,3 +383,3 @@ if (objectContainsValues(filter, Object.keys(aggregators))) { | ||
return o.some(e => objectContainsValues(e, vals)); | ||
if (_.isObject(o)) | ||
if (o === Object(o)) | ||
return Object.keys(o).some(key => objectContainsValues(o[key], vals)); | ||
@@ -359,3 +411,2 @@ return vals.indexOf(o) >= 0; | ||
function translateValues(op) { | ||
// TODO: check if undef implementation is actually correct | ||
// TODO: check if handled correctly when outside of select block | ||
@@ -370,6 +421,135 @@ return { | ||
result[s] = translateTerm(binding[s]); | ||
else | ||
result[s] = undefined; | ||
} | ||
return result; | ||
}) | ||
}; | ||
} | ||
// PATH COMPONENTS | ||
function translateAlt(path) { | ||
if (path.left.type === types.NPS || path.right.type === types.NPS) { | ||
let left = translatePathComponent(path.left); | ||
let right = translatePathComponent(path.right); | ||
return { | ||
type: 'path', | ||
pathType: '!', | ||
items: [{ | ||
type: 'path', | ||
pathType: '|', | ||
items: [].concat(left.items[0].items, right.items[0].items) | ||
}] | ||
}; | ||
} | ||
return { | ||
type: 'path', | ||
pathType: '|', | ||
items: [translatePathComponent(path.left), translatePathComponent(path.right)] | ||
}; | ||
} | ||
function translateInv(path) { | ||
if (path.path.type === types.NPS) { | ||
return { | ||
type: 'path', | ||
pathType: '!', | ||
items: [{ | ||
type: 'path', | ||
pathType: '|', | ||
items: [path.iris.map((iri) => { | ||
return { | ||
type: 'path', | ||
pathType: '^', | ||
items: [iri] | ||
}; | ||
})] | ||
}] | ||
}; | ||
} | ||
return { | ||
type: 'path', | ||
pathType: '^', | ||
items: [translatePathComponent(path.path)] | ||
}; | ||
} | ||
function translateLink(path) { | ||
return translateTerm(path.iri); | ||
} | ||
function translateNps(path) { | ||
return { | ||
type: 'path', | ||
pathType: '!', | ||
items: [{ | ||
type: 'path', | ||
pathType: '|', | ||
items: path.iris.map(translateTerm) | ||
}] | ||
}; | ||
} | ||
function translateOneOrMorePath(path) { | ||
return { | ||
type: 'path', | ||
pathType: '+', | ||
items: [translatePathComponent(path.path)] | ||
}; | ||
} | ||
function translateSeq(path) { | ||
return { | ||
type: 'path', | ||
pathType: '/', | ||
items: [translatePathComponent(path.left), translatePathComponent(path.right)] | ||
}; | ||
} | ||
function translateZeroOrMorePath(path) { | ||
return { | ||
type: 'path', | ||
pathType: '*', | ||
items: [translatePathComponent(path.path)] | ||
}; | ||
} | ||
function translateZeroOrOnePath(path) { | ||
return { | ||
type: 'path', | ||
pathType: '?', | ||
items: [translatePathComponent(path.path)] | ||
}; | ||
} | ||
function removeQuads(op) { | ||
return removeQuadsRecursive(op, {}); | ||
} | ||
// remove quads | ||
function removeQuadsRecursive(op, graphs) { | ||
if (Array.isArray(op)) | ||
return op.map(sub => removeQuadsRecursive(sub, graphs)); | ||
if (!op.type) | ||
return op; | ||
if ((op.type === types.PATTERN || op.type === types.PATH) && op.graph && op.graph.value.length > 0) { | ||
graphs[op.graph.value] = op.graph; | ||
return op; | ||
} | ||
let result = {}; | ||
let keyGraphs = {}; | ||
let graphNames = {}; | ||
for (let key of Object.keys(op)) { | ||
result[key] = removeQuadsRecursive(op[key], graphs); | ||
let graphsArray = Object.keys(graphs); | ||
if (graphsArray.length > 0) { | ||
let graphName = graphsArray[0]; | ||
keyGraphs[key] = graphs[graphName]; | ||
graphNames[keyGraphs[key].value] = keyGraphs[key]; | ||
delete graphs[graphName]; | ||
} | ||
} | ||
let graphNameSet = Object.keys(graphNames); | ||
if (graphNameSet.length > 0) { | ||
// also need to create graph statement if we are at the edge of the query | ||
if (graphNameSet.length === 1 && op.type !== types.PROJECT) | ||
graphs[graphNameSet[0]] = graphNames[graphNameSet[0]]; | ||
else { | ||
// multiple graphs, need to create graph objects for them | ||
for (let key of Object.keys(keyGraphs)) | ||
result[key] = { type: 'graph', input: result[key], name: keyGraphs[key] }; | ||
} | ||
} | ||
return result; | ||
} | ||
//# sourceMappingURL=sparql.js.map |
@@ -148,3 +148,3 @@ "use strict"; | ||
if (exp.operator === 'in' || exp.operator === 'notin') | ||
exp.args = [exp.args[0]].concat(exp.args[1]); // sparql.js uses 2 arguments with the second one bing a list | ||
exp.args = [exp.args[0]].concat(exp.args[1]); // sparql.js uses 2 arguments with the second one being a list | ||
return factory.createOperatorExpression(exp.operator, exp.args.map(translateExpression)); | ||
@@ -151,0 +151,0 @@ } |
{ | ||
"name": "sparqlalgebrajs", | ||
"version": "0.6.6", | ||
"version": "0.7.0", | ||
"description": "Convert SPARQL to SPARL algebra", | ||
@@ -16,16 +16,14 @@ "author": "Joachim Van Herwegen", | ||
"rdf-data-model": "^1.0.0", | ||
"rdf-string": "^1.1.0", | ||
"sparqljs": "^1.5.2" | ||
"sparqljs": "^2.0.2" | ||
}, | ||
"devDependencies": { | ||
"@types/lodash": "^4.14.77", | ||
"@types/chai": "^4.1.2", | ||
"@types/lodash.isequal": "^4.5.2", | ||
"@types/minimist": "^1.2.0", | ||
"@types/n3": "0.0.3", | ||
"@types/mocha": "^2.2.48", | ||
"@types/node": "^8.0.50", | ||
"@types/rdf-data-model": "^1.0.1", | ||
"@types/rdf-js": "^1.0.1", | ||
"lodash": "^4.13.1", | ||
"chai": "^4.1.2", | ||
"mocha": "^3.5.3", | ||
"n3": "^0.11.2", | ||
"pre-commit": "^1.2.2", | ||
@@ -32,0 +30,0 @@ "typescript": "^2.5.3" |
@@ -25,2 +25,4 @@ # SPARQL to SPARQL Algebra converter | ||
Translating back to SPARQL can be done with the `toSparql` (or `toSparqlJs`) function. | ||
## Algebra object | ||
@@ -125,2 +127,6 @@ The algebra object contains a `types` object, | ||
no prefixes are used (all uris get expanded) | ||
and the project operation always gets used (even in the case of `SELECT *`). | ||
and the project operation always gets used (even in the case of `SELECT *`). | ||
## A note on tests | ||
Every test consists of a sparql file and a corresponding json file containg the algebra result. | ||
Tests ending with `(quads)` in their name are tested/generated with `quads: true` in the options. |
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
56532
4
11
1403
131
+ Addedsparqljs@2.2.3(transitive)
- Removedrdf-string@^1.1.0
- Removed@rdfjs/types@1.1.2(transitive)
- Removed@types/node@22.9.0(transitive)
- Removedrdf-data-factory@1.1.2(transitive)
- Removedrdf-string@1.6.3(transitive)
- Removedsparqljs@1.5.2(transitive)
- Removedundici-types@6.19.8(transitive)
Updatedsparqljs@^2.0.2