aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
blob: 13641ace4497a6dad11520d29119548304526743 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package tools.refinery.store.query.viatra.internal;

import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory;
import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory;
import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
import tools.refinery.store.model.ModelStore;
import tools.refinery.store.model.ModelStoreBuilder;
import tools.refinery.store.query.Dnf;
import tools.refinery.store.query.viatra.ViatraModelQueryBuilder;
import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery;
import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher;

import java.util.*;
import java.util.function.Function;

public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder implements ViatraModelQueryBuilder {
	private ViatraQueryEngineOptions.Builder engineOptionsBuilder;
	private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery();
	private final Set<Dnf> vacuousQueries = new LinkedHashSet<>();
	private final Map<Dnf, IQuerySpecification<RawPatternMatcher>> querySpecifications = new LinkedHashMap<>();

	public ViatraModelQueryBuilderImpl(ModelStoreBuilder storeBuilder) {
		super(storeBuilder);
		engineOptionsBuilder = new ViatraQueryEngineOptions.Builder()
				.withDefaultBackend(ReteBackendFactory.INSTANCE)
				.withDefaultCachingBackend(ReteBackendFactory.INSTANCE)
				.withDefaultSearchBackend(LocalSearchGenericBackendFactory.INSTANCE);
	}

	@Override
	public ViatraModelQueryBuilder engineOptions(ViatraQueryEngineOptions engineOptions) {
		engineOptionsBuilder = new ViatraQueryEngineOptions.Builder(engineOptions);
		return this;
	}

	@Override
	public ViatraModelQueryBuilder defaultHint(QueryEvaluationHint queryEvaluationHint) {
		engineOptionsBuilder.withDefaultHint(queryEvaluationHint);
		return this;
	}

	@Override
	public ViatraModelQueryBuilder backend(IQueryBackendFactory queryBackendFactory) {
		engineOptionsBuilder.withDefaultBackend(queryBackendFactory);
		return this;
	}

	@Override
	public ViatraModelQueryBuilder cachingBackend(IQueryBackendFactory queryBackendFactory) {
		engineOptionsBuilder.withDefaultCachingBackend(queryBackendFactory);
		return this;
	}

	@Override
	public ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory) {
		engineOptionsBuilder.withDefaultSearchBackend(queryBackendFactory);
		return this;
	}

	@Override
	public ViatraModelQueryBuilder query(Dnf query) {
		if (querySpecifications.containsKey(query) || vacuousQueries.contains(query)) {
			// Ignore duplicate queries.
			return this;
		}
		var reduction = query.getReduction();
		switch (reduction) {
		case NOT_REDUCIBLE -> {
			var pQuery = dnf2PQuery.translate(query);
			querySpecifications.put(query, pQuery.build());
		}
		case ALWAYS_FALSE -> vacuousQueries.add(query);
		case ALWAYS_TRUE -> throw new IllegalArgumentException(
				"Query %s is relationally unsafe (it matches every tuple)".formatted(query.name()));
		default -> throw new IllegalArgumentException("Unknown reduction: " + reduction);
		}
		return this;
	}

	@Override
	public ViatraModelQueryBuilder query(Dnf query, QueryEvaluationHint queryEvaluationHint) {
		query(query);
		hint(query, queryEvaluationHint);
		return this;
	}

	@Override
	public ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint) {
		dnf2PQuery.setComputeHint(computeHint);
		return this;
	}

	@Override
	public ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint) {
		var pQuery = dnf2PQuery.getAlreadyTranslated(dnf);
		if (pQuery == null) {
			if (vacuousQueries.contains(dnf)) {
				// Ignore hits for queries that will never be executed by the query engine.
				return this;
			}
			throw new IllegalArgumentException(
					"Cannot specify hint for %s, because it was not added to the query engine".formatted(dnf.name()));
		}
		pQuery.setEvaluationHints(pQuery.getEvaluationHints().overrideBy(queryEvaluationHint));
		return this;
	}

	@Override
	public ViatraModelQueryStoreAdapterImpl createStoreAdapter(ModelStore store) {
		validateSymbols(store);
		return new ViatraModelQueryStoreAdapterImpl(store, engineOptionsBuilder.build(), dnf2PQuery.getRelationViews(),
				Collections.unmodifiableMap(querySpecifications), Collections.unmodifiableSet(vacuousQueries));
	}

	private void validateSymbols(ModelStore store) {
		var symbols = store.getSymbols();
		for (var relationView : dnf2PQuery.getRelationViews().keySet()) {
			var symbol = relationView.getSymbol();
			if (!symbols.contains(symbol)) {
				throw new IllegalArgumentException("Cannot query relation view %s: symbol %s is not in the model"
						.formatted(relationView, symbol));
			}
		}
	}
}