Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package fr.inria.corese.core.next.query.impl.sparql.execution;

import fr.inria.corese.core.next.query.api.exception.QueryEvaluationException;
import fr.inria.corese.core.next.query.api.result.TupleQueryResult;
import fr.inria.corese.core.next.query.impl.parser.SparqlParser;
import fr.inria.corese.core.next.query.impl.result.CoreseTupleQueryResult;
import fr.inria.corese.core.next.query.impl.sparql.ast.AskQueryAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.QueryAst;
import fr.inria.corese.core.next.query.impl.sparql.ast.SelectQueryAst;
import fr.inria.corese.core.next.query.impl.sparql.bridge.CoreseAstQueryBuilder;
import fr.inria.corese.core.next.query.kgram.core.Eval;
import fr.inria.corese.core.next.query.kgram.core.Mappings;
import fr.inria.corese.core.next.query.kgram.core.Query;
import fr.inria.corese.core.next.query.kgram.core.SparqlException;
import fr.inria.corese.core.next.query.kgram.execution.RdfTermMatcher;
import fr.inria.corese.core.next.query.kgram.execution.SparqlKgramEvaluator;
import fr.inria.corese.core.next.query.kgram.tool.StorageManagerProducer;
import fr.inria.corese.core.next.storagemanager.api.StorageManager;

import java.util.Objects;

