From 3e4a5ddee4ef3120d61a415bd4ff9bc1d0cc5654 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Sun, 25 Oct 2026 10:04:19 +0100 Subject: [PATCH] Test the raster aggregates the surface carries against MobilityDB AggregatesCatalogTest registers rasterUnion, sameAlignmentAgg and summaryStatsAgg among the names of the catalog and tests their seven overloads over two rasters against MobilityDB's answers: the four forms of rasterUnion as the hex WKB of the united raster and sameAlignmentAgg as its boolean, among the overloads answering a text, and the two forms of summaryStatsAgg by the six fields of the summarystats record the aggregate answers, read in Java in the order of the record, since no SQL function of the surface reads a field of it. Witness: JMEOS's generator carries the aggregates answering a boolean or a structure and reading an integer or boolean argument, and MobilityDB reads the shorter raster aggregate forms as their siblings do, so the three names leave the list of names the surface leaves out. Measured: the two rows are tile 1 of the union tiles of MobilityDB's 500_raster and the same tile one tile east, sharing no pixel, so every pixel operation is independent of the order of the rows; the answers are those MobilityDB gives over them, harvested through its ctest harness: rasterUnion as ST_AsHexWKB writes it, and summaryStatsAgg (16, 80, 5, 2.7386127875258306, 1, 9) over band 1 and (18, 900, 50, 25.81988897471611, 10, 90) over band 2. --- .../spark/AggregatesCatalogTest.java | 60 +++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/mobilitydb/spark/AggregatesCatalogTest.java b/src/test/java/org/mobilitydb/spark/AggregatesCatalogTest.java index bba52063..6827e146 100644 --- a/src/test/java/org/mobilitydb/spark/AggregatesCatalogTest.java +++ b/src/test/java/org/mobilitydb/spark/AggregatesCatalogTest.java @@ -41,9 +41,9 @@ /** * Every aggregate overload the typed Spark SQL surface registers, over two rows of sample values, * against the answer MobilityDB gives over the same rows in the time zone UTC; and the names it - * registers, the catalog's but for the raster aggregates, which #_catalog_aggregates of JMEOS's - * codegen_jvm.py leaves out. A value whose text carries a coordinate or a float is written with - * six decimals, as MobilityDuck's test/sql/aggregates_catalog.test writes it over the same rows. + * registers, every aggregate name of the catalog. A value whose text carries a coordinate or a + * float is written with six decimals, as MobilityDuck's test/sql/aggregates_catalog.test writes it + * over the same rows. */ class AggregatesCatalogTest { @@ -70,8 +70,13 @@ static void stopSpark() { } } + /** The value of the query's first row. */ + private static Object value(String sql) { + return spark.sql(sql).collectAsList().get(0).get(0); + } + private static String answer(String sql) { - return (String) spark.sql(sql).collectAsList().get(0).get(0); + return (String) value(sql); } /** Each aggregate name of the catalog the surface registers, registered. */ @@ -81,9 +86,12 @@ Stream theNameIsRegistered() { "appendSequenceAgg", "extent", "mergeAgg", + "rasterUnion", + "sameAlignmentAgg", "setUnionAgg", "spanUnionAgg", "spansetUnionAgg", + "summaryStatsAgg", "tAndAgg", "tAvg", "tCentroid", @@ -102,12 +110,6 @@ Stream theNameIsRegistered() { .map(name -> DynamicTest.dynamicTest(name, () -> assertEquals(true, spark.catalog().functionExists(name)))); } - /** Each aggregate name of the catalog whose every entry is over a raster, not registered. */ - @TestFactory - Stream theNameIsNotRegistered() { - return Stream.of("rasterUnion", "sameAlignmentAgg", "summaryStatsAgg") - .map(name -> DynamicTest.dynamicTest(name, () -> assertEquals(false, spark.catalog().functionExists(name)))); - } /** Each overload: its signature, its query over two rows, and MobilityDB's answer. */ private static final String[][] OVERLOADS = { @@ -540,6 +542,21 @@ Stream theNameIsNotRegistered() { {"mergeAgg(ttext)", "SELECT asText(mergeAgg(v)) FROM (SELECT /*+ REPARTITION(2) */ ttextFromText(s) AS v FROM VALUES ('\"a\"@2020-01-01'), ('\"b\"@2020-01-02') AS t(s))", "{\"a\"@2020-01-01 00:00:00+00, \"b\"@2020-01-02 00:00:00+00}"}, + {"rasterUnion(raster)", + "SELECT rasterAsHexWKB(rasterUnion(v), 'NDR') FROM (SELECT /*+ REPARTITION(2) */ rasterFromHexWKB(s) AS v FROM VALUES ('0100000200000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00'), ('0100000200000000000000F03F000000000000F0BF0000000000000840000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00') AS t(s))", + "0100000200000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000060003004A003C1CC60000803F00000040000040400000803F000000400000404000008040003C1CC60000C04000008040003C1CC60000C0400000E04000000041000010410000E04000000041000010414600000A0014001E000A0014001E00280032003C00280032003C00460050005A00460050005A00"}, + {"rasterUnion(raster, INT)", + "SELECT rasterAsHexWKB(rasterUnion(v, 2), 'NDR') FROM (SELECT /*+ REPARTITION(2) */ rasterFromHexWKB(s) AS v FROM VALUES ('0100000200000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00'), ('0100000200000000000000F03F000000000000F0BF0000000000000840000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00') AS t(s))", + "0100000100000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000060003004600000A0014001E000A0014001E00280032003C00280032003C00460050005A00460050005A00"}, + {"rasterUnion(raster, VARCHAR)", + "SELECT rasterAsHexWKB(rasterUnion(v, 'MAX'), 'NDR') FROM (SELECT /*+ REPARTITION(2) */ rasterFromHexWKB(s) AS v FROM VALUES ('0100000200000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00'), ('0100000200000000000000F03F000000000000F0BF0000000000000840000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00') AS t(s))", + "0100000200000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000060003004A003C1CC60000803F00000040000040400000803F000000400000404000008040003C1CC60000C04000008040003C1CC60000C0400000E04000000041000010410000E04000000041000010414600000A0014001E000A0014001E00280032003C00280032003C00460050005A00460050005A00"}, + {"rasterUnion(raster, INT, VARCHAR)", + "SELECT rasterAsHexWKB(rasterUnion(v, 1, 'SUM'), 'NDR') FROM (SELECT /*+ REPARTITION(2) */ rasterFromHexWKB(s) AS v FROM VALUES ('0100000200000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00'), ('0100000200000000000000F03F000000000000F0BF0000000000000840000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00') AS t(s))", + "0100000100000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000060003004A003C1CC60000803F00000040000040400000803F000000400000404000008040003C1CC60000C04000008040003C1CC60000C0400000E04000000041000010410000E0400000004100001041"}, + {"sameAlignmentAgg(raster)", + "SELECT CASE WHEN sameAlignmentAgg(v) THEN 'true' ELSE 'false' END FROM (SELECT /*+ REPARTITION(2) */ rasterFromHexWKB(s) AS v FROM VALUES ('0100000200000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00'), ('0100000200000000000000F03F000000000000F0BF0000000000000840000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00') AS t(s))", + "true"}, {"setUnionAgg(cbuffer)", "SELECT asText(setUnionAgg(v), 6) FROM (SELECT /*+ REPARTITION(2) */ cbufferFromText(s) AS v FROM VALUES ('Cbuffer(Point(1 1),0.5)'), ('Cbuffer(Point(2 2),0.5)') AS t(s))", "{\"Cbuffer(POINT(1 1),0.5)\", \"Cbuffer(POINT(2 2),0.5)\"}"}, @@ -906,4 +923,27 @@ Stream theOverloadAnswersWhatMobilityDBAnswers() { return Stream.of(OVERLOADS) .map(c -> DynamicTest.dynamicTest(c[0], () -> assertEquals(c[2], answer(c[1])))); } + + /** Each overload of summaryStatsAgg: its query over the two rasters of the raster cases + * of #OVERLOADS and the six fields of the summarystats record MobilityDB answers, count, + * sum, mean, standard deviation, minimum and maximum, read here in the order of the + * record, as no SQL function of the surface reads a field of it. */ + private static final Object[][] SUMMARIES = { + {"summaryStatsAgg(raster, BOOLEAN)", + "SELECT summaryStatsAgg(v, true) FROM (SELECT /*+ REPARTITION(2) */ rasterFromHexWKB(s) AS v FROM VALUES ('0100000200000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00'), ('0100000200000000000000F03F000000000000F0BF0000000000000840000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00') AS t(s))", + new double[] {16, 80, 5, 2.7386127875258306, 1, 9}}, + {"summaryStatsAgg(raster, INT, BOOLEAN)", + "SELECT summaryStatsAgg(v, 2, true) FROM (SELECT /*+ REPARTITION(2) */ rasterFromHexWKB(s) AS v FROM VALUES ('0100000200000000000000F03F000000000000F0BF0000000000000000000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00'), ('0100000200000000000000F03F000000000000F0BF0000000000000840000000000000084000000000000000000000000000000000110F0000030003004A003C1CC60000803F000000400000404000008040003C1CC60000C0400000E04000000041000010410600000A0014001E00280032003C00460050005A00') AS t(s))", + new double[] {18, 900, 50, 25.81988897471611, 10, 90}}}; + + @TestFactory + Stream theSummaryAnswersWhatMobilityDBAnswers() { + return Stream.of(SUMMARIES) + .map(c -> DynamicTest.dynamicTest((String) c[0], () -> { + double[] expected = (double[]) c[2]; + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], ((Number) ((org.apache.spark.sql.Row) value((String) c[1])).get(i)).doubleValue(), "field " + i); + } + })); + } }