32
32
import com .apple .foundationdb .record .provider .foundationdb .FDBRecordContext ;
33
33
import com .apple .foundationdb .record .provider .foundationdb .FDBRecordStoreBase ;
34
34
import com .apple .foundationdb .record .query .RecordQuery ;
35
+ import com .apple .foundationdb .record .query .expressions .Comparisons ;
35
36
import com .apple .foundationdb .record .query .expressions .Query ;
36
37
import com .apple .foundationdb .record .query .plan .AvailableFields ;
37
38
import com .apple .foundationdb .record .query .plan .QueryPlanner ;
38
39
import com .apple .foundationdb .record .query .plan .RecordQueryPlanner ;
39
40
import com .apple .foundationdb .record .query .plan .cascades .AliasMap ;
41
+ import com .apple .foundationdb .record .query .plan .cascades .CascadesPlanner ;
40
42
import com .apple .foundationdb .record .query .plan .cascades .CorrelationIdentifier ;
43
+ import com .apple .foundationdb .record .query .plan .cascades .GraphExpansion ;
41
44
import com .apple .foundationdb .record .query .plan .cascades .Quantifier ;
42
45
import com .apple .foundationdb .record .query .plan .cascades .Reference ;
43
46
import com .apple .foundationdb .record .query .plan .cascades .explain .PlannerGraph ;
47
+ import com .apple .foundationdb .record .query .plan .cascades .expressions .LogicalSortExpression ;
48
+ import com .apple .foundationdb .record .query .plan .cascades .expressions .RecursiveExpression ;
44
49
import com .apple .foundationdb .record .query .plan .cascades .expressions .RelationalExpression ;
50
+ import com .apple .foundationdb .record .query .plan .cascades .predicates .ValuePredicate ;
51
+ import com .apple .foundationdb .record .query .plan .cascades .values .FieldValue ;
45
52
import com .apple .foundationdb .record .query .plan .cascades .values .ObjectValue ;
53
+ import com .apple .foundationdb .record .query .plan .cascades .values .QuantifiedObjectValue ;
54
+ import com .apple .foundationdb .record .query .plan .cascades .values .RecursivePriorValue ;
46
55
import com .apple .foundationdb .record .query .plan .cascades .values .Value ;
47
56
import com .apple .foundationdb .record .query .plan .cascades .values .translation .TranslationMap ;
48
57
import com .apple .foundationdb .record .query .plan .plans .QueryResult ;
51
60
import com .apple .test .Tags ;
52
61
import com .google .protobuf .Message ;
53
62
import org .junit .jupiter .api .Tag ;
54
- import org .junit .jupiter .api .Test ;
55
63
56
64
import javax .annotation .Nonnull ;
57
65
import javax .annotation .Nullable ;
58
66
import java .util .List ;
59
67
import java .util .Set ;
60
68
69
+ import static com .apple .foundationdb .record .provider .foundationdb .query .FDBSimpleQueryGraphTest .fullTypeScan ;
61
70
import static org .junit .jupiter .api .Assertions .assertEquals ;
62
71
import static org .junit .jupiter .api .Assertions .fail ;
63
72
67
76
@ Tag (Tags .RequiresFDB )
68
77
public class RecursiveQueryTest extends FDBRecordStoreQueryTestBase {
69
78
70
- @ Test
79
+ @ DualPlannerTest ()
71
80
public void testUp () throws Exception {
72
81
loadRecords ();
73
82
74
83
final RecordQueryPlan plan ;
75
84
if (planner instanceof RecordQueryPlanner ) {
76
85
plan = buildAncestorPlan ((RecordQueryPlanner )planner );
86
+ } else if (planner instanceof CascadesPlanner ) {
87
+ plan = buildAncestorPlan ((CascadesPlanner )planner );
77
88
} else {
78
89
plan = null ;
79
90
fail ("unsupported planner type" );
@@ -84,6 +95,7 @@ public void testUp() throws Exception {
84
95
assertEquals (expected , actual );
85
96
}
86
97
98
+ @ Nonnull
87
99
private RecordQueryPlan buildAncestorPlan (@ Nonnull RecordQueryPlanner planner ) throws Exception {
88
100
final RecordQuery rootQuery = RecordQuery .newBuilder ()
89
101
.setRecordType ("MyChildRecord" )
@@ -96,13 +108,42 @@ private RecordQueryPlan buildAncestorPlan(@Nonnull RecordQueryPlanner planner) t
96
108
return recursiveQuery (planner , rootQuery , childQuery , "parent_rec_no" );
97
109
}
98
110
99
- @ Test
111
+ @ Nonnull
112
+ private RecordQueryPlan buildAncestorPlan (@ Nonnull CascadesPlanner cascadesPlanner ) {
113
+ return planGraph (
114
+ () -> {
115
+ var rootQuantifier = fullTypeScan (cascadesPlanner .getRecordMetaData (), "MyChildRecord" );
116
+ var graphExpansionBuilder = GraphExpansion .builder ();
117
+ graphExpansionBuilder .addQuantifier (rootQuantifier );
118
+ graphExpansionBuilder .addPredicate (
119
+ new ValuePredicate (FieldValue .ofFieldName (QuantifiedObjectValue .of (rootQuantifier .getAlias (), rootQuantifier .getFlowedObjectType ()), "str_value" ),
120
+ new Comparisons .SimpleComparison (Comparisons .Type .EQUALS , "three" )));
121
+ rootQuantifier = Quantifier .forEach (Reference .of (graphExpansionBuilder .build ().buildSimpleSelectOverQuantifier ((Quantifier .ForEach )rootQuantifier )));
122
+
123
+ var childQuantifier = fullTypeScan (cascadesPlanner .getRecordMetaData (), "MyChildRecord" );
124
+ graphExpansionBuilder = GraphExpansion .builder ();
125
+ graphExpansionBuilder .addQuantifier (childQuantifier );
126
+ var aliasForPrior = CorrelationIdentifier .uniqueID ();
127
+ graphExpansionBuilder .addPredicate (
128
+ new ValuePredicate (FieldValue .ofFieldName (QuantifiedObjectValue .of (childQuantifier .getAlias (), childQuantifier .getFlowedObjectType ()), "rec_no" ),
129
+ new Comparisons .ValueComparison (Comparisons .Type .EQUALS , FieldValue .ofFieldName (RecursivePriorValue .of (aliasForPrior , childQuantifier .getFlowedObjectType ()), "parent_rec_no" ))));
130
+ childQuantifier = Quantifier .forEach (Reference .of (graphExpansionBuilder .build ().buildSimpleSelectOverQuantifier ((Quantifier .ForEach )childQuantifier )), aliasForPrior );
131
+
132
+ final var resultStrValue = FieldValue .ofFieldName (QuantifiedObjectValue .of (childQuantifier .getAlias (), childQuantifier .getFlowedObjectType ()), "str_value" );
133
+ final var recursive = new RecursiveExpression (resultStrValue , rootQuantifier , childQuantifier );
134
+ return Reference .of (LogicalSortExpression .unsorted (Quantifier .forEach (Reference .of (recursive ))));
135
+ });
136
+ }
137
+
138
+ @ DualPlannerTest ()
100
139
public void testDown () throws Exception {
101
140
loadRecords ();
102
141
103
142
final RecordQueryPlan plan ;
104
143
if (planner instanceof RecordQueryPlanner ) {
105
144
plan = buildDescendantPlan ((RecordQueryPlanner )planner );
145
+ } else if (planner instanceof CascadesPlanner ) {
146
+ plan = buildDescendantPlan ((CascadesPlanner )planner );
106
147
} else {
107
148
plan = null ;
108
149
fail ("unsupported planner type" );
@@ -126,6 +167,33 @@ private static RecordQueryPlan buildDescendantPlan(@Nonnull RecordQueryPlanner p
126
167
return recursiveQuery (planner , rootQuery , childQuery , "rec_no" );
127
168
}
128
169
170
+ @ Nonnull
171
+ private RecordQueryPlan buildDescendantPlan (@ Nonnull CascadesPlanner cascadesPlanner ) {
172
+ return planGraph (
173
+ () -> {
174
+ var rootQuantifier = fullTypeScan (cascadesPlanner .getRecordMetaData (), "MyChildRecord" );
175
+ var graphExpansionBuilder = GraphExpansion .builder ();
176
+ graphExpansionBuilder .addQuantifier (rootQuantifier );
177
+ graphExpansionBuilder .addPredicate (
178
+ new ValuePredicate (FieldValue .ofFieldName (QuantifiedObjectValue .of (rootQuantifier .getAlias (), rootQuantifier .getFlowedObjectType ()), "parent_rec_no" ),
179
+ new Comparisons .NullComparison (Comparisons .Type .IS_NULL )));
180
+ rootQuantifier = Quantifier .forEach (Reference .of (graphExpansionBuilder .build ().buildSimpleSelectOverQuantifier ((Quantifier .ForEach )rootQuantifier )));
181
+
182
+ var childQuantifier = fullTypeScan (cascadesPlanner .getRecordMetaData (), "MyChildRecord" );
183
+ graphExpansionBuilder = GraphExpansion .builder ();
184
+ graphExpansionBuilder .addQuantifier (childQuantifier );
185
+ var aliasForPrior = CorrelationIdentifier .uniqueID ();
186
+ graphExpansionBuilder .addPredicate (
187
+ new ValuePredicate (FieldValue .ofFieldName (QuantifiedObjectValue .of (childQuantifier .getAlias (), childQuantifier .getFlowedObjectType ()), "parent_rec_no" ),
188
+ new Comparisons .ValueComparison (Comparisons .Type .EQUALS , FieldValue .ofFieldName (RecursivePriorValue .of (aliasForPrior , childQuantifier .getFlowedObjectType ()), "rec_no" ))));
189
+ childQuantifier = Quantifier .forEach (Reference .of (graphExpansionBuilder .build ().buildSimpleSelectOverQuantifier ((Quantifier .ForEach )childQuantifier )), aliasForPrior );
190
+
191
+ final var resultStrValue = FieldValue .ofFieldName (QuantifiedObjectValue .of (childQuantifier .getAlias (), childQuantifier .getFlowedObjectType ()), "str_value" );
192
+ final var recursive = new RecursiveExpression (resultStrValue , rootQuantifier , childQuantifier );
193
+ return Reference .of (LogicalSortExpression .unsorted (Quantifier .forEach (Reference .of (recursive ))));
194
+ });
195
+ }
196
+
129
197
private void openParentChildRecordStore (FDBRecordContext context ) throws Exception {
130
198
final RecordMetaDataBuilder metaDataBuilder = RecordMetaData .newBuilder ().setRecords (TestRecordsParentChildRelationshipProto .getDescriptor ());
131
199
createOrOpenRecordStore (context , metaDataBuilder .getRecordMetaData ());
0 commit comments