/**
* Internal executor for the first autonomous Corese-next SPARQL query path.
*
* <p>This class is intentionally small and transitional. It validates the
* autonomous execution path:</p>
*
* <pre>
* SPARQL string -> next parser -> next AST -> next KGRAM -> StorageManagerProducer -> StorageManager
* </pre>
*
* <p>Only simple SELECT and ASK execution are supported here. A stable public
* query API, graph query execution, updates, and advanced SPARQL 1.1 runtime
* features still need to be designed separately.</p>
*/
public final class NextSparqlPipelineExecutor {

private final StorageManager storage;
private final SparqlParser parser;
private final CoreseAstQueryBuilder queryBuilder;

/**
* Creates an executor backed by the given next storage manager.
*
* @param storage storage manager used by {@link StorageManagerProducer} to
* read RDF statements during KGRAM evaluation
*/
public NextSparqlPipelineExecutor(StorageManager storage) {
this(storage, new SparqlParser(), new CoreseAstQueryBuilder());
}

/**
* Creates an executor with explicit collaborators.
*
* <p>This constructor is package-private so tests can inject parser or bridge
* variants without exposing these transitional wiring details as public API.</p>
*/
NextSparqlPipelineExecutor(
StorageManager storage,
SparqlParser parser,
CoreseAstQueryBuilder queryBuilder) {
this.storage = Objects.requireNonNull(storage, "storage");
this.parser = Objects.requireNonNull(parser, "parser");
this.queryBuilder = Objects.requireNonNull(queryBuilder, "queryBuilder");
}

/**
* Evaluates a SELECT query through the autonomous next pipeline.
*
* @param sparql SPARQL query string to parse and evaluate
* @return tuple result backed by the KGRAM mappings produced from next storage
* @throws IllegalArgumentException when the query is not a SELECT query
* @throws QueryEvaluationException when KGRAM evaluation fails
*/
public TupleQueryResult evaluateTuple(String sparql) {
QueryAst ast = parser.parse(sparql);
if (!(ast instanceof SelectQueryAst select)) {
throw new IllegalArgumentException("Tuple evaluation requires a SELECT query, got: "
+ ast.getClass().getSimpleName());
}
return new CoreseTupleQueryResult(evaluate(queryBuilder.toNextQuery(select)));
}

/**
* Evaluates an ASK query through the autonomous next pipeline.
*
* @param sparql SPARQL query string to parse and evaluate
* @return {@code true} when at least one mapping matches the ASK pattern
* @throws IllegalArgumentException when the query is not an ASK query
* @throws QueryEvaluationException when KGRAM evaluation fails
*/
public boolean evaluateBoolean(String sparql) {
QueryAst ast = parser.parse(sparql);
if (!(ast instanceof AskQueryAst ask)) {
throw new IllegalArgumentException("Boolean evaluation requires an ASK query, got: "
+ ast.getClass().getSimpleName());
}
return evaluate(queryBuilder.toNextQuery(ask)).size() > 0;
}

private Mappings evaluate(Query query) {
try {
Eval eval = Eval.create(
new StorageManagerProducer(storage),
new SparqlKgramEvaluator(),
new RdfTermMatcher());
return eval.query(query);
} catch (SparqlException e) {
throw new QueryEvaluationException("Failed to evaluate query with the next pipeline: " + e.getMessage(), e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package fr.inria.corese.core.next.query.kgram.execution;

import fr.inria.corese.core.next.query.kgram.api.core.Edge;
import fr.inria.corese.core.next.query.kgram.api.core.Node;
import fr.inria.corese.core.next.query.kgram.api.query.Environment;
import fr.inria.corese.core.next.query.kgram.api.query.Matcher;

/**
* Matcher for RDF term comparisons during KGRAM graph pattern execution.
*
* <p>This implementation follows the strict RDF-term matching needed by SPARQL
* graph pattern evaluation: constants must match the candidate RDF term,
* unbound variables can bind to any candidate term, and already-bound variables
* must keep the same value.</p>
*
* <p>It intentionally does not perform entailment, type subsumption, or
* {@code owl:sameAs} expansion. Those semantics should be added explicitly,
* either by extending this matcher or by introducing a dedicated matcher.</p>
*/
public final class RdfTermMatcher implements Matcher {

private int mode = Matcher.UNDEF;

@Override
public boolean match(Edge query, Edge target, Environment environment) {
return match(query.getNode(0), target.getNode(0), environment)
&& match(query.getNode(1), target.getNode(1), environment)
&& match(predicateNode(query), target.getEdgeNode(), environment);
}

@Override
public boolean match(Node query, Node target, Environment environment) {
// Null is an absent KGRAM node, not a wildcard. Wildcards are represented by unbound variables.
if (query == null || target == null) {
return query == target;
}
if (query.isVariable()) {
Node bound = environment == null ? null : environment.getNode(query);
return bound == null || bound.match(target);
}
return query.match(target);
}

@Override
public boolean same(Node queryNode, Node left, Node right, Environment environment) {
// queryNode is unused for strict RDF-term equality, but richer matchers may need it.
return left != null && left.same(right);
}

@Override
public int getMode() {
return mode;
}

@Override
public void setMode(int mode) {
this.mode = mode;
}

private static Node predicateNode(Edge edge) {
return edge.getEdgeVariable() == null ? edge.getEdgeNode() : edge.getEdgeVariable();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package fr.inria.corese.core.next.query.kgram.execution;

import fr.inria.corese.core.next.query.kgram.api.query.Environment;
import fr.inria.corese.core.next.query.kgram.api.query.Evaluator;
import fr.inria.corese.core.next.query.kgram.api.query.Producer;
import fr.inria.corese.core.next.query.kgram.core.Eval;

/**
* KGRAM evaluator for SPARQL query execution.
*
* <p>KGRAM requires an {@link Evaluator} even for simple basic graph patterns.
* This implementation currently covers expression-free graph pattern execution:
* edge enumeration is delegated to the producer, and RDF term comparison is
* delegated to the matcher.</p>
*
* <p>SPARQL expression features such as FILTER, BIND, and function calls should
* be added here when they enter the supported execution scope, so expression
* evaluation remains part of the same KGRAM runtime path.</p>
*/
public final class SparqlKgramEvaluator implements Evaluator {

private Mode mode = Mode.KGRAM_MODE;

@Override
public Mode getMode() {
return mode;
}

@Override
public void setMode(Mode mode) {
this.mode = mode;
}

@Override
public void setProducer(Producer producer) {
// Expression-free graph pattern execution does not need producer state here.
}

@Override
public void setKGRAM(Eval eval) {
// Eval is driven by the caller for the currently supported execution scope.
}

@Override
public void start(Environment environment) {
// Graph pattern execution currently needs no evaluator-side initialization.
}

@Override
public void finish(Environment environment) {
// Graph pattern execution currently needs no evaluator-side cleanup.
}

@Override
public void init(Environment environment) {
// Expression evaluation state will be initialized here when supported.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Minimal runtime collaborators used to execute Corese-next queries with KGRAM.
*
* <p>This package contains execution components, not parser or storage helpers.
* The current implementations deliberately cover the first autonomous SELECT/ASK
* path and document their unsupported SPARQL features explicitly.</p>
*/
package fr.inria.corese.core.next.query.kgram.execution;
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import fr.inria.corese.core.next.data.api.Value;
import fr.inria.corese.core.next.data.api.ValueFactory;
import fr.inria.corese.core.next.data.impl.temp.CoreseAdaptedValueFactory;
import fr.inria.corese.core.next.query.api.exception.UnsupportedQueryFeatureException;
import fr.inria.corese.core.next.query.kgram.api.core.Edge;
import fr.inria.corese.core.next.query.kgram.api.core.Graph;
import fr.inria.corese.core.next.query.kgram.api.core.Node;
Expand All @@ -24,7 +25,12 @@
import java.util.stream.Stream;

/**
* KGRAM producer backed by the Corese-next {@link StorageManager}.
* KGRAM producer backed by a Corese-next {@link StorageManager}.
*
* <p>The producer is responsible for translating KGRAM graph-pattern requests
* into storage-layer {@link StatementPattern} queries, then adapting returned
* RDF statements back into KGRAM edges. It does not decide the final RDF-term
* matching policy; that remains the matcher responsibility.</p>
*/
public final class StorageManagerProducer extends ProducerDefault {

Expand Down Expand Up @@ -91,7 +97,8 @@ public Iterable<Edge> getEdges(
Node source,
Node start,
int index) {
throw new UnsupportedOperationException("StorageManagerProducer does not support path/regex edge enumeration yet");
throw new UnsupportedQueryFeatureException(
"Property path edge enumeration is not supported yet by StorageManagerProducer");
}

@Override
Expand Down Expand Up @@ -151,15 +158,15 @@ public boolean isBindable(Node node) {
@Override
public Mappings getMappings(Node graphNode, List<Node> from, Exp exp, Environment environment) {
if (!exp.isBGP()) {
throw new UnsupportedOperationException("StorageManagerProducer only supports BGP mappings");
throw new IllegalArgumentException("StorageManagerProducer can only materialize BGP expressions");
}
Comment thread
remiceres marked this conversation as resolved.

List<BindingSet> bindings = new ArrayList<>();
bindings.add(new BindingSet());
for (Exp element : exp) {
if (!element.isEdge()) {
throw new UnsupportedOperationException(
"StorageManagerProducer only supports EDGE expressions inside BGP mappings");
throw new IllegalArgumentException(
"StorageManagerProducer can only materialize EDGE expressions inside BGP mappings");
}
Comment thread
remiceres marked this conversation as resolved.
bindings = join(graphNode, from, element.getEdge(), environment, bindings);
if (bindings.isEmpty()) {
Expand Down Expand Up @@ -338,7 +345,7 @@ private Node resolve(Node queryNode, Environment environment) {
/**
* Extends partial bindings with the matches of one triple pattern.
*
* <p>Each input binding is exposed through a temporary {@link BindingEnvironment}, so already
* <p>Each input binding is exposed through a layered {@link BindingEnvironment}, so already
* bound variables are pushed into {@link #queryPattern(Node, List, Edge, Environment)} before
* querying storage.
*
Expand Down
Loading
Loading