From 00bb66e8595093ff4fe46ea67c39e6fcfd602680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Manciot?= Date: Wed, 11 Mar 2026 18:06:54 +0100 Subject: [PATCH 1/4] fix: Issue #42 --- .../elastic/client/ElasticConversion.scala | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/core/src/main/scala/app/softnetwork/elastic/client/ElasticConversion.scala b/core/src/main/scala/app/softnetwork/elastic/client/ElasticConversion.scala index 450f273c..0b10b109 100644 --- a/core/src/main/scala/app/softnetwork/elastic/client/ElasticConversion.scala +++ b/core/src/main/scala/app/softnetwork/elastic/client/ElasticConversion.scala @@ -166,24 +166,28 @@ trait ElasticConversion { val ret = parseAggregations(aggs, ListMap.empty, fieldAliases, aggregations) val groupedRows: Map[String, Seq[ListMap[String, Any]]] = ret.groupBy(_.getOrElse("bucket_root", "").toString) - groupedRows.values.foldLeft(Seq(ListMap.empty[String, Any])) { (acc, group) => - for { - accMap <- acc - groupMap <- group - } yield accMap ++ groupMap - } + groupedRows.values + .foldLeft(Seq(ListMap.empty[String, Any])) { (acc, group) => + for { + accMap <- acc + groupMap <- group + } yield accMap ++ groupMap + } + .map(_ - "bucket_root") case (Some(hits), Some(aggs)) if hits.isEmpty => // Case 3 : aggregations with no hits val ret = parseAggregations(aggs, ListMap.empty, fieldAliases, aggregations) val groupedRows: Map[String, Seq[ListMap[String, Any]]] = ret.groupBy(_.getOrElse("bucket_root", "").toString) - groupedRows.values.foldLeft(Seq(ListMap.empty[String, Any])) { (acc, group) => - for { - accMap <- acc - groupMap <- group - } yield accMap ++ groupMap - } + groupedRows.values + .foldLeft(Seq(ListMap.empty[String, Any])) { (acc, group) => + for { + accMap <- acc + groupMap <- group + } yield accMap ++ groupMap + } + .map(_ - "bucket_root") case (Some(hits), Some(aggs)) if hits.nonEmpty => // Case 4 : Hits + global aggregations + top_hits aggregations From a03a9f386b81de162efaf916be194c973cae3296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Manciot?= Date: Wed, 11 Mar 2026 18:21:59 +0100 Subject: [PATCH 2/4] fix: Issue #43 --- .../elastic/client/ElasticConversion.scala | 4 +- .../client/ElasticConversionSpec.scala | 48 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/app/softnetwork/elastic/client/ElasticConversion.scala b/core/src/main/scala/app/softnetwork/elastic/client/ElasticConversion.scala index 0b10b109..6a51f881 100644 --- a/core/src/main/scala/app/softnetwork/elastic/client/ElasticConversion.scala +++ b/core/src/main/scala/app/softnetwork/elastic/client/ElasticConversion.scala @@ -208,7 +208,9 @@ trait ElasticConversion { } // Normalize all rows at the end, after all transformations (flattening, aggregation merging) - rows.map(row => normalizeRow(row, fields)) + // Filter out "*" from fields — it is an artifact of COUNT(*) and not a real column + val effectiveFields = fields.filterNot(_ == "*") + rows.map(row => normalizeRow(row, effectiveFields)) } def findKeyValue(path: String, map: Map[String, Any]): Option[Any] = { diff --git a/core/src/test/scala/app/softnetwork/elastic/client/ElasticConversionSpec.scala b/core/src/test/scala/app/softnetwork/elastic/client/ElasticConversionSpec.scala index 6c7ebe8f..823c1240 100644 --- a/core/src/test/scala/app/softnetwork/elastic/client/ElasticConversionSpec.scala +++ b/core/src/test/scala/app/softnetwork/elastic/client/ElasticConversionSpec.scala @@ -655,6 +655,54 @@ class ElasticConversionSpec extends AnyFlatSpec with Matchers with ElasticConver } } + it should "parse global aggregation COUNT(*) without GROUP BY" in { + // Simulates: SELECT COUNT(*) FROM ecommerce + // ES response for a global value_count aggregation (no buckets, no GROUP BY) + val results = + """{ + | "took": 3, + | "timed_out": false, + | "hits": { "total": { "value": 20, "relation": "eq" }, "hits": [] }, + | "aggregations": { + | "COUNT(*)" : { + | "value": 20 + | } + | } + |}""".stripMargin + + val aggregations = ListMap( + "COUNT(*)" -> ClientAggregation( + aggName = "COUNT(*)", + aggType = AggregationType.Count, + distinct = false, + sourceField = "*", + windowing = false, + bucketPath = "", + bucketRoot = "" + ) + ) + + // fields as produced by the SQL parser: "*" (artifact) + "COUNT(*)" (actual column) + val fields = Seq("*", "COUNT(*)") + + parseResponse(results, ListMap.empty, aggregations, fields) match { + case Success(rows) => + rows.foreach(println) + rows.size shouldBe 1 + val row = rows.head + // Issue #002: bucket_root must not leak into the output + row.keys should not contain "bucket_root" + // Issue #003: "*" must not appear as a column + row.keys should not contain "*" + // Only the aggregation column should be present + row.keys should contain("COUNT(*)") + row("COUNT(*)") shouldBe 20L + row.size shouldBe 1 + case Failure(error) => + throw error + } + } + it should "parse window results with distinct partitions" in { val results = """ From 59856000020cda23e517b49bb1e6bd531d9b72d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Manciot?= Date: Wed, 11 Mar 2026 18:26:16 +0100 Subject: [PATCH 3/4] prepare release v0.17.4 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 73dbe828..015662dc 100644 --- a/build.sbt +++ b/build.sbt @@ -20,7 +20,7 @@ ThisBuild / organization := "app.softnetwork" name := "softclient4es" -ThisBuild / version := "0.17-SNAPSHOT" +ThisBuild / version := "0.17.4" ThisBuild / scalaVersion := scala213 From 083a0bb4db9f1c9b48dc29c181179311a9a4da1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Manciot?= Date: Wed, 11 Mar 2026 18:53:53 +0100 Subject: [PATCH 4/4] update README references to v0.17.3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ca79add..999d1911 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ For programmatic access, add SoftClient4ES to your project: resolvers += "Softnetwork" at "https://softnetwork.jfrog.io/artifactory/releases/" // Choose your Elasticsearch version -libraryDependencies += "app.softnetwork.elastic" %% "softclient4es8-java-client" % "0.17.3" +libraryDependencies += "app.softnetwork.elastic" %% "softclient4es8-java-client" % "0.17.4" // Add the community extensions for materialized views (optional) libraryDependencies += "app.softnetwork.elastic" %% "softclient4es-community-extensions" % "0.1.1" ```