meta-log-db
Version:
Native database package for Meta-Log (ProLog, DataLog, R5RS)
275 lines • 8.86 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SparqlExecutor = void 0;
/**
* Enhanced SPARQL Query Executor
* Executes parsed SPARQL queries with full feature support
*/
class SparqlExecutor {
constructor(triples) {
this.triples = triples;
}
/**
* Execute SPARQL query
*/
async execute(query) {
// Execute WHERE clause patterns
let bindings = this.executePatterns(query.where);
// Apply OPTIONAL patterns
if (query.optional) {
bindings = this.applyOptional(bindings, query.optional);
}
// Apply FILTER expressions
if (query.filters && query.filters.length > 0) {
bindings = this.applyFilters(bindings, query.filters);
}
// Apply DISTINCT
if (query.distinct) {
bindings = this.applyDistinct(bindings);
}
// Apply ORDER BY
if (query.orderBy && query.orderBy.length > 0) {
bindings = this.applyOrderBy(bindings, query.orderBy);
}
// Apply LIMIT and OFFSET
if (query.offset) {
bindings = bindings.slice(query.offset);
}
if (query.limit) {
bindings = bindings.slice(0, query.limit);
}
// Project variables
bindings = this.projectVariables(bindings, query.variables);
return {
results: {
bindings: bindings.map(b => this.formatBinding(b))
}
};
}
/**
* Execute triple patterns
*/
executePatterns(patterns) {
if (patterns.length === 0) {
return [{}];
}
let bindings = [{}];
for (const pattern of patterns) {
const newBindings = [];
for (const binding of bindings) {
const matches = this.matchPattern(pattern, binding);
for (const match of matches) {
newBindings.push({ ...binding, ...match });
}
}
bindings = newBindings;
}
return bindings;
}
/**
* Match a pattern against triples with existing bindings
*/
matchPattern(pattern, existingBindings) {
const subject = this.resolveValue(pattern.subject, existingBindings);
const predicate = this.resolveValue(pattern.predicate, existingBindings);
const object = this.resolveValue(pattern.object, existingBindings);
const matches = [];
const patternTriple = {
subject: subject.startsWith('?') ? undefined : subject,
predicate: predicate.startsWith('?') ? undefined : predicate,
object: object.startsWith('?') ? undefined : object
};
const matchingTriples = this.queryTriples(patternTriple);
for (const triple of matchingTriples) {
const newBinding = {};
if (subject.startsWith('?')) {
newBinding[subject] = triple.subject;
}
if (predicate.startsWith('?')) {
newBinding[predicate] = triple.predicate;
}
if (object.startsWith('?')) {
const objValue = typeof triple.object === 'string' ? triple.object : triple.object.value;
newBinding[object] = objValue;
}
// Check if binding is consistent with existing bindings
let consistent = true;
for (const [key, value] of Object.entries(newBinding)) {
if (existingBindings[key] && existingBindings[key] !== value) {
consistent = false;
break;
}
}
if (consistent) {
matches.push(newBinding);
}
}
return matches;
}
/**
* Resolve variable or literal value
*/
resolveValue(value, bindings) {
if (value.startsWith('?')) {
return bindings[value] || value;
}
return value;
}
/**
* Query triples by pattern
*/
queryTriples(pattern) {
return this.triples.filter(triple => {
if (pattern.subject && triple.subject !== pattern.subject) {
return false;
}
if (pattern.predicate && triple.predicate !== pattern.predicate) {
return false;
}
if (pattern.object) {
const objStr = typeof triple.object === 'string'
? triple.object
: triple.object.value;
if (objStr !== pattern.object) {
return false;
}
}
return true;
});
}
/**
* Apply OPTIONAL patterns
*/
applyOptional(bindings, optionalPatterns) {
const result = [];
for (const binding of bindings) {
const optionalMatches = this.executePatterns(optionalPatterns);
if (optionalMatches.length > 0) {
for (const match of optionalMatches) {
result.push({ ...binding, ...match });
}
}
else {
result.push(binding);
}
}
return result;
}
/**
* Apply FILTER expressions
*/
applyFilters(bindings, filters) {
return bindings.filter(binding => {
return filters.every(filter => this.evaluateFilter(filter, binding));
});
}
/**
* Evaluate a filter expression
*/
evaluateFilter(filter, binding) {
const leftValue = this.resolveValue(filter.left, binding);
const rightValue = filter.right ? this.resolveValue(filter.right, binding) : undefined;
switch (filter.type) {
case 'equals':
return leftValue === rightValue;
case 'notEquals':
return leftValue !== rightValue;
case 'greaterThan':
return parseFloat(leftValue) > parseFloat(rightValue || '0');
case 'lessThan':
return parseFloat(leftValue) < parseFloat(rightValue || '0');
case 'bound':
return binding[filter.left] !== undefined;
case 'regex':
// Simplified regex - full implementation would parse regex properly
return true;
default:
return true;
}
}
/**
* Apply DISTINCT
*/
applyDistinct(bindings) {
const seen = new Set();
const result = [];
for (const binding of bindings) {
const key = JSON.stringify(binding);
if (!seen.has(key)) {
seen.add(key);
result.push(binding);
}
}
return result;
}
/**
* Apply ORDER BY
*/
applyOrderBy(bindings, orderBy) {
return [...bindings].sort((a, b) => {
for (const order of orderBy) {
const aValue = a[order.variable] || '';
const bValue = b[order.variable] || '';
let comparison = 0;
if (aValue < bValue)
comparison = -1;
else if (aValue > bValue)
comparison = 1;
if (order.direction === 'DESC') {
comparison = -comparison;
}
if (comparison !== 0) {
return comparison;
}
}
return 0;
});
}
/**
* Project variables (SELECT clause)
*/
projectVariables(bindings, variables) {
if (variables.includes('*')) {
return bindings;
}
return bindings.map(binding => {
const projected = {};
for (const variable of variables) {
if (binding[variable]) {
projected[variable] = binding[variable];
}
}
return projected;
});
}
/**
* Format binding for output
*/
formatBinding(binding) {
const formatted = {};
for (const [key, value] of Object.entries(binding)) {
formatted[key] = {
value,
type: this.inferType(value)
};
}
return formatted;
}
/**
* Infer RDF type from value
*/
inferType(value) {
if (value.startsWith('http://') || value.startsWith('https://') || value.startsWith('<')) {
return 'uri';
}
if (value.startsWith('"') && value.endsWith('"')) {
return 'literal';
}
if (/^-?\d+$/.test(value)) {
return 'typed-literal';
}
return 'literal';
}
}
exports.SparqlExecutor = SparqlExecutor;
//# sourceMappingURL=sparql-executor.js.map