diff --git a/src/main/java/fr/inria/corese/core/next/query/impl/engine/storage/StorageBindingEnvironment.java b/src/main/java/fr/inria/corese/core/next/query/impl/engine/storage/StorageBindingEnvironment.java new file mode 100644 index 000000000..f5a7fe369 --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/query/impl/engine/storage/StorageBindingEnvironment.java @@ -0,0 +1,228 @@ +package fr.inria.corese.core.next.query.impl.engine.storage; + +import fr.inria.corese.core.next.data.api.model.DatatypeValue; +import fr.inria.corese.core.next.query.impl.engine.eval.ApproximateSearchEnv; +import fr.inria.corese.core.next.query.impl.engine.eval.Eval; +import fr.inria.corese.core.next.query.impl.engine.event.KgramEventDispatcher; +import fr.inria.corese.core.next.query.impl.engine.event.ProcessVisitor; +import fr.inria.corese.core.next.query.impl.engine.model.BindingContext; +import fr.inria.corese.core.next.query.impl.engine.model.Edge; +import fr.inria.corese.core.next.query.impl.engine.model.Expr; +import fr.inria.corese.core.next.query.impl.engine.model.Node; +import fr.inria.corese.core.next.query.impl.engine.path.Path; +import fr.inria.corese.core.next.query.impl.engine.pattern.Exp; +import fr.inria.corese.core.next.query.impl.engine.pattern.Query; +import fr.inria.corese.core.next.query.impl.engine.solution.Mapping; +import fr.inria.corese.core.next.query.impl.engine.solution.Mappings; +import fr.inria.corese.core.next.query.impl.engine.spi.Environment; + +import java.util.List; +import java.util.Map; + +/** + * Environment overlay used while joining BGP edges. + * + *
Local bindings produced by earlier triple patterns take precedence over the delegate + * environment, which lets later patterns see already-bound variables.
+ */ +final class StorageBindingEnvironment implements Environment { + + private final Environment delegate; + private final StorageBindingSet bindings; + + StorageBindingEnvironment(Environment delegate, StorageBindingSet bindings) { + this.delegate = delegate; + this.bindings = bindings; + } + + @Override + public Node getNode(Node queryNode) { + Node node = bindings.get(queryNode); + if (node != null) { + return node; + } + return delegate == null ? null : delegate.getNode(queryNode); + } + + @Override + public boolean isBound(Node queryNode) { + return getNode(queryNode) != null; + } + + @Override + public Query getQuery() { + return delegate == null ? null : delegate.getQuery(); + } + + @Override + public BindingContext getBind() { + return delegate == null ? null : delegate.getBind(); + } + + @Override + public void setBind(BindingContext bindingContext) { + if (delegate != null) { + delegate.setBind(bindingContext); + } + } + + @Override + public boolean hasBind() { + return delegate != null && delegate.hasBind(); + } + + @Override + public Node getGraphNode() { + return delegate == null ? null : delegate.getGraphNode(); + } + + @Override + public Node getNode(Expr varExpr) { + return delegate == null ? null : delegate.getNode(varExpr); + } + + @Override + public Node getNode(String label) { + return delegate == null ? null : delegate.getNode(label); + } + + @Override + public Node getQueryNode(int n) { + return delegate == null ? null : delegate.getQueryNode(n); + } + + @Override + public Node getQueryNode(String label) { + return delegate == null ? null : delegate.getQueryNode(label); + } + + @Override + public int pathLength(Node queryNode) { + return delegate == null ? 0 : delegate.pathLength(queryNode); + } + + @Override + public Path getPath(Node queryNode) { + return delegate == null ? null : delegate.getPath(queryNode); + } + + @Override + public int count() { + return delegate == null ? 0 : delegate.count(); + } + + @Override + public KgramEventDispatcher getEventManager() { + return delegate == null ? null : delegate.getEventManager(); + } + + @Override + public Object getObject() { + return delegate == null ? null : delegate.getObject(); + } + + @Override + public void setObject(Object object) { + if (delegate != null) { + delegate.setObject(object); + } + } + + @Override + public Exp getExp() { + return delegate == null ? null : delegate.getExp(); + } + + @Override + public void setExp(Exp exp) { + if (delegate != null) { + delegate.setExp(exp); + } + } + + @Override + public MapThe 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 + * into storage-layer {@link fr.inria.corese.core.next.storage.api.model.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.
*/ public final class StorageManagerProducer extends ProducerDefault { @@ -63,7 +53,8 @@ public IterableUnbound KGRAM variables become {@code null} components, which the storage API
- * interprets as wildcards. Impossible RDF combinations, such as a literal subject
- * or predicate, are represented as an empty-result pattern.
- *
- * @param graphNode current GRAPH node, or {@code null} for the default graph context
- * @param from active FROM/FROM NAMED restriction computed by KGRAM
- * @param queryEdge KGRAM triple pattern to translate
- * @param environment current bindings used to resolve already-bound variables
- * @return a storage query pattern, or an empty-result marker when no RDF statement can match
- */
- private StorageQueryPattern queryPattern(Node graphNode, List KGRAM stores variable predicates in {@link Edge#getEdgeVariable()}; {@link Edge#getEdgeNode()}
- * may only be the technical root-property placeholder for {@code ?s ?p ?o} patterns.
- *
- * @param queryEdge KGRAM edge whose predicate must be read
- * @return the predicate variable when present, otherwise the constant predicate node
- */
- private Node predicateQueryNode(Edge queryEdge) {
- return queryEdge.getEdgeVariable() == null ? queryEdge.getEdgeNode() : queryEdge.getEdgeVariable();
- }
-
- /**
- * Selects the storage contexts for the active graph pattern.
- *
- * SPARQL evaluates {@code GRAPH Constants resolve to themselves, bound variables resolve to their current target node,
- * and unbound variables resolve to {@code null}.
- *
- * @param queryNode KGRAM query node to resolve
- * @param environment current bindings, or {@code null}
- * @return resolved node, or {@code null} when the node is unbound
- */
- private Node resolve(Node queryNode, Environment environment) {
- if (queryNode == null) {
- return null;
- }
- if (queryNode.isConstant()) {
- return queryNode;
- }
- return environment == null ? null : environment.getNode(queryNode);
- }
-
/**
* Extends partial bindings with the matches of one triple pattern.
*
- * 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.
+ * Each input binding is exposed through a layered {@link StorageBindingEnvironment}, so already
+ * bound variables are pushed into {@link StoragePatternTranslator#translate} before
+ * querying storage. This avoids using {@code null} to represent impossible RDF patterns while still
- * keeping a non-null placeholder pattern for the record state.
- */
- private record StorageQueryPattern(StatementPattern statementPattern, boolean noMatch) {
-
- private static StorageQueryPattern of(StatementPattern statementPattern) {
- return new StorageQueryPattern(statementPattern, false);
- }
-
- private static StorageQueryPattern emptyResult() {
- return new StorageQueryPattern(StatementPattern.matchAll(), true);
- }
- }
-
- /**
- * Storage contexts selected for a query pattern.
- *
- * An empty context list means "all contexts" for the storage API; {@code noMatch}
- * distinguishes this from a graph/dataset restriction that cannot produce results.
- */
- private record ContextSelection(List Local bindings produced by earlier triple patterns take precedence over the delegate
- * environment, which lets later patterns see already-bound variables.
- */
- private static final class BindingEnvironment implements Environment {
-
- private final Environment delegate;
- private final BindingSet bindings;
-
- private BindingEnvironment(Environment delegate, BindingSet bindings) {
- this.delegate = delegate;
- this.bindings = bindings;
- }
-
- @Override
- public Node getNode(Node queryNode) {
- Node node = bindings.get(queryNode);
- if (node != null) {
- return node;
- }
- return delegate == null ? null : delegate.getNode(queryNode);
- }
-
- @Override
- public Query getQuery() {
- return delegate == null ? null : delegate.getQuery();
- }
-
- @Override
- public BindingContext getBind() {
- return delegate == null ? null : delegate.getBind();
- }
-
- @Override
- public void setBind(BindingContext bindingContext) {
- if (delegate != null) {
- delegate.setBind(bindingContext);
- }
- }
-
- @Override
- public boolean hasBind() {
- return delegate != null && delegate.hasBind();
- }
-
- @Override
- public Node getGraphNode() {
- return delegate == null ? null : delegate.getGraphNode();
- }
-
- @Override
- public Node getNode(Expr varExpr) {
- return delegate == null ? null : delegate.getNode(varExpr);
- }
-
- @Override
- public Node getNode(String label) {
- return delegate == null ? null : delegate.getNode(label);
- }
-
- @Override
- public Node getQueryNode(int n) {
- return delegate == null ? null : delegate.getQueryNode(n);
- }
-
- @Override
- public Node getQueryNode(String label) {
- return delegate == null ? null : delegate.getQueryNode(label);
- }
-
- @Override
- public boolean isBound(Node queryNode) {
- return getNode(queryNode) != null;
- }
-
- @Override
- public int pathLength(Node queryNode) {
- return delegate == null ? 0 : delegate.pathLength(queryNode);
- }
-
- @Override
- public Path getPath(Node queryNode) {
- return delegate == null ? null : delegate.getPath(queryNode);
- }
-
- @Override
- public int count() {
- return delegate == null ? 0 : delegate.count();
- }
-
- @Override
- public KgramEventDispatcher getEventManager() {
- return delegate == null ? null : delegate.getEventManager();
- }
-
- @Override
- public Object getObject() {
- return delegate == null ? null : delegate.getObject();
- }
-
- @Override
- public void setObject(Object object) {
- if (delegate != null) {
- delegate.setObject(object);
- }
- }
-
- @Override
- public Exp getExp() {
- return delegate == null ? null : delegate.getExp();
- }
-
- @Override
- public void setExp(Exp exp) {
- if (delegate != null) {
- delegate.setExp(exp);
- }
- }
-
- @Override
- public java.util.Map This avoids using {@code null} to represent impossible RDF patterns while still
+ * keeping a non-null placeholder pattern for the record state. An empty context list means "all contexts" for the storage API; {@code noMatch}
+ * distinguishes this from a graph/dataset restriction that cannot produce results. Unbound KGRAM variables become {@code null} components, which the storage API
+ * interprets as wildcards. Impossible RDF combinations, such as a literal subject
+ * or predicate, are represented as an empty-result pattern. KGRAM stores variable predicates in {@link Edge#getEdgeVariable()}; {@link Edge#getEdgeNode()}
+ * may only be the technical root-property placeholder for {@code ?s ?p ?o} patterns. SPARQL evaluates {@code GRAPH Constants resolve to themselves, bound variables resolve to their current target node,
+ * and unbound variables resolve to {@code null}.