aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-10-25 16:41:33 +0200
committerLibravatar GitHub <noreply@github.com>2021-10-25 16:41:33 +0200
commit23c95273d15eb6d39db432c89fe87738aee2777d (patch)
tree4599414555ac8f41ac0a6190c47758f8a5179aeb
parentMerge pull request #4 from garamibence/feat/direct-rules (diff)
parentRemoved unnecessary quantified variables (diff)
downloadrefinery-23c95273d15eb6d39db432c89fe87738aee2777d.tar.gz
refinery-23c95273d15eb6d39db432c89fe87738aee2777d.tar.zst
refinery-23c95273d15eb6d39db432c89fe87738aee2777d.zip
Merge pull request #5 from OszkarSemerath/main
Quarriable model support for treemap data structures
-rw-r--r--store/src/main/java/tools/refinery/store/map/internal/MutableNode.java22
-rw-r--r--store/src/main/java/tools/refinery/store/query/QueriableModel.java30
-rw-r--r--store/src/main/java/tools/refinery/store/query/QueriableModelStore.java23
-rw-r--r--store/src/main/java/tools/refinery/store/query/QueriableModelStoreImpl.java127
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/DNF2PQuery.java50
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/ModelUpdateListener.java109
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/PredicateResult.java24
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/PredicateTranslator.java210
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/QueriableModelImpl.java213
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/RawPatternMatcher.java57
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/RelationUpdateListener.java52
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/RelationalEngineContext.java2
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/RelationalRuntimeContext.java14
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/RelationalScope.java (renamed from store/src/main/java/tools/refinery/store/query/RelationalScope.java)22
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/ViewUpdate.java4
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/ViewUpdateBuffer.java46
-rw-r--r--store/src/main/java/tools/refinery/store/query/internal/ViewUpdateTranslator.java (renamed from store/src/main/java/tools/refinery/store/query/internal/RelationUpdateListenerEntry.java)27
-rw-r--r--store/src/main/java/tools/refinery/store/query/view/FilteredRelationView.java2
-rw-r--r--store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java2
-rw-r--r--store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java2
-rw-r--r--store/src/main/java/tools/refinery/store/query/view/RelationView.java2
-rw-r--r--store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java22
-rw-r--r--store/src/test/java/tools/refinery/store/query/test/QueryTest.java422
-rw-r--r--store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java58
24 files changed, 1000 insertions, 542 deletions
diff --git a/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java b/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java
index 7e94758c..54853010 100644
--- a/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java
+++ b/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java
@@ -79,7 +79,7 @@ public class MutableNode<K, V> extends Node<K, V> {
79 } 79 }
80 80
81 @Override 81 @Override
82 public Node<K, V> putValue(K key, V value, OldValueBox<V> oldValue, ContinousHashProvider<? super K> hashProvider, 82 public Node<K, V> putValue(K key, V value, OldValueBox<V> oldValueBox, ContinousHashProvider<? super K> hashProvider,
83 V defaultValue, int hash, int depth) { 83 V defaultValue, int hash, int depth) {
84 int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); 84 int selectedHashFragment = hashFragment(hash, shiftDepth(depth));
85 @SuppressWarnings("unchecked") 85 @SuppressWarnings("unchecked")
@@ -89,20 +89,20 @@ public class MutableNode<K, V> extends Node<K, V> {
89 if (keyCandidate.equals(key)) { 89 if (keyCandidate.equals(key)) {
90 // The key is equals to an existing key -> update entry 90 // The key is equals to an existing key -> update entry
91 if (value == defaultValue) { 91 if (value == defaultValue) {
92 return removeEntry(selectedHashFragment, oldValue); 92 return removeEntry(selectedHashFragment, oldValueBox);
93 } else { 93 } else {
94 return updateValue(value, oldValue, selectedHashFragment); 94 return updateValue(value, oldValueBox, selectedHashFragment);
95 } 95 }
96 } else { 96 } else {
97 // The key is not equivalent to an existing key on the same hash bin 97 // The key is not equivalent to an existing key on the same hash bin
98 // -> split entry if it is necessary 98 // -> split entry if it is necessary
99 if (value == defaultValue) { 99 if (value == defaultValue) {
100 // Value is default -> do not need to add new node 100 // Value is default -> do not need to add new node
101 oldValue.setOldValue(defaultValue); 101 oldValueBox.setOldValue(defaultValue);
102 return this; 102 return this;
103 } else { 103 } else {
104 // Value is not default -> Split entry data to a new node 104 // Value is not default -> Split entry data to a new node
105 oldValue.setOldValue(defaultValue); 105 oldValueBox.setOldValue(defaultValue);
106 return moveDownAndSplit(hashProvider, key, value, keyCandidate, hash, depth, selectedHashFragment); 106 return moveDownAndSplit(hashProvider, key, value, keyCandidate, hash, depth, selectedHashFragment);
107 } 107 }
108 } 108 }
@@ -112,28 +112,26 @@ public class MutableNode<K, V> extends Node<K, V> {
112 var nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1]; 112 var nodeCandidate = (Node<K, V>) content[2 * selectedHashFragment + 1];
113 if (nodeCandidate != null) { 113 if (nodeCandidate != null) {
114 // If it has value, it is a subnode -> upate that 114 // If it has value, it is a subnode -> upate that
115 var newNode = nodeCandidate.putValue(key, value, oldValue, hashProvider, defaultValue, 115 var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue,
116 newHash(hashProvider, key, hash, depth + 1), depth + 1); 116 newHash(hashProvider, key, hash, depth + 1), depth + 1);
117 return updateWithSubNode(selectedHashFragment, newNode, value.equals(defaultValue)); 117 return updateWithSubNode(selectedHashFragment, newNode, value.equals(defaultValue));
118 } else { 118 } else {
119 // If it does not have value, put it in the empty place 119 // If it does not have value, put it in the empty place
120 if (value == defaultValue) { 120 if (value == defaultValue) {
121 // dont need to add new key-value pair 121 // dont need to add new key-value pair
122 oldValue.setOldValue(defaultValue); 122 oldValueBox.setOldValue(defaultValue);
123 return this; 123 return this;
124 } else { 124 } else {
125 return addEntry(key, value, oldValue, selectedHashFragment); 125 return addEntry(key, value, oldValueBox, selectedHashFragment, defaultValue);
126 } 126 }
127 127
128 } 128 }
129 } 129 }
130 } 130 }
131 131
132 private Node<K, V> addEntry(K key, V value, OldValueBox<V> oldValueBox, int selectedHashFragment) { 132 private Node<K, V> addEntry(K key, V value, OldValueBox<V> oldValueBox, int selectedHashFragment, V defaultValue) {
133 content[2 * selectedHashFragment] = key; 133 content[2 * selectedHashFragment] = key;
134 @SuppressWarnings("unchecked") 134 oldValueBox.setOldValue(defaultValue);
135 V oldValue = (V) content[2 * selectedHashFragment + 1];
136 oldValueBox.setOldValue(oldValue);
137 content[2 * selectedHashFragment + 1] = value; 135 content[2 * selectedHashFragment + 1] = value;
138 updateHash(); 136 updateHash();
139 return this; 137 return this;
diff --git a/store/src/main/java/tools/refinery/store/query/QueriableModel.java b/store/src/main/java/tools/refinery/store/query/QueriableModel.java
new file mode 100644
index 00000000..f669b3ed
--- /dev/null
+++ b/store/src/main/java/tools/refinery/store/query/QueriableModel.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query;
2
3import java.util.Optional;
4import java.util.Set;
5import java.util.stream.Stream;
6
7import tools.refinery.store.model.Model;
8import tools.refinery.store.query.building.DNFPredicate;
9
10public interface QueriableModel extends Model{
11 Set<DNFPredicate> getPredicates();
12
13 void flushChanges();
14
15 boolean hasResult(DNFPredicate predicate);
16
17 boolean hasResult(DNFPredicate predicate, Object[] parameters);
18
19 Optional<Object[]> oneResult(DNFPredicate predicate);
20
21 Optional<Object[]> oneResult(DNFPredicate predicate, Object[] parameters);
22
23 Stream<Object[]> allResults(DNFPredicate predicate);
24
25 Stream<Object[]> allResults(DNFPredicate predicate, Object[] parameters);
26
27 int countResults(DNFPredicate predicate);
28
29 int countResults(DNFPredicate predicate, Object[] parameters);
30}
diff --git a/store/src/main/java/tools/refinery/store/query/QueriableModelStore.java b/store/src/main/java/tools/refinery/store/query/QueriableModelStore.java
new file mode 100644
index 00000000..3a5b51ff
--- /dev/null
+++ b/store/src/main/java/tools/refinery/store/query/QueriableModelStore.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query;
2
3import java.util.Set;
4
5import tools.refinery.store.model.ModelDiffCursor;
6import tools.refinery.store.model.ModelStore;
7import tools.refinery.store.model.representation.DataRepresentation;
8import tools.refinery.store.query.building.DNFPredicate;
9import tools.refinery.store.query.view.RelationView;
10
11public interface QueriableModelStore extends ModelStore{
12 @SuppressWarnings("squid:S1452")
13 Set<DataRepresentation<?, ?>> getDataRepresentations();
14 @SuppressWarnings("squid:S1452")
15 Set<RelationView<?>> getViews();
16 Set<DNFPredicate> getPredicates();
17
18 QueriableModel createModel();
19 QueriableModel createModel(long state);
20
21 Set<Long> getStates();
22 ModelDiffCursor getDiffCursor(long from, long to);
23}
diff --git a/store/src/main/java/tools/refinery/store/query/QueriableModelStoreImpl.java b/store/src/main/java/tools/refinery/store/query/QueriableModelStoreImpl.java
new file mode 100644
index 00000000..653783dd
--- /dev/null
+++ b/store/src/main/java/tools/refinery/store/query/QueriableModelStoreImpl.java
@@ -0,0 +1,127 @@
1package tools.refinery.store.query;
2
3import java.util.Collections;
4import java.util.HashMap;
5import java.util.Map;
6import java.util.Set;
7
8import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
9
10import tools.refinery.store.model.ModelDiffCursor;
11import tools.refinery.store.model.ModelStore;
12import tools.refinery.store.model.ModelStoreImpl;
13import tools.refinery.store.model.representation.DataRepresentation;
14import tools.refinery.store.query.building.DNFAnd;
15import tools.refinery.store.query.building.DNFAtom;
16import tools.refinery.store.query.building.DNFPredicate;
17import tools.refinery.store.query.building.PredicateAtom;
18import tools.refinery.store.query.building.RelationAtom;
19import tools.refinery.store.query.internal.DNF2PQuery;
20import tools.refinery.store.query.internal.QueriableModelImpl;
21import tools.refinery.store.query.internal.RawPatternMatcher;
22import tools.refinery.store.query.internal.DNF2PQuery.SimplePQuery;
23import tools.refinery.store.query.view.RelationView;
24
25public class QueriableModelStoreImpl implements QueriableModelStore {
26 protected final ModelStore store;
27 protected final Set<RelationView<?>> relationViews;
28 protected final Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates;
29
30 public QueriableModelStoreImpl(Set<DataRepresentation<?, ?>> dataRepresentations,
31 Set<RelationView<?>> relationViews, Set<DNFPredicate> predicates) {
32 this.store = new ModelStoreImpl(dataRepresentations);
33 validateViews(dataRepresentations, relationViews);
34 this.relationViews = Collections.unmodifiableSet(relationViews);
35 validatePredicates(relationViews, predicates);
36 this.predicates = initPredicates(predicates);
37 }
38
39 private void validateViews(Set<DataRepresentation<?, ?>> dataRepresentations, Set<RelationView<?>> relationViews) {
40 for (RelationView<?> relationView : relationViews) {
41 // TODO: make it work for non-relation representation?
42 if (!dataRepresentations.contains(relationView.getRepresentation())) {
43 throw new IllegalArgumentException(
44 DataRepresentation.class.getSimpleName() + " " + relationView.getStringID() + " added to "
45 + QueriableModelStore.class.getSimpleName() + " without a referred representation.");
46 }
47 }
48 }
49
50 private void validatePredicates(Set<RelationView<?>> relationViews, Set<DNFPredicate> predicates) {
51 for (DNFPredicate dnfPredicate : predicates) {
52 for (DNFAnd clause : dnfPredicate.getClauses()) {
53 for (DNFAtom atom : clause.getConstraints()) {
54 if (atom instanceof RelationAtom relationAtom) {
55 validateRelationAtom(relationViews, dnfPredicate, relationAtom);
56 } else if (atom instanceof PredicateAtom predicateAtom) {
57 validatePredicateAtom(predicates, dnfPredicate, predicateAtom);
58 }
59 }
60 }
61 }
62 }
63
64 private void validateRelationAtom(Set<RelationView<?>> relationViews, DNFPredicate dnfPredicate,
65 RelationAtom relationAtom) {
66 if (!relationViews.contains(relationAtom.getView())) {
67 throw new IllegalArgumentException(DNFPredicate.class.getSimpleName() + " "
68 + dnfPredicate.getUniqueName() + " contains reference to a view of "
69 + relationAtom.getView().getRepresentation().getName()
70 + " that is not in the model.");
71 }
72 }
73 private void validatePredicateAtom(Set<DNFPredicate> predicates, DNFPredicate dnfPredicate,
74 PredicateAtom predicateAtom) {
75 if (!predicates.contains(predicateAtom.getReferred())) {
76 throw new IllegalArgumentException(
77 DNFPredicate.class.getSimpleName() + " " + dnfPredicate.getUniqueName()
78 + " contains reference to a predicate "
79 + predicateAtom.getReferred().getName()
80 + "that is not in the model.");
81 }
82 }
83
84 private Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> initPredicates(Set<DNFPredicate> predicates) {
85 Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> result = new HashMap<>();
86 Map<DNFPredicate, SimplePQuery> dnf2PQueryMap = new HashMap<>();
87 for (DNFPredicate dnfPredicate : predicates) {
88 GenericQuerySpecification<RawPatternMatcher> query = DNF2PQuery.translate(dnfPredicate,dnf2PQueryMap).build();
89 result.put(dnfPredicate, query);
90 }
91
92 return result;
93 }
94
95 @Override
96 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
97 return store.getDataRepresentations();
98 }
99 @Override
100 public Set<RelationView<?>> getViews() {
101 return this.relationViews;
102 }
103 @Override
104 public Set<DNFPredicate> getPredicates() {
105 return predicates.keySet();
106 }
107
108 @Override
109 public QueriableModel createModel() {
110 return new QueriableModelImpl(this, this.store.createModel(), predicates);
111 }
112
113 @Override
114 public QueriableModel createModel(long state) {
115 return new QueriableModelImpl(this, this.store.createModel(state), predicates);
116 }
117
118 @Override
119 public synchronized Set<Long> getStates() {
120 return this.store.getStates();
121 }
122
123 @Override
124 public synchronized ModelDiffCursor getDiffCursor(long from, long to) {
125 return this.store.getDiffCursor(from, to);
126 }
127}
diff --git a/store/src/main/java/tools/refinery/store/query/internal/DNF2PQuery.java b/store/src/main/java/tools/refinery/store/query/internal/DNF2PQuery.java
index c9c8245b..c72e0234 100644
--- a/store/src/main/java/tools/refinery/store/query/internal/DNF2PQuery.java
+++ b/store/src/main/java/tools/refinery/store/query/internal/DNF2PQuery.java
@@ -8,7 +8,6 @@ import java.util.List;
8import java.util.Map; 8import java.util.Map;
9import java.util.Set; 9import java.util.Set;
10 10
11import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
12import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; 11import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
13import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine; 12import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
14import org.eclipse.viatra.query.runtime.api.scope.QueryScope; 13import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
@@ -27,7 +26,6 @@ import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
27import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; 26import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
28import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 27import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
29 28
30import tools.refinery.store.query.RelationalScope;
31import tools.refinery.store.query.building.DNFAnd; 29import tools.refinery.store.query.building.DNFAnd;
32import tools.refinery.store.query.building.DNFAtom; 30import tools.refinery.store.query.building.DNFAtom;
33import tools.refinery.store.query.building.DNFPredicate; 31import tools.refinery.store.query.building.DNFPredicate;
@@ -37,9 +35,9 @@ import tools.refinery.store.query.building.RelationAtom;
37import tools.refinery.store.query.building.Variable; 35import tools.refinery.store.query.building.Variable;
38 36
39public class DNF2PQuery { 37public class DNF2PQuery {
40 private static Map<DNFPredicate, SimplePQuery> DNF2PQueryMap = new HashMap<>(); 38 //private static Map<DNFPredicate, SimplePQuery> DNF2PQueryMap = new HashMap<>();
41 39
42 public static SimplePQuery translate(DNFPredicate predicate) { 40 public static SimplePQuery translate(DNFPredicate predicate, Map<DNFPredicate, SimplePQuery> DNF2PQueryMap) {
43 SimplePQuery query = DNF2PQueryMap.get(predicate); 41 SimplePQuery query = DNF2PQueryMap.get(predicate);
44 if (query != null) { 42 if (query != null) {
45 return query; 43 return query;
@@ -48,25 +46,29 @@ public class DNF2PQuery {
48 Map<Variable, PParameter> parameters = new HashMap<>(); 46 Map<Variable, PParameter> parameters = new HashMap<>();
49 47
50 predicate.getVariables().forEach(variable -> parameters.put(variable, new PParameter(variable.getName()))); 48 predicate.getVariables().forEach(variable -> parameters.put(variable, new PParameter(variable.getName())));
51 query.setParameter(new ArrayList<>(parameters.values())); 49 List<PParameter> parameterList = new ArrayList<>();
50 for(var param : predicate.getVariables()) {
51 parameterList.add(parameters.get(param));
52 }
53 query.setParameter(parameterList);
52 for (DNFAnd clause : predicate.getClauses()) { 54 for (DNFAnd clause : predicate.getClauses()) {
53 PBody body = new PBody(query); 55 PBody body = new PBody(query);
54 List<ExportedParameter> symbolicParameters = new ArrayList<>(); 56 List<ExportedParameter> symbolicParameters = new ArrayList<>();
55 parameters.forEach((variable, parameter) -> { 57 for(var param : predicate.getVariables()) {
56 PVariable pVar = body.getOrCreateVariableByName(variable.getName()); 58 PVariable pVar = body.getOrCreateVariableByName(param.getName());
57 symbolicParameters.add(new ExportedParameter(body, pVar, parameter)); 59 symbolicParameters.add(new ExportedParameter(body, pVar, parameters.get(param)));
58 }); 60 }
59 body.setSymbolicParameters(symbolicParameters); 61 body.setSymbolicParameters(symbolicParameters);
60 query.addBody(body); 62 query.addBody(body);
61 for (DNFAtom constraint : clause.getConstraints()) { 63 for (DNFAtom constraint : clause.getConstraints()) {
62 translateDNFAtom(constraint, body); 64 translateDNFAtom(constraint, body, DNF2PQueryMap);
63 } 65 }
64 } 66 }
65 DNF2PQueryMap.put(predicate, query); 67 DNF2PQueryMap.put(predicate, query);
66 return query; 68 return query;
67 } 69 }
68 70
69 private static void translateDNFAtom(DNFAtom constraint, PBody body) { 71 private static void translateDNFAtom(DNFAtom constraint, PBody body, Map<DNFPredicate, SimplePQuery> DNF2PQueryMap) {
70 if (constraint instanceof EquivalenceAtom equivalence) { 72 if (constraint instanceof EquivalenceAtom equivalence) {
71 translateEquivalenceAtom(equivalence, body); 73 translateEquivalenceAtom(equivalence, body);
72 } 74 }
@@ -74,7 +76,7 @@ public class DNF2PQuery {
74 translateRelationAtom(relation, body); 76 translateRelationAtom(relation, body);
75 } 77 }
76 if (constraint instanceof PredicateAtom predicate) { 78 if (constraint instanceof PredicateAtom predicate) {
77 translatePredicateAtom(predicate, body); 79 translatePredicateAtom(predicate, body, DNF2PQueryMap);
78 } 80 }
79 } 81 }
80 82
@@ -99,7 +101,7 @@ public class DNF2PQuery {
99 new TypeConstraint(body, Tuples.flatTupleOf(variables), relation.getView()); 101 new TypeConstraint(body, Tuples.flatTupleOf(variables), relation.getView());
100 } 102 }
101 103
102 private static void translatePredicateAtom(PredicateAtom predicate, PBody body) { 104 private static void translatePredicateAtom(PredicateAtom predicate, PBody body, Map<DNFPredicate, SimplePQuery> DNF2PQueryMap) {
103 Object[] variables = new Object[predicate.getSubstitution().size()]; 105 Object[] variables = new Object[predicate.getSubstitution().size()];
104 for (int i = 0; i < predicate.getSubstitution().size(); i++) { 106 for (int i = 0; i < predicate.getSubstitution().size(); i++) {
105 variables[i] = body.getOrCreateVariableByName(predicate.getSubstitution().get(i).getName()); 107 variables[i] = body.getOrCreateVariableByName(predicate.getSubstitution().get(i).getName());
@@ -110,17 +112,17 @@ public class DNF2PQuery {
110 throw new IllegalArgumentException("Transitive Predicate Atoms must be binary."); 112 throw new IllegalArgumentException("Transitive Predicate Atoms must be binary.");
111 } 113 }
112 new BinaryTransitiveClosure(body, Tuples.flatTupleOf(variables), 114 new BinaryTransitiveClosure(body, Tuples.flatTupleOf(variables),
113 DNF2PQuery.translate(predicate.getReferred())); 115 DNF2PQuery.translate(predicate.getReferred(), DNF2PQueryMap));
114 } else { 116 } else {
115 new PositivePatternCall(body, Tuples.flatTupleOf(variables), 117 new PositivePatternCall(body, Tuples.flatTupleOf(variables),
116 DNF2PQuery.translate(predicate.getReferred())); 118 DNF2PQuery.translate(predicate.getReferred(), DNF2PQueryMap));
117 } 119 }
118 } else { 120 } else {
119 if (predicate.isTransitive()) { 121 if (predicate.isTransitive()) {
120 throw new InputMismatchException("Transitive Predicate Atoms cannot be negative."); 122 throw new InputMismatchException("Transitive Predicate Atoms cannot be negative.");
121 } else { 123 } else {
122 new NegativePatternCall(body, Tuples.flatTupleOf(variables), 124 new NegativePatternCall(body, Tuples.flatTupleOf(variables),
123 DNF2PQuery.translate(predicate.getReferred())); 125 DNF2PQuery.translate(predicate.getReferred(), DNF2PQueryMap));
124 } 126 }
125 } 127 }
126 } 128 }
@@ -160,8 +162,8 @@ public class DNF2PQuery {
160 return bodies; 162 return bodies;
161 } 163 }
162 164
163 public GenericQuerySpecification<GenericPatternMatcher> build() { 165 public GenericQuerySpecification<RawPatternMatcher> build() {
164 return new GenericQuerySpecification<GenericPatternMatcher>(this) { 166 return new GenericQuerySpecification<RawPatternMatcher>(this) {
165 167
166 @Override 168 @Override
167 public Class<? extends QueryScope> getPreferredScopeClass() { 169 public Class<? extends QueryScope> getPreferredScopeClass() {
@@ -169,13 +171,17 @@ public class DNF2PQuery {
169 } 171 }
170 172
171 @Override 173 @Override
172 protected GenericPatternMatcher instantiate(ViatraQueryEngine engine) { 174 protected RawPatternMatcher instantiate(ViatraQueryEngine engine) {
173 return defaultInstantiate(engine); 175 RawPatternMatcher matcher = engine.getExistingMatcher(this);
176 if (matcher == null) {
177 matcher = engine.getMatcher(this);
178 }
179 return matcher;
174 } 180 }
175 181
176 @Override 182 @Override
177 public GenericPatternMatcher instantiate() { 183 public RawPatternMatcher instantiate() {
178 return new GenericPatternMatcher(this); 184 return new RawPatternMatcher(this);
179 } 185 }
180 186
181 }; 187 };
diff --git a/store/src/main/java/tools/refinery/store/query/internal/ModelUpdateListener.java b/store/src/main/java/tools/refinery/store/query/internal/ModelUpdateListener.java
new file mode 100644
index 00000000..c784cb3c
--- /dev/null
+++ b/store/src/main/java/tools/refinery/store/query/internal/ModelUpdateListener.java
@@ -0,0 +1,109 @@
1package tools.refinery.store.query.internal;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.Map;
6import java.util.Set;
7
8import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
9import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
10
11import tools.refinery.store.model.Tuple;
12import tools.refinery.store.model.representation.Relation;
13import tools.refinery.store.query.view.RelationView;
14
15public class ModelUpdateListener {
16 /**
17 * Collections of Relations and their Views.
18 */
19 private final Map<Relation<?>, Set<RelationView<?>>> relation2View;
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.get(relation);
39 if (views == null) {
40 views = new HashSet<>();
41 relation2View.put(relation, views);
42 }
43 views.add(view);
44
45 // 2. register notifier map to views, if necessary
46 if (!view2Buffers.containsKey(view)) {
47 view2Buffers.put(view, new HashSet<>());
48 }
49 }
50
51 boolean containsRelationalView(RelationView<?> relationalKey) {
52 return view2Buffers.containsKey(relationalKey);
53 }
54
55 <D> void addListener(RelationView<D> relationView, ITuple seed, IQueryRuntimeContextListener listener) {
56 if (view2Buffers.containsKey(relationView)) {
57 ViewUpdateTranslator<D> updateListener = new ViewUpdateTranslator<>(relationView, seed, listener);
58 ViewUpdateBuffer<D> updateBuffer = new ViewUpdateBuffer<>(updateListener);
59 view2Buffers.get(relationView).add(updateBuffer);
60 } else
61 throw new IllegalArgumentException();
62 }
63
64 void removeListener(RelationView<?> relationView, ITuple seed, IQueryRuntimeContextListener listener) {
65 if (view2Buffers.containsKey(relationView)) {
66 Set<ViewUpdateBuffer<?>> buffers = this.view2Buffers.get(relationView);
67 for(var buffer : buffers) {
68 if(buffer.getUpdateListener().key == seed && buffer.getUpdateListener().listener == listener) {
69 // remove buffer and terminate immediately, or it will break iterator.
70 buffers.remove(buffer);
71 return;
72 }
73 }
74 } else
75 throw new IllegalArgumentException();
76 }
77
78 public <D> void addUpdate(Relation<D> relation, Tuple key, D oldValue, D newValue) {
79 var views = this.relation2View.get(relation);
80 if (views != null) {
81 for (var view : views) {
82 var buffers = this.view2Buffers.get(view);
83 for (var buffer : buffers) {
84 @SuppressWarnings("unchecked")
85 var typedBuffer = (ViewUpdateBuffer<D>) buffer;
86 typedBuffer.addChange(key, oldValue, newValue);
87 }
88 }
89 }
90 }
91
92 public boolean hasChange() {
93 for (var bufferCollection : this.view2Buffers.values()) {
94 for (ViewUpdateBuffer<?> buffer : bufferCollection) {
95 if (buffer.hasChange())
96 return true;
97 }
98 }
99 return false;
100 }
101
102 public void flush() {
103 for (var bufferCollection : this.view2Buffers.values()) {
104 for (ViewUpdateBuffer<?> buffer : bufferCollection) {
105 buffer.flush();
106 }
107 }
108 }
109}
diff --git a/store/src/main/java/tools/refinery/store/query/internal/PredicateResult.java b/store/src/main/java/tools/refinery/store/query/internal/PredicateResult.java
new file mode 100644
index 00000000..65d23eb6
--- /dev/null
+++ b/store/src/main/java/tools/refinery/store/query/internal/PredicateResult.java
@@ -0,0 +1,24 @@
1package tools.refinery.store.query.internal;
2
3import java.util.Optional;
4import java.util.stream.Stream;
5
6public interface PredicateResult {
7
8 boolean hasResult();
9
10 boolean hasResult(Object[] parameters);
11
12 Optional<Object[]> oneResult();
13
14 Optional<Object[]> oneResult(Object[] parameters);
15
16 Stream<Object[]> allResults();
17
18 Stream<Object[]> allResults(Object[] parameters);
19
20 int countResults();
21
22 int countResults(Object[] parameters);
23
24} \ No newline at end of file
diff --git a/store/src/main/java/tools/refinery/store/query/internal/PredicateTranslator.java b/store/src/main/java/tools/refinery/store/query/internal/PredicateTranslator.java
deleted file mode 100644
index 6b050182..00000000
--- a/store/src/main/java/tools/refinery/store/query/internal/PredicateTranslator.java
+++ /dev/null
@@ -1,210 +0,0 @@
1package tools.refinery.store.query.internal;
2
3import java.util.ArrayList;
4import java.util.HashMap;
5import java.util.LinkedHashSet;
6import java.util.LinkedList;
7import java.util.List;
8import java.util.Map;
9import java.util.Set;
10
11import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
12import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
13import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
14import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
15import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
16import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
17import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
18import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality;
19import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
20import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality;
21import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
22import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure;
23import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
24import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
25import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
26import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery;
27import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
28import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
29import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
30import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
31
32import tools.refinery.store.query.RelationalScope;
33import tools.refinery.store.query.view.RelationView;
34
35public class PredicateTranslator extends BasePQuery {
36
37 private final Map<String, PParameter> parameters = new HashMap<String, PParameter>();
38 private String fullyQualifiedName;
39 private LinkedList<PBody> bodies = new LinkedList<PBody>();
40 private List<ExportedParameter> symbolicParameters;
41
42 public PredicateTranslator(String fullyQualifiedName) {
43 super(PVisibility.PUBLIC);
44 this.fullyQualifiedName = fullyQualifiedName;
45 PBody body = new PBody(this);
46 bodies.add(body);
47 }
48
49 @Override
50 public String getFullyQualifiedName() {
51 return fullyQualifiedName;
52 }
53
54 public PredicateTranslator addParameter(String name, RelationView<?> type) {
55 PParameter parameter = new PParameter(name);
56 parameters.put(name, parameter);
57
58 PBody body = bodies.peekLast();
59 List<ExportedParameter> symbolicParameters = new ArrayList<>();
60 parameters.forEach((pName, pParameter) -> {
61 PVariable var = body.getOrCreateVariableByName(pName);
62 symbolicParameters.add(new ExportedParameter(body, var, pParameter));
63 });
64 body.setSymbolicParameters(symbolicParameters);
65
66 return this;
67 }
68
69 @Override
70 public List<PParameter> getParameters() {
71 return new ArrayList<PParameter>(parameters.values());
72 }
73 public <D> PredicateTranslator addConstraint(RelationView<D> view, String... name) {
74 if(name.length != view.getArity()) {
75 throw new IllegalArgumentException("Arity ("+view.getArity()+") does not match parameter numbers ("+name.length+")");
76 }
77 PBody body = bodies.peekLast();
78 Object[] variables = new Object[name.length];
79 for(int i = 0; i<name.length; i++) {
80 variables[i] = body.getOrCreateVariableByName(name[i]);
81 }
82 new TypeConstraint(body, Tuples.flatTupleOf(variables), view);
83 return this;
84 }
85
86// // Type constraint
87// public RelationQuery addConstraint(String type, String name) {
88// PBody body = bodies.peekLast();
89// PVariable var = body.getOrCreateVariableByName(name);
90// new TypeConstraint(body, Tuples.flatTupleOf(var), new StringExactInstancesKey(type));
91// return this;
92// }
93//
94// // Relation constraint
95// public RelationQuery addConstraint(String type, String sourceName, String targetName) {
96// PBody body = bodies.peekLast();
97// PVariable var_source = body.getOrCreateVariableByName(sourceName);
98// PVariable var_target = body.getOrCreateVariableByName(targetName);
99// new TypeConstraint(body, Tuples.flatTupleOf(var_source, var_target),
100// new StringStructuralFeatureInstancesKey(type));
101// return this;
102// }
103
104 // Create new Body
105 public PredicateTranslator or() {
106 PBody body = new PBody(this);
107 List<ExportedParameter> symbolicParameters = new ArrayList<>();
108 parameters.forEach((name, parameter) -> {
109 PVariable var = body.getOrCreateVariableByName(name);
110 symbolicParameters.add(new ExportedParameter(body, var, parameter));
111 });
112 body.setSymbolicParameters(symbolicParameters);
113 bodies.add(body);
114 return this;
115 }
116
117 // Equality constraint
118 public PredicateTranslator addEquality(String sourceName, String targetName) {
119 PBody body = bodies.peekLast();
120 PVariable var_source = body.getOrCreateVariableByName(sourceName);
121 PVariable var_target = body.getOrCreateVariableByName(targetName);
122 new Equality(body, var_source, var_target);
123 return this;
124 }
125
126 // Inequality constraint
127 public PredicateTranslator addInequality(String sourceName, String targetName) {
128 PBody body = bodies.peekLast();
129 PVariable var_source = body.getOrCreateVariableByName(sourceName);
130 PVariable var_target = body.getOrCreateVariableByName(targetName);
131 new Inequality(body, var_source, var_target);
132 return this;
133 }
134
135 // Positive pattern call
136 public PredicateTranslator addPatternCall(PQuery query, String... names) {
137 PBody body = bodies.peekLast();
138 PVariable[] vars = new PVariable[names.length];
139 for (int i = 0; i < names.length; i++) {
140 vars[i] = body.getOrCreateVariableByName(names[i]);
141 }
142 new PositivePatternCall(body, Tuples.flatTupleOf(vars), query);
143 return this;
144 }
145
146 // Negative pattern call
147 public PredicateTranslator addNegativePatternCall(PQuery query, String... names) {
148 PBody body = bodies.peekLast();
149 PVariable[] vars = new PVariable[names.length];
150 for (int i = 0; i < names.length; i++) {
151 vars[i] = body.getOrCreateVariableByName(names[i]);
152 }
153 new NegativePatternCall(body, Tuples.flatTupleOf(vars), query);
154 return this;
155 }
156
157 // Binary transitive closure pattern call
158 public PredicateTranslator addBinaryTransitiveClosure(PQuery query, String sourceName, String targetName) {
159 PBody body = bodies.peekLast();
160 PVariable var_source = body.getOrCreateVariableByName(sourceName);
161 PVariable var_target = body.getOrCreateVariableByName(targetName);
162 new BinaryTransitiveClosure(body, Tuples.flatTupleOf(var_source, var_target), query);
163 return this;
164 }
165
166 // Binary reflexive transitive closure pattern call
167 public PredicateTranslator addBinaryReflexiveTransitiveClosure(PQuery query, String sourceName, String targetName) {
168 PBody body = bodies.peekLast();
169 PVariable var_source = body.getOrCreateVariableByName(sourceName);
170 PVariable var_target = body.getOrCreateVariableByName(targetName);
171 new BinaryReflexiveTransitiveClosure(body, Tuples.flatTupleOf(var_source, var_target), query,
172 query.getParameters().get(0).getDeclaredUnaryType());
173 return this;
174 }
175
176 @Override
177 public Set<PBody> doGetContainedBodies() {
178 setEvaluationHints(new QueryEvaluationHint(null, QueryEvaluationHint.BackendRequirement.UNSPECIFIED));
179 return new LinkedHashSet<PBody>(bodies);
180 }
181
182 public void addSymbolicParameters(ExportedParameter symbolicParameter) {
183 checkMutability();
184 if (symbolicParameters == null) {
185 symbolicParameters = new ArrayList<>();
186 }
187 symbolicParameters.add(symbolicParameter);
188 }
189
190 public GenericQuerySpecification<GenericPatternMatcher> build() {
191 return new GenericQuerySpecification<GenericPatternMatcher>(this) {
192
193 @Override
194 public Class<? extends QueryScope> getPreferredScopeClass() {
195 return RelationalScope.class;
196 }
197
198 @Override
199 protected GenericPatternMatcher instantiate(ViatraQueryEngine engine) {
200 return defaultInstantiate(engine);
201 }
202
203 @Override
204 public GenericPatternMatcher instantiate() {
205 return new GenericPatternMatcher(this);
206 }
207
208 };
209 }
210} \ No newline at end of file
diff --git a/store/src/main/java/tools/refinery/store/query/internal/QueriableModelImpl.java b/store/src/main/java/tools/refinery/store/query/internal/QueriableModelImpl.java
new file mode 100644
index 00000000..f30b947a
--- /dev/null
+++ b/store/src/main/java/tools/refinery/store/query/internal/QueriableModelImpl.java
@@ -0,0 +1,213 @@
1package tools.refinery.store.query.internal;
2
3import java.util.HashMap;
4import java.util.Map;
5import java.util.Optional;
6import java.util.Set;
7import java.util.stream.Stream;
8
9import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine;
10import org.eclipse.viatra.query.runtime.api.GenericQueryGroup;
11import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
12import org.eclipse.viatra.query.runtime.api.IQueryGroup;
13
14import tools.refinery.store.map.Cursor;
15import tools.refinery.store.map.DiffCursor;
16import tools.refinery.store.model.Model;
17import tools.refinery.store.model.ModelDiffCursor;
18import tools.refinery.store.model.Tuple;
19import tools.refinery.store.model.representation.DataRepresentation;
20import tools.refinery.store.model.representation.Relation;
21import tools.refinery.store.query.QueriableModel;
22import tools.refinery.store.query.QueriableModelStore;
23import tools.refinery.store.query.building.DNFPredicate;
24
25public class QueriableModelImpl implements QueriableModel {
26 protected final QueriableModelStore store;
27 protected final Model model;
28 protected final Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates2PQuery;
29
30 protected RelationalScope scope;
31 protected AdvancedViatraQueryEngine engine;
32 protected Map<DNFPredicate, RawPatternMatcher> predicate2Matcher;
33
34 public QueriableModelImpl(QueriableModelStore store, Model model,
35 Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates2PQuery) {
36 this.store = store;
37 this.model = model;
38 this.predicates2PQuery = predicates2PQuery;
39 initEngine();
40 }
41
42 private void initEngine() {
43 this.scope = new RelationalScope(this.model, this.store.getViews());
44 this.engine = AdvancedViatraQueryEngine.createUnmanagedEngine(this.scope);
45 this.predicate2Matcher = initMatchers(this.engine, this.predicates2PQuery);
46 }
47
48 private Map<DNFPredicate, RawPatternMatcher> initMatchers(AdvancedViatraQueryEngine engine,
49 Map<DNFPredicate, GenericQuerySpecification<RawPatternMatcher>> predicates2pQuery) {
50 // 1. prepare group
51 IQueryGroup queryGroup = GenericQueryGroup.of(Set.copyOf(predicates2pQuery.values()));
52 engine.prepareGroup(queryGroup, null);
53
54 // 2. then get all matchers
55 Map<DNFPredicate, RawPatternMatcher> result = new HashMap<>();
56 for (var entry : predicates2pQuery.entrySet()) {
57 var matcher = engine.getMatcher(entry.getValue());
58 result.put(entry.getKey(), matcher);
59 }
60 return result;
61 }
62
63 @Override
64 public Set<DataRepresentation<?, ?>> getDataRepresentations() {
65 return model.getDataRepresentations();
66 }
67
68 @Override
69 public Set<DNFPredicate> getPredicates() {
70 return store.getPredicates();
71 }
72
73 @Override
74 public <K, V> V get(DataRepresentation<K, V> representation, K key) {
75 return model.get(representation, key);
76 }
77
78 @Override
79 public <K, V> Cursor<K, V> getAll(DataRepresentation<K, V> representation) {
80 return model.getAll(representation);
81 }
82
83 @SuppressWarnings("unchecked")
84 @Override
85 public <K, V> V put(DataRepresentation<K, V> representation, K key, V value) {
86 V oldValue = this.model.put(representation, key, value);
87 if(representation instanceof Relation<?> relation) {
88 this.scope.processUpdate((Relation<V>)relation, (Tuple)key, oldValue, value);
89 }
90 return oldValue;
91 }
92
93 @Override
94 public <K, V> void putAll(DataRepresentation<K, V> representation, Cursor<K, V> cursor) {
95 if(representation instanceof Relation<?>) {
96 @SuppressWarnings("unchecked")
97 Relation<V> relation = (Relation<V>) representation;
98 while(cursor.move()) {
99 Tuple key = (Tuple) cursor.getKey();
100 V newValue = cursor.getValue();
101 V oldValue = this.model.put(relation, key, newValue);
102 this.scope.processUpdate(relation, key, oldValue, newValue);
103 }
104 } else {
105 this.model.putAll(representation, cursor);
106 }
107 }
108
109 @Override
110 public <K, V> long getSize(DataRepresentation<K, V> representation) {
111 return model.getSize(representation);
112 }
113
114 protected PredicateResult getPredicateResult(DNFPredicate predicate) {
115 var result = this.predicate2Matcher.get(predicate);
116 if (result == null) {
117 throw new IllegalArgumentException("Model does not contain predicate " + predicate.getName() + "!");
118 } else
119 return result;
120 }
121
122 protected void validateParameters(DNFPredicate predicate, Object[] parameters) {
123 int predicateArity = predicate.getVariables().size();
124 int parameterArity = parameters.length;
125 if (parameterArity != predicateArity) {
126 throw new IllegalArgumentException("Predicate " + predicate.getName() + " with " + predicateArity
127 + " arity called with different number of parameters (" + parameterArity + ")!");
128 }
129 }
130
131 @Override
132 public boolean hasResult(DNFPredicate predicate) {
133 return getPredicateResult(predicate).hasResult();
134 }
135
136 @Override
137 public boolean hasResult(DNFPredicate predicate, Object[] parameters) {
138 validateParameters(predicate, parameters);
139 return getPredicateResult(predicate).hasResult(parameters);
140 }
141
142 @Override
143 public Optional<Object[]> oneResult(DNFPredicate predicate){
144 return getPredicateResult(predicate).oneResult();
145 }
146
147 @Override
148 public Optional<Object[]> oneResult(DNFPredicate predicate, Object[] parameters){
149 validateParameters(predicate, parameters);
150 return getPredicateResult(predicate).oneResult(parameters);
151 }
152
153 @Override
154 public Stream<Object[]> allResults(DNFPredicate predicate){
155 return getPredicateResult(predicate).allResults();
156 }
157
158 @Override
159 public Stream<Object[]> allResults(DNFPredicate predicate, Object[] parameters){
160 validateParameters(predicate, parameters);
161 return getPredicateResult(predicate).allResults(parameters);
162 }
163
164 @Override
165 public int countResults(DNFPredicate predicate){
166 return getPredicateResult(predicate).countResults();
167 }
168
169 @Override
170 public int countResults(DNFPredicate predicate, Object[] parameters){
171 validateParameters(predicate, parameters);
172 return getPredicateResult(predicate).countResults(parameters);
173
174 }
175 @Override
176 public void flushChanges() {
177 //this.initEngine();
178 this.scope.flush();
179 }
180
181 @Override
182 public ModelDiffCursor getDiffCursor(long to) {
183 return model.getDiffCursor(to);
184 }
185
186 @Override
187 public long commit() {
188 return this.model.commit();
189 }
190
191 @Override
192 public void restore(long state) {
193 restoreWithDiffReplay(state);
194 }
195
196 public void restoreWithDiffReplay(long state) {
197 var modelDiffCursor = getDiffCursor(state);
198 for(DataRepresentation<?,?> dataRepresentation : this.getDataRepresentations()) {
199 restoreRepresentationWithDiffReplay(modelDiffCursor, dataRepresentation);
200 }
201 }
202
203 private <K,V> void restoreRepresentationWithDiffReplay(ModelDiffCursor modelDiffCursor,
204 DataRepresentation<K, V> dataRepresentation) {
205 DiffCursor<K,V> diffCursor = modelDiffCursor.getCursor(dataRepresentation);
206 this.putAll(dataRepresentation, diffCursor);
207 }
208
209 public void restoreWithReinit(long state) {
210 model.restore(state);
211 this.initEngine();
212 }
213}
diff --git a/store/src/main/java/tools/refinery/store/query/internal/RawPatternMatcher.java b/store/src/main/java/tools/refinery/store/query/internal/RawPatternMatcher.java
new file mode 100644
index 00000000..c6d6353c
--- /dev/null
+++ b/store/src/main/java/tools/refinery/store/query/internal/RawPatternMatcher.java
@@ -0,0 +1,57 @@
1package tools.refinery.store.query.internal;
2
3import java.util.Optional;
4import java.util.stream.Stream;
5
6import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
7import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
8import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
9import org.eclipse.viatra.query.runtime.matchers.tuple.AbstractTuple;
10
11public class RawPatternMatcher extends GenericPatternMatcher implements PredicateResult{
12
13 protected final Object[] empty;
14
15 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
16 super(specification);
17 this.empty = new Object[specification.getParameterNames().size()];
18 }
19
20 @Override
21 public boolean hasResult() {
22 return hasResult(empty);
23 }
24 @Override
25 public boolean hasResult(Object[] parameters) {
26 return this.backend.hasMatch(parameters);
27 }
28 @Override
29 public Optional<Object[]> oneResult() {
30 return oneResult(empty);
31 }
32 @Override
33 public Optional<Object[]> oneResult(Object[] parameters) {
34 Optional<Tuple> tuple = this.backend.getOneArbitraryMatch(parameters);
35 if(tuple.isPresent()) {
36 return Optional.of(tuple.get().getElements());
37 } else {
38 return Optional.empty();
39 }
40 }
41 @Override
42 public Stream<Object[]> allResults() {
43 return allResults(empty);
44 }
45 @Override
46 public Stream<Object[]> allResults(Object[] parameters) {
47 return this.backend.getAllMatches(parameters).map(AbstractTuple::getElements);
48 }
49 @Override
50 public int countResults() {
51 return countResults(empty);
52 }
53 @Override
54 public int countResults(Object[] parameters) {
55 return backend.countMatches(parameters);
56 }
57}
diff --git a/store/src/main/java/tools/refinery/store/query/internal/RelationUpdateListener.java b/store/src/main/java/tools/refinery/store/query/internal/RelationUpdateListener.java
deleted file mode 100644
index cf5260f6..00000000
--- a/store/src/main/java/tools/refinery/store/query/internal/RelationUpdateListener.java
+++ /dev/null
@@ -1,52 +0,0 @@
1package tools.refinery.store.query.internal;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.Map;
6import java.util.Set;
7
8import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
9import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
10
11import tools.refinery.store.model.Tuple;
12import tools.refinery.store.query.view.RelationView;
13
14public class RelationUpdateListener {
15 private final Map<RelationView<?>,Set<RelationUpdateListenerEntry<?>>> view2Listeners;
16
17 public RelationUpdateListener(Set<RelationView<?>> relationViews) {
18 view2Listeners = new HashMap<>();
19 for(RelationView<?> relationView : relationViews) {
20 view2Listeners.put(relationView, new HashSet<>());
21 }
22 }
23 public boolean containsRelationalView(RelationView<?> relationalKey) {
24 RelationView<?> relationView = relationalKey.getWrappedKey();
25 return view2Listeners.containsKey(relationView);
26 }
27 public void addListener(RelationView<?> relationalKey, ITuple seed, IQueryRuntimeContextListener listener) {
28 RelationView<?> relationView = relationalKey.getWrappedKey();
29 if(view2Listeners.containsKey(relationView)) {
30 RelationUpdateListenerEntry<?> entry = new RelationUpdateListenerEntry<>(relationalKey, seed, listener);
31 view2Listeners.get(relationView).add(entry);
32 } else throw new IllegalArgumentException();
33 }
34 public void removeListener(RelationView<?> relationalKey, ITuple seed, IQueryRuntimeContextListener listener) {
35 RelationView<?> relationView = relationalKey.getWrappedKey();
36 if(view2Listeners.containsKey(relationView)) {
37 RelationUpdateListenerEntry<?> entry = new RelationUpdateListenerEntry<>(relationalKey, seed, listener);
38 view2Listeners.get(relationView).remove(entry);
39 } else throw new IllegalArgumentException();
40 }
41
42 public <D> void processChange(RelationView<D> relationView, Tuple tuple, D oldValue, D newValue) {
43 Set<RelationUpdateListenerEntry<?>> listeners = view2Listeners.get(relationView);
44 if(listeners != null) {
45 for(RelationUpdateListenerEntry<?> listener : listeners) {
46 @SuppressWarnings("unchecked")
47 RelationUpdateListenerEntry<D> typeCorrectListener = (RelationUpdateListenerEntry<D>) listener;
48 typeCorrectListener.processChange(tuple, oldValue, newValue);
49 }
50 } else throw new IllegalArgumentException("View was not indexed in constructor "+relationView);
51 }
52}
diff --git a/store/src/main/java/tools/refinery/store/query/internal/RelationalEngineContext.java b/store/src/main/java/tools/refinery/store/query/internal/RelationalEngineContext.java
index 691baf81..dfbd8545 100644
--- a/store/src/main/java/tools/refinery/store/query/internal/RelationalEngineContext.java
+++ b/store/src/main/java/tools/refinery/store/query/internal/RelationalEngineContext.java
@@ -11,7 +11,7 @@ public class RelationalEngineContext implements IEngineContext{
11 private final RelationalRuntimeContext runtimeContext; 11 private final RelationalRuntimeContext runtimeContext;
12 12
13 13
14 public RelationalEngineContext(Model model, RelationUpdateListener updateListener) { 14 public RelationalEngineContext(Model model, ModelUpdateListener updateListener) {
15 runtimeContext = new RelationalRuntimeContext(model, updateListener); 15 runtimeContext = new RelationalRuntimeContext(model, updateListener);
16 } 16 }
17 17
diff --git a/store/src/main/java/tools/refinery/store/query/internal/RelationalRuntimeContext.java b/store/src/main/java/tools/refinery/store/query/internal/RelationalRuntimeContext.java
index da118f26..d5112128 100644
--- a/store/src/main/java/tools/refinery/store/query/internal/RelationalRuntimeContext.java
+++ b/store/src/main/java/tools/refinery/store/query/internal/RelationalRuntimeContext.java
@@ -25,12 +25,12 @@ import tools.refinery.store.query.view.RelationView;
25 25
26public class RelationalRuntimeContext implements IQueryRuntimeContext { 26public class RelationalRuntimeContext implements IQueryRuntimeContext {
27 private final RelationalQueryMetaContext metaContext = new RelationalQueryMetaContext(); 27 private final RelationalQueryMetaContext metaContext = new RelationalQueryMetaContext();
28 private final RelationUpdateListener relationUpdateListener; 28 private final ModelUpdateListener modelUpdateListener;
29 private final Model model; 29 private final Model model;
30 30
31 public RelationalRuntimeContext(Model model, RelationUpdateListener relationUpdateListener) { 31 public RelationalRuntimeContext(Model model, ModelUpdateListener relationUpdateListener) {
32 this.model = model; 32 this.model = model;
33 this.relationUpdateListener = relationUpdateListener; 33 this.modelUpdateListener = relationUpdateListener;
34 } 34 }
35 35
36 @Override 36 @Override
@@ -58,7 +58,7 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
58 @Override 58 @Override
59 public boolean isIndexed(IInputKey key, IndexingService service) { 59 public boolean isIndexed(IInputKey key, IndexingService service) {
60 if(key instanceof RelationView<?> relationalKey) { 60 if(key instanceof RelationView<?> relationalKey) {
61 return this.relationUpdateListener.containsRelationalView(relationalKey); 61 return this.modelUpdateListener.containsRelationalView(relationalKey);
62 } else { 62 } else {
63 return false; 63 return false;
64 } 64 }
@@ -74,7 +74,7 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
74 RelationView<?> checkKey(IInputKey key) { 74 RelationView<?> checkKey(IInputKey key) {
75 if(key instanceof RelationView) { 75 if(key instanceof RelationView) {
76 RelationView<?> relationViewKey = (RelationView<?>) key; 76 RelationView<?> relationViewKey = (RelationView<?>) key;
77 if(relationUpdateListener.containsRelationalView(relationViewKey)) { 77 if(modelUpdateListener.containsRelationalView(relationViewKey)) {
78 return relationViewKey; 78 return relationViewKey;
79 } else { 79 } else {
80 throw new IllegalStateException("Query is asking for non-indexed key"); 80 throw new IllegalStateException("Query is asking for non-indexed key");
@@ -144,14 +144,14 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
144 @Override 144 @Override
145 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { 145 public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
146 RelationView<?> relationalKey = checkKey(key); 146 RelationView<?> relationalKey = checkKey(key);
147 this.relationUpdateListener.addListener(relationalKey, seed, listener); 147 this.modelUpdateListener.addListener(relationalKey, seed, listener);
148 148
149 } 149 }
150 150
151 @Override 151 @Override
152 public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) { 152 public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener) {
153 RelationView<?> relationalKey = checkKey(key); 153 RelationView<?> relationalKey = checkKey(key);
154 this.relationUpdateListener.removeListener(relationalKey, seed, listener); 154 this.modelUpdateListener.removeListener(relationalKey, seed, listener);
155 } 155 }
156 156
157 @Override 157 @Override
diff --git a/store/src/main/java/tools/refinery/store/query/RelationalScope.java b/store/src/main/java/tools/refinery/store/query/internal/RelationalScope.java
index 5fe8083a..e8d45356 100644
--- a/store/src/main/java/tools/refinery/store/query/RelationalScope.java
+++ b/store/src/main/java/tools/refinery/store/query/internal/RelationalScope.java
@@ -1,4 +1,4 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.internal;
2 2
3import java.util.Set; 3import java.util.Set;
4 4
@@ -10,23 +10,31 @@ import org.eclipse.viatra.query.runtime.api.scope.QueryScope;
10 10
11import tools.refinery.store.model.Model; 11import tools.refinery.store.model.Model;
12import tools.refinery.store.model.Tuple; 12import tools.refinery.store.model.Tuple;
13import tools.refinery.store.query.internal.RelationUpdateListener; 13import tools.refinery.store.model.representation.Relation;
14import tools.refinery.store.query.internal.RelationalEngineContext;
15import tools.refinery.store.query.view.RelationView; 14import tools.refinery.store.query.view.RelationView;
16 15
17public class RelationalScope extends QueryScope{ 16public class RelationalScope extends QueryScope{
18 private final Model model; 17 private final Model model;
19 private final RelationUpdateListener updateListener; 18 private final ModelUpdateListener updateListener;
20 19
21 public RelationalScope(Model model, Set<RelationView<?>> relationViews) { 20 public RelationalScope(Model model, Set<RelationView<?>> relationViews) {
22 this.model = model; 21 this.model = model;
23 updateListener = new RelationUpdateListener(relationViews); 22 this.updateListener = new ModelUpdateListener(relationViews);
23 //this.changeListener = new
24 } 24 }
25 25
26 public <D> void processUpdate(RelationView<D> relationView, Tuple key, D oldValue, D newValue) { 26 public <D> void processUpdate(Relation<D> relation, Tuple key, D oldValue, D newValue) {
27 updateListener.processChange(relationView, key, oldValue, newValue); 27 updateListener.addUpdate(relation, key, oldValue, newValue);
28 }
29
30 public boolean hasChange() {
31 return updateListener.hasChange();
28 } 32 }
29 33
34 public void flush() {
35 updateListener.flush();
36 }
37
30 @Override 38 @Override
31 protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener, 39 protected IEngineContext createEngineContext(ViatraQueryEngine engine, IIndexingErrorListener errorListener,
32 Logger logger) { 40 Logger logger) {
diff --git a/store/src/main/java/tools/refinery/store/query/internal/ViewUpdate.java b/store/src/main/java/tools/refinery/store/query/internal/ViewUpdate.java
new file mode 100644
index 00000000..d4a295f8
--- /dev/null
+++ b/store/src/main/java/tools/refinery/store/query/internal/ViewUpdate.java
@@ -0,0 +1,4 @@
1package tools.refinery.store.query.internal;
2
3record ViewUpdate (Object[] tuple, boolean isInsertion) {
4}
diff --git a/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateBuffer.java b/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateBuffer.java
new file mode 100644
index 00000000..6bc4c96a
--- /dev/null
+++ b/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateBuffer.java
@@ -0,0 +1,46 @@
1package tools.refinery.store.query.internal;
2
3import java.util.ArrayList;
4import java.util.Arrays;
5import java.util.List;
6
7import tools.refinery.store.model.Tuple;
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 hasChange() {
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/store/src/main/java/tools/refinery/store/query/internal/RelationUpdateListenerEntry.java b/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateTranslator.java
index 860a80b7..4596b9c1 100644
--- a/store/src/main/java/tools/refinery/store/query/internal/RelationUpdateListenerEntry.java
+++ b/store/src/main/java/tools/refinery/store/query/internal/ViewUpdateTranslator.java
@@ -1,6 +1,5 @@
1package tools.refinery.store.query.internal; 1package tools.refinery.store.query.internal;
2 2
3import java.util.Arrays;
4import java.util.Objects; 3import java.util.Objects;
5 4
6import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; 5import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
@@ -10,30 +9,24 @@ import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
10import tools.refinery.store.model.Tuple; 9import tools.refinery.store.model.Tuple;
11import tools.refinery.store.query.view.RelationView; 10import tools.refinery.store.query.view.RelationView;
12 11
13public class RelationUpdateListenerEntry<D> { 12public class ViewUpdateTranslator<D> {
14 final RelationView<D> key; 13 final RelationView<D> key;
15 final ITuple filter; 14 final ITuple filter;
16 final IQueryRuntimeContextListener listener; 15 final IQueryRuntimeContextListener listener;
17 16
18 public RelationUpdateListenerEntry(RelationView<D> key, ITuple filter, IQueryRuntimeContextListener listener) { 17 public ViewUpdateTranslator(RelationView<D> key, ITuple filter, IQueryRuntimeContextListener listener) {
19 super(); 18 super();
20 this.key = key; 19 this.key = key;
21 this.filter = filter; 20 this.filter = filter;
22 this.listener = listener; 21 this.listener = listener;
23 } 22 }
24 23
25 public void processChange(Tuple tuple, D oldValue, D newValue) { 24 public void processChange(ViewUpdate change) {
26 Object[] oldTuple = isMatching(key.getWrappedKey().transform(tuple, oldValue), filter); 25 listener.update(key, Tuples.flatTupleOf(change.tuple()), change.isInsertion());
27 Object[] newTuple = isMatching(key.getWrappedKey().transform(tuple, newValue), filter); 26 }
28 27
29 if(!Arrays.equals(oldTuple, newTuple)) { 28 public Object[] isMatching(Tuple tuple, D value){
30 if(oldTuple != null) { 29 return isMatching(key.getWrappedKey().transform(tuple, value), filter);
31 listener.update(key, Tuples.flatTupleOf(oldTuple), false);
32 }
33 if(newTuple != null) {
34 listener.update(key, Tuples.flatTupleOf(newTuple), true);
35 }
36 }
37 } 30 }
38 31
39 private Object[] isMatching(Object[] tuple, ITuple filter) { 32 private Object[] isMatching(Object[] tuple, ITuple filter) {
@@ -55,9 +48,9 @@ public class RelationUpdateListenerEntry<D> {
55 public boolean equals(Object obj) { 48 public boolean equals(Object obj) {
56 if (this == obj) 49 if (this == obj)
57 return true; 50 return true;
58 if (!(obj instanceof RelationUpdateListenerEntry)) 51 if (!(obj instanceof ViewUpdateTranslator))
59 return false; 52 return false;
60 RelationUpdateListenerEntry<?> other = (RelationUpdateListenerEntry<?>) obj; 53 ViewUpdateTranslator<?> other = (ViewUpdateTranslator<?>) obj;
61 return Objects.equals(filter, other.filter) && Objects.equals(key, other.key) 54 return Objects.equals(filter, other.filter) && Objects.equals(key, other.key)
62 && Objects.equals(listener, other.listener); 55 && Objects.equals(listener, other.listener);
63 } 56 }
diff --git a/store/src/main/java/tools/refinery/store/query/view/FilteredRelationView.java b/store/src/main/java/tools/refinery/store/query/view/FilteredRelationView.java
index 2e264c44..3531195a 100644
--- a/store/src/main/java/tools/refinery/store/query/view/FilteredRelationView.java
+++ b/store/src/main/java/tools/refinery/store/query/view/FilteredRelationView.java
@@ -32,7 +32,7 @@ public class FilteredRelationView<D> extends RelationView<D>{
32 public static Object[] toTuple1Array(Tuple t) { 32 public static Object[] toTuple1Array(Tuple t) {
33 Object[] result = new Object[t.getSize()]; 33 Object[] result = new Object[t.getSize()];
34 for(int i = 0; i<t.getSize(); i++) { 34 for(int i = 0; i<t.getSize(); i++) {
35 result[i] = t.get(i); 35 result[i] = Tuple.of(t.get(i));
36 } 36 }
37 return result; 37 return result;
38 } 38 }
diff --git a/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java b/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
index f54d4428..db9ba4b8 100644
--- a/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
+++ b/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
@@ -37,7 +37,7 @@ public class FunctionalRelationView<D> extends RelationView<D> {
37 public static <D> Object[] toTuple1ArrayPlusValue(Tuple t, D value) { 37 public static <D> Object[] toTuple1ArrayPlusValue(Tuple t, D value) {
38 Object[] result = new Object[t.getSize()+1]; 38 Object[] result = new Object[t.getSize()+1];
39 for(int i = 0; i<t.getSize(); i++) { 39 for(int i = 0; i<t.getSize(); i++) {
40 result[i] = t.get(i); 40 result[i] = Tuple.of(t.get(i));
41 } 41 }
42 result[t.getSize()] = value; 42 result[t.getSize()] = value;
43 return result; 43 return result;
diff --git a/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java b/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java
index 1d1932cc..6fa387a1 100644
--- a/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java
+++ b/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java
@@ -10,7 +10,7 @@ public class KeyOnlyRelationView extends FilteredRelationView<Boolean>{
10 } 10 }
11 @Override 11 @Override
12 protected boolean filter(Tuple key, Boolean value) { 12 protected boolean filter(Tuple key, Boolean value) {
13 return true; 13 return !value.equals(representation.getDefaultValue());
14 } 14 }
15 15
16} 16}
diff --git a/store/src/main/java/tools/refinery/store/query/view/RelationView.java b/store/src/main/java/tools/refinery/store/query/view/RelationView.java
index 2c2a37c4..efe9945f 100644
--- a/store/src/main/java/tools/refinery/store/query/view/RelationView.java
+++ b/store/src/main/java/tools/refinery/store/query/view/RelationView.java
@@ -74,8 +74,6 @@ public abstract class RelationView<D> extends BaseInputKeyWrapper<RelationView<D
74 public boolean equals(Object obj) { 74 public boolean equals(Object obj) {
75 if (this == obj) 75 if (this == obj)
76 return true; 76 return true;
77 if (!super.equals(obj))
78 return false;
79 if (!(obj instanceof RelationView)) 77 if (!(obj instanceof RelationView))
80 return false; 78 return false;
81 @SuppressWarnings("unchecked") 79 @SuppressWarnings("unchecked")
diff --git a/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java b/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java
new file mode 100644
index 00000000..f0d5d927
--- /dev/null
+++ b/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.map.tests;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4
5import org.junit.jupiter.api.Test;
6
7import tools.refinery.store.map.VersionedMapStore;
8import tools.refinery.store.map.VersionedMapStoreImpl;
9import tools.refinery.store.model.Tuple;
10import tools.refinery.store.model.TupleHashProvider;
11
12class MapUnitTests {
13 @Test
14 void defaultTest() {
15 VersionedMapStore<Tuple, Boolean> store = new VersionedMapStoreImpl<Tuple, Boolean>(TupleHashProvider.singleton(), false);
16 var map = store.createMap();
17 var out1 = map.put(Tuple.of(0), true);
18 assertEquals(false, out1);
19 var out2 = map.put(Tuple.of(1), true);
20 assertEquals(false, out2);
21 }
22}
diff --git a/store/src/test/java/tools/refinery/store/query/test/QueryTest.java b/store/src/test/java/tools/refinery/store/query/test/QueryTest.java
index b1014700..02381bcd 100644
--- a/store/src/test/java/tools/refinery/store/query/test/QueryTest.java
+++ b/store/src/test/java/tools/refinery/store/query/test/QueryTest.java
@@ -1,106 +1,51 @@
1package tools.refinery.store.query.test; 1package tools.refinery.store.query.test;
2 2
3import static org.junit.jupiter.api.Assertions.assertEquals; 3import static org.junit.jupiter.api.Assertions.assertEquals;
4import static org.junit.jupiter.api.Assertions.assertFalse;
5import static org.junit.jupiter.api.Assertions.assertTrue;
6 4
5import java.util.ArrayList;
7import java.util.Arrays; 6import java.util.Arrays;
8import java.util.Collection; 7import java.util.Collections;
9import java.util.HashSet; 8import java.util.HashSet;
10import java.util.List; 9import java.util.List;
11import java.util.Set; 10import java.util.Set;
11import java.util.stream.Stream;
12 12
13import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine;
14import org.eclipse.viatra.query.runtime.api.GenericPatternMatch;
15import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
16import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
17import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
18import org.junit.jupiter.api.Disabled;
19import org.junit.jupiter.api.Test; 13import org.junit.jupiter.api.Test;
20 14
21import tools.refinery.store.model.Model;
22import tools.refinery.store.model.ModelStore;
23import tools.refinery.store.model.ModelStoreImpl;
24import tools.refinery.store.model.Tuple; 15import tools.refinery.store.model.Tuple;
25import tools.refinery.store.model.representation.Relation; 16import tools.refinery.store.model.representation.Relation;
26import tools.refinery.store.model.representation.TruthValue; 17import tools.refinery.store.model.representation.TruthValue;
27import tools.refinery.store.query.RelationalScope; 18import tools.refinery.store.query.QueriableModel;
19import tools.refinery.store.query.QueriableModelStore;
20import tools.refinery.store.query.QueriableModelStoreImpl;
28import tools.refinery.store.query.building.DNFAnd; 21import tools.refinery.store.query.building.DNFAnd;
29import tools.refinery.store.query.building.DNFPredicate; 22import tools.refinery.store.query.building.DNFPredicate;
30import tools.refinery.store.query.building.EquivalenceAtom; 23import tools.refinery.store.query.building.EquivalenceAtom;
31import tools.refinery.store.query.building.PredicateAtom; 24import tools.refinery.store.query.building.PredicateAtom;
32import tools.refinery.store.query.building.RelationAtom; 25import tools.refinery.store.query.building.RelationAtom;
33import tools.refinery.store.query.building.Variable; 26import tools.refinery.store.query.building.Variable;
34import tools.refinery.store.query.internal.DNF2PQuery;
35import tools.refinery.store.query.internal.PredicateTranslator;
36import tools.refinery.store.query.view.FilteredRelationView; 27import tools.refinery.store.query.view.FilteredRelationView;
37import tools.refinery.store.query.view.FunctionalRelationView;
38import tools.refinery.store.query.view.KeyOnlyRelationView; 28import tools.refinery.store.query.view.KeyOnlyRelationView;
39import tools.refinery.store.query.view.RelationView; 29import tools.refinery.store.query.view.RelationView;
40 30
41class QueryTest { 31class QueryTest {
42 @Test
43 void minimalTest() {
44 Relation<Boolean> person = new Relation<>("Person", 1, false);
45
46 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
47 GenericQuerySpecification<GenericPatternMatcher> personQuery = (new PredicateTranslator("PersonQuery"))
48 .addParameter("p", persionView).addConstraint(persionView, "p").build();
49
50 ModelStore store = new ModelStoreImpl(Set.of(person));
51 Model model = store.createModel();
52
53 model.put(person, Tuple.of(0), true);
54 model.put(person, Tuple.of(1), true);
55
56 RelationalScope scope = new RelationalScope(model, Set.of(persionView));
57
58 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope);
59 GenericPatternMatcher personMatcher = engine.getMatcher(personQuery);
60
61 assertEquals(2, personMatcher.countMatches());
62 }
63
64 void modelBuildingTest() {
65 Relation<Boolean> person = new Relation<>("Person", 1, false);
66 Relation<Integer> age = new Relation<Integer>("age", 1, null);
67 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
68
69 ModelStore store = new ModelStoreImpl(Set.of(person, age, friend));
70 Model model = store.createModel();
71
72 model.put(person, Tuple.of(0), true);
73 model.put(person, Tuple.of(1), true);
74 model.put(age, Tuple.of(0), 3);
75 model.put(age, Tuple.of(1), 1);
76 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
77 model.put(friend, Tuple.of(1, 0), TruthValue.UNKNOWN);
78
79 // Sanity check
80 assertTrue(model.get(person, Tuple.of(0)));
81 assertTrue(model.get(person, Tuple.of(1)));
82 assertFalse(model.get(person, Tuple.of(2)));
83
84 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
85 RelationView<Integer> ageView = new FunctionalRelationView<>(age);
86 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
87 RelationView<TruthValue> friendMayView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.may());
88 32
89 RelationalScope scope = new RelationalScope(model, Set.of(persionView, ageView, friendMustView, friendMayView)); 33 static void compareMatchSets(Stream<Object[]> matchSet, Set<List<Tuple>> expected) {
90 34 Set<List<Tuple>> translatedMatchSet = new HashSet<>();
91 GenericQuerySpecification<GenericPatternMatcher> personQuery = (new PredicateTranslator("PersonQuery")) 35 var interator = matchSet.iterator();
92 .addParameter("p", persionView).addConstraint(persionView, "p").build(); 36 while (interator.hasNext()) {
93 37 var element = interator.next();
94 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope); 38 List<Tuple> elementToTranslatedMatchSet = new ArrayList<>();
95 GenericPatternMatcher personMatcher = engine.getMatcher(personQuery); 39 for (int i = 0; i < element.length; i++) {
96 Collection<GenericPatternMatch> personMatches = personMatcher.getAllMatches(); 40 elementToTranslatedMatchSet.add((Tuple) element[i]);
97 for (GenericPatternMatch personMatch : personMatches) { 41 }
98 System.out.println(personMatch); 42 translatedMatchSet.add(elementToTranslatedMatchSet);
99 } 43 }
44
45 assertEquals(expected, translatedMatchSet);
100 } 46 }
101 47
102 @Test 48 @Test
103 @Disabled
104 void typeConstraintTest() { 49 void typeConstraintTest() {
105 Relation<Boolean> person = new Relation<>("Person", 1, false); 50 Relation<Boolean> person = new Relation<>("Person", 1, false);
106 Relation<Boolean> asset = new Relation<>("Asset", 1, false); 51 Relation<Boolean> asset = new Relation<>("Asset", 1, false);
@@ -108,28 +53,24 @@ class QueryTest {
108 53
109 List<Variable> parameters = Arrays.asList(new Variable("p1")); 54 List<Variable> parameters = Arrays.asList(new Variable("p1"));
110 RelationAtom personRelationAtom = new RelationAtom(persionView, parameters); 55 RelationAtom personRelationAtom = new RelationAtom(persionView, parameters);
111 DNFAnd clause = new DNFAnd(new HashSet<>(parameters), Arrays.asList(personRelationAtom)); 56 DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom));
112 DNFPredicate predicate = new DNFPredicate("TypeConstraint", parameters, Arrays.asList(clause)); 57 DNFPredicate predicate = new DNFPredicate("TypeConstraint", parameters, Arrays.asList(clause));
113 GenericQuerySpecification<GenericPatternMatcher> query = DNF2PQuery.translate(predicate).build();
114 58
115 ModelStore store = new ModelStoreImpl(Set.of(person, asset)); 59 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, asset), Set.of(persionView),
116 Model model = store.createModel(); 60 Set.of(predicate));
61 QueriableModel model = store.createModel();
117 62
118 model.put(person, Tuple.of(0), true); 63 model.put(person, Tuple.of(0), true);
119 model.put(person, Tuple.of(1), true); 64 model.put(person, Tuple.of(1), true);
120 model.put(asset, Tuple.of(1), true); 65 model.put(asset, Tuple.of(1), true);
121 model.put(asset, Tuple.of(2), true); 66 model.put(asset, Tuple.of(2), true);
122 67
123 RelationalScope scope = new RelationalScope(model, Set.of(persionView)); 68 model.flushChanges();
124 69 assertEquals(2, model.countResults(predicate));
125 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope); 70 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0)), List.of(Tuple.of(1))));
126 GenericPatternMatcher matcher = engine.getMatcher(query);
127
128 assertEquals(2, matcher.countMatches());
129 } 71 }
130 72
131 @Test 73 @Test
132 @Disabled
133 void relationConstraintTest() { 74 void relationConstraintTest() {
134 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); 75 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
135 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); 76 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
@@ -143,14 +84,15 @@ class QueryTest {
143 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); 84 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
144 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); 85 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
145 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); 86 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
146 DNFAnd clause = new DNFAnd(new HashSet<>(parameters), 87 DNFAnd clause = new DNFAnd(Collections.emptySet(),
147 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom)); 88 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
148 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); 89 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
149 90
150 GenericQuerySpecification<GenericPatternMatcher> query = DNF2PQuery.translate(predicate).build(); 91 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
92 Set.of(persionView, friendMustView), Set.of(predicate));
93 QueriableModel model = store.createModel();
151 94
152 ModelStore store = new ModelStoreImpl(Set.of(person, friend)); 95 assertEquals(0, model.countResults(predicate));
153 Model model = store.createModel();
154 96
155 model.put(person, Tuple.of(0), true); 97 model.put(person, Tuple.of(0), true);
156 model.put(person, Tuple.of(1), true); 98 model.put(person, Tuple.of(1), true);
@@ -159,17 +101,16 @@ class QueryTest {
159 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE); 101 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
160 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); 102 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
161 103
162 RelationalScope scope = new RelationalScope(model, Set.of(persionView, friendMustView)); 104 assertEquals(0, model.countResults(predicate));
163
164 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope);
165 GenericPatternMatcher matcher = engine.getMatcher(query);
166 105
167 assertEquals(3, matcher.countMatches()); 106 model.flushChanges();
107 assertEquals(3, model.countResults(predicate));
108 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0), Tuple.of(1)),
109 List.of(Tuple.of(1), Tuple.of(0)), List.of(Tuple.of(1), Tuple.of(2))));
168 } 110 }
169 111
170 @Test 112 @Test
171 @Disabled 113 void andTest() {
172 void patternCallTest() {
173 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); 114 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
174 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); 115 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
175 RelationView<Boolean> persionView = new KeyOnlyRelationView(person); 116 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
@@ -181,44 +122,44 @@ class QueryTest {
181 122
182 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); 123 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
183 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); 124 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
184 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); 125 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
185 DNFAnd clause = new DNFAnd(new HashSet<>(parameters), 126 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p2, p1));
186 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom)); 127 DNFAnd clause = new DNFAnd(Collections.emptySet(),
187 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); 128 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom1, friendRelationAtom2));
188 129 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
189 Variable p3 = new Variable("p3");
190 Variable p4 = new Variable("p4");
191 List<Variable> substitution = Arrays.asList(p3, p4);
192 RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3));
193 RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4));
194 PredicateAtom friendPredicateAtom = new PredicateAtom(true, false, friendPredicate, substitution);
195 DNFAnd patternCallClause = new DNFAnd(new HashSet<>(substitution),
196 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
197 DNFPredicate predicate = new DNFPredicate("PatternCall", substitution, Arrays.asList(patternCallClause));
198 130
199 GenericQuerySpecification<GenericPatternMatcher> query = DNF2PQuery.translate(predicate).build(); 131 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
132 Set.of(persionView, friendMustView), Set.of(predicate));
133 QueriableModel model = store.createModel();
200 134
201 ModelStore store = new ModelStoreImpl(Set.of(person, friend)); 135 assertEquals(0, model.countResults(predicate));
202 Model model = store.createModel();
203 136
204 model.put(person, Tuple.of(0), true); 137 model.put(person, Tuple.of(0), true);
205 model.put(person, Tuple.of(1), true); 138 model.put(person, Tuple.of(1), true);
206 model.put(person, Tuple.of(2), true); 139 model.put(person, Tuple.of(2), true);
207 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
208 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
209 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
210 140
211 RelationalScope scope = new RelationalScope(model, Set.of(persionView, friendMustView)); 141 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
142 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
212 143
213 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope); 144 model.flushChanges();
214 GenericPatternMatcher matcher = engine.getMatcher(query); 145 assertEquals(0, model.countResults(predicate));
215 146
216 assertEquals(3, matcher.countMatches()); 147 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
148 model.flushChanges();
149 assertEquals(2, model.countResults(predicate));
150 compareMatchSets(model.allResults(predicate),
151 Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(1), Tuple.of(0))));
152
153 model.put(friend, Tuple.of(2, 0), TruthValue.TRUE);
154 model.flushChanges();
155 assertEquals(4, model.countResults(predicate));
156 compareMatchSets(model.allResults(predicate),
157 Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(1), Tuple.of(0)),
158 List.of(Tuple.of(0), Tuple.of(2)), List.of(Tuple.of(2), Tuple.of(0))));
217 } 159 }
218 160
219 @Test 161 @Test
220 @Disabled 162 void existTest() {
221 void negativePatternCallTest() {
222 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); 163 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
223 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); 164 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
224 RelationView<Boolean> persionView = new KeyOnlyRelationView(person); 165 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
@@ -226,30 +167,20 @@ class QueryTest {
226 167
227 Variable p1 = new Variable("p1"); 168 Variable p1 = new Variable("p1");
228 Variable p2 = new Variable("p2"); 169 Variable p2 = new Variable("p2");
229 List<Variable> parameters = Arrays.asList(p1, p2); 170 List<Variable> parameters = Arrays.asList(p1);
230 171
231 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); 172 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
232 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); 173 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
233 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); 174 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
234 DNFAnd clause = new DNFAnd(new HashSet<>(parameters), 175 DNFAnd clause = new DNFAnd(Set.of(p2),
235 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom)); 176 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
236 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); 177 DNFPredicate predicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
237
238 Variable p3 = new Variable("p3");
239 Variable p4 = new Variable("p4");
240 List<Variable> substitution = Arrays.asList(p3, p4);
241 RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3));
242 RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4));
243 PredicateAtom friendPredicateAtom = new PredicateAtom(false, false, friendPredicate, substitution);
244 DNFAnd negativePatternCallClause = new DNFAnd(new HashSet<>(substitution),
245 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
246 DNFPredicate predicate = new DNFPredicate("NegativePatternCall", substitution,
247 Arrays.asList(negativePatternCallClause));
248 178
249 GenericQuerySpecification<GenericPatternMatcher> query = DNF2PQuery.translate(predicate).build(); 179 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
180 Set.of(persionView, friendMustView), Set.of(predicate));
181 QueriableModel model = store.createModel();
250 182
251 ModelStore store = new ModelStoreImpl(Set.of(person, friend)); 183 assertEquals(0, model.countResults(predicate));
252 Model model = store.createModel();
253 184
254 model.put(person, Tuple.of(0), true); 185 model.put(person, Tuple.of(0), true);
255 model.put(person, Tuple.of(1), true); 186 model.put(person, Tuple.of(1), true);
@@ -258,16 +189,64 @@ class QueryTest {
258 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE); 189 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
259 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); 190 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
260 191
261 RelationalScope scope = new RelationalScope(model, Set.of(persionView, friendMustView)); 192 assertEquals(0, model.countResults(predicate));
193
194 model.flushChanges();
195 assertEquals(2, model.countResults(predicate));
196 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0)), List.of(Tuple.of(1))));
197 }
198
199 @Test
200 void orTest() {
201 Relation<Boolean> person = new Relation<>("Person", 1, false);
202 Relation<Boolean> animal = new Relation<>("Animal", 1, false);
203 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
204 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
205 RelationView<Boolean> animalView = new KeyOnlyRelationView(animal);
206 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
207
208 Variable p1 = new Variable("p1");
209 Variable p2 = new Variable("p2");
210 List<Variable> parameters = Arrays.asList(p1, p2);
211
212 // Person-Person friendship
213 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
214 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
215 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
216 DNFAnd clause1 = new DNFAnd(Collections.emptySet(),
217 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom1));
218
219 // Animal-Animal friendship
220 RelationAtom animalRelationAtom1 = new RelationAtom(animalView, Arrays.asList(p1));
221 RelationAtom animalRelationAtom2 = new RelationAtom(animalView, Arrays.asList(p2));
222 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
223 DNFAnd clause2 = new DNFAnd(Collections.emptySet(),
224 Arrays.asList(animalRelationAtom1, animalRelationAtom2, friendRelationAtom2));
225
226 // No inter-species friendship
227
228 DNFPredicate predicate = new DNFPredicate("Or", parameters, Arrays.asList(clause1, clause2));
262 229
263 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope); 230 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, animal, friend),
264 GenericPatternMatcher matcher = engine.getMatcher(query); 231 Set.of(persionView, animalView, friendMustView), Set.of(predicate));
232 QueriableModel model = store.createModel();
265 233
266 assertEquals(6, matcher.countMatches()); 234 model.put(person, Tuple.of(0), true);
235 model.put(person, Tuple.of(1), true);
236 model.put(animal, Tuple.of(2), true);
237 model.put(animal, Tuple.of(3), true);
238 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
239 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
240 model.put(friend, Tuple.of(2, 3), TruthValue.TRUE);
241 model.put(friend, Tuple.of(3, 0), TruthValue.TRUE);
242
243 model.flushChanges();
244 assertEquals(2, model.countResults(predicate));
245 compareMatchSets(model.allResults(predicate),
246 Set.of(List.of(Tuple.of(0), Tuple.of(1)), List.of(Tuple.of(2), Tuple.of(3))));
267 } 247 }
268 248
269 @Test 249 @Test
270 @Disabled
271 void equalityTest() { 250 void equalityTest() {
272 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); 251 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
273 RelationView<Boolean> persionView = new KeyOnlyRelationView(person); 252 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
@@ -279,29 +258,24 @@ class QueryTest {
279 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); 258 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
280 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); 259 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
281 EquivalenceAtom equivalenceAtom = new EquivalenceAtom(true, p1, p2); 260 EquivalenceAtom equivalenceAtom = new EquivalenceAtom(true, p1, p2);
282 DNFAnd clause = new DNFAnd(new HashSet<>(parameters), 261 DNFAnd clause = new DNFAnd(Collections.emptySet(),
283 Arrays.asList(personRelationAtom1, personRelationAtom2, equivalenceAtom)); 262 Arrays.asList(personRelationAtom1, personRelationAtom2, equivalenceAtom));
284 DNFPredicate predicate = new DNFPredicate("Equality", parameters, Arrays.asList(clause)); 263 DNFPredicate predicate = new DNFPredicate("Equality", parameters, Arrays.asList(clause));
285 264
286 GenericQuerySpecification<GenericPatternMatcher> query = DNF2PQuery.translate(predicate).build(); 265 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person), Set.of(persionView), Set.of(predicate));
287 266 QueriableModel model = store.createModel();
288 ModelStore store = new ModelStoreImpl(Set.of(person));
289 Model model = store.createModel();
290 267
291 model.put(person, Tuple.of(0), true); 268 model.put(person, Tuple.of(0), true);
292 model.put(person, Tuple.of(1), true); 269 model.put(person, Tuple.of(1), true);
293 model.put(person, Tuple.of(2), true); 270 model.put(person, Tuple.of(2), true);
294 271
295 RelationalScope scope = new RelationalScope(model, Set.of(persionView)); 272 model.flushChanges();
296 273 assertEquals(3, model.countResults(predicate));
297 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope); 274 compareMatchSets(model.allResults(predicate), Set.of(List.of(Tuple.of(0), Tuple.of(0)),
298 GenericPatternMatcher matcher = engine.getMatcher(query); 275 List.of(Tuple.of(1), Tuple.of(1)), List.of(Tuple.of(2), Tuple.of(2))));
299
300 assertEquals(3, matcher.countMatches());
301 } 276 }
302 277
303 @Test 278 @Test
304 @Disabled
305 void inequalityTest() { 279 void inequalityTest() {
306 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); 280 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
307 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); 281 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
@@ -318,14 +292,13 @@ class QueryTest {
318 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p3)); 292 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p3));
319 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p2, p3)); 293 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p2, p3));
320 EquivalenceAtom inequivalenceAtom = new EquivalenceAtom(false, p1, p2); 294 EquivalenceAtom inequivalenceAtom = new EquivalenceAtom(false, p1, p2);
321 DNFAnd clause = new DNFAnd(new HashSet<>(parameters), Arrays.asList(personRelationAtom1, personRelationAtom2, 295 DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom1, personRelationAtom2,
322 friendRelationAtom1, friendRelationAtom2, inequivalenceAtom)); 296 friendRelationAtom1, friendRelationAtom2, inequivalenceAtom));
323 DNFPredicate predicate = new DNFPredicate("Inequality", parameters, Arrays.asList(clause)); 297 DNFPredicate predicate = new DNFPredicate("Inequality", parameters, Arrays.asList(clause));
324 298
325 GenericQuerySpecification<GenericPatternMatcher> query = DNF2PQuery.translate(predicate).build(); 299 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
326 300 Set.of(persionView, friendMustView), Set.of(predicate));
327 ModelStore store = new ModelStoreImpl(Set.of(person, friend)); 301 QueriableModel model = store.createModel();
328 Model model = store.createModel();
329 302
330 model.put(person, Tuple.of(0), true); 303 model.put(person, Tuple.of(0), true);
331 model.put(person, Tuple.of(1), true); 304 model.put(person, Tuple.of(1), true);
@@ -333,17 +306,14 @@ class QueryTest {
333 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE); 306 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE);
334 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); 307 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
335 308
336 RelationalScope scope = new RelationalScope(model, Set.of(persionView, friendMustView)); 309 model.flushChanges();
337 310 assertEquals(2, model.countResults(predicate));
338 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope); 311 compareMatchSets(model.allResults(predicate),
339 GenericPatternMatcher matcher = engine.getMatcher(query); 312 Set.of(List.of(Tuple.of(0), Tuple.of(1), Tuple.of(2)), List.of(Tuple.of(1), Tuple.of(0), Tuple.of(2))));
340
341 assertEquals(2, matcher.countMatches());
342 } 313 }
343 314
344 @Test 315 @Test
345 @Disabled 316 void patternCallTest() {
346 void transitivePatternCallTest() {
347 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false); 317 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
348 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); 318 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
349 RelationView<Boolean> persionView = new KeyOnlyRelationView(person); 319 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
@@ -356,7 +326,7 @@ class QueryTest {
356 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); 326 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
357 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); 327 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
358 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); 328 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
359 DNFAnd clause = new DNFAnd(new HashSet<>(parameters), 329 DNFAnd clause = new DNFAnd(Collections.emptySet(),
360 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom)); 330 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
361 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause)); 331 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
362 332
@@ -365,39 +335,32 @@ class QueryTest {
365 List<Variable> substitution = Arrays.asList(p3, p4); 335 List<Variable> substitution = Arrays.asList(p3, p4);
366 RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3)); 336 RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3));
367 RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4)); 337 RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4));
368 PredicateAtom friendPredicateAtom = new PredicateAtom(true, true, friendPredicate, substitution); 338 PredicateAtom friendPredicateAtom = new PredicateAtom(true, false, friendPredicate, substitution);
369 DNFAnd patternCallClause = new DNFAnd(new HashSet<>(substitution), 339 DNFAnd patternCallClause = new DNFAnd(Collections.emptySet(),
370 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom)); 340 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
371 DNFPredicate predicate = new DNFPredicate("TransitivePatternCall", substitution, 341 DNFPredicate predicate = new DNFPredicate("PatternCall", substitution, Arrays.asList(patternCallClause));
372 Arrays.asList(patternCallClause));
373
374 GenericQuerySpecification<GenericPatternMatcher> query = DNF2PQuery.translate(predicate).build();
375 342
376 ModelStore store = new ModelStoreImpl(Set.of(person, friend)); 343 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
377 Model model = store.createModel(); 344 Set.of(persionView, friendMustView), Set.of(friendPredicate, predicate));
345 QueriableModel model = store.createModel();
378 346
379 model.put(person, Tuple.of(0), true); 347 model.put(person, Tuple.of(0), true);
380 model.put(person, Tuple.of(1), true); 348 model.put(person, Tuple.of(1), true);
381 model.put(person, Tuple.of(2), true); 349 model.put(person, Tuple.of(2), true);
382 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE); 350 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
351 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
383 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE); 352 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
384 353
385 RelationalScope scope = new RelationalScope(model, Set.of(persionView, friendMustView)); 354 model.flushChanges();
386
387 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope);
388 GenericPatternMatcher matcher = engine.getMatcher(query);
389 355
390 assertEquals(3, matcher.countMatches()); 356 assertEquals(3, model.countResults(friendPredicate));
391 } 357 }
392 358
393 @Test 359 @Test
394 @Disabled 360 void negativePatternCallTest() {
395 void orTest() { 361 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
396 Relation<Boolean> person = new Relation<>("Person", 1, false);
397 Relation<Boolean> animal = new Relation<>("Animal", 1, false);
398 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE); 362 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
399 RelationView<Boolean> persionView = new KeyOnlyRelationView(person); 363 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
400 RelationView<Boolean> animalView = new KeyOnlyRelationView(animal);
401 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must()); 364 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
402 365
403 Variable p1 = new Variable("p1"); 366 Variable p1 = new Variable("p1");
@@ -406,36 +369,77 @@ class QueryTest {
406 369
407 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1)); 370 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
408 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2)); 371 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
409 RelationAtom friendRelationAtom1 = new RelationAtom(friendMustView, Arrays.asList(p1, p2)); 372 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
410 DNFAnd clause1 = new DNFAnd(new HashSet<>(parameters), 373 DNFAnd clause = new DNFAnd(Collections.emptySet(),
411 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom1)); 374 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
412 375 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
413 RelationAtom animalRelationAtom1 = new RelationAtom(animalView, Arrays.asList(p1));
414 RelationAtom animalRelationAtom2 = new RelationAtom(animalView, Arrays.asList(p2));
415 RelationAtom friendRelationAtom2 = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
416 DNFAnd clause2 = new DNFAnd(new HashSet<>(parameters),
417 Arrays.asList(animalRelationAtom1, animalRelationAtom2, friendRelationAtom2));
418 376
419 DNFPredicate predicate = new DNFPredicate("Or", parameters, Arrays.asList(clause1, clause2)); 377 Variable p3 = new Variable("p3");
420 GenericQuerySpecification<GenericPatternMatcher> query = DNF2PQuery.translate(predicate).build(); 378 Variable p4 = new Variable("p4");
379 List<Variable> substitution = Arrays.asList(p3, p4);
380 RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3));
381 RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4));
382 PredicateAtom friendPredicateAtom = new PredicateAtom(false, false, friendPredicate, substitution);
383 DNFAnd negativePatternCallClause = new DNFAnd(Collections.emptySet(),
384 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
385 DNFPredicate predicate = new DNFPredicate("NegativePatternCall", substitution,
386 Arrays.asList(negativePatternCallClause));
421 387
422 ModelStore store = new ModelStoreImpl(Set.of(person, animal, friend)); 388 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
423 Model model = store.createModel(); 389 Set.of(persionView, friendMustView), Set.of(friendPredicate, predicate));
390 QueriableModel model = store.createModel();
424 391
425 model.put(person, Tuple.of(0), true); 392 model.put(person, Tuple.of(0), true);
426 model.put(person, Tuple.of(1), true); 393 model.put(person, Tuple.of(1), true);
427 model.put(animal, Tuple.of(2), true); 394 model.put(person, Tuple.of(2), true);
428 model.put(animal, Tuple.of(3), true);
429 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE); 395 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
430 model.put(friend, Tuple.of(0, 2), TruthValue.TRUE); 396 model.put(friend, Tuple.of(1, 0), TruthValue.TRUE);
431 model.put(friend, Tuple.of(2, 3), TruthValue.TRUE); 397 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
432 model.put(friend, Tuple.of(3, 0), TruthValue.TRUE); 398
399 model.flushChanges();
400 assertEquals(6, model.countResults(predicate));
401 }
402
403 @Test
404 void transitivePatternCallTest() {
405 Relation<Boolean> person = new Relation<Boolean>("Person", 1, false);
406 Relation<TruthValue> friend = new Relation<>("friend", 2, TruthValue.FALSE);
407 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
408 RelationView<TruthValue> friendMustView = new FilteredRelationView<TruthValue>(friend, (k, v) -> v.must());
409
410 Variable p1 = new Variable("p1");
411 Variable p2 = new Variable("p2");
412 List<Variable> parameters = Arrays.asList(p1, p2);
433 413
434 RelationalScope scope = new RelationalScope(model, Set.of(persionView, animalView, friendMustView)); 414 RelationAtom personRelationAtom1 = new RelationAtom(persionView, Arrays.asList(p1));
415 RelationAtom personRelationAtom2 = new RelationAtom(persionView, Arrays.asList(p2));
416 RelationAtom friendRelationAtom = new RelationAtom(friendMustView, Arrays.asList(p1, p2));
417 DNFAnd clause = new DNFAnd(Collections.emptySet(),
418 Arrays.asList(personRelationAtom1, personRelationAtom2, friendRelationAtom));
419 DNFPredicate friendPredicate = new DNFPredicate("RelationConstraint", parameters, Arrays.asList(clause));
435 420
436 ViatraQueryEngine engine = AdvancedViatraQueryEngine.on(scope); 421 Variable p3 = new Variable("p3");
437 GenericPatternMatcher matcher = engine.getMatcher(query); 422 Variable p4 = new Variable("p4");
423 List<Variable> substitution = Arrays.asList(p3, p4);
424 RelationAtom personRelationAtom3 = new RelationAtom(persionView, Arrays.asList(p3));
425 RelationAtom personRelationAtom4 = new RelationAtom(persionView, Arrays.asList(p4));
426 PredicateAtom friendPredicateAtom = new PredicateAtom(true, true, friendPredicate, substitution);
427 DNFAnd patternCallClause = new DNFAnd(Collections.emptySet(),
428 Arrays.asList(personRelationAtom3, personRelationAtom4, friendPredicateAtom));
429 DNFPredicate predicate = new DNFPredicate("TransitivePatternCall", substitution,
430 Arrays.asList(patternCallClause));
431
432 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, friend),
433 Set.of(persionView, friendMustView), Set.of(friendPredicate, predicate));
434 QueriableModel model = store.createModel();
438 435
439 assertEquals(2, matcher.countMatches()); 436 model.put(person, Tuple.of(0), true);
437 model.put(person, Tuple.of(1), true);
438 model.put(person, Tuple.of(2), true);
439 model.put(friend, Tuple.of(0, 1), TruthValue.TRUE);
440 model.put(friend, Tuple.of(1, 2), TruthValue.TRUE);
441
442 model.flushChanges();
443 assertEquals(3, model.countResults(predicate));
440 } 444 }
441} \ No newline at end of file 445} \ No newline at end of file
diff --git a/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java b/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java
new file mode 100644
index 00000000..e72186b9
--- /dev/null
+++ b/store/src/test/java/tools/refinery/store/query/test/QueryTransactionTest.java
@@ -0,0 +1,58 @@
1package tools.refinery.store.query.test;
2
3import static org.junit.jupiter.api.Assertions.assertEquals;
4
5import java.util.Arrays;
6import java.util.Collections;
7import java.util.List;
8import java.util.Set;
9
10import org.junit.jupiter.api.Test;
11
12import tools.refinery.store.model.Tuple;
13import tools.refinery.store.model.representation.Relation;
14import tools.refinery.store.query.QueriableModel;
15import tools.refinery.store.query.QueriableModelStore;
16import tools.refinery.store.query.QueriableModelStoreImpl;
17import tools.refinery.store.query.building.DNFAnd;
18import tools.refinery.store.query.building.DNFPredicate;
19import tools.refinery.store.query.building.RelationAtom;
20import tools.refinery.store.query.building.Variable;
21import tools.refinery.store.query.view.KeyOnlyRelationView;
22import tools.refinery.store.query.view.RelationView;
23
24class QueryTransactionTest {
25 @Test
26 void flushTest() {
27 Relation<Boolean> person = new Relation<>("Person", 1, false);
28 Relation<Boolean> asset = new Relation<>("Asset", 1, false);
29 RelationView<Boolean> persionView = new KeyOnlyRelationView(person);
30
31 List<Variable> parameters = Arrays.asList(new Variable("p1"));
32 RelationAtom personRelationAtom = new RelationAtom(persionView, parameters);
33 DNFAnd clause = new DNFAnd(Collections.emptySet(), Arrays.asList(personRelationAtom));
34 DNFPredicate predicate = new DNFPredicate("TypeConstraint", parameters, Arrays.asList(clause));
35
36 QueriableModelStore store = new QueriableModelStoreImpl(Set.of(person, asset), Set.of(persionView),
37 Set.of(predicate));
38 QueriableModel model = store.createModel();
39
40 assertEquals(0, model.countResults(predicate));
41
42 model.put(person, Tuple.of(0), true);
43 model.put(person, Tuple.of(1), true);
44 model.put(asset, Tuple.of(1), true);
45 model.put(asset, Tuple.of(2), true);
46
47 assertEquals(0, model.countResults(predicate));
48
49 model.flushChanges();
50 assertEquals(2, model.countResults(predicate));
51
52 model.put(person, Tuple.of(4), true);
53 assertEquals(2, model.countResults(predicate));
54
55 model.flushChanges();
56 assertEquals(3, model.countResults(predicate));
57 }
58}