Skip to content

Commit cd01357

Browse files
authored
No projection (#401)
1 parent 45e6b03 commit cd01357

File tree

7 files changed

+109
-86
lines changed

7 files changed

+109
-86
lines changed

app/genres/fiori-service.cds

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ using AdminService from '../../srv/admin-service';
77

88
annotate AdminService.GenreHierarchy with @Aggregation.RecursiveHierarchy#GenreHierarchy: {
99
$Type: 'Aggregation.RecursiveHierarchyType',
10-
NodeProperty: node_id, // identifies a node
11-
ParentNavigationProperty: parent // navigates to a node's parent
10+
NodeProperty: ID, // identifies a node
11+
ParentNavigationProperty: parnt // navigates to a node's parent
1212
};
1313

1414
annotate AdminService.GenreHierarchy with @Hierarchy.RecursiveHierarchy#GenreHierarchy: {

db/books.cds

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ annotate Authors with
4747
*/
4848
entity Genres {
4949
key ID : Integer;
50-
node : Integer not null;
51-
parent_node : Integer default 0;
5250
name : localized String(255);
5351
descr : localized String(1000);
54-
parent : Association to one Genres on parent.node = parent_node;
52+
parnt : Association to Genres;
53+
children : Composition of many Genres
54+
on children.parnt = $self;
5555
}

db/data/my.bookshop-Genres.csv

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
ID;parent_node;name;node
2-
10;;Fiction;10
3-
11;10;Drama;11
4-
12;10;Poetry;12
5-
13;10;Fantasy;13
6-
14;10;Science Fiction;14
7-
15;10;Romance;15
8-
16;10;Mystery;16
9-
17;10;Thriller;17
10-
18;10;Dystopia;18
11-
19;10;Fairy Tale;19
12-
20;;Non-Fiction;20
13-
21;20;Biography;21
14-
22;21;Autobiography;22
15-
23;20;Essay;23
16-
24;20;Speech;24
1+
ID;parnt_ID;name
2+
10;;Fiction
3+
11;10;Drama
4+
12;10;Poetry
5+
13;10;Fantasy
6+
14;10;Science Fiction
7+
15;10;Romance
8+
16;10;Mystery
9+
17;10;Thriller
10+
18;10;Dystopia
11+
19;10;Fairy Tale
12+
20;;Non-Fiction
13+
21;20;Biography
14+
22;21;Autobiography
15+
23;20;Essay
16+
24;20;Speech

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323

2424
<!-- DEPENDENCIES VERSION -->
2525
<jdk.version>21</jdk.version>
26-
<cds.services.version>3.4.0</cds.services.version>
26+
<cds.services.version>3.5.0</cds.services.version>
2727
<cloud.sdk.version>5.13.0</cloud.sdk.version>
2828
<xsuaa.version>3.5.3</xsuaa.version>
2929
<cf-java-logging-support.version>3.8.4</cf-java-logging-support.version>
30-
<cds.cdsdk-version>8.4.1</cds.cdsdk-version>
30+
<cds.cdsdk-version>8.5.0</cds.cdsdk-version>
3131
</properties>
3232

3333
<modules>

