aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-interpreter
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store-query-interpreter')
-rw-r--r--subprojects/store-query-interpreter/src/main/java/tools/refinery/store/query/interpreter/internal/pquery/Dnf2PQuery.java101
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/FunctionalQueryTest.java1
-rw-r--r--subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/LeftJoinTest.java129
3 files changed, 187 insertions, 44 deletions
diff --git a/subprojects/store-query-interpreter/src/main/java/tools/refinery/store/query/interpreter/internal/pquery/Dnf2PQuery.java b/subprojects/store-query-interpreter/src/main/java/tools/refinery/store/query/interpreter/internal/pquery/Dnf2PQuery.java
index 24205cf4..4d30f998 100644
--- a/subprojects/store-query-interpreter/src/main/java/tools/refinery/store/query/interpreter/internal/pquery/Dnf2PQuery.java
+++ b/subprojects/store-query-interpreter/src/main/java/tools/refinery/store/query/interpreter/internal/pquery/Dnf2PQuery.java
@@ -1,17 +1,31 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
6package tools.refinery.store.query.interpreter.internal.pquery; 6package tools.refinery.store.query.interpreter.internal.pquery;
7 7
8import tools.refinery.interpreter.matchers.backend.IQueryBackendFactory;
9import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint;
10import tools.refinery.interpreter.matchers.context.IInputKey;
11import tools.refinery.interpreter.matchers.psystem.PBody;
12import tools.refinery.interpreter.matchers.psystem.PVariable;
13import tools.refinery.interpreter.matchers.psystem.aggregations.BoundAggregator;
14import tools.refinery.interpreter.matchers.psystem.aggregations.IMultisetAggregationOperator;
15import tools.refinery.interpreter.matchers.psystem.annotations.PAnnotation;
8import tools.refinery.interpreter.matchers.psystem.annotations.ParameterReference; 16import tools.refinery.interpreter.matchers.psystem.annotations.ParameterReference;
9import tools.refinery.interpreter.matchers.psystem.basicdeferred.*; 17import tools.refinery.interpreter.matchers.psystem.basicdeferred.*;
10import tools.refinery.interpreter.matchers.psystem.basicenumerables.*;
11import tools.refinery.interpreter.matchers.psystem.basicenumerables.Connectivity; 18import tools.refinery.interpreter.matchers.psystem.basicenumerables.Connectivity;
19import tools.refinery.interpreter.matchers.psystem.basicenumerables.*;
20import tools.refinery.interpreter.matchers.psystem.queries.PParameter;
21import tools.refinery.interpreter.matchers.psystem.queries.PParameterDirection;
22import tools.refinery.interpreter.matchers.psystem.queries.PQuery;
23import tools.refinery.interpreter.matchers.tuple.Tuple;
24import tools.refinery.interpreter.matchers.tuple.Tuples;
12import tools.refinery.store.query.Constraint; 25import tools.refinery.store.query.Constraint;
13import tools.refinery.store.query.dnf.Dnf; 26import tools.refinery.store.query.dnf.Dnf;
14import tools.refinery.store.query.dnf.DnfClause; 27import tools.refinery.store.query.dnf.DnfClause;
28import tools.refinery.store.query.dnf.FunctionalDependency;
15import tools.refinery.store.query.dnf.SymbolicParameter; 29import tools.refinery.store.query.dnf.SymbolicParameter;
16import tools.refinery.store.query.literal.*; 30import tools.refinery.store.query.literal.*;
17import tools.refinery.store.query.term.ConstantTerm; 31import tools.refinery.store.query.term.ConstantTerm;
@@ -20,19 +34,6 @@ import tools.refinery.store.query.term.StatelessAggregator;
20import tools.refinery.store.query.term.Variable; 34import tools.refinery.store.query.term.Variable;
21import tools.refinery.store.query.view.AnySymbolView; 35import tools.refinery.store.query.view.AnySymbolView;
22import tools.refinery.store.util.CycleDetectingMapper; 36import tools.refinery.store.util.CycleDetectingMapper;
23import tools.refinery.interpreter.matchers.backend.IQueryBackendFactory;
24import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint;
25import tools.refinery.interpreter.matchers.context.IInputKey;
26import tools.refinery.interpreter.matchers.psystem.PBody;
27import tools.refinery.interpreter.matchers.psystem.PVariable;
28import tools.refinery.interpreter.matchers.psystem.aggregations.BoundAggregator;
29import tools.refinery.interpreter.matchers.psystem.aggregations.IMultisetAggregationOperator;
30import tools.refinery.interpreter.matchers.psystem.annotations.PAnnotation;
31import tools.refinery.interpreter.matchers.psystem.queries.PParameter;
32import tools.refinery.interpreter.matchers.psystem.queries.PParameterDirection;
33import tools.refinery.interpreter.matchers.psystem.queries.PQuery;
34import tools.refinery.interpreter.matchers.tuple.Tuple;
35import tools.refinery.interpreter.matchers.tuple.Tuples;
36 37
37import java.util.ArrayList; 38import java.util.ArrayList;
38import java.util.HashMap; 39import java.util.HashMap;
@@ -79,15 +80,7 @@ public class Dnf2PQuery {
79 pQuery.setParameters(parameterList); 80 pQuery.setParameters(parameterList);
80 81
81 for (var functionalDependency : dnfQuery.getFunctionalDependencies()) { 82 for (var functionalDependency : dnfQuery.getFunctionalDependencies()) {
82 var functionalDependencyAnnotation = new PAnnotation("FunctionalDependency"); 83 var functionalDependencyAnnotation = getFunctionalDependencyAnnotation(functionalDependency);
83 for (var forEachVariable : functionalDependency.forEach()) {
84 var reference = new ParameterReference(forEachVariable.getUniqueName());
85 functionalDependencyAnnotation.addAttribute("forEach", reference);
86 }
87 for (var uniqueVariable : functionalDependency.unique()) {
88 var reference = new ParameterReference(uniqueVariable.getUniqueName());
89 functionalDependencyAnnotation.addAttribute("unique", reference);
90 }
91 pQuery.addAnnotation(functionalDependencyAnnotation); 84 pQuery.addAnnotation(functionalDependencyAnnotation);
92 } 85 }
93 86
@@ -108,26 +101,33 @@ public class Dnf2PQuery {
108 return pQuery; 101 return pQuery;
109 } 102 }
110 103
111 private void translateLiteral(Literal literal, PBody body) { 104 private static PAnnotation getFunctionalDependencyAnnotation(FunctionalDependency<Variable> functionalDependency) {
112 if (literal instanceof EquivalenceLiteral equivalenceLiteral) { 105 var functionalDependencyAnnotation = new PAnnotation("FunctionalDependency");
113 translateEquivalenceLiteral(equivalenceLiteral, body); 106 for (var forEachVariable : functionalDependency.forEach()) {
114 } else if (literal instanceof CallLiteral callLiteral) { 107 var reference = new ParameterReference(forEachVariable.getUniqueName());
115 translateCallLiteral(callLiteral, body); 108 functionalDependencyAnnotation.addAttribute("forEach", reference);
116 } else if (literal instanceof ConstantLiteral constantLiteral) { 109 }
117 translateConstantLiteral(constantLiteral, body); 110 for (var uniqueVariable : functionalDependency.unique()) {
118 } else if (literal instanceof AssignLiteral<?> assignLiteral) { 111 var reference = new ParameterReference(uniqueVariable.getUniqueName());
119 translateAssignLiteral(assignLiteral, body); 112 functionalDependencyAnnotation.addAttribute("unique", reference);
120 } else if (literal instanceof CheckLiteral checkLiteral) {
121 translateCheckLiteral(checkLiteral, body);
122 } else if (literal instanceof CountLiteral countLiteral) {
123 translateCountLiteral(countLiteral, body);
124 } else if (literal instanceof AggregationLiteral<?, ?> aggregationLiteral) {
125 translateAggregationLiteral(aggregationLiteral, body);
126 } else if (literal instanceof RepresentativeElectionLiteral representativeElectionLiteral) {
127 translateRepresentativeElectionLiteral(representativeElectionLiteral, body);
128 } else {
129 throw new IllegalArgumentException("Unknown literal: " + literal.toString());
130 } 113 }
114 return functionalDependencyAnnotation;
115 }
116
117 private void translateLiteral(Literal literal, PBody body) {
118 switch (literal) {
119 case EquivalenceLiteral equivalenceLiteral -> translateEquivalenceLiteral(equivalenceLiteral, body);
120 case CallLiteral callLiteral -> translateCallLiteral(callLiteral, body);
121 case ConstantLiteral constantLiteral -> translateConstantLiteral(constantLiteral, body);
122 case AssignLiteral<?> assignLiteral -> translateAssignLiteral(assignLiteral, body);
123 case CheckLiteral checkLiteral -> translateCheckLiteral(checkLiteral, body);
124 case CountLiteral countLiteral -> translateCountLiteral(countLiteral, body);
125 case AggregationLiteral<?, ?> aggregationLiteral -> translateAggregationLiteral(aggregationLiteral, body);
126 case LeftJoinLiteral<?> leftJoinLiteral -> translateLeftJoinLiteral(leftJoinLiteral, body);
127 case RepresentativeElectionLiteral representativeElectionLiteral ->
128 translateRepresentativeElectionLiteral(representativeElectionLiteral, body);
129 case null, default -> throw new IllegalArgumentException("Unknown literal: " + literal);
130 }
131 } 131 }
132 132
133 private void translateEquivalenceLiteral(EquivalenceLiteral equivalenceLiteral, PBody body) { 133 private void translateEquivalenceLiteral(EquivalenceLiteral equivalenceLiteral, PBody body) {
@@ -244,6 +244,21 @@ public class Dnf2PQuery {
244 aggregatedColumn); 244 aggregatedColumn);
245 } 245 }
246 246
247 private <T> void translateLeftJoinLiteral(LeftJoinLiteral<T> leftJoinLiteral, PBody body) {
248 var wrappedCall = wrapperFactory.maybeWrapConstraint(leftJoinLiteral);
249 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
250 var placeholderVariable = body.getOrCreateVariableByName(
251 leftJoinLiteral.getPlaceholderVariable().getUniqueName());
252 var optionalColumn = substitution.invertIndex().get(placeholderVariable);
253 if (optionalColumn == null) {
254 throw new IllegalStateException("Placeholder variable %s not found in substitution %s"
255 .formatted(placeholderVariable, substitution));
256 }
257 var resultVariable = body.getOrCreateVariableByName(leftJoinLiteral.getResultVariable().getUniqueName());
258 new LeftJoinConstraint(body, substitution, wrappedCall.pattern(), resultVariable, optionalColumn,
259 leftJoinLiteral.getDefaultValue());
260 }
261
247 private void translateRepresentativeElectionLiteral(RepresentativeElectionLiteral literal, PBody body) { 262 private void translateRepresentativeElectionLiteral(RepresentativeElectionLiteral literal, PBody body) {
248 var substitution = translateSubstitution(literal.getArguments(), body); 263 var substitution = translateSubstitution(literal.getArguments(), body);
249 var pattern = wrapConstraintWithIdentityArguments(literal.getTarget()); 264 var pattern = wrapConstraintWithIdentityArguments(literal.getTarget());
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/FunctionalQueryTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/FunctionalQueryTest.java
index b6c96676..ca1512d8 100644
--- a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/FunctionalQueryTest.java
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/FunctionalQueryTest.java
@@ -554,7 +554,6 @@ class FunctionalQueryTest {
554 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 554 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
555 555
556 queryEngine.flushChanges(); 556 queryEngine.flushChanges();
557 queryEngine.flushChanges();
558 assertNullableResults(Map.of( 557 assertNullableResults(Map.of(
559 Tuple.of(0), Optional.of(25), 558 Tuple.of(0), Optional.of(25),
560 Tuple.of(1), Optional.of(32), 559 Tuple.of(1), Optional.of(32),
diff --git a/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/LeftJoinTest.java b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/LeftJoinTest.java
new file mode 100644
index 00000000..4c849e9d
--- /dev/null
+++ b/subprojects/store-query-interpreter/src/test/java/tools/refinery/store/query/interpreter/LeftJoinTest.java
@@ -0,0 +1,129 @@
1/*
2 * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.interpreter;
7
8import org.junit.jupiter.api.Test;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.dnf.Query;
12import tools.refinery.store.query.term.int_.IntTerms;
13import tools.refinery.store.query.view.AnySymbolView;
14import tools.refinery.store.query.view.FunctionView;
15import tools.refinery.store.query.view.KeyOnlyView;
16import tools.refinery.store.representation.Symbol;
17import tools.refinery.store.tuple.Tuple;
18
19import java.util.List;
20import java.util.Map;
21import java.util.Optional;
22
23import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertNullableResults;
24import static tools.refinery.store.query.interpreter.tests.QueryAssertions.assertResults;
25
26class LeftJoinTest {
27 private static final Symbol<Boolean> person = Symbol.of("Person", 1);
28 private static final Symbol<Integer> age = Symbol.of("age", 1, Integer.class);
29 private static final AnySymbolView personView = new KeyOnlyView<>(person);
30 private static final FunctionView<Integer> ageView = new FunctionView<>(age);
31
32 @Test
33 void unarySymbolTest() {
34 var query = Query.of("Query", Integer.class, (builder, p1, output) -> builder
35 .clause(
36 personView.call(p1),
37 output.assign(ageView.leftJoin(18, p1))
38 ));
39
40 var store = ModelStore.builder()
41 .symbols(person, age)
42 .with(QueryInterpreterAdapter.builder()
43 .queries(query))
44 .build();
45
46 var model = store.createEmptyModel();
47 var personInterpretation = model.getInterpretation(person);
48 var ageInterpretation = model.getInterpretation(age);
49 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
50 var queryResultSet = queryEngine.getResultSet(query);
51
52 personInterpretation.put(Tuple.of(0), true);
53 personInterpretation.put(Tuple.of(1), true);
54 personInterpretation.put(Tuple.of(2), true);
55
56 ageInterpretation.put(Tuple.of(2), 24);
57
58 queryEngine.flushChanges();
59 assertNullableResults(Map.of(
60 Tuple.of(0), Optional.of(18),
61 Tuple.of(1), Optional.of(18),
62 Tuple.of(2), Optional.of(24),
63 Tuple.of(3), Optional.empty()
64 ), queryResultSet);
65
66 personInterpretation.put(Tuple.of(0), false);
67
68 ageInterpretation.put(Tuple.of(1), 20);
69 ageInterpretation.put(Tuple.of(2), null);
70
71 queryEngine.flushChanges();
72 assertNullableResults(Map.of(
73 Tuple.of(0), Optional.empty(),
74 Tuple.of(1), Optional.of(20),
75 Tuple.of(2), Optional.of(18),
76 Tuple.of(3), Optional.empty()
77 ), queryResultSet);
78 }
79
80 @Test
81 void unarySymbolWithAssignmentTest() {
82 // Tests an edge case where the outer joined variable is already bound in the query plan.
83 var query = Query.of("Query", (builder, p1) -> builder
84 .clause(Integer.class, v1 -> List.of(
85 personView.call(p1),
86 v1.assign(IntTerms.constant(18)),
87 v1.assign(ageView.leftJoin(18, p1))
88 )));
89
90 var store = ModelStore.builder()
91 .symbols(person, age)
92 .with(QueryInterpreterAdapter.builder()
93 .queries(query))
94 .build();
95
96 var model = store.createEmptyModel();
97 var personInterpretation = model.getInterpretation(person);
98 var ageInterpretation = model.getInterpretation(age);
99 var queryEngine = model.getAdapter(ModelQueryAdapter.class);
100 var queryResultSet = queryEngine.getResultSet(query);
101
102 personInterpretation.put(Tuple.of(0), true);
103 personInterpretation.put(Tuple.of(1), true);
104 personInterpretation.put(Tuple.of(2), true);
105
106 ageInterpretation.put(Tuple.of(2), 24);
107
108 queryEngine.flushChanges();
109 assertResults(Map.of(
110 Tuple.of(0), true,
111 Tuple.of(1), true,
112 Tuple.of(2), false,
113 Tuple.of(3), false
114 ), queryResultSet);
115
116 personInterpretation.put(Tuple.of(0), false);
117
118 ageInterpretation.put(Tuple.of(1), 20);
119 ageInterpretation.put(Tuple.of(2), null);
120
121 queryEngine.flushChanges();
122 assertResults(Map.of(
123 Tuple.of(0), false,
124 Tuple.of(1), false,
125 Tuple.of(2), true,
126 Tuple.of(3), false
127 ), queryResultSet);
128 }
129}