aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-09-26 00:39:23 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-10-03 20:06:52 +0200
commitaac386f0d8c4e4585026b11bfeca20f378f7f261 (patch)
treecad56fc2c88a1abd56258d64b5ce3a16baaff011 /subprojects/store-query-viatra
parentchore: fix some warnings (diff)
downloadrefinery-aac386f0d8c4e4585026b11bfeca20f378f7f261.tar.gz
refinery-aac386f0d8c4e4585026b11bfeca20f378f7f261.tar.zst
refinery-aac386f0d8c4e4585026b11bfeca20f378f7f261.zip
refactor: move viatra into a separate subproject
Diffstat (limited to 'subprojects/store-query-viatra')
-rw-r--r--subprojects/store-query-viatra/build.gradle16
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java132
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RawPatternMatcher.java51
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java43
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraQueryableModel.java219
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java58
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java33
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java44
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java172
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java114
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SimplePQuery.java74
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ModelUpdateListener.java105
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdate.java34
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateBuffer.java46
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateTranslator.java62
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java432
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTransactionTest.java56
17 files changed, 1691 insertions, 0 deletions
diff --git a/subprojects/store-query-viatra/build.gradle b/subprojects/store-query-viatra/build.gradle
new file mode 100644
index 00000000..32a23fe7
--- /dev/null
+++ b/subprojects/store-query-viatra/build.gradle
@@ -0,0 +1,16 @@
1plugins {
2 id 'refinery-java-library'
3}
4
5configurations.testRuntimeClasspath {
6 // VIATRA requires log4j 1.x, but we use log4j-over-slf4j instead
7 exclude group: 'log4j', module: 'log4j'
8}
9
10dependencies {
11 implementation libs.ecore
12 implementation libs.viatra
13 api project(':refinery-store')
14 testImplementation libs.slf4j.simple
15 testImplementation libs.slf4j.log4j
16}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java
new file mode 100644
index 00000000..5a02ca08
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraQueryableModelStore.java
@@ -0,0 +1,132 @@
1package tools.refinery.store.query.viatra;
2
3import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
4import tools.refinery.store.model.ModelDiffCursor;
5import tools.refinery.store.model.ModelStore;
6import tools.refinery.store.model.ModelStoreImpl;
7import tools.refinery.store.model.representation.DataRepresentation;
8import tools.refinery.store.query.QueryableModel;
9import tools.refinery.store.query.QueryableModelStore;
10import tools.refinery.store.query.building.*;
11import tools.refinery.store.query.viatra.internal.ViatraQueryableModel;
12import tools.refinery.store.query.viatra.internal.pquery.DNF2PQuery;
13import tools.refinery.store.query.viatra.internal.RawPatternMatcher;
14import tools.refinery.store.query.viatra.internal.pquery.SimplePQuery;
15import tools.refinery.store.query.view.RelationView;
16
17import java.util.Collections;
18import java.util.HashMap;
19import java.util.Map;
20import java.util.Set;
21
22public class ViatraQueryableModelStore implements QueryableModelStore {
23 protected final ModelStore store;
24 protected final Set<RelationView<?>> relationViews;
25 protected final Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates;
26
27 public ViatraQueryableModelStore(ModelStore store, Set<RelationView<?>> relationViews,
28 Set<DNFPredicate> predicates) {
29 this.store = store;
30 validateViews(store.getDataRepresentations(), relationViews);
31 this.relationViews = Collections.unmodifiableSet(relationViews);
32 validatePredicates(relationViews, predicates);
33 this.predicates = initPredicates(predicates);
34 }
35
36 public ViatraQueryableModelStore(Set<DataRepresentation<?, ?>> dataRepresentations,
37 Set<RelationView<?>> relationViews, Set<DNFPredicate> predicates) {
38 this(new ModelStoreImpl(dataRepresentations), relationViews, predicates);
39 }
40
41 private void validateViews(Set<DataRepresentation<?, ?>> dataRepresentations, Set<RelationView<?>> relationViews) {
42 for (RelationView<?> relationView : relationViews) {
43 if (!dataRepresentations.contains(relationView.getRepresentation())) {
44 throw new IllegalArgumentException(
45 DataRepresentation.class.getSimpleName() + " " + relationView.getStringID() + " added to "
46 + QueryableModelStore.class.getSimpleName() + " without a referred representation.");
47 }
48 }
49 }
50
51 private void validatePredicates(Set<RelationView<?>> relationViews, Set<DNFPredicate> predicates) {
52 for (DNFPredicate dnfPredicate : predicates) {
53 for (DNFAnd clause : dnfPredicate.getClauses()) {
54 for (DNFAtom atom : clause.getConstraints()) {
55 if (atom instanceof RelationAtom relationAtom) {
56 validateRelationAtom(relationViews, dnfPredicate, relationAtom);
57 } else if (atom instanceof PredicateAtom predicateAtom) {
58 validatePredicateAtom(predicates, dnfPredicate, predicateAtom);
59 }
60 }
61 }
62 }
63 }
64
65 private void validateRelationAtom(Set<RelationView<?>> relationViews, DNFPredicate dnfPredicate,
66 RelationAtom relationAtom) {
67 if (!relationViews.contains(relationAtom.view())) {
68 throw new IllegalArgumentException(DNFPredicate.class.getSimpleName() + " "
69 + dnfPredicate.getUniqueName() + " contains reference to a view of "
70 + relationAtom.view().getRepresentation().getName()
71 + " that is not in the model.");
72 }
73 }
74
75 private void validatePredicateAtom(Set<DNFPredicate> predicates, DNFPredicate dnfPredicate,
76 PredicateAtom predicateAtom) {
77 if (!predicates.contains(predicateAtom.getReferred())) {
78 throw new IllegalArgumentException(
79 DNFPredicate.class.getSimpleName() + " " + dnfPredicate.getUniqueName()
80 + " contains reference to a predicate "
81 + predicateAtom.getReferred().getName()
82 + "that is not in the model.");
83 }
84 }
85
86 private Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> initPredicates(Set<DNFPredicate> predicates) {
87 Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> result = new HashMap<>();
88 Map<DNFPredicate, SimplePQuery> dnf2PQueryMap = new HashMap<>();
89 for (DNFPredicate dnfPredicate : predicates) {
90 GenericQuerySpecification<RawPatternMatcher> query =
91 DNF2PQuery.translate(dnfPredicate, dnf2PQueryMap).build();
92 result.put(dnfPredicate, query);
93 }
94
95 return result;
96 }
97
98 @Override
99 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
100 return store.getDataRepresentations();
101 }
102
103 @Override
104 public Set<RelationView<?>> getViews() {
105 return this.relationViews;
106 }
107
108 @Override
109 public Set<DNFPredicate> getPredicates() {
110 return predicates.keySet();
111 }
112
113 @Override
114 public QueryableModel createModel() {
115 return new ViatraQueryableModel(this, this.store.createModel(), predicates);
116 }
117
118 @Override
119 public QueryableModel createModel(long state) {
120 return new ViatraQueryableModel(this, this.store.createModel(state), predicates);
121 }
122
123 @Override
124 public synchronized Set<Long> getStates() {
125 return this.store.getStates();
126 }
127
128 @Override
129 public synchronized ModelDiffCursor getDiffCursor(long from, long to) {
130 return this.store.getDiffCursor(from, to);
131 }
132}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RawPatternMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RawPatternMatcher.java
new file mode 100644
index 00000000..be348a63
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RawPatternMatcher.java
@@ -0,0 +1,51 @@
1package tools.refinery.store.query.viatra.internal;
2
3import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
4import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
5import org.eclipse.viatra.query.runtime.matchers.tuple.AbstractTuple;
6import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
7
8import java.util.Optional;
9import java.util.stream.Stream;
10
11public class RawPatternMatcher extends GenericPatternMatcher {
12 protected final Object[] empty;
13
14 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
15 super(specification);
16 this.empty = new Object[specification.getParameterNames().size()];
17 }
18
19 public boolean hasResult() {
20 return hasResult(empty);
21 }
22
23 public boolean hasResult(Object[] parameters) {
24 return this.backend.hasMatch(parameters);
25 }
26
27 public Optional<Object[]> oneResult() {
28 return oneResult(empty);
29 }
30
31 public Optional<Object[]> oneResult(Object[] parameters) {
32 Optional<Tuple> tuple = this.backend.getOneArbitraryMatch(parameters);
33 return tuple.map(AbstractTuple::getElements);
34 }
35
36 public Stream<Object[]> allResults() {
37 return allResults(empty);
38 }
39
40 public Stream<Object[]> allResults(Object[] parameters) {
41 return this.backend.getAllMatches(parameters).map(AbstractTuple::getElements);
42 }
43
44 public int countResults() {
45 return countResults(empty);
46 }
47
48 public int countResults(Object[] parameters) {
49 return backend.countMatches(parameters);
50 }
51}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
new file mode 100644
index 00000000..8dfa22e0
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
@@ -0,0 +1,43 @@
1package tools.refinery.store.query.viatra.internal;
2
3import org.apache.log4j.Logger;
4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
5import org.eclipse.viatra.query.runtime.api.scope.IEngineContext;
6import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener;
7import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
8import tools.refinery.store.model.Model;
9import tools.refinery.store.model.Tuple;
10import tools.refinery.store.model.representation.Relation;
11import tools.refinery.store.query.viatra.internal.context.RelationalEngineContext;
12import tools.refinery.store.query.viatra.internal.viewupdate.ModelUpdateListener;
13import tools.refinery.store.query.view.RelationView;
14
15import java.util.Set;
16
17public class RelationalScope extends QueryScope {
18 private final Model model;
19 private final ModelUpdateListener updateListener;
20
21 public RelationalScope(Model model, Set<RelationView<?>> relationViews) {
22 this.model = model;
23 this.updateListener = new ModelUpdateListener(relationViews);
24 }
25
26 public <D> void processUpdate(Relation<D> relation, Tuple key, D oldValue, D newValue) {
27 updateListener.addUpdate(relation, key, oldValue, newValue);
28 }
29
30 public boolean hasChanges() {
31 return updateListener.hasChanges();
32 }
33
34 public void flush() {
35 updateListener.flush();
36 }
37
38 @Override
39 protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener,
40 Logger logger) {
41 return new RelationalEngineContext(model, updateListener);
42 }
43}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraQueryableModel.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraQueryableModel.java
new file mode 100644
index 00000000..3803702d
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraQueryableModel.java
@@ -0,0 +1,219 @@
1package tools.refinery.store.query.viatra.internal;
2
3import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine;
4import org.eclipse.viatra.query.runtime.api.GenericQueryGroup;
5import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
6import org.eclipse.viatra.query.runtime.api.IQueryGroup;
7import tools.refinery.store.map.Cursor;
8import tools.refinery.store.map.DiffCursor;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.model.ModelDiffCursor;
11import tools.refinery.store.model.Tuple;
12import tools.refinery.store.model.representation.DataRepresentation;
13import tools.refinery.store.model.representation.Relation;
14import tools.refinery.store.query.QueryableModel;
15import tools.refinery.store.query.QueryableModelStore;
16import tools.refinery.store.query.building.DNFPredicate;
17
18import java.util.HashMap;
19import java.util.Map;
20import java.util.Optional;
21import java.util.Set;
22import java.util.stream.Stream;
23
24public class ViatraQueryableModel implements QueryableModel {
25 protected final QueryableModelStore store;
26
27 protected final Model model;
28
29 protected final Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates2PQuery;
30
31 protected RelationalScope scope;
32
33 protected AdvancedViatraQueryEngine engine;
34
35 protected Map<DNFPredicate, RawPatternMatcher> predicate2Matcher;
36
37 public ViatraQueryableModel(QueryableModelStore store, Model model,
38 Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates2PQuery) {
39 this.store = store;
40 this.model = model;
41 this.predicates2PQuery = predicates2PQuery;
42 initEngine();
43 }
44
45 private void initEngine() {
46 this.scope = new RelationalScope(this.model, this.store.getViews());
47 this.engine = AdvancedViatraQueryEngine.createUnmanagedEngine(this.scope);
48 this.predicate2Matcher = initMatchers(this.engine, this.predicates2PQuery);
49 }
50
51 private Map<DNFPredicate, RawPatternMatcher> initMatchers(
52 AdvancedViatraQueryEngine engine,
53 Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates2pQuery) {
54 // 1. prepare group
55 IQueryGroup queryGroup = GenericQueryGroup.of(Set.copyOf(predicates2pQuery.values()));
56 engine.prepareGroup(queryGroup, null);
57
58 // 2. then get all matchers
59 Map<DNFPredicate, RawPatternMatcher> result = new HashMap<>();
60 for (var entry : predicates2pQuery.entrySet()) {
61 var matcher = engine.getMatcher(entry.getValue());
62 result.put(entry.getKey(), matcher);
63 }
64 return result;
65 }
66
67 @Override
68 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
69 return model.getDataRepresentations();
70 }
71
72 @Override
73 public Set<DNFPredicate> getPredicates() {
74 return store.getPredicates();
75 }
76
77 @Override
78 public <K, V> V get(DataRepresentation<K, V> representation, K key) {
79 return model.get(representation, key);
80 }
81
82 @Override
83 public <K, V> Cursor<K, V> getAll(DataRepresentation<K, V> representation) {
84 return model.getAll(representation);
85 }
86
87 @SuppressWarnings("unchecked")
88 @Override
89 public <K, V> V put(DataRepresentation<K, V> representation, K key, V value) {
90 V oldValue = this.model.put(representation, key, value);
91 if (representation instanceof Relation<?> relation) {
92 this.scope.processUpdate((Relation<V>) relation, (Tuple) key, oldValue, value);
93 }
94 return oldValue;
95 }
96
97 @Override
98 public <K, V> void putAll(DataRepresentation<K, V> representation, Cursor<K, V> cursor) {
99 if (representation instanceof Relation<?>) {
100 //noinspection RedundantSuppression
101 @SuppressWarnings("unchecked")
102 Relation<V> relation = (Relation<V>) representation;
103 while (cursor.move()) {
104 Tuple key = (Tuple) cursor.getKey();
105 V newValue = cursor.getValue();
106 V oldValue = this.model.put(relation, key, newValue);
107 this.scope.processUpdate(relation, key, oldValue, newValue);
108 }
109 } else {
110 this.model.putAll(representation, cursor);
111 }
112 }
113
114 @Override
115 public <K, V> long getSize(DataRepresentation<K, V> representation) {
116 return model.getSize(representation);
117 }
118
119 protected RawPatternMatcher getMatcher(DNFPredicate predicate) {
120 var result = this.predicate2Matcher.get(predicate);
121 if (result == null) {
122 throw new IllegalArgumentException("Model does not contain predicate %s".formatted(predicate.getName()));
123 } else
124 return result;
125 }
126
127 protected void validateParameters(DNFPredicate predicate, Object[] parameters) {
128 int predicateArity = predicate.getVariables().size();
129 int parameterArity = parameters.length;
130 if (parameterArity != predicateArity) {
131 throw new IllegalArgumentException(
132 "Predicate %s with %d arity called with different number of parameters (%d)"
133 .formatted(predicate.getName(), predicateArity, parameterArity));
134 }
135 }
136
137 @Override
138 public boolean hasResult(DNFPredicate predicate) {
139 return getMatcher(predicate).hasResult();
140 }
141
142 @Override
143 public boolean hasResult(DNFPredicate predicate, Object[] parameters) {
144 validateParameters(predicate, parameters);
145 return getMatcher(predicate).hasResult(parameters);
146 }
147
148 @Override
149 public Optional<Object[]> oneResult(DNFPredicate predicate) {
150 return getMatcher(predicate).oneResult();
151 }
152
153 @Override
154 public Optional<Object[]> oneResult(DNFPredicate predicate, Object[] parameters) {
155 validateParameters(predicate, parameters);
156 return getMatcher(predicate).oneResult(parameters);
157 }
158
159 @Override
160 public Stream<Object[]> allResults(DNFPredicate predicate) {
161 return getMatcher(predicate).allResults();
162 }
163
164 @Override
165 public Stream<Object[]> allResults(DNFPredicate predicate, Object[] parameters) {
166 validateParameters(predicate, parameters);
167 return getMatcher(predicate).allResults(parameters);
168 }
169
170 @Override
171 public int countResults(DNFPredicate predicate) {
172 return getMatcher(predicate).countResults();
173 }
174
175 @Override
176 public int countResults(DNFPredicate predicate, Object[] parameters) {
177 validateParameters(predicate, parameters);
178 return getMatcher(predicate).countResults(parameters);
179
180 }
181
182 @Override
183 public boolean hasChanges() {
184 return scope.hasChanges();
185 }
186
187 @Override
188 public void flushChanges() {
189 this.scope.flush();
190 }
191
192 @Override
193 public ModelDiffCursor getDiffCursor(long to) {
194 return model.getDiffCursor(to);
195 }
196
197 @Override
198 public long commit() {
199 return this.model.commit();
200 }
201
202 @Override
203 public void restore(long state) {
204 restoreWithDiffReplay(state);
205 }
206
207 private void restoreWithDiffReplay(long state) {
208 var modelDiffCursor = getDiffCursor(state);
209 for (DataRepresentation<?, ?> dataRepresentation : this.getDataRepresentations()) {
210 restoreRepresentationWithDiffReplay(modelDiffCursor, dataRepresentation);
211 }
212 }
213
214 private <K, V> void restoreRepresentationWithDiffReplay(ModelDiffCursor modelDiffCursor,
215 DataRepresentation<K, V> dataRepresentation) {
216 DiffCursor<K, V> diffCursor = modelDiffCursor.getCursor(dataRepresentation);
217 this.putAll(dataRepresentation, diffCursor);
218 }
219}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java
new file mode 100644
index 00000000..4b311a64
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java
@@ -0,0 +1,58 @@
1package tools.refinery.store.query.viatra.internal.context;
2
3import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex;
4import org.eclipse.viatra.query.runtime.api.scope.IIndexingErrorListener;
5import org.eclipse.viatra.query.runtime.api.scope.IInstanceObserver;
6import org.eclipse.viatra.query.runtime.api.scope.ViatraBaseIndexChangeListener;
7
8import java.lang.reflect.InvocationTargetException;
9import java.util.concurrent.Callable;
10
11/**
12 * Copied from <code>org.eclipse.viatra.query.runtime.tabular.TabularEngineContext</code>
13 */
14public class DummyBaseIndexer implements IBaseIndex {
15 @Override
16 public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException {
17 try {
18 return callable.call();
19 } catch (Exception e) {
20 throw new InvocationTargetException(e);
21 }
22 }
23
24 @Override
25 public void addBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) {
26 // no notification support
27 }
28
29 @Override
30 public void removeBaseIndexChangeListener(ViatraBaseIndexChangeListener listener) {
31 // no notification support
32 }
33
34 @Override
35 public void resampleDerivedFeatures() {
36 throw new UnsupportedOperationException();
37 }
38
39 @Override
40 public boolean addIndexingErrorListener(IIndexingErrorListener listener) {
41 return true;
42 }
43
44 @Override
45 public boolean removeIndexingErrorListener(IIndexingErrorListener listener) {
46 return true;
47 }
48
49 @Override
50 public boolean addInstanceObserver(IInstanceObserver observer, Object observedObject) {
51 return true;
52 }
53
54 @Override
55 public boolean removeInstanceObserver(IInstanceObserver observer, Object observedObject) {
56 return true;
57 }
58}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
new file mode 100644
index 00000000..882734cb
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
@@ -0,0 +1,33 @@
1package tools.refinery.store.query.viatra.internal.context;
2
3import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex;
4import org.eclipse.viatra.query.runtime.api.scope.IEngineContext;
5import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext;
6
7import tools.refinery.store.model.Model;
8import tools.refinery.store.query.viatra.internal.viewupdate.ModelUpdateListener;
9
10public class RelationalEngineContext implements IEngineContext {
11 private final IBaseIndex baseIndex = new DummyBaseIndexer();
12 private final RelationalRuntimeContext runtimeContext;
13
14 public RelationalEngineContext(Model model, ModelUpdateListener updateListener) {
15 runtimeContext = new RelationalRuntimeContext(model, updateListener);
16 }
17
18 @Override
19 public IBaseIndex getBaseIndex() {
20 return this.baseIndex;
21 }
22
23 @Override
24 public void dispose() {
25 // Nothing to dispose, because lifecycle is not controlled by the engine.
26 }
27
28 @Override
29 public IQueryRuntimeContext getQueryRuntimeContext() {
30 return runtimeContext;
31 }
32
33}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
new file mode 100644
index 00000000..64c23c61
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
@@ -0,0 +1,44 @@
1package tools.refinery.store.query.viatra.internal.context;
2
3import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext;
4import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
5import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication;
6import tools.refinery.store.query.view.RelationView;
7
8import java.util.*;
9
10/**
11 * The meta context information for String scopes.
12 */
13public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
14 @Override
15 public boolean isEnumerable(IInputKey key) {
16 ensureValidKey(key);
17 return key.isEnumerable();
18 }
19
20 @Override
21 public boolean isStateless(IInputKey key) {
22 ensureValidKey(key);
23 return true;
24 }
25
26 @Override
27 public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) {
28 ensureValidKey(implyingKey);
29 return Set.of();
30 }
31
32 @Override
33 public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) {
34 ensureValidKey(key);
35 return Map.of();
36 }
37
38 public void ensureValidKey(IInputKey key) {
39 if (key instanceof RelationView<?>) {
40 return;
41 }
42 throw new IllegalArgumentException("The input key %s is not a valid input key.".formatted(key));
43 }
44}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
new file mode 100644
index 00000000..0bd1b807
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
@@ -0,0 +1,172 @@
1package tools.refinery.store.query.viatra.internal.context;
2
3import org.eclipse.viatra.query.runtime.matchers.context.*;
4import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
5import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
6import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
7import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
8import org.eclipse.viatra.query.runtime.matchers.util.Accuracy;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.query.viatra.internal.viewupdate.ModelUpdateListener;
11import tools.refinery.store.query.view.RelationView;
12
13import java.lang.reflect.InvocationTargetException;
14import java.util.Iterator;
15import java.util.Optional;
16import java.util.concurrent.Callable;
17
18import static tools.refinery.store.util.CollectionsUtil.filter;
19import static tools.refinery.store.util.CollectionsUtil.map;
20
21public class RelationalRuntimeContext implements IQueryRuntimeContext {
22 private final RelationalQueryMetaContext metaContext = new RelationalQueryMetaContext();
23
24 private final ModelUpdateListener modelUpdateListener;
25
26 private final Model model;
27
28 public RelationalRuntimeContext(Model model, ModelUpdateListener relationUpdateListener) {
29 this.model = model;
30 this.modelUpdateListener = relationUpdateListener;
31 }
32
33 @Override
34 public IQueryMetaContext getMetaContext() {
35 return metaContext;
36 }
37
38 @Override
39 public <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException {
40 try {
41 return callable.call();
42 } catch (Exception e) {
43 throw new InvocationTargetException(e);
44 }
45 }
46
47 @Override
48 public boolean isCoalescing() {
49 return true;
50 }
51
52 @Override
53 public boolean isIndexed(IInputKey key, IndexingService service) {
54 if (key instanceof RelationView<?> relationalKey) {
55 return this.modelUpdateListener.containsRelationalView(relationalKey);
56 } else {
57 return false;
58 }
59 }
60
61 @Override
62 public void ensureIndexed(IInputKey key, IndexingService service) {
63 if (!isIndexed(key, service)) {
64 throw new IllegalStateException("Engine tries to index a new key " + key);
65 }
66 }
67
68 @SuppressWarnings("squid:S1452")
69 RelationView<?> checkKey(IInputKey key) {
70 if (key instanceof RelationView<?> relationViewKey) {
71 if (modelUpdateListener.containsRelationalView(relationViewKey)) {
72 return relationViewKey;
73 } else {
74 throw new IllegalStateException("Query is asking for non-indexed key");
75 }
76 } else {
77 throw new IllegalStateException("Query is asking for non-relational key");
78 }
79 }
80
81 @Override
82 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
83 RelationView<?> relationalViewKey = checkKey(key);
84 Iterable<Object[]> allObjects = relationalViewKey.getAll(model);
85 Iterable<Object[]> filteredBySeed = filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed));
86 Iterator<Object[]> iterator = filteredBySeed.iterator();
87 int result = 0;
88 while (iterator.hasNext()) {
89 iterator.next();
90 result++;
91 }
92 return result;
93 }
94
95 @Override
96 public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) {
97 return Optional.empty();
98 }
99
100 @Override
101 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
102 RelationView<?> relationalViewKey = checkKey(key);
103 Iterable<Object[]> allObjects = relationalViewKey.getAll(model);
104 Iterable<Object[]> filteredBySeed = filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed));
105 return map(filteredBySeed, Tuples::flatTupleOf);
106 }
107
108 private boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) {
109 for (int i = 0; i < seedMask.indices.length; i++) {
110 final Object seedElement = seed.get(i);
111 final Object tupleElement = tuple[seedMask.indices[i]];
112 if (!tupleElement.equals(seedElement)) {
113 return false;
114 }
115 }
116 return true;
117 }
118
119 @Override
120 public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) {
121 return enumerateTuples(key, seedMask, seed);
122 }
123
124 @Override
125 public boolean containsTuple(IInputKey key, ITuple seed) {
126 RelationView<?> relationalViewKey = checkKey(key);
127 return relationalViewKey.get(model, seed.getElements());
128 }
129
130 @Override
131 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
132 RelationView<?> relationalKey = checkKey(key);
133 this.modelUpdateListener.addListener(relationalKey, seed, listener);
134
135 }
136
137 @Override
138 public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
139 RelationView<?> relationalKey = checkKey(key);
140 this.modelUpdateListener.removeListener(relationalKey, seed, listener);
141 }
142
143 @Override
144 public Object wrapElement(Object externalElement) {
145 return externalElement;
146 }
147
148 @Override
149 public Object unwrapElement(Object internalElement) {
150 return internalElement;
151 }
152
153 @Override
154 public Tuple wrapTuple(Tuple externalElements) {
155 return externalElements;
156 }
157
158 @Override
159 public Tuple unwrapTuple(Tuple internalElements) {
160 return internalElements;
161 }
162
163 @Override
164 public void ensureWildcardIndexing(IndexingService service) {
165 throw new UnsupportedOperationException();
166 }
167
168 @Override
169 public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException {
170 runnable.run();
171 }
172}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
new file mode 100644
index 00000000..c093be47
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
@@ -0,0 +1,114 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
4import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
5import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality;
6import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
7import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality;
8import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
9import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
10import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
11import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
12import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
13import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
14import tools.refinery.store.query.building.*;
15
16import java.util.*;
17
18public class DNF2PQuery {
19 private DNF2PQuery() {
20 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
21 }
22
23 public static SimplePQuery translate(DNFPredicate predicate, Map<DNFPredicate, SimplePQuery> dnf2PQueryMap) {
24 SimplePQuery query = dnf2PQueryMap.get(predicate);
25 if (query != null) {
26 return query;
27 }
28 query = new SimplePQuery(predicate.getName());
29 Map<Variable, PParameter> parameters = new HashMap<>();
30
31 predicate.getVariables().forEach(variable -> parameters.put(variable, new PParameter(variable.getName())));
32 List<PParameter> parameterList = new ArrayList<>();
33 for (var param : predicate.getVariables()) {
34 parameterList.add(parameters.get(param));
35 }
36 query.setParameters(parameterList);
37 for (DNFAnd clause : predicate.getClauses()) {
38 PBody body = new PBody(query);
39 List<ExportedParameter> symbolicParameters = new ArrayList<>();
40 for (var param : predicate.getVariables()) {
41 PVariable pVar = body.getOrCreateVariableByName(param.getName());
42 symbolicParameters.add(new ExportedParameter(body, pVar, parameters.get(param)));
43 }
44 body.setSymbolicParameters(symbolicParameters);
45 query.addBody(body);
46 for (DNFAtom constraint : clause.getConstraints()) {
47 translateDNFAtom(constraint, body, dnf2PQueryMap);
48 }
49 }
50 dnf2PQueryMap.put(predicate, query);
51 return query;
52 }
53
54 private static void translateDNFAtom(DNFAtom constraint, PBody body,
55 Map<DNFPredicate, SimplePQuery> dnf2PQueryMap) {
56 if (constraint instanceof EquivalenceAtom equivalence) {
57 translateEquivalenceAtom(equivalence, body);
58 }
59 if (constraint instanceof RelationAtom relation) {
60 translateRelationAtom(relation, body);
61 }
62 if (constraint instanceof PredicateAtom predicate) {
63 translatePredicateAtom(predicate, body, dnf2PQueryMap);
64 }
65 }
66
67 private static void translateEquivalenceAtom(EquivalenceAtom equivalence, PBody body) {
68 PVariable varSource = body.getOrCreateVariableByName(equivalence.getLeft().getName());
69 PVariable varTarget = body.getOrCreateVariableByName(equivalence.getRight().getName());
70 if (equivalence.isPositive())
71 new Equality(body, varSource, varTarget);
72 else
73 new Inequality(body, varSource, varTarget);
74 }
75
76 private static void translateRelationAtom(RelationAtom relation, PBody body) {
77 if (relation.substitution().size() != relation.view().getArity()) {
78 throw new IllegalArgumentException("Arity (%d) does not match parameter numbers (%d)".formatted(
79 relation.view().getArity(), relation.substitution().size()));
80 }
81 Object[] variables = new Object[relation.substitution().size()];
82 for (int i = 0; i < relation.substitution().size(); i++) {
83 variables[i] = body.getOrCreateVariableByName(relation.substitution().get(i).getName());
84 }
85 new TypeConstraint(body, Tuples.flatTupleOf(variables), relation.view());
86 }
87
88 private static void translatePredicateAtom(PredicateAtom predicate, PBody body,
89 Map<DNFPredicate, SimplePQuery> dnf2PQueryMap) {
90 Object[] variables = new Object[predicate.getSubstitution().size()];
91 for (int i = 0; i < predicate.getSubstitution().size(); i++) {
92 variables[i] = body.getOrCreateVariableByName(predicate.getSubstitution().get(i).getName());
93 }
94 if (predicate.isPositive()) {
95 if (predicate.isTransitive()) {
96 if (predicate.getSubstitution().size() != 2) {
97 throw new IllegalArgumentException("Transitive Predicate Atoms must be binary.");
98 }
99 new BinaryTransitiveClosure(body, Tuples.flatTupleOf(variables),
100 DNF2PQuery.translate(predicate.getReferred(), dnf2PQueryMap));
101 } else {
102 new PositivePatternCall(body, Tuples.flatTupleOf(variables),
103 DNF2PQuery.translate(predicate.getReferred(), dnf2PQueryMap));
104 }
105 } else {
106 if (predicate.isTransitive()) {
107 throw new InputMismatchException("Transitive Predicate Atoms cannot be negative.");
108 } else {
109 new NegativePatternCall(body, Tuples.flatTupleOf(variables),
110 DNF2PQuery.translate(predicate.getReferred(), dnf2PQueryMap));
111 }
112 }
113 }
114}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SimplePQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SimplePQuery.java
new file mode 100644
index 00000000..a367cbf2
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SimplePQuery.java
@@ -0,0 +1,74 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
5import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
6import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
7import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
8import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery;
9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
11import tools.refinery.store.query.viatra.internal.RawPatternMatcher;
12import tools.refinery.store.query.viatra.internal.RelationalScope;
13
14import java.util.LinkedHashSet;
15import java.util.List;
16import java.util.Set;
17
18public class SimplePQuery extends BasePQuery {
19 private final String fullyQualifiedName;
20 private List<PParameter> parameters;
21 private final LinkedHashSet<PBody> bodies = new LinkedHashSet<>();
22
23 public SimplePQuery(String name) {
24 super(PVisibility.PUBLIC);
25 fullyQualifiedName = name;
26 setEvaluationHints(new QueryEvaluationHint(null, QueryEvaluationHint.BackendRequirement.UNSPECIFIED));
27 }
28
29 @Override
30 public String getFullyQualifiedName() {
31 return fullyQualifiedName;
32 }
33
34 public void setParameters(List<PParameter> parameters) {
35 this.parameters = parameters;
36 }
37
38 @Override
39 public List<PParameter> getParameters() {
40 return parameters;
41 }
42
43 public void addBody(PBody body) {
44 bodies.add(body);
45 }
46
47 @Override
48 protected Set<PBody> doGetContainedBodies() {
49 return bodies;
50 }
51
52 public GenericQuerySpecification<RawPatternMatcher> build() {
53 return new GenericQuerySpecification<>(this) {
54 @Override
55 public Class<? extends QueryScope> getPreferredScopeClass() {
56 return RelationalScope.class;
57 }
58
59 @Override
60 protected RawPatternMatcher instantiate(ViatraQueryEngine engine) {
61 RawPatternMatcher matcher = engine.getExistingMatcher(this);
62 if (matcher == null) {
63 matcher = engine.getMatcher(this);
64 }
65 return matcher;
66 }
67
68 @Override
69 public RawPatternMatcher instantiate() {
70 return new RawPatternMatcher(this);
71 }
72 };
73 }
74}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ModelUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ModelUpdateListener.java
new file mode 100644
index 00000000..25919888
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ModelUpdateListener.java
@@ -0,0 +1,105 @@
1package tools.refinery.store.query.viatra.internal.viewupdate;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
4import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
5import tools.refinery.store.model.Tuple;
6import tools.refinery.store.model.representation.Relation;
7import tools.refinery.store.query.view.RelationView;
8
9import java.util.HashMap;
10import java.util.HashSet;
11import java.util.Map;
12import java.util.Set;
13
14public class ModelUpdateListener {
15 /**
16 * Collections of Relations and their Views.
17 */
18 private final Map<Relation<?>, Set<RelationView<?>>> relation2View;
19
20 /**
21 * Collection of Views and their buffers.
22 */
23 private final Map<RelationView<?>, Set<ViewUpdateBuffer<?>>> view2Buffers;
24
25 public ModelUpdateListener(Set<RelationView<?>> relationViews) {
26 this.relation2View = new HashMap<>();
27 this.view2Buffers = new HashMap<>();
28
29 for (RelationView<?> relationView : relationViews) {
30 registerView(relationView);
31 }
32 }
33
34 private void registerView(RelationView<?> view) {
35 Relation<?> relation = view.getRepresentation();
36
37 // 1. register views to relations, if necessary
38 var views = relation2View.computeIfAbsent(relation, x -> new HashSet<>());
39 views.add(view);
40
41 // 2. register notifier map to views, if necessary
42 view2Buffers.computeIfAbsent(view, x -> new HashSet<>());
43 }
44
45 public boolean containsRelationalView(RelationView<?> relationalKey) {
46 return view2Buffers.containsKey(relationalKey);
47 }
48
49 public <D> void addListener(RelationView<D> relationView, ITuple seed, IQueryRuntimeContextListener listener) {
50 if (view2Buffers.containsKey(relationView)) {
51 ViewUpdateTranslator<D> updateListener = new ViewUpdateTranslator<>(relationView, seed, listener);
52 ViewUpdateBuffer<D> updateBuffer = new ViewUpdateBuffer<>(updateListener);
53 view2Buffers.get(relationView).add(updateBuffer);
54 } else
55 throw new IllegalArgumentException();
56 }
57
58 public void removeListener(RelationView<?> relationView, ITuple seed, IQueryRuntimeContextListener listener) {
59 if (view2Buffers.containsKey(relationView)) {
60 Set<ViewUpdateBuffer<?>> buffers = this.view2Buffers.get(relationView);
61 for (var buffer : buffers) {
62 if (buffer.getUpdateListener().equals(relationView, seed, listener)) {
63 // remove buffer and terminate immediately, or it will break iterator.
64 buffers.remove(buffer);
65 return;
66 }
67 }
68 } else {
69 throw new IllegalArgumentException("Relation view is not registered for updates");
70 }
71 }
72
73 public <D> void addUpdate(Relation<D> relation, Tuple key, D oldValue, D newValue) {
74 var views = this.relation2View.get(relation);
75 if (views == null) {
76 return;
77 }
78 for (var view : views) {
79 var buffers = this.view2Buffers.get(view);
80 for (var buffer : buffers) {
81 @SuppressWarnings("unchecked")
82 var typedBuffer = (ViewUpdateBuffer<D>) buffer;
83 typedBuffer.addChange(key, oldValue, newValue);
84 }
85 }
86 }
87
88 public boolean hasChanges() {
89 for (var bufferCollection : this.view2Buffers.values()) {
90 for (ViewUpdateBuffer<?> buffer : bufferCollection) {
91 if (buffer.hasChanges())
92 return true;
93 }
94 }
95 return false;
96 }
97
98 public void flush() {
99 for (var bufferCollection : this.view2Buffers.values()) {
100 for (ViewUpdateBuffer<?> buffer : bufferCollection) {
101 buffer.flush();
102 }
103 }
104 }
105}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdate.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdate.java
new file mode 100644
index 00000000..c727f046
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdate.java
@@ -0,0 +1,34 @@
1package tools.refinery.store.query.viatra.internal.viewupdate;
2
3import java.util.Arrays;
4import java.util.Objects;
5
6record ViewUpdate (Object[] tuple, boolean isInsertion) {
7
8 @Override
9 public int hashCode() {
10 final int prime = 31;
11 int result = 1;
12 result = prime * result + Arrays.deepHashCode(tuple);
13 result = prime * result + Objects.hash(isInsertion);
14 return result;
15 }
16
17 @Override
18 public boolean equals(Object obj) {
19 if (this == obj)
20 return true;
21 if (obj == null)
22 return false;
23 if (getClass() != obj.getClass())
24 return false;
25 ViewUpdate other = (ViewUpdate) obj;
26 return isInsertion == other.isInsertion && Arrays.deepEquals(tuple, other.tuple);
27 }
28
29 @Override
30 public String toString() {
31 return "ViewUpdate [" + Arrays.toString(tuple) + "insertion= "+this.isInsertion+"]";
32 }
33
34}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateBuffer.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateBuffer.java
new file mode 100644
index 00000000..5a4243f2
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateBuffer.java
@@ -0,0 +1,46 @@
1package tools.refinery.store.query.viatra.internal.viewupdate;
2
3import tools.refinery.store.model.Tuple;
4
5import java.util.ArrayList;
6import java.util.Arrays;
7import java.util.List;
8
9public class ViewUpdateBuffer<D> {
10 protected final ViewUpdateTranslator<D> updateListener;
11 protected final List<ViewUpdate> buffer = new ArrayList<>();
12
13 public ViewUpdateBuffer(ViewUpdateTranslator<D> updateListener) {
14 this.updateListener = updateListener;
15 }
16
17 public ViewUpdateTranslator<D> getUpdateListener() {
18 return updateListener;
19 }
20
21 public boolean hasChanges() {
22 return !buffer.isEmpty();
23 }
24
25 public void addChange(Tuple tuple, D oldValue, D newValue) {
26 if (oldValue != newValue) {
27 Object[] oldTuple = updateListener.isMatching(tuple, oldValue);
28 Object[] newTuple = updateListener.isMatching(tuple, newValue);
29 if (!Arrays.equals(oldTuple, newTuple)) {
30 if (oldTuple != null) {
31 buffer.add(new ViewUpdate(oldTuple, false));
32 }
33 if (newTuple != null) {
34 buffer.add(new ViewUpdate(newTuple, true));
35 }
36 }
37 }
38 }
39
40 public void flush() {
41 for (ViewUpdate viewChange : buffer) {
42 updateListener.processChange(viewChange);
43 }
44 buffer.clear();
45 }
46}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateTranslator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateTranslator.java
new file mode 100644
index 00000000..2f7f9a9c
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/viewupdate/ViewUpdateTranslator.java
@@ -0,0 +1,62 @@
1package tools.refinery.store.query.viatra.internal.viewupdate;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
4import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
5import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
6import tools.refinery.store.model.Tuple;
7import tools.refinery.store.query.view.RelationView;
8
9import java.util.Objects;
10
11public class ViewUpdateTranslator<D> {
12 private final RelationView<D> key;
13
14 private final ITuple filter;
15
16 private final IQueryRuntimeContextListener listener;
17
18 public ViewUpdateTranslator(RelationView<D> key, ITuple filter, IQueryRuntimeContextListener listener) {
19 super();
20 this.key = key;
21 this.filter = filter;
22 this.listener = listener;
23 }
24
25 public boolean equals(RelationView<?> relationView, ITuple seed, IQueryRuntimeContextListener listener) {
26 return key == relationView && filter.equals(seed) && this.listener == listener;
27 }
28
29 public void processChange(ViewUpdate change) {
30 listener.update(key, Tuples.flatTupleOf(change.tuple()), change.isInsertion());
31 }
32
33 public Object[] isMatching(Tuple tuple, D value) {
34 return isMatching(key.getWrappedKey().transform(tuple, value), filter);
35 }
36
37 @SuppressWarnings("squid:S1168")
38 private Object[] isMatching(Object[] tuple, ITuple filter) {
39 for (int i = 0; i < filter.getSize(); i++) {
40 final Object filterObject = filter.get(i);
41 if (filterObject != null && !filterObject.equals(tuple[i])) {
42 return null;
43 }
44 }
45 return tuple;
46 }
47
48 @Override
49 public int hashCode() {
50 return Objects.hash(filter, key, listener);
51 }
52
53 @Override
54 public boolean equals(Object obj) {
55 if (this == obj)
56 return true;
57 if (!(obj instanceof ViewUpdateTranslator<?> other))
58 return false;
59 return Objects.equals(filter, other.filter) && Objects.equals(key, other.key)
60 && Objects.equals(listener, other.listener);
61 }
62}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java
new file mode 100644
index 00000000..4307ab6b
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTest.java
@@ -0,0 +1,432 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.junit.jupiter.api.Test;
4import tools.refinery.store.model.Tuple;
5import tools.refinery.store.model.representation.Relation;
6import tools.refinery.store.model.representation.TruthValue;
7import tools.refinery.store.query.QueryableModel;
8import tools.refinery.store.query.QueryableModelStore;
9import tools.refinery.store.query.building.*;
10import tools.refinery.store.query.viatra.ViatraQueryableModelStore;
11import tools.refinery.store.query.view.FilteredRelationView;
12import tools.refinery.store.query.view.KeyOnlyRelationView;
13import tools.refinery.store.query.view.RelationView;
14
15import java.util.*;
16import java.util.stream.Stream;
17
18import static org.junit.jupiter.api.Assertions.assertEquals;
19
20class QueryTest {
21 @Test
22 void typeConstraintTest() {
23 Relation<Boolean> person = new Relation<>("Person", 1, false);
24 Relation<Boolean> asset = new Relation<>("Asset", 1, false);
25 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
26
27 List<Variable> parameters = List.of(new Variable("p1"));
28 RelationAtom personRelationAtom = new RelationAtom(personView, parameters);
29 DNFAnd clause = new DNFAnd(Collections.emptySet(), List.of(personRelationAtom));
30 DNFPredicate predicate = new DNFPredicate("TypeConstraint", parameters, List.of(clause));
31
32 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, asset), Set.of(personView),
33 Set.of(predicate));
34 QueryableModel model = store.createModel();
35
36 model.put(person, Tuple.of(0), true);
37 model.put(person, Tuple.of(1), true);
38 model.put(asset, Tuple.of(1), true);
39 model.put(asset, Tuple.of(2), true);
40
41 model.flushChanges();
42 assertEquals(2, model.countResults(predicate));
43 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0)), List.of(Tuple.of(1))));
44 }
45
46 @Test
47 void relationConstraintTest() {
48 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
49 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
50 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
51 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
52
53 Variable p1 = new Variable("p1");
54 Variable p2 = new Variable("p2");
55 List<Variable> parameters = Arrays.asList(p1, p2);
56
57 RelationAtom personRelationAtom1 = new RelationAtom(personView, List.of(p1));
58 RelationAtom personRelationAtom2 = new RelationAtom(personView, List.of(p2));
59 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
60 DNFAnd clause = new DNFAnd(Collections.emptySet(),
61 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
62 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, List.of(clause));
63
64 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, friend),
65 Set.of(personView, friendMustView), Set.of(predicate));
66 QueryableModel model = store.createModel();
67
68 assertEquals(0, model.countResults(predicate));
69
70 model.put(person, Tuple.of(0), true);
71 model.put(person, Tuple.of(1), true);
72 model.put(person, Tuple.of(2), true);
73 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
74 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
75 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
76
77 assertEquals(0, model.countResults(predicate));
78
79 model.flushChanges();
80 assertEquals(3, model.countResults(predicate));
81 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0), Tuple.of(1)),
82 List.of(Tuple.of(1), Tuple.of(0)), List.of(Tuple.of(1), Tuple.of(2))));
83 }
84
85 @Test
86 void andTest() {
87 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
88 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
89 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
90 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
91
92 Variable p1 = new Variable("p1");
93 Variable p2 = new Variable("p2");
94 List<Variable> parameters = Arrays.asList(p1, p2);
95
96 RelationAtom personRelationAtom1 = new RelationAtom(personView, List.of(p1));
97 RelationAtom personRelationAtom2 = new RelationAtom(personView, List.of(p2));
98 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
99 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p2, p1));
100 DNFAnd clause = new DNFAnd(Collections.emptySet(),
101 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom1, friendRelationAtom2));
102 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, List.of(clause));
103
104 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, friend),
105 Set.of(personView, friendMustView), Set.of(predicate));
106 QueryableModel model = store.createModel();
107
108 assertEquals(0, model.countResults(predicate));
109
110 model.put(person, Tuple.of(0), true);
111 model.put(person, Tuple.of(1), true);
112 model.put(person, Tuple.of(2), true);
113
114 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
115 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
116
117 model.flushChanges();
118 assertEquals(0, model.countResults(predicate));
119
120 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
121 model.flushChanges();
122 assertEquals(2, model.countResults(predicate));
123 compareMatchSets(model.allResults(predicate),
124 Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(1), Tuple.of(0))));
125
126 model.put(friend, Tuple.of(2, 0), TruthValue.TRUE);
127 model.flushChanges();
128 assertEquals(4, model.countResults(predicate));
129 compareMatchSets(model.allResults(predicate),
130 Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(1), Tuple.of(0)),
131 List.of(Tuple.of(0), Tuple.of(2)), List.of(Tuple.of(2), Tuple.of(0))));
132 }
133
134 @Test
135 void existTest() {
136 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
137 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
138 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
139 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
140
141 Variable p1 = new Variable("p1");
142 Variable p2 = new Variable("p2");
143 List<Variable> parameters = List.of(p1);
144
145 RelationAtom personRelationAtom1 = new RelationAtom(personView, List.of(p1));
146 RelationAtom personRelationAtom2 = new RelationAtom(personView, List.of(p2));
147 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
148 DNFAnd clause = new DNFAnd(Set.of(p2),
149 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
150 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, List.of(clause));
151
152 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, friend),
153 Set.of(personView, friendMustView), Set.of(predicate));
154 QueryableModel model = store.createModel();
155
156 assertEquals(0, model.countResults(predicate));
157
158 model.put(person, Tuple.of(0), true);
159 model.put(person, Tuple.of(1), true);
160 model.put(person, Tuple.of(2), true);
161 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
162 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
163 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
164
165 assertEquals(0, model.countResults(predicate));
166
167 model.flushChanges();
168 assertEquals(2, model.countResults(predicate));
169 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0)), List.of(Tuple.of(1))));
170 }
171
172 @Test
173 void orTest() {
174 Relation<Boolean> person = new Relation<>("Person", 1, false);
175 Relation<Boolean> animal = new Relation<>("Animal", 1, false);
176 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
177 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
178 RelationView<Boolean> animalView = new KeyOnlyRelationView(animal);
179 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
180
181 Variable p1 = new Variable("p1");
182 Variable p2 = new Variable("p2");
183 List<Variable> parameters = Arrays.asList(p1, p2);
184
185 // Person-Person friendship
186 RelationAtom personRelationAtom1 = new RelationAtom(personView, List.of(p1));
187 RelationAtom personRelationAtom2 = new RelationAtom(personView, List.of(p2));
188 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
189 DNFAnd clause1 = new DNFAnd(Collections.emptySet(),
190 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom1));
191
192 // Animal-Animal friendship
193 RelationAtom animalRelationAtom1 = new RelationAtom(animalView, List.of(p1));
194 RelationAtom animalRelationAtom2 = new RelationAtom(animalView, List.of(p2));
195 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
196 DNFAnd clause2 = new DNFAnd(Collections.emptySet(),
197 Arrays.asList(animalRelationAtom1, animalRelationAtom2, friendRelationAtom2));
198
199 // No inter-species friendship
200
201 DNFPredicate predicate = new DNFPredicate("Or", parameters, Arrays.asList(clause1, clause2));
202
203 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, animal, friend),
204 Set.of(personView, animalView, friendMustView), Set.of(predicate));
205 QueryableModel model = store.createModel();
206
207 model.put(person, Tuple.of(0), true);
208 model.put(person, Tuple.of(1), true);
209 model.put(animal, Tuple.of(2), true);
210 model.put(animal, Tuple.of(3), true);
211 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
212 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
213 model.put(friend, Tuple.of(2, 3), TruthValue.TRUE);
214 model.put(friend, Tuple.of(3, 0), TruthValue.TRUE);
215
216 model.flushChanges();
217 assertEquals(2, model.countResults(predicate));
218 compareMatchSets(model.allResults(predicate),
219 Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(2), Tuple.of(3))));
220 }
221
222 @Test
223 void equalityTest() {
224 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
225 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
226
227 Variable p1 = new Variable("p1");
228 Variable p2 = new Variable("p2");
229 List<Variable> parameters = Arrays.asList(p1, p2);
230
231 RelationAtom personRelationAtom1 = new RelationAtom(personView, List.of(p1));
232 RelationAtom personRelationAtom2 = new RelationAtom(personView, List.of(p2));
233 EquivalenceAtom equivalenceAtom = new EquivalenceAtom(true, p1, p2);
234 DNFAnd clause = new DNFAnd(Collections.emptySet(),
235 Arrays.asList(personRelationAtom1, personRelationAtom2, equivalenceAtom));
236 DNFPredicate predicate = new DNFPredicate("Equality", parameters, List.of(clause));
237
238 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person), Set.of(personView), Set.of(predicate));
239 QueryableModel model = store.createModel();
240
241 model.put(person, Tuple.of(0), true);
242 model.put(person, Tuple.of(1), true);
243 model.put(person, Tuple.of(2), true);
244
245 model.flushChanges();
246 assertEquals(3, model.countResults(predicate));
247 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0), Tuple.of(0)),
248 List.of(Tuple.of(1), Tuple.of(1)), List.of(Tuple.of(2), Tuple.of(2))));
249 }
250
251 @Test
252 void inequalityTest() {
253 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
254 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
255 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
256 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
257
258 Variable p1 = new Variable("p1");
259 Variable p2 = new Variable("p2");
260 Variable p3 = new Variable("p3");
261 List<Variable> parameters = Arrays.asList(p1, p2, p3);
262
263 RelationAtom personRelationAtom1 = new RelationAtom(personView, List.of(p1));
264 RelationAtom personRelationAtom2 = new RelationAtom(personView, List.of(p2));
265 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p3));
266 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p2, p3));
267 EquivalenceAtom inequivalenceAtom = new EquivalenceAtom(false, p1, p2);
268 DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom1, personRelationAtom2,
269 friendRelationAtom1, friendRelationAtom2, inequivalenceAtom));
270 DNFPredicate predicate = new DNFPredicate("Inequality", parameters, List.of(clause));
271
272 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, friend),
273 Set.of(personView, friendMustView), Set.of(predicate));
274 QueryableModel model = store.createModel();
275
276 model.put(person, Tuple.of(0), true);
277 model.put(person, Tuple.of(1), true);
278 model.put(person, Tuple.of(2), true);
279 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
280 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
281
282 model.flushChanges();
283 assertEquals(2, model.countResults(predicate));
284 compareMatchSets(model.allResults(predicate),
285 Set.of(List.of(Tuple.of(0), Tuple.of(1), Tuple.of(2)), List.of(Tuple.of(1), Tuple.of(0), Tuple.of(2))));
286 }
287
288 @Test
289 void patternCallTest() {
290 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
291 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
292 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
293 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
294
295 Variable p1 = new Variable("p1");
296 Variable p2 = new Variable("p2");
297 List<Variable> parameters = Arrays.asList(p1, p2);
298
299 RelationAtom personRelationAtom1 = new RelationAtom(personView, List.of(p1));
300 RelationAtom personRelationAtom2 = new RelationAtom(personView, List.of(p2));
301 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
302 DNFAnd clause = new DNFAnd(Collections.emptySet(),
303 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
304 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, List.of(clause));
305
306 Variable p3 = new Variable("p3");
307 Variable p4 = new Variable("p4");
308 List<Variable> substitution = Arrays.asList(p3, p4);
309 RelationAtom personRelationAtom3 = new RelationAtom(personView, List.of(p3));
310 RelationAtom personRelationAtom4 = new RelationAtom(personView, List.of(p4));
311 PredicateAtom friendPredicateAtom = new PredicateAtom(true, false, friendPredicate, substitution);
312 DNFAnd patternCallClause = new DNFAnd(Collections.emptySet(),
313 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
314 DNFPredicate predicate = new DNFPredicate("PatternCall", substitution, List.of(patternCallClause));
315
316 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, friend),
317 Set.of(personView, friendMustView), Set.of(friendPredicate, predicate));
318 QueryableModel model = store.createModel();
319
320 model.put(person, Tuple.of(0), true);
321 model.put(person, Tuple.of(1), true);
322 model.put(person, Tuple.of(2), true);
323 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
324 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
325 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
326
327 model.flushChanges();
328
329 assertEquals(3, model.countResults(friendPredicate));
330 }
331
332 @Test
333 void negativePatternCallTest() {
334 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
335 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
336 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
337 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
338
339 Variable p1 = new Variable("p1");
340 Variable p2 = new Variable("p2");
341 List<Variable> parameters = Arrays.asList(p1, p2);
342
343 RelationAtom personRelationAtom1 = new RelationAtom(personView, List.of(p1));
344 RelationAtom personRelationAtom2 = new RelationAtom(personView, List.of(p2));
345 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
346 DNFAnd clause = new DNFAnd(Collections.emptySet(),
347 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
348 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, List.of(clause));
349
350 Variable p3 = new Variable("p3");
351 Variable p4 = new Variable("p4");
352 List<Variable> substitution = Arrays.asList(p3, p4);
353 RelationAtom personRelationAtom3 = new RelationAtom(personView, List.of(p3));
354 RelationAtom personRelationAtom4 = new RelationAtom(personView, List.of(p4));
355 PredicateAtom friendPredicateAtom = new PredicateAtom(false, false, friendPredicate, substitution);
356 DNFAnd negativePatternCallClause = new DNFAnd(Collections.emptySet(),
357 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
358 DNFPredicate predicate = new DNFPredicate("NegativePatternCall", substitution,
359 List.of(negativePatternCallClause));
360
361 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, friend),
362 Set.of(personView, friendMustView), Set.of(friendPredicate, predicate));
363 QueryableModel model = store.createModel();
364
365 model.put(person, Tuple.of(0), true);
366 model.put(person, Tuple.of(1), true);
367 model.put(person, Tuple.of(2), true);
368 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
369 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
370 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
371
372 model.flushChanges();
373 assertEquals(6, model.countResults(predicate));
374 }
375
376 @Test
377 void transitivePatternCallTest() {
378 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
379 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
380 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
381 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
382
383 Variable p1 = new Variable("p1");
384 Variable p2 = new Variable("p2");
385 List<Variable> parameters = Arrays.asList(p1, p2);
386
387 RelationAtom personRelationAtom1 = new RelationAtom(personView, List.of(p1));
388 RelationAtom personRelationAtom2 = new RelationAtom(personView, List.of(p2));
389 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
390 DNFAnd clause = new DNFAnd(Collections.emptySet(),
391 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
392 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, List.of(clause));
393
394 Variable p3 = new Variable("p3");
395 Variable p4 = new Variable("p4");
396 List<Variable> substitution = Arrays.asList(p3, p4);
397 RelationAtom personRelationAtom3 = new RelationAtom(personView, List.of(p3));
398 RelationAtom personRelationAtom4 = new RelationAtom(personView, List.of(p4));
399 PredicateAtom friendPredicateAtom = new PredicateAtom(true, true, friendPredicate, substitution);
400 DNFAnd patternCallClause = new DNFAnd(Collections.emptySet(),
401 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
402 DNFPredicate predicate = new DNFPredicate("TransitivePatternCall", substitution,
403 List.of(patternCallClause));
404
405 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, friend),
406 Set.of(personView, friendMustView), Set.of(friendPredicate, predicate));
407 QueryableModel model = store.createModel();
408
409 model.put(person, Tuple.of(0), true);
410 model.put(person, Tuple.of(1), true);
411 model.put(person, Tuple.of(2), true);
412 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
413 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
414
415 model.flushChanges();
416 assertEquals(3, model.countResults(predicate));
417 }
418 static void compareMatchSets(Stream<Object[]> matchSet, Set<List<Tuple>> expected) {
419 Set<List<Tuple>> translatedMatchSet = new HashSet<>();
420 var iterator = matchSet.iterator();
421 while (iterator.hasNext()) {
422 var element = iterator.next();
423 List<Tuple> elementToTranslatedMatchSet = new ArrayList<>();
424 for (Object o : element) {
425 elementToTranslatedMatchSet.add((Tuple) o);
426 }
427 translatedMatchSet.add(elementToTranslatedMatchSet);
428 }
429
430 assertEquals(expected, translatedMatchSet);
431 }
432}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTransactionTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTransactionTest.java
new file mode 100644
index 00000000..613d0074
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryTransactionTest.java
@@ -0,0 +1,56 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.junit.jupiter.api.Test;
4import tools.refinery.store.model.Tuple;
5import tools.refinery.store.model.representation.Relation;
6import tools.refinery.store.query.QueryableModel;
7import tools.refinery.store.query.QueryableModelStore;
8import tools.refinery.store.query.building.DNFAnd;
9import tools.refinery.store.query.building.DNFPredicate;
10import tools.refinery.store.query.building.RelationAtom;
11import tools.refinery.store.query.building.Variable;
12import tools.refinery.store.query.viatra.ViatraQueryableModelStore;
13import tools.refinery.store.query.view.KeyOnlyRelationView;
14import tools.refinery.store.query.view.RelationView;
15
16import java.util.Collections;
17import java.util.List;
18import java.util.Set;
19
20import static org.junit.jupiter.api.Assertions.assertEquals;
21
22class QueryTransactionTest {
23 @Test
24 void flushTest() {
25 Relation<Boolean> person = new Relation<>("Person", 1, false);
26 Relation<Boolean> asset = new Relation<>("Asset", 1, false);
27 RelationView<Boolean> personView = new KeyOnlyRelationView(person);
28
29 List<Variable> parameters = List.of(new Variable("p1"));
30 RelationAtom personRelationAtom = new RelationAtom(personView, parameters);
31 DNFAnd clause = new DNFAnd(Collections.emptySet(), List.of(personRelationAtom));
32 DNFPredicate predicate = new DNFPredicate("TypeConstraint", parameters, List.of(clause));
33
34 QueryableModelStore store = new ViatraQueryableModelStore(Set.of(person, asset), Set.of(personView),
35 Set.of(predicate));
36 QueryableModel model = store.createModel();
37
38 assertEquals(0, model.countResults(predicate));
39
40 model.put(person, Tuple.of(0), true);
41 model.put(person, Tuple.of(1), true);
42 model.put(asset, Tuple.of(1), true);
43 model.put(asset, Tuple.of(2), true);
44
45 assertEquals(0, model.countResults(predicate));
46
47 model.flushChanges();
48 assertEquals(2, model.countResults(predicate));
49
50 model.put(person, Tuple.of(4), true);
51 assertEquals(2, model.countResults(predicate));
52
53 model.flushChanges();
54 assertEquals(3, model.countResults(predicate));
55 }
56}