srv/admin-service.cds

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ service AdminService @(requires : 'admin') {
1616
entity Orders as select from my.Orders;
1717
extend my.Genres with Hierarchy;
1818

19-
entity GenreHierarchy as projection on my.Genres {
20-
node as node_id,
21-
parent_node as parent_id,
22-
*
23-
} excluding { node, parent_node }
19+
entity GenreHierarchy as projection on my.Genres;
2420

2521
@cds.persistence.skip
2622
entity Upload @odata.singleton {

srv/src/main/java/my/bookshop/handlers/HierarchyHandler.java

Lines changed: 83 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import cds.gen.adminservice.GenreHierarchy;
4040
import cds.gen.adminservice.GenreHierarchy_;
4141

42+
import static cds.gen.adminservice.AdminService_.GENRE_HIERARCHY;
43+
4244
@Component
4345
@Profile("default")
4446
@ServiceName(AdminService_.CDS_NAME)
@@ -47,8 +49,8 @@ public class HierarchyHandler implements EventHandler {
4749
private final PersistenceService db;
4850

4951
HierarchyHandler(PersistenceService db) {
50-
this.db = db;
51-
}
52+
this.db = db;
53+
}
5254

5355
@Before(event = CqnService.EVENT_READ, entity = GenreHierarchy_.CDS_NAME)
5456
public void readGenreHierarchy(CdsReadEventContext event) {
@@ -58,12 +60,12 @@ public void readGenreHierarchy(CdsReadEventContext event) {
5860
if (trafos.size() < 1) {
5961
return;
6062
}
61-
63+
6264
if (getTopLevels(trafos) instanceof CqnTopLevelsTransformation topLevels) {
6365
result = topLevels(topLevels, CQL.TRUE);
6466
} else if (trafos.get(0) instanceof CqnDescendantsTransformation descendants) {
6567
result = handleDescendants(descendants);
66-
} else if (trafos.get(0) instanceof CqnAncestorsTransformation ancestors) {
68+
} else if (trafos.get(0) instanceof CqnAncestorsTransformation ancestors) {
6769
if (trafos.size() == 2 && trafos.get(1) instanceof CqnTopLevelsTransformation topLevels) {
6870
result = handleAncestors(ancestors, topLevels);
6971
} else if (trafos.size() == 3 && trafos.get(2) instanceof CqnTopLevelsTransformation topLevels) {
@@ -77,7 +79,8 @@ public void readGenreHierarchy(CdsReadEventContext event) {
7779
private CqnTopLevelsTransformation getTopLevels(List<CqnTransformation> trafos) {
7880
if (trafos.get(0) instanceof CqnTopLevelsTransformation topLevels) {
7981
return topLevels;
80-
} else if (trafos.size() == 2 && trafos.get(0) instanceof CqnOrderByTransformation && trafos.get(1) instanceof CqnTopLevelsTransformation topLevels) {
82+
} else if (trafos.size() == 2 && trafos.get(0) instanceof CqnOrderByTransformation
83+
&& trafos.get(1) instanceof CqnTopLevelsTransformation topLevels) {
8184
return topLevels;
8285
}
8386
return null;
@@ -92,16 +95,17 @@ private void setResult(CdsReadEventContext event, List<GenreHierarchy> result) {
9295
}
9396

9497
private void addDrillState(List<GenreHierarchy> ghs) {
95-
List<Integer> ids = ghs.stream().map(gh -> gh.getNodeId()).toList();
96-
Set<Integer> parents = ghs.stream().map(gh -> gh.getParentId()).filter(p -> p != 0).collect(Collectors.toSet());
97-
CqnSelect q = Select.from(AdminService_.GENRE_HIERARCHY, gh -> gh.parent()).columns(gh -> gh.node_id())
98-
.where(gh -> gh.node_id().in(ids));
98+
List<Integer> ids = ghs.stream().map(gh -> gh.getId()).toList();
99+
Set<Integer> parents = ghs.stream().map(gh -> gh.getParntId()).filter(p -> p != null)
100+
.collect(Collectors.toSet());
101+
CqnSelect q = Select.from(GENRE_HIERARCHY).columns(gh -> gh.parnt_ID().as("id"))
102+
.where(gh -> gh.parnt_ID().in(ids));
99103
Set<Object> nonLeafs = db
100104
.run(q)
101-
.stream().map(r -> r.get(GenreHierarchy.NODE_ID)).collect(Collectors.toSet());
105+
.stream().map(r -> r.get("id")).collect(Collectors.toSet());
102106

103107
for (GenreHierarchy gh : ghs) {
104-
Integer id = gh.getNodeId();
108+
Integer id = gh.getId();
105109
if (nonLeafs.contains(id)) {
106110
if (parents.contains(id)) {
107111
gh.setDrillState("expanded");
@@ -111,42 +115,43 @@ private void addDrillState(List<GenreHierarchy> ghs) {
111115
} else {
112116
gh.setDrillState("leaf");
113117
}
114-
}
118+
}
115119
}
116-
117-
private List<GenreHierarchy> handleDescendants(CqnDescendantsTransformation descendants) {
118-
Map<Integer, GenreHierarchy> lookup = new HashMap<>();
119-
CqnFilterTransformation filter = (CqnFilterTransformation) descendants.transformations().get(0);
120-
CqnSelect getRoot = Select.from(AdminService_.GENRE_HIERARCHY).where(filter.filter());
121-
GenreHierarchy root = db.run(getRoot).single(GenreHierarchy.class);
122-
lookup.put(root.getNodeId(), root);
123120

124-
CqnPredicate parentFilter = CQL.copy(filter.filter(), new Modifier() {
121+
private CqnPredicate descendantsFilter(CqnDescendantsTransformation descendants) {
122+
CqnTransformation trafo = descendants.transformations().get(0);
123+
CqnPredicate start = ((CqnFilterTransformation) trafo).filter();
124+
CqnPredicate result = CQL.FALSE;
125+
if (descendants.keepStart()) {
126+
result = CQL.or(result, start);
127+
}
128+
CqnPredicate children = CQL.copy(start, new Modifier() {
125129
@Override
126130
public CqnValue ref(CqnElementRef ref) {
127-
return CQL.get(GenreHierarchy.PARENT_ID);
131+
return CQL.get(GenreHierarchy.PARNT_ID);
128132
}
129133
});
134+
result = CQL.or(result, children);
130135

131-
CqnSelect childrenCQN = Select.from(AdminService_.GENRE_HIERARCHY).where(parentFilter);
132-
List<GenreHierarchy> children = db.run(childrenCQN).listOf(GenreHierarchy.class);
133-
children.forEach(gh -> lookup.put(gh.getNodeId(), gh));
134-
children.forEach(gh -> gh.setParent(lookup.get(gh.getParentId())));
135-
136-
return children.stream().sorted(new Sorter()).toList();
136+
return result;
137137
}
138138

139-
private List<GenreHierarchy> handleAncestors(CqnAncestorsTransformation ancestors, CqnTopLevelsTransformation topLevels) {
139+
private CqnPredicate ancestorsFilter(CqnAncestorsTransformation ancestors) {
140140
CqnTransformation trafo = ancestors.transformations().get(0);
141-
Select<GenreHierarchy_> inner = Select.from(AdminService_.GENRE_HIERARCHY).columns(gh -> gh.node_id());
141+
Select<GenreHierarchy_> inner = Select.from(GENRE_HIERARCHY).columns(gh -> gh.ID());
142142
if (trafo instanceof CqnFilterTransformation filter) {
143143
inner.where(filter.filter());
144144
} else if (trafo instanceof CqnSearchTransformation search) {
145145
inner.search(search.search());
146146
}
147-
Select<GenreHierarchy_> outer = Select.from(AdminService_.GENRE_HIERARCHY).columns(gh -> gh.node_id().as("i0"), gh -> gh.parent().node_id().as("i1"),
148-
gh -> gh.parent().parent().node_id().as("i2"), gh -> gh.parent().parent().parent().node_id().as("i3"),
149-
gh -> gh.parent().parent().parent().parent().node_id().as("i4")).where(gh -> gh.node_id().in(inner));
147+
148+
Select<GenreHierarchy_> outer = Select.from(GENRE_HIERARCHY)
149+
.columns(gh -> gh.ID().as("i0"),
150+
gh -> gh.parnt().ID().as("i1"),
151+
gh -> gh.parnt().parnt().ID().as("i2"),
152+
gh -> gh.parnt().parnt().parnt().ID().as("i3"),
153+
gh -> gh.parnt().parnt().parnt().parnt().ID().as("i4"))
154+
.where(gh -> gh.ID().in(inner));
150155

151156
Set<Integer> ancestorIds = new HashSet<>();
152157
db.run(outer).stream().forEach(r -> {
@@ -157,7 +162,30 @@ private List<GenreHierarchy> handleAncestors(CqnAncestorsTransformation ancestor
157162
addIfNotNull(ancestorIds, r, "i4");
158163
});
159164

160-
CqnPredicate filter = CQL.get("node_id").in(ancestorIds.stream().toList());
165+
return CQL.get(GenreHierarchy_.ID).in(ancestorIds.stream().toList());
166+
}
167+
168+
private List<GenreHierarchy> handleDescendants(CqnDescendantsTransformation descendants) {
169+
CqnPredicate filter = descendantsFilter(descendants);
170+
CqnSelect childrenCQN = Select.from(GENRE_HIERARCHY).where(filter);
171+
List<GenreHierarchy> nodes = db.run(childrenCQN).listOf(GenreHierarchy.class);
172+
173+
connect(nodes);
174+
175+
return nodes.stream().sorted(new Sorter()).toList();
176+
}
177+
178+
private static void connect(List<GenreHierarchy> nodes) {
179+
Map<Integer, GenreHierarchy> lookup = new HashMap<>();
180+
nodes.forEach(gh -> lookup.put(gh.getId(), gh));
181+
nodes.forEach(gh -> gh.setParnt(lookup.get(gh.getParntId())));
182+
nodes.forEach(gh -> gh.setDistanceFromRoot(distanceFromRoot(gh)));
183+
}
184+
185+
private List<GenreHierarchy> handleAncestors(CqnAncestorsTransformation ancestors,
186+
CqnTopLevelsTransformation topLevels) {
187+
CqnPredicate filter = ancestorsFilter(ancestors);
188+
161189
return topLevels(topLevels, filter);
162190
}
163191

@@ -166,7 +194,7 @@ private void addIfNotNull(Set<Integer> ancestorIds, Row r, String key) {
166194
if (id != null) {
167195
ancestorIds.add(id);
168196
}
169-
}
197+
}
170198

171199
private List<GenreHierarchy> topLevels(CqnTopLevelsTransformation topLevels, CqnPredicate filter) {
172200
return topLevels.levels() < 0 ? topLevelsAll(filter) : topLevelsLimit(topLevels, filter);
@@ -177,40 +205,41 @@ private List<GenreHierarchy> topLevelsLimit(CqnTopLevelsTransformation topLevels
177205
Map <Integer, GenreHierarchy> lookup = new HashMap<>();
178206
Map<Object, Long> expandLevels = topLevels.expandLevels();
179207

180-
CqnSelect getRoots = Select.from(AdminService_.GENRE_HIERARCHY).where(gh -> gh.parent_id().eq(0).and(filter));
208+
CqnSelect getRoots = Select.from(GENRE_HIERARCHY).where(gh -> gh.parnt_ID().isNull().and(filter));
181209
List<GenreHierarchy> roots = db.run(getRoots).listOf(GenreHierarchy.class);
182210
roots.forEach(root -> {
183211
root.setDistanceFromRoot(0l);
184-
lookup.put(root.getNodeId(), root);
185-
List<Integer> parents = List.of(root.getNodeId());
212+
lookup.put(root.getId(), root);
213+
List<Integer> parents = List.of(root.getId());
186214
for (long i = 1; i < limit; i++) {
187215
List<Integer> ps = parents;
188-
CqnSelect getChildren = Select.from(AdminService_.GENRE_HIERARCHY).where(gh -> gh.parent_id().in(ps).and(filter));
216+
CqnSelect getChildren = Select.from(GENRE_HIERARCHY)
217+
.where(gh -> gh.parnt_ID().in(ps).and(filter));
189218
List<GenreHierarchy> children = db.run(getChildren).listOf(GenreHierarchy.class);
190219
if (children.isEmpty()) {
191220
break;
192221
}
193222
long dfr = i;
194223
parents = children.stream().peek(gh -> {
195-
gh.setParent(lookup.get(gh.getParentId()));
224+
gh.setParnt(lookup.get(gh.getParntId()));
196225
gh.setDistanceFromRoot(dfr);
197-
lookup.put(gh.getNodeId(), gh);
198-
}).map(GenreHierarchy::getNodeId).toList();
226+
lookup.put(gh.getId(), gh);
227+
}).map(GenreHierarchy::getId).toList();
199228
}
200229
});
201230

202231
if (!expandLevels.isEmpty()) {
203232
List<Integer> expandedIds = expandLevels.keySet().stream().map(key -> (Integer) key).toList();
204233
CqnSelect expandedCQN = Select.from(AdminService_.GENRE_HIERARCHY).where(gh ->
205234
CQL.and(filter,
206-
CQL.or(gh.node_id().in(expandedIds), gh.parent_id().in(expandedIds))));
235+
CQL.or(gh.ID().in(expandedIds), gh.parnt_ID().in(expandedIds))));
207236

208237
List<GenreHierarchy> expanded = db.run(expandedCQN).listOf(GenreHierarchy.class);
209238
expanded.forEach(gh -> {
210-
if (!lookup.keySet().contains(gh.getNodeId())) {
211-
gh.setParent(lookup.get(gh.getParentId()));
239+
if (!lookup.keySet().contains(gh.getId())) {
240+
gh.setParnt(lookup.get(gh.getParntId()));
212241
gh.setDistanceFromRoot(distanceFromRoot(gh));
213-
lookup.put(gh.getNodeId(), gh);
242+
lookup.put(gh.getId(), gh);
214243
}
215244
});
216245

@@ -220,22 +249,19 @@ private List<GenreHierarchy> topLevelsLimit(CqnTopLevelsTransformation topLevels
220249
}
221250

222251
private List<GenreHierarchy> topLevelsAll(CqnPredicate filter) {
223-
Map<Integer, GenreHierarchy> lookup = new HashMap<>();
224-
225-
CqnSelect allCqn = Select.from(AdminService_.GENRE_HIERARCHY).where(filter);
252+
CqnSelect allCqn = Select.from(GENRE_HIERARCHY).where(filter);
226253
var all = db.run(allCqn).listOf(GenreHierarchy.class);
227-
all.forEach(gh -> lookup.put(gh.getNodeId(), gh));
228-
all.forEach(gh -> gh.setParent(lookup.get(gh.getParentId())));
229-
all.forEach(gh -> gh.setDistanceFromRoot(distanceFromRoot(gh)));
254+
255+
connect(all);
230256

231257
return all.stream().sorted(new Sorter()).toList();
232258
}
233259

234260
private static long distanceFromRoot(GenreHierarchy gh) {
235261
long dfr = 0;
236-
while (gh.getParent() != null) {
262+
while (gh.getParnt() != null) {
237263
dfr++;
238-
gh = gh.getParent();
264+
gh = gh.getParnt();
239265
}
240266

241267
return dfr;
@@ -260,14 +286,14 @@ public int compare(GenreHierarchy gh1, GenreHierarchy gh2) {
260286
return res;
261287
}
262288

263-
Deque<String> getPath(GenreHierarchy gh){
289+
Deque<String> getPath(GenreHierarchy gh) {
264290
Deque<String> path = new ArrayDeque<>();
265291
do {
266292
path.push(gh.getName());
267-
gh = gh.getParent();
268-
} while (gh != null);
293+
gh = gh.getParnt();
294+
} while (gh != null);
269295

270296
return path;
271-
}
297+
}
272298
}
273299
}

srv/src/main/resources/application.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
---
22
logging:
33
level:
4-
'[com.sap.cds.auditlog]': DEBUG
4+
com.sap.cds.auditlog: DEBUG
5+
com.sap.cds.persistence.sql: DEBUG
56
spring:
67
jmx:
78
enabled: true

0 commit comments

Comments
 (0)