Skip to content

Commit 5107acc

Browse files
committed
Fix VFBqueryJsonProcessor compile against AQueryResult / Variable
Build #2169 failed at the Maven install step of uk.ac.vfb.geppetto. The previous implementation accessed row.getValues() on the AQueryResult loop variable (the abstract parent does not expose that method — only the concrete QueryResult / SerializableQueryResult subclasses do, with incompatible value types) and called variable.getName() which is not on the Variable EClass. Rewritten to mirror SOLRQueryProcessor.process(): iterate by row index and look up each cell via results.getValue(headerName, rowIdx). The public behaviour is unchanged - still emits the 9-column class- connectivity table with the queried term synthesised into the missing Upstream_Class or Downstream_Class column. Variable label resolution falls back to variable.getId() (a short_form like FBbt_00100246), wrapped in the standard [id](id) markdown link form. The frontend's table renderer resolves it to the human label on display, same as it does for the partner classes returned by the API.
1 parent 7380158 commit 5107acc

1 file changed

Lines changed: 76 additions & 71 deletions

File tree

src/main/java/uk/ac/vfb/geppetto/VFBqueryJsonProcessor.java

Lines changed: 76 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.geppetto.core.datasources.GeppettoDataSourceException;
77
import org.geppetto.core.model.GeppettoModelAccess;
88
import org.geppetto.datasources.AQueryProcessor;
9-
import org.geppetto.model.datasources.AQueryResult;
109
import org.geppetto.model.datasources.DataSource;
1110
import org.geppetto.model.datasources.DatasourcesFactory;
1211
import org.geppetto.model.datasources.ProcessQuery;
@@ -16,18 +15,24 @@
1615

1716
/**
1817
* Converts the QueryResults emitted by VFBqueryResponseProcessor (one row per
19-
* VFBquery `rows` entry, values still typed as Object) into the
20-
* SerializableQueryResult shape the geppetto-vfb frontend expects (string-
21-
* formatted cells, composite IDs for connectivity tables, synthesised
22-
* "queried term" column where the v2 chain produced both Upstream_Class and
23-
* Downstream_Class regardless of direction).
18+
* VFBquery `rows` entry, values still typed as Object and addressed by column
19+
* header name) into the SerializableQueryResult shape the geppetto-vfb
20+
* frontend expects (string-formatted cells, composite IDs for connectivity
21+
* tables, synthesised "queried term" column where the v2 chain produced both
22+
* Upstream_Class and Downstream_Class regardless of direction).
2423
*
2524
* Output shape is byte-equivalent to the existing SOLRQueryProcessor output
2625
* for hasClassConnectivity, so no frontend change is needed for the pilot.
2726
*
2827
* For non-connectivity VFBquery responses this processor degrades to a
29-
* straight Object→String stringification per cell, which is the right
30-
* behaviour for the Shape-A single-step migrations that come next.
28+
* straight Object-to-String pass-through of every column in header order,
29+
* which is the right behaviour for the Shape-A single-step migrations that
30+
* come next.
31+
*
32+
* Value access deliberately mirrors SOLRQueryProcessor.process(): iterate by
33+
* row index and pull each cell via results.getValue(headerName, rowIdx),
34+
* rather than calling .getValues() on the AQueryResult loop variable (which
35+
* the abstract parent does not expose).
3136
*
3237
* @author robertcourt
3338
*/
@@ -38,6 +43,18 @@ public class VFBqueryJsonProcessor extends AQueryProcessor
3843

3944
private static final String DELIM = "----";
4045

46+
// Header titles emitted by VFBqueryResponseProcessor for class connectivity.
47+
// These are the `title` fields from the VFBquery /run_query response.
48+
private static final String COL_ID = "ID";
49+
private static final String COL_UPSTREAM = "Upstream Class";
50+
private static final String COL_DOWNSTREAM = "Downstream Class";
51+
private static final String COL_TOTAL_N = "Total N";
52+
private static final String COL_CONNECTED_N = "Connected N";
53+
private static final String COL_PERCENT = "% Connected";
54+
private static final String COL_PAIRWISE = "Pairwise Connections";
55+
private static final String COL_TOTAL_WEIGHT = "Total Weight";
56+
private static final String COL_AVG_WEIGHT = "Avg Weight";
57+
4158
private final Map<String, Object> processingOutputMap = new HashMap<String, Object>();
4259

