Skip to content

Commit 635ca33

Browse files
committed
sort stringified set elements with one field in explorer ui
Stringified set elements are now outputted in sorted order when each element has exactly one field. Previously the output order was determined by the element ordinals. This change only affects the Hollow Explorer UI.
1 parent a64f19e commit 635ca33

File tree

7 files changed

+178
-14
lines changed

7 files changed

+178
-14
lines changed

hollow-explorer-ui/src/main/java/com/netflix/hollow/explorer/ui/pages/BrowseSelectedTypePage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ protected void renderPage(HttpServletRequest req, VelocityContext ctx, Writer wr
136136
htmlEscapingWriter.append("ERROR: Key " + key + " was not found!");
137137
} else if (ordinal != null && !ordinal.equals(ORDINAL_NONE)) {
138138
HollowStringifier stringifier = "json".equals(req.getParameter("display"))
139-
? new HollowRecordJsonStringifier() : new HollowRecordStringifier();
139+
? new HollowRecordJsonStringifier(true, true, true) : new HollowRecordStringifier(false, false, true, true);
140140
stringifier.stringify(htmlEscapingWriter, ui.getStateEngine(),
141141
getTypeState(req).getSchema().getName(), ordinal);
142142
}

hollow/src/main/java/com/netflix/hollow/core/read/HollowReadFieldUtils.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919
import com.netflix.hollow.core.memory.encoding.HashCodes;
2020
import com.netflix.hollow.core.read.dataaccess.HollowDataAccess;
2121
import com.netflix.hollow.core.read.dataaccess.HollowObjectTypeDataAccess;
22+
import com.netflix.hollow.core.read.dataaccess.HollowTypeDataAccess;
2223
import com.netflix.hollow.core.schema.HollowObjectSchema;
2324
import java.util.Arrays;
2425

2526
/**
26-
* Useful utility methods for interacting with a {@link HollowDataAccess}.
27+
* Useful utility methods for interacting with a {@link HollowDataAccess}.
2728
*/
2829
public class HollowReadFieldUtils {
2930

@@ -142,7 +143,7 @@ public static boolean fieldsAreEqual(HollowObjectTypeDataAccess typeAccess1, int
142143
* @param typeAccess the type access
143144
* @param ordinal the ordinal
144145
* @param fieldPosition the field position
145-
* @return a displayable String for a field from an OBJECT record.
146+
* @return a displayable String for a field from an OBJECT record.
146147
*/
147148
public static String displayString(HollowObjectTypeDataAccess typeAccess, int ordinal, int fieldPosition) {
148149
HollowObjectSchema schema = typeAccess.getSchema();
@@ -263,6 +264,51 @@ public static boolean fieldValueEquals(HollowObjectTypeDataAccess typeAccess, in
263264
throw new IllegalStateException("I don't know how to test equality for a " + schema.getFieldType(fieldPosition));
264265
}
265266

267+
/**
268+
* @param typeAccess the type access
269+
* @param fieldPosition the field position
270+
* @param ordinal1 the first object ordinal
271+
* @param ordinal2 the second object ordinal
272+
* @return compareTo result for the two field values at the specified field position
273+
*/
274+
public static int compareFieldValues(HollowObjectTypeDataAccess typeAccess, int fieldPosition, int ordinal1, int ordinal2) {
275+
HollowObjectSchema schema = typeAccess.getSchema();
276+
277+
switch(schema.getFieldType(fieldPosition)) {
278+
case BOOLEAN:
279+
return Boolean.compare(typeAccess.readBoolean(ordinal1, fieldPosition), typeAccess.readBoolean(ordinal2, fieldPosition));
280+
case BYTES:
281+
return 0;
282+
case DOUBLE:
283+
return Double.compare(typeAccess.readDouble(ordinal1, fieldPosition), typeAccess.readDouble(ordinal2, fieldPosition));
284+
case FLOAT:
285+
return Float.compare(typeAccess.readFloat(ordinal1, fieldPosition), typeAccess.readFloat(ordinal2, fieldPosition));
286+
case INT:
287+
return Integer.compare(typeAccess.readInt(ordinal1, fieldPosition), typeAccess.readInt(ordinal2, fieldPosition));
288+
case LONG:
289+
return Long.compare(typeAccess.readLong(ordinal1, fieldPosition), typeAccess.readLong(ordinal2, fieldPosition));
290+
case STRING:
291+
return typeAccess.readString(ordinal1, fieldPosition).compareTo(typeAccess.readString(ordinal2, fieldPosition));
292+
case REFERENCE:
293+
// For reference types, if the referenced type is an object,
294+
// then compare the first field of the object, otherwise assume
295+
// equality.
296+
HollowTypeDataAccess typeDataAccess = typeAccess.getDataAccess().getTypeDataAccess(schema.getReferencedType(fieldPosition));
297+
if (typeDataAccess instanceof HollowObjectTypeDataAccess) {
298+
HollowObjectTypeDataAccess refTypeAccess = (HollowObjectTypeDataAccess) typeDataAccess;
299+
HollowObjectSchema refSchema = refTypeAccess.getSchema();
300+
if (refSchema.numFields() == 1) {
301+
return compareFieldValues(refTypeAccess, 0,
302+
typeAccess.readOrdinal(ordinal1, fieldPosition),
303+
typeAccess.readOrdinal(ordinal2, fieldPosition));
304+
}
305+
}
306+
return 0;
307+
}
308+
309+
throw new IllegalStateException("I don't know how to compare a " + schema.getFieldType(fieldPosition));
310+
}
311+
266312
/**
267313
* @param data the byte array
268314
* @return The hash code for a byte array which would be returned from {@link #fieldHashCode(HollowObjectTypeDataAccess, int, int)}

hollow/src/main/java/com/netflix/hollow/tools/stringifier/HollowRecordJsonStringifier.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.netflix.hollow.tools.stringifier;
1818

1919
import static com.netflix.hollow.core.HollowConstants.ORDINAL_NONE;
20+
import static com.netflix.hollow.core.read.HollowReadFieldUtils.compareFieldValues;
2021

2122
import com.netflix.hollow.api.objects.HollowRecord;
2223
import com.netflix.hollow.core.read.dataaccess.HollowDataAccess;
@@ -40,6 +41,7 @@
4041
import java.util.Collections;
4142
import java.util.HashSet;
4243
import java.util.Iterator;
44+
import java.util.List;
4345
import java.util.Set;
4446

4547
/**
@@ -51,21 +53,28 @@ public class HollowRecordJsonStringifier implements HollowStringifier<HollowReco
5153
private final Set<String> excludeObjectTypes = new HashSet<String>();
5254
private final boolean collapseAllSingleFieldObjects;
5355
private final boolean prettyPrint;
56+
private final boolean sortSingleFieldSetElements;
5457

5558
public HollowRecordJsonStringifier() {
56-
this(true, true);
59+
this(true, true, false);
5760
}
5861

5962
public HollowRecordJsonStringifier(boolean prettyPrint, boolean collapseAllSingleFieldObjects) {
63+
this(prettyPrint, collapseAllSingleFieldObjects, false);
64+
}
65+
66+
public HollowRecordJsonStringifier(boolean prettyPrint, boolean collapseAllSingleFieldObjects, boolean sortSingleFieldSetElements) {
6067
this.prettyPrint = prettyPrint;
6168
this.collapseAllSingleFieldObjects = collapseAllSingleFieldObjects;
6269
this.collapseObjectTypes = Collections.emptySet();
70+
this.sortSingleFieldSetElements = sortSingleFieldSetElements;
6371
}
6472

6573

6674
public HollowRecordJsonStringifier(boolean indent, String... collapseObjectTypes) {
6775
this.prettyPrint = indent;
6876
this.collapseAllSingleFieldObjects = false;
77+
this.sortSingleFieldSetElements = false;
6978
this.collapseObjectTypes = new HashSet<String>();
7079

7180
for (String collapseObjectType : collapseObjectTypes) {
@@ -284,15 +293,30 @@ private void appendSetStringify(Writer writer, HollowDataAccess dataAccess, Holl
284293

285294
int elementOrdinal = iter.next();
286295

287-
if (elementOrdinal == HollowOrdinalIterator.NO_MORE_ORDINALS) {
296+
List<Integer> elementOrdinals = new java.util.ArrayList<>();
297+
while (elementOrdinal != HollowOrdinalIterator.NO_MORE_ORDINALS) {
298+
elementOrdinals.add(elementOrdinal);
299+
elementOrdinal = iter.next();
300+
}
301+
302+
HollowTypeDataAccess elementTypeDataAccess = dataAccess.getTypeDataAccess(elementType);
303+
if (sortSingleFieldSetElements && elementTypeDataAccess instanceof HollowObjectTypeDataAccess) {
304+
HollowObjectSchema elementSchema = ((HollowObjectTypeDataAccess) elementTypeDataAccess).getSchema();
305+
if (elementSchema.numFields() == 1) {
306+
elementOrdinals.sort((o1, o2) ->
307+
compareFieldValues((HollowObjectTypeDataAccess) elementTypeDataAccess, 0, o1, o2));
308+
}
309+
}
310+
311+
if (elementOrdinals.isEmpty()) {
288312
writer.append("[]");
289313
} else {
290314
boolean firstElement = true;
291315
writer.append("[");
292316
if (prettyPrint)
293317
writer.append(NEWLINE);
294318

295-
while(elementOrdinal != HollowOrdinalIterator.NO_MORE_ORDINALS) {
319+
for (Integer ord : elementOrdinals) {
296320
if (firstElement)
297321
firstElement = false;
298322
else
@@ -301,9 +325,7 @@ private void appendSetStringify(Writer writer, HollowDataAccess dataAccess, Holl
301325
if (prettyPrint)
302326
appendIndentation(writer, indentation);
303327

304-
appendStringify(writer, dataAccess, elementType, elementOrdinal, indentation);
305-
306-
elementOrdinal = iter.next();
328+
appendStringify(writer, dataAccess, elementType, ord, indentation);
307329
}
308330

309331
if (prettyPrint) {

hollow/src/main/java/com/netflix/hollow/tools/stringifier/HollowRecordStringifier.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package com.netflix.hollow.tools.stringifier;
1818

19+
import static com.netflix.hollow.core.read.HollowReadFieldUtils.compareFieldValues;
1920
import static com.netflix.hollow.core.read.iterator.HollowOrdinalIterator.NO_MORE_ORDINALS;
2021

2122
import com.netflix.hollow.api.objects.HollowRecord;
@@ -38,6 +39,7 @@
3839
import java.util.Arrays;
3940
import java.util.HashSet;
4041
import java.util.Iterator;
42+
import java.util.List;
4143
import java.util.Set;
4244

4345
/**
@@ -48,15 +50,21 @@ public class HollowRecordStringifier implements HollowStringifier<HollowRecordSt
4850
private final boolean showOrdinals;
4951
private final boolean showTypes;
5052
private final boolean collapseSingleFieldObjects;
53+
private final boolean sortSingleFieldSetElements;
5154

5255
public HollowRecordStringifier() {
53-
this(false, false, true);
56+
this(false, false, true, false);
5457
}
5558

5659
public HollowRecordStringifier(boolean showOrdinals, boolean showTypes, boolean collapseSingleFieldObjects) {
60+
this(showOrdinals, showTypes, collapseSingleFieldObjects, false);
61+
}
62+
63+
public HollowRecordStringifier(boolean showOrdinals, boolean showTypes, boolean collapseSingleFieldObjects, boolean sortSingleFieldSetElements) {
5764
this.showOrdinals = showOrdinals;
5865
this.showTypes = showTypes;
5966
this.collapseSingleFieldObjects = collapseSingleFieldObjects;
67+
this.sortSingleFieldSetElements = sortSingleFieldSetElements;
6068
}
6169

6270
@Override
@@ -184,15 +192,28 @@ private void appendSetStringify(Writer writer, HollowDataAccess dataAccess,
184192

185193
int elementOrdinal = iter.next();
186194

195+
List<Integer> elementOrdinals = new java.util.ArrayList<>();
187196
while(elementOrdinal != NO_MORE_ORDINALS) {
197+
elementOrdinals.add(elementOrdinal);
198+
elementOrdinal = iter.next();
199+
}
200+
201+
HollowTypeDataAccess elementTypeDataAccess = dataAccess.getTypeDataAccess(elementType);
202+
if (sortSingleFieldSetElements && elementTypeDataAccess instanceof HollowObjectTypeDataAccess) {
203+
HollowObjectSchema elementSchema = ((HollowObjectTypeDataAccess) elementTypeDataAccess).getSchema();
204+
if (elementSchema.numFields() == 1) {
205+
elementOrdinals.sort((o1, o2) ->
206+
compareFieldValues((HollowObjectTypeDataAccess) elementTypeDataAccess, 0, o1, o2));
207+
}
208+
}
209+
210+
for (Integer ord : elementOrdinals) {
188211
writer.append(NEWLINE);
189212

190213
appendIndentation(writer, indentation);
191214
writer.append("e: ");
192215

193-
appendStringify(writer, dataAccess, elementType, elementOrdinal, indentation);
194-
195-
elementOrdinal = iter.next();
216+
appendStringify(writer, dataAccess, elementType, ord, indentation);
196217
}
197218
}
198219

hollow/src/test/java/com/netflix/hollow/tools/stringifier/AbstractHollowRecordStringifierTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.netflix.hollow.core.write.objectmapper.HollowObjectMapper;
2525
import java.io.IOException;
2626
import java.io.StringWriter;
27+
import java.util.Set;
2728

2829
/**
2930
* Code shared between HollowRecordStringifierTest and HollowRecordJsonStringifierTest.
@@ -73,6 +74,21 @@ public TypeWithNestedNonPrimitive(Double value, TypeWithNonPrimitive nestedType)
7374
}
7475
}
7576

77+
static class TypeWithSetOfPrimitives {
78+
private final Set<TypeWithPrimitive> values;
79+
80+
public TypeWithSetOfPrimitives(Set<TypeWithPrimitive> values) {
81+
this.values = values;
82+
}
83+
}
84+
85+
static class TypeWithSetOfStrings {
86+
private final Set<TypeWithString> values;
87+
88+
public TypeWithSetOfStrings(Set<TypeWithString> values) {
89+
this.values = values;
90+
}
91+
}
7692

7793
/**
7894
* Sends instances of a type through the HollowRecordStringifier. This concatenates records

hollow/src/test/java/com/netflix/hollow/tools/stringifier/HollowRecordJsonStringifierTest.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,35 @@ public void testStringifyStringWithEscapeChars() throws IOException {
206206
stringifyType(TypeWithString.class, true, false, new TypeWithString(testString)));
207207
}
208208

209+
@Test
210+
public void testStringifySetOfPrimitives() throws IOException {
211+
Assert.assertEquals("Set of primitives should be printed correctly",
212+
"[3,200,1000]",
213+
stringifyType(TypeWithSetOfPrimitives.class, false, false,
214+
new TypeWithSetOfPrimitives(
215+
new java.util.HashSet<>(Arrays.asList(
216+
new TypeWithPrimitive(1000),
217+
new TypeWithPrimitive(200),
218+
new TypeWithPrimitive(3)
219+
)))));
220+
}
221+
222+
@Test
223+
public void testStringifySetOfStrings() throws IOException {
224+
Assert.assertEquals("Set of strings should be printed correctly",
225+
"[\"a\",\"b\",\"c\"]",
226+
stringifyType(TypeWithSetOfStrings.class, false, false,
227+
new TypeWithSetOfStrings(
228+
new java.util.HashSet<>(Arrays.asList(
229+
new TypeWithString("c"),
230+
new TypeWithString("a"),
231+
new TypeWithString("b")
232+
)))));
233+
}
234+
235+
209236
private static <T> String stringifyType(Class<T> clazz, boolean prettyPrint, boolean expanded, T... instances) throws IOException {
210-
HollowRecordJsonStringifier stringifier = new HollowRecordJsonStringifier(prettyPrint, !expanded);
237+
HollowRecordJsonStringifier stringifier = new HollowRecordJsonStringifier(prettyPrint, !expanded, true);
211238
// HollowRecordJsonStringifier stringifier = expanded
212239
// ? new HollowRecordJsonStringifier(prettyPrint, false) : new HollowRecordJsonStringifier();
213240
return stringifyType(clazz, stringifier, instances);

hollow/src/test/java/com/netflix/hollow/tools/stringifier/HollowRecordStringifierTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,38 @@ public void testStringifyIterator() throws IOException {
135135
"\n]", writer.toString());
136136
}
137137

138+
@Test
139+
public void testStringifySetOfPrimitives() throws IOException {
140+
Assert.assertEquals("Set of primitives should be printed correctly",
141+
"\n e: 3\n" +
142+
" e: 200\n" +
143+
" e: 1000",
144+
stringifyType(TypeWithSetOfPrimitives.class,
145+
new HollowRecordStringifier(false, false, true, true),
146+
new TypeWithSetOfPrimitives(
147+
new java.util.HashSet<>(Arrays.asList(
148+
new TypeWithPrimitive(1000),
149+
new TypeWithPrimitive(200),
150+
new TypeWithPrimitive(3)
151+
)))));
152+
}
153+
154+
@Test
155+
public void testStringifySetOfStrings() throws IOException {
156+
Assert.assertEquals("Set of strings should be printed correctly",
157+
"\n e: a\n" +
158+
" e: b\n" +
159+
" e: c",
160+
stringifyType(TypeWithSetOfStrings.class,
161+
new HollowRecordStringifier(false, false, true, true),
162+
new TypeWithSetOfStrings(
163+
new java.util.HashSet<>(Arrays.asList(
164+
new TypeWithString("c"),
165+
new TypeWithString("a"),
166+
new TypeWithString("b")
167+
)))));
168+
}
169+
138170
private static <T> String stringifyType(Class<T> clazz, boolean expanded, T... instances) throws IOException {
139171
HollowRecordStringifier stringifier = expanded
140172
? new HollowRecordStringifier(true, true, false) : new HollowRecordStringifier();

0 commit comments

Comments
 (0)