@@ -10,3 +10,101 @@ request.
10
10
The supergraph is a GraphQL IDL document that contains metadata for the query
11
11
planner that describes the relationship between type system members and the type
12
12
system members on subgraphs.
13
+
14
+ ## PlanOptions Algorithm
15
+
16
+ The PlanOptions algorithm computes which source schemas (options) can resolve a specific path in the
17
+ composite schema. The algorithm examines each step of the path to determine which source schemas can
18
+ fulfill the requested fields.
19
+
20
+ ### Formal Specification
21
+
22
+ me
23
+
24
+ Query.me.profile.age
25
+ [
26
+ (Query, me),
27
+ (User, profile),
28
+ (Profile, age)
29
+ ]
30
+
31
+ Mutation.createUser.query.me
32
+ [
33
+ (Mutation, createUser),
34
+ (CreateUserPayload, query),
35
+ (Query, me)
36
+ ]
37
+ // Technically the first item doesnt have to check all source schemas twice (inintializ eoptions as [ ] )
38
+ //
39
+
40
+
41
+
42
+ PlanOptions(path):
43
+
44
+
45
+ - Let {pathElements} be the list of tuples ({type}, {field}) in the provided {path}.
46
+
47
+ - Let ({initialType}, {initialField}) be the first element in {pathElements}.
48
+
49
+ - Let {sourceSchemas} be an empty set.
50
+ - For each {sourceSchema} in all source schemas:
51
+ - If {sourceSchema} does not define {initialType}.{initialField}
52
+ - Continue to the next {sourceSchema}.
53
+ - Add {sourceSchema} to {sourceSchemas}.
54
+
55
+ - For each {pathElement} in {pathElements} starting from the second element:
56
+ - Initialize a new empty set {nextSchemas}.
57
+ - Set {currentType} and {currentField} to the respective elements of the current {pathElement}.
58
+ - For each {currentSchema} in {sourceSchemas}:
59
+ - For each {candidateSchema} in all source schemas:
60
+ - If {candidateSchema} does not define {currentType}.{currentField}
61
+ - Continue to the next {candidateSchema}.
62
+
63
+ - If {candidateSchema} not equals {currentSchema}:
64
+ - Continue to the next {candidateSchema}.
65
+ - If {IsReachable(option, candidateSchema, currentType)} returns false:
66
+ - Continue to the next {candidateSchema}.
67
+
68
+ - If {currentField} on {currentType} in {candidateSchema} defines a requirement:
69
+ - If {ResolveRequirement(candidateSchema, currentType, currentField)} returns a valid requirement:
70
+ - If the requirement is not satisfied by {currentSchema}:
71
+ - Continue to the next {candidateSchema}.
72
+ - Add {candidateSchema} to {nextSchemas}.
73
+
74
+ - If {nextSchemas} is empty after:
75
+ - Return an empty set.
76
+
77
+ - Set {sourceSchemas} equal to {nextSchemas} to proceed to the next element in the path.
78
+
79
+ - return {sourceSchemas}.
80
+
81
+ IsReachable(sourceSchema, targetSchema, type):
82
+ - If {targetSchema} does not define {type}
83
+ - return false
84
+ - If {sourceSchema} equals {targetSchema}
85
+ - return true
86
+ - Let {lookups} be the set of all lookup fields in {targetSchema} that return {type}
87
+ - For each {lookup} in {lookups}:
88
+ - Let {keyFields} be the set of fields that form the key for {lookup} in {targetSchema}
89
+ - If {keyFields} is empty
90
+ - return false // ??
91
+ - For each {keyField} in {keyFields}:
92
+ - PlanOptions({keyField}) ??
93
+ - return false
94
+
95
+ ResolveRequirement(schema, type, field):
96
+ - Let {requirements} be the parsed selection map from requirements on {field}
97
+ - Let {otherSchemas} be the set of all source schemas excluding {schema}
98
+ - For each {requiredField} in {requirements}:
99
+ - Let {fieldPath} be the path to {requiredField} starting from {type}
100
+ - Let {canResolve} be false
101
+ - For each {otherSchema} in {otherSchemas}:
102
+ - If {otherSchema} defines {type} and {fieldPath}
103
+ - If {IsReachable(schema, otherSchema, type)} returns true
104
+ - Set {canResolve} to true
105
+ - Break
106
+ - If {canResolve} is false
107
+ - return false
108
+ - return true
109
+
110
+
0 commit comments