Skip to content

Commit 3ed0ff2

Browse files
committed
Restrict parent changes
1 parent db8e0a6 commit 3ed0ff2

7 files changed

Lines changed: 276 additions & 21 deletions

File tree

src/main/java/graphql/schema/diffing/DiffImpl.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.google.common.collect.Multiset;
55
import com.google.common.collect.Multisets;
66
import graphql.Internal;
7+
import graphql.schema.diffing.DiffImpl.OptimalEdit;
78

89
import java.util.ArrayList;
910
import java.util.Collection;
@@ -21,6 +22,7 @@
2122
import static graphql.Assert.assertTrue;
2223
import static graphql.schema.diffing.EditorialCostForMapping.baseEditorialCostForMapping;
2324
import static graphql.schema.diffing.EditorialCostForMapping.editorialCostForMapping;
25+
import static graphql.schema.diffing.SchemaParentRestrictions.getNonFixedRestrictions;
2426

2527
/**
2628
* This is an algorithm calculating the optimal edit to change the source graph into the target graph.
@@ -201,20 +203,22 @@ private void addChildToQueue(int fixedEditorialCost,
201203
double[][] costMatrixForHungarianAlgo = new double[costMatrixSize][costMatrixSize];
202204
double[][] costMatrix = new double[costMatrixSize][costMatrixSize];
203205

204-
205206
Map<Vertex, Double> isolatedVerticesCache = new LinkedHashMap<>();
206207

208+
Map<Vertex, Vertex> parentRestrictions = getNonFixedRestrictions(parentPartialMapping, completeSourceGraph, completeTargetGraph);
209+
207210
for (int i = parentLevel; i < allSources.size(); i++) {
208211
Vertex v = allSources.get(i);
209212
int j = 0;
210213
for (Vertex u : availableTargetVertices) {
211-
double cost = calcLowerBoundMappingCost(v, u, parentPartialMapping, isolatedVerticesCache);
214+
double cost = calcLowerBoundMappingCost(v, u, parentPartialMapping, isolatedVerticesCache, parentRestrictions);
212215
costMatrixForHungarianAlgo[i - parentLevel][j] = cost;
213216
costMatrix[i - parentLevel][j] = cost;
214217
j++;
215218
}
216219
runningCheck.check();
217220
}
221+
218222
HungarianAlgorithm hungarianAlgorithm = new HungarianAlgorithm(costMatrixForHungarianAlgo);
219223
int[] assignments = hungarianAlgorithm.execute();
220224
int editorialCostForMapping = editorialCostForMapping(fixedEditorialCost, parentPartialMapping, completeSourceGraph, completeTargetGraph);
@@ -223,7 +227,6 @@ private void addChildToQueue(int fixedEditorialCost,
223227

224228
Mapping newMapping = parentPartialMapping.extendMapping(v_i, availableTargetVertices.get(assignments[0]));
225229

226-
227230
if (lowerBoundForPartialMapping >= optimalEdit.ged) {
228231
return;
229232
}
@@ -298,7 +301,6 @@ private void calculateRestOfChildren(List<Vertex> availableTargetVertices,
298301

299302
runningCheck.check();
300303
}
301-
302304
}
303305

304306
// this retrieves the next sibling from MappingEntry.sibling and adds it to the queue if the lowerBound is less than the current upperBound
@@ -396,7 +398,25 @@ private double getCostMatrixSum(double[][] costMatrix, int[] assignments) {
396398
private double calcLowerBoundMappingCost(Vertex v,
397399
Vertex u,
398400
Mapping partialMapping,
399-
Map<Vertex, Double> isolatedVerticesCache) {
401+
Map<Vertex, Double> isolatedVerticesCache,
402+
Map<Vertex, Vertex> parentRestrictions) {
403+
if (parentRestrictions.containsKey(v) || partialMapping.hasParentRestriction(v)) {
404+
Vertex uParentRestriction = parentRestrictions.get(v);
405+
if (uParentRestriction == null) {
406+
uParentRestriction = partialMapping.getParentRestriction(v);
407+
}
408+
409+
Collection<Edge> parentEdges = completeTargetGraph.getAdjacentEdgesInverseNonCopy(u);
410+
if (parentEdges.size() != 1) {
411+
return Integer.MAX_VALUE;
412+
}
413+
414+
Vertex uParent = parentEdges.iterator().next().getFrom();
415+
if (uParent != uParentRestriction) {
416+
return Integer.MAX_VALUE;
417+
}
418+
}
419+
400420
if (!possibleMappings.mappingPossible(v, u)) {
401421
return Integer.MAX_VALUE;
402422
}

src/main/java/graphql/schema/diffing/EditorialCostForMapping.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public static int baseEditorialCostForMapping(Mapping mapping, // can be a parti
3434
List<EditOperation> editOperationsResult) {
3535
int cost = 0;
3636

37-
3837
for (int i = 0; i < mapping.size(); i++) {
3938
Vertex sourceVertex = mapping.getSource(i);
4039
Vertex targetVertex = mapping.getTarget(i);

src/main/java/graphql/schema/diffing/Mapping.java

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@
66

77
import java.util.ArrayList;
88
import java.util.Collections;
9+
import java.util.HashMap;
10+
import java.util.LinkedHashMap;
911
import java.util.List;
12+
import java.util.Map;
1013
import java.util.function.BiConsumer;
1114
import java.util.function.Consumer;
15+
import java.util.function.Predicate;
1216

1317
/**
1418
* A mapping (in the math sense) from a list of vertices to another list of
@@ -20,6 +24,7 @@
2024
public class Mapping {
2125

2226

27+
private final Map<Vertex, Vertex> fixedParentRestrictions;
2328
private final BiMap<Vertex, Vertex> fixedMappings;
2429
private final List<Vertex> fixedSourceList;
2530
private final List<Vertex> fixedTargetList;
@@ -28,12 +33,14 @@ public class Mapping {
2833
private final List<Vertex> sourceList;
2934
private final List<Vertex> targetList;
3035

31-
private Mapping(BiMap<Vertex, Vertex> fixedMappings,
36+
private Mapping(Map<Vertex, Vertex> fixedParentRestrictions,
37+
BiMap<Vertex, Vertex> fixedMappings,
3238
List<Vertex> fixedSourceList,
3339
List<Vertex> fixedTargetList,
3440
BiMap<Vertex, Vertex> map,
3541
List<Vertex> sourceList,
3642
List<Vertex> targetList) {
43+
this.fixedParentRestrictions = fixedParentRestrictions;
3744
this.fixedMappings = fixedMappings;
3845
this.fixedSourceList = fixedSourceList;
3946
this.fixedTargetList = fixedTargetList;
@@ -42,9 +49,26 @@ private Mapping(BiMap<Vertex, Vertex> fixedMappings,
4249
this.targetList = targetList;
4350
}
4451

45-
public static Mapping newMapping(BiMap<Vertex, Vertex> fixedMappings, List<Vertex> fixedSourceList, List<Vertex> fixedTargetList) {
46-
return new Mapping(fixedMappings, fixedSourceList, fixedTargetList, HashBiMap.create(), Collections.emptyList(), Collections.emptyList());
52+
public static Mapping newMapping(Map<Vertex, Vertex> fixedParentRestrictions,
53+
BiMap<Vertex, Vertex> fixedMappings,
54+
List<Vertex> fixedSourceList,
55+
List<Vertex> fixedTargetList) {
56+
return new Mapping(
57+
fixedParentRestrictions,
58+
fixedMappings,
59+
fixedSourceList,
60+
fixedTargetList,
61+
HashBiMap.create(),
62+
Collections.emptyList(),
63+
Collections.emptyList());
64+
}
65+
66+
public boolean hasParentRestriction(Vertex v) {
67+
return fixedParentRestrictions.containsKey(v);
68+
}
4769

70+
public Vertex getParentRestriction(Vertex v) {
71+
return fixedParentRestrictions.get(v);
4872
}
4973

5074
public Vertex getSource(Vertex target) {
@@ -118,14 +142,14 @@ public Mapping copyMappingWithLastElementRemoved() {
118142
newMap.remove(this.sourceList.get(this.sourceList.size() - 1));
119143
List<Vertex> newSourceList = new ArrayList<>(this.sourceList.subList(0, this.sourceList.size() - 1));
120144
List<Vertex> newTargetList = new ArrayList<>(this.targetList.subList(0, this.targetList.size() - 1));
121-
return new Mapping(fixedMappings, fixedSourceList, fixedTargetList, newMap, newSourceList, newTargetList);
145+
return new Mapping(fixedParentRestrictions, fixedMappings, fixedSourceList, fixedTargetList, newMap, newSourceList, newTargetList);
122146
}
123147

124148
public Mapping copy() {
125149
HashBiMap<Vertex, Vertex> newMap = HashBiMap.create(map);
126150
List<Vertex> newSourceList = new ArrayList<>(this.sourceList);
127151
List<Vertex> newTargetList = new ArrayList<>(this.targetList);
128-
return new Mapping(fixedMappings, fixedSourceList, fixedTargetList, newMap, newSourceList, newTargetList);
152+
return new Mapping(fixedParentRestrictions, fixedMappings, fixedSourceList, fixedTargetList, newMap, newSourceList, newTargetList);
129153
}
130154

131155
public Mapping extendMapping(Vertex source, Vertex target) {
@@ -135,7 +159,7 @@ public Mapping extendMapping(Vertex source, Vertex target) {
135159
newSourceList.add(source);
136160
List<Vertex> newTargetList = new ArrayList<>(this.targetList);
137161
newTargetList.add(target);
138-
return new Mapping(fixedMappings, fixedSourceList, fixedTargetList, newMap, newSourceList, newTargetList);
162+
return new Mapping(fixedParentRestrictions, fixedMappings, fixedSourceList, fixedTargetList, newMap, newSourceList, newTargetList);
139163
}
140164

141165
public void forEachTarget(Consumer<? super Vertex> action) {
@@ -168,6 +192,6 @@ public Mapping invert() {
168192
Vertex t = map.get(s);
169193
invertedMap.put(t, s);
170194
}
171-
return new Mapping(invertedFixedMappings, fixedTargetList, fixedSourceList, invertedMap, targetList, sourceList);
195+
return new Mapping(fixedParentRestrictions, invertedFixedMappings, fixedTargetList, fixedSourceList, invertedMap, targetList, sourceList);
172196
}
173197
}

src/main/java/graphql/schema/diffing/SchemaDiffing.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import static graphql.Assert.assertTrue;
1818
import static graphql.schema.diffing.EditorialCostForMapping.baseEditorialCostForMapping;
19+
import static graphql.schema.diffing.SchemaParentRestrictions.getFixedRestrictions;
1920

2021
@Internal
2122
public class SchemaDiffing {
@@ -57,7 +58,11 @@ private DiffImpl.OptimalEdit diffImpl(SchemaGraph sourceGraph, SchemaGraph targe
5758
PossibleMappingsCalculator possibleMappingsCalculator = new PossibleMappingsCalculator(sourceGraph, targetGraph, runningCheck);
5859
PossibleMappingsCalculator.PossibleMappings possibleMappings = possibleMappingsCalculator.calculate();
5960

61+
List<Vertex> nonMappedSource = new ArrayList<>(sourceGraph.getVertices());
62+
nonMappedSource.removeAll(possibleMappings.fixedOneToOneSources);
63+
6064
Mapping startMapping = Mapping.newMapping(
65+
getFixedRestrictions(sourceGraph, possibleMappings.fixedOneToOneMappings, nonMappedSource),
6166
possibleMappings.fixedOneToOneMappings,
6267
possibleMappings.fixedOneToOneSources,
6368
possibleMappings.fixedOneToOneTargets);
@@ -67,15 +72,11 @@ private DiffImpl.OptimalEdit diffImpl(SchemaGraph sourceGraph, SchemaGraph targe
6772
return new DiffImpl.OptimalEdit(sourceGraph, targetGraph, startMapping, baseEditorialCostForMapping(startMapping, sourceGraph, targetGraph));
6873
}
6974

70-
List<Vertex> nonMappedSource = new ArrayList<>(sourceGraph.getVertices());
71-
nonMappedSource.removeAll(possibleMappings.fixedOneToOneSources);
72-
7375
List<Vertex> nonMappedTarget = new ArrayList<>(targetGraph.getVertices());
7476
nonMappedTarget.removeAll(possibleMappings.fixedOneToOneTargets);
7577

7678
runningCheck.check();
7779

78-
7980
int isolatedSourceCount = (int) nonMappedSource.stream().filter(Vertex::isIsolated).count();
8081
int isolatedTargetCount = (int) nonMappedTarget.stream().filter(Vertex::isIsolated).count();
8182
if (isolatedTargetCount > isolatedSourceCount) {
@@ -87,6 +88,7 @@ private DiffImpl.OptimalEdit diffImpl(SchemaGraph sourceGraph, SchemaGraph targe
8788
fixedOneToOneInverted.put(t, s);
8889
}
8990
Mapping startMappingInverted = Mapping.newMapping(
91+
getFixedRestrictions(targetGraph, fixedOneToOneInverted, nonMappedTarget),
9092
fixedOneToOneInverted,
9193
possibleMappings.fixedOneToOneTargets,
9294
possibleMappings.fixedOneToOneSources
@@ -109,7 +111,6 @@ private DiffImpl.OptimalEdit diffImpl(SchemaGraph sourceGraph, SchemaGraph targe
109111
DiffImpl.OptimalEdit optimalEdit = diffImpl.diffImpl(startMappingInverted, targetVertices, sourceVertices, algoIterationCount);
110112
DiffImpl.OptimalEdit invertedBackOptimalEdit = new DiffImpl.OptimalEdit(sourceGraph, targetGraph, optimalEdit.mapping.invert(), optimalEdit.ged);
111113
return invertedBackOptimalEdit;
112-
113114
} else {
114115
sortVertices(nonMappedSource, sourceGraph, possibleMappings);
115116

@@ -132,5 +133,4 @@ private void sortVertices(List<Vertex> vertices, SchemaGraph schemaGraph, Possib
132133
Comparator<Vertex> vertexComparator = Comparator.comparing(schemaGraph::adjacentEdgesAndInverseCount).reversed();
133134
vertices.sort(vertexComparator);
134135
}
135-
136136
}

src/main/java/graphql/schema/diffing/SchemaGraph.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,21 @@ public Vertex getAppliedDirectiveContainerForAppliedDirective(Vertex appliedDire
258258
return adjacentVertices.get(0);
259259
}
260260

261+
/**
262+
* Assuming the child has one parent, this gets it.
263+
*/
264+
public Vertex getSingleParent(Vertex child) {
265+
Collection<Edge> adjacentVertices = this.getAdjacentEdgesInverseNonCopy(child);
266+
assertTrue(adjacentVertices.size() == 1, () -> format("No parent found for %s", child));
267+
return adjacentVertices.iterator().next().getFrom();
268+
}
269+
261270
public int getAppliedDirectiveIndex(Vertex appliedDirective) {
262271
List<Edge> adjacentEdges = this.getAdjacentEdgesInverseCopied(appliedDirective);
263272
assertTrue(adjacentEdges.size() == 1, () -> format("No applied directive container found for %s", appliedDirective));
264273
return Integer.parseInt(adjacentEdges.get(0).getLabel());
265274
}
266275

267-
268276
public Vertex getEnumForEnumValue(Vertex enumValue) {
269277
List<Vertex> adjacentVertices = this.getAdjacentVerticesInverse(enumValue);
270278
assertTrue(adjacentVertices.size() == 1, () -> format("No enum found for %s", enumValue));
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package graphql.schema.diffing;
2+
3+
import com.google.common.collect.BiMap;
4+
import graphql.Internal;
5+
6+
import java.util.LinkedHashMap;
7+
import java.util.List;
8+
import java.util.Map;
9+
10+
import static graphql.Assert.assertTrue;
11+
12+
@Internal
13+
public class SchemaParentRestrictions {
14+
public static boolean isApplicableChildVertex(Vertex vertex) {
15+
return vertex.isOfType(SchemaGraph.FIELD)
16+
|| vertex.isOfType(SchemaGraph.INPUT_FIELD)
17+
|| vertex.isOfType(SchemaGraph.ENUM_VALUE)
18+
|| vertex.isOfType(SchemaGraph.ARGUMENT);
19+
}
20+
21+
public static boolean isApplicableParentVertex(Vertex vertex) {
22+
return vertex.isOfType(SchemaGraph.INPUT_OBJECT)
23+
|| vertex.isOfType(SchemaGraph.OBJECT)
24+
|| vertex.isOfType(SchemaGraph.ENUM);
25+
}
26+
27+
public static Map<Vertex, Vertex> getFixedRestrictions(SchemaGraph sourceGraph,
28+
BiMap<Vertex, Vertex> sourceToTargetMapping,
29+
List<Vertex> needsFixing) {
30+
Map<Vertex, Vertex> restrictions = new LinkedHashMap<>();
31+
32+
for (Vertex vertex : needsFixing) {
33+
if (isApplicableChildVertex(vertex)) {
34+
Vertex sourceParent = sourceGraph.getSingleParent(vertex);
35+
Vertex fixedTargetParent = sourceToTargetMapping.get(sourceParent);
36+
37+
if (fixedTargetParent != null) {
38+
for (Edge edge : sourceGraph.getAdjacentEdgesNonCopy(sourceParent)) {
39+
Vertex sibling = edge.getTo();
40+
41+
if (isApplicableChildVertex(sibling)) {
42+
restrictions.put(sibling, fixedTargetParent);
43+
}
44+
}
45+
}
46+
}
47+
}
48+
49+
return restrictions;
50+
}
51+
52+
public static Map<Vertex, Vertex> getNonFixedRestrictions(Mapping parentMapping,
53+
SchemaGraph completeSourceGraph,
54+
SchemaGraph completeTargetGraph) {
55+
Map<Vertex, Vertex> restrictions = new LinkedHashMap<>();
56+
57+
parentMapping.forEachNonFixedSourceAndTarget((source, target) -> {
58+
if (isApplicableParentVertex(source) && isApplicableParentVertex(target)) {
59+
for (Edge edge : completeSourceGraph.getAdjacentEdgesNonCopy(source)) {
60+
Vertex child = edge.getTo();
61+
62+
if (isApplicableChildVertex(child)) {
63+
restrictions.put(child, target);
64+
}
65+
}
66+
} else if (isApplicableChildVertex(source) && isApplicableChildVertex(target)) {
67+
Vertex sourceParent = completeSourceGraph.getSingleParent(source);
68+
Vertex targetParent = completeTargetGraph.getSingleParent(target);
69+
70+
for (Edge edge : completeSourceGraph.getAdjacentEdgesNonCopy(sourceParent)) {
71+
Vertex sibling = edge.getTo();
72+
73+
if (isApplicableChildVertex(sibling)) {
74+
restrictions.put(sibling, targetParent);
75+
}
76+
}
77+
}
78+
});
79+
80+
return restrictions;
81+
}
82+
}

0 commit comments

Comments
 (0)