4360
/*
@@ -63,19 +80,19 @@ public QueryResults process(ProcessQuery query, DataSource dataSource, Variable
6380

6481
QueryResults out = DatasourcesFactory.eINSTANCE.createQueryResults();
6582

66-
// Detect connectivity-style responses by header names. The upstream-class
67-
// VFBquery call returns headers ["ID", "Upstream Class", "Total N", ...]
68-
// (titles, not raw col ids); downstream returns ["ID", "Downstream Class", ...].
69-
// We synthesise the missing column so output matches the v2 9-column shape.
70-
boolean isUpstreamClassConnectivity = headerContains(results, "Upstream Class")
71-
&& !headerContains(results, "Downstream Class");
72-
boolean isDownstreamClassConnectivity = headerContains(results, "Downstream Class")
73-
&& !headerContains(results, "Upstream Class");
74-
boolean isClassConnectivity = isUpstreamClassConnectivity || isDownstreamClassConnectivity;
83+
// Detect connectivity-style responses by header titles. The upstream-class
84+
// VFBquery call returns ["ID", "Upstream Class", "Total N", ...]; downstream
85+
// returns ["ID", "Downstream Class", ...]. We synthesise the missing column
86+
// so output matches the v2 9-column shape.
87+
boolean isUpstreamCall = results.getHeader().contains(COL_UPSTREAM)
88+
&& !results.getHeader().contains(COL_DOWNSTREAM);
89+
boolean isDownstreamCall = results.getHeader().contains(COL_DOWNSTREAM)
90+
&& !results.getHeader().contains(COL_UPSTREAM);
91+
boolean isClassConnectivity = isUpstreamCall || isDownstreamCall;
7592

7693
if (isClassConnectivity)
7794
{
78-
buildClassConnectivityRows(variable, results, out, isUpstreamClassConnectivity);
95+
buildClassConnectivityRows(variable, results, out, isUpstreamCall);
7996
}
8097
else
8198
{
@@ -86,7 +103,7 @@ public QueryResults process(ProcessQuery query, DataSource dataSource, Variable
86103
{
87104
System.out.println("VFBqueryJsonProcessor: " + out.getResults().size() + " rows in "
88105
+ (System.currentTimeMillis() - t0) + " ms (mode="
89-
+ (isClassConnectivity ? (isUpstreamClassConnectivity ? "upstream-class" : "downstream-class") : "generic")
106+
+ (isClassConnectivity ? (isUpstreamCall ? "upstream-class" : "downstream-class") : "generic")
90107
+ ")");
91108
}
92109

@@ -114,32 +131,22 @@ private void buildClassConnectivityRows(Variable variable, QueryResults in, Quer
114131
out.getHeader().add("Total_Weight");
115132
out.getHeader().add("Avg_Weight");
116133

117-
// Resolve the column positions we need from the input header.
118-
int idxId = headerIndex(in, "ID");
119-
int idxClass = upstreamCall ? headerIndex(in, "Upstream Class") : headerIndex(in, "Downstream Class");
120-
int idxTotalN = headerIndex(in, "Total N");
121-
int idxConnectedN = headerIndex(in, "Connected N");
122-
int idxPercent = headerIndex(in, "% Connected");
123-
int idxPairwise = headerIndex(in, "Pairwise Connections");
124-
int idxTotalWeight = headerIndex(in, "Total Weight");
125-
int idxAvgWeight = headerIndex(in, "Avg Weight");
134+
String queriedId = variable != null && variable.getId() != null ? variable.getId() : "";
135+
// Markdown link form matches the rest of the VFB table column format —
136+
// the V3 frontend renders these as in-app navigation links. We don't
137+
// have the term's human label at this layer; the renderer resolves it
138+
// from the id, same as it does for the partner classes.
139+
String queriedMarkdown = "[" + queriedId + "](" + queriedId + ")";
126140

127-
String queriedId = variable != null ? variable.getId() : "";
128-
String queriedLabel = variable != null ? variable.getName() : "";
129-
if (queriedLabel == null || queriedLabel.isEmpty())
130-
{
131-
queriedLabel = queriedId;
132-
}
133-
// Markdown link form matches the rest of the VFB table column format
134-
// (the V3 frontend renders these as in-app navigation links).
135-
String queriedMarkdown = "[" + queriedLabel + "](" + queriedId + ")";
141+
String partnerColumn = upstreamCall ? COL_UPSTREAM : COL_DOWNSTREAM;
136142

137-
for (AQueryResult row : in.getResults())
143+
int n = in.getResults().size();
144+
for (int i = 0; i < n; i++)
138145
{
139146
SerializableQueryResult r = DatasourcesFactory.eINSTANCE.createSerializableQueryResult();
140147

141-
String partnerId = stringAt(row, idxId);
142-
String partnerMarkdown = stringAt(row, idxClass);
148+
String partnerId = stringValue(in, COL_ID, i);
149+
String partnerMarkdown = stringValue(in, partnerColumn, i);
143150
String upstreamId = upstreamCall ? partnerId : queriedId;
144151
String downstreamId = upstreamCall ? queriedId : partnerId;
145152
String upstreamMarkdown = upstreamCall ? partnerMarkdown : queriedMarkdown;
@@ -148,88 +155,85 @@ private void buildClassConnectivityRows(Variable variable, QueryResults in, Quer
148155
r.getValues().add(upstreamId + DELIM + downstreamId);
149156
r.getValues().add(upstreamMarkdown);
150157
r.getValues().add(downstreamMarkdown);
151-
r.getValues().add(formatInt(valueAt(row, idxTotalN), 6));
152-
r.getValues().add(formatInt(valueAt(row, idxConnectedN), 6));
153-
r.getValues().add(formatPercent(valueAt(row, idxPercent)));
154-
r.getValues().add(formatInt(valueAt(row, idxPairwise), 8));
155-
r.getValues().add(formatInt(valueAt(row, idxTotalWeight), 9));
156-
r.getValues().add(formatFloat(valueAt(row, idxAvgWeight), 7, 1));
158+
r.getValues().add(formatInt(in, COL_TOTAL_N, i, 6));
159+
r.getValues().add(formatInt(in, COL_CONNECTED_N, i, 6));
160+
r.getValues().add(formatPercent(in, COL_PERCENT, i));
161+
r.getValues().add(formatInt(in, COL_PAIRWISE, i, 8));
162+
r.getValues().add(formatInt(in, COL_TOTAL_WEIGHT, i, 9));
163+
r.getValues().add(formatFloat(in, COL_AVG_WEIGHT, i, 7, 1));
157164

158165
out.getResults().add(r);
159166
}
160167
}
161168

162169
/**
163-
* Generic ObjectString stringification for non-connectivity VFBquery
170+
* Generic Object-to-String stringification for non-connectivity VFBquery
164171
* responses. Used by Shape-A migrations (single-step Cypher queries) once
165172
* those land. Header titles are passed through unchanged.
166173
*/
167174
private void buildGenericRows(QueryResults in, QueryResults out)
168175
{
169176
out.getHeader().addAll(in.getHeader());
170-
for (AQueryResult row : in.getResults())
177+
int n = in.getResults().size();
178+
for (int i = 0; i < n; i++)
171179
{
172180
SerializableQueryResult r = DatasourcesFactory.eINSTANCE.createSerializableQueryResult();
173-
for (Object v : row.getValues())
181+
for (String col : in.getHeader())
174182
{
183+
Object v = safeGetValue(in, col, i);
175184
r.getValues().add(v == null ? "" : v.toString());
176185
}
177186
out.getResults().add(r);
178187
}
179188
}
180189

181-
private static boolean headerContains(QueryResults r, String title)
182-
{
183-
return r.getHeader().indexOf(title) >= 0;
184-
}
185-
186-
private static int headerIndex(QueryResults r, String title)
187-
{
188-
return r.getHeader().indexOf(title);
189-
}
190-
191-
private static Object valueAt(AQueryResult row, int idx)
190+
private static Object safeGetValue(QueryResults in, String col, int rowIdx)
192191
{
193-
if (idx < 0 || idx >= row.getValues().size())
192+
try
193+
{
194+
return in.getValue(col, rowIdx);
195+
}
196+
catch (RuntimeException e)
194197
{
195198
return null;
196199
}
197-
return row.getValues().get(idx);
198200
}
199201

200-
private static String stringAt(AQueryResult row, int idx)
202+
private static String stringValue(QueryResults in, String col, int rowIdx)
201203
{
202-
Object v = valueAt(row, idx);
204+
Object v = safeGetValue(in, col, rowIdx);
203205
return v == null ? "" : v.toString();
204206
}
205207

206-
private static String formatInt(Object v, int width)
208+
private static String formatInt(QueryResults in, String col, int rowIdx, int width)
207209
{
210+
Object v = safeGetValue(in, col, rowIdx);
208211
if (v == null)
209212
{
210213
return "";
211214
}
212-
long n;
215+
long ln;
213216
if (v instanceof Number)
214217
{
215-
n = ((Number) v).longValue();
218+
ln = ((Number) v).longValue();
216219
}
217220
else
218221
{
219222
try
220223
{
221-
n = (long) Double.parseDouble(v.toString());
224+
ln = (long) Double.parseDouble(v.toString());
222225
}
223226
catch (NumberFormatException e)
224227
{
225228
return v.toString();
226229
}
227230
}
228-
return String.format("%1$" + width + "d", n);
231+
return String.format("%1$" + width + "d", ln);
229232
}
230233

231-
private static String formatPercent(Object v)
234+
private static String formatPercent(QueryResults in, String col, int rowIdx)
232235
{
236+
Object v = safeGetValue(in, col, rowIdx);
233237
if (v == null)
234238
{
235239
return "";
@@ -253,8 +257,9 @@ private static String formatPercent(Object v)
253257
return String.format("%5.1f%%", d);
254258
}
255259

256-
private static String formatFloat(Object v, int width, int precision)
260+
private static String formatFloat(QueryResults in, String col, int rowIdx, int width, int precision)
257261
{
262+
Object v = safeGetValue(in, col, rowIdx);
258263
if (v == null)
259264
{
260265
return "";

0 commit comments

Comments
 (0)