Repository: squashql/squashql
Pull request #240 (2024-01-16 12:56:27)

Add jackson JSR310Module


Patch:
diff --git a/core/pom.xml b/core/pom.xml
index 74badb7..2706ac2 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -34,6 +34,10 @@
       <groupId>com.fasterxml.jackson.core</groupId>
       <artifactId>jackson-databind</artifactId>
     </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.datatype</groupId>
+      <artifactId>jackson-datatype-jsr310</artifactId>
+    </dependency>
     <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
diff --git a/core/src/main/java/io/squashql/jackson/JacksonUtil.java b/core/src/main/java/io/squashql/jackson/JacksonUtil.java
index 3fa617a..253e132 100644
--- a/core/src/main/java/io/squashql/jackson/JacksonUtil.java
+++ b/core/src/main/java/io/squashql/jackson/JacksonUtil.java
@@ -3,6 +3,7 @@ package io.squashql.jackson;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
 import io.squashql.query.Field;
 import io.squashql.query.dto.QueryDto;
 
@@ -16,6 +17,7 @@ public class JacksonUtil {
     simpleModule.addKeyDeserializer(Field.class, new QueryDto.KeyFieldDeserializer());
     simpleModule.addKeySerializer(Field.class, new QueryDto.KeyFieldSerializer());
     OBJECT_MAPPER.registerModule(simpleModule);
+    OBJECT_MAPPER.registerModule(new JSR310Module());
   }
 
   public static String serialize(Object any) {
diff --git a/core/src/main/java/io/squashql/query/FieldAndAggFunc.java b/core/src/main/java/io/squashql/query/FieldAndAggFunc.java
new file mode 100644
index 0000000..bc57bff
--- /dev/null
+++ b/core/src/main/java/io/squashql/query/FieldAndAggFunc.java
@@ -0,0 +1,11 @@
+package io.squashql.query;
+
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor // For Jackson
+@AllArgsConstructor
+public class FieldAndAggFunc {
+  public Field field;
+  public String aggFunc;
+}
diff --git a/core/src/main/java/io/squashql/query/QueryResolver.java b/core/src/main/java/io/squashql/query/QueryResolver.java
index 685d9f5..3b51828 100644
--- a/core/src/main/java/io/squashql/query/QueryResolver.java
+++ b/core/src/main/java/io/squashql/query/QueryResolver.java
@@ -8,7 +8,6 @@ import io.squashql.store.Store;
 import io.squashql.type.*;
 import lombok.Data;
 import lombok.Value;
-import org.eclipse.collections.impl.tuple.Tuples;
 
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
@@ -338,7 +337,7 @@ public class QueryResolver {
   private CompiledMeasure compileVectorTupleAggMeasure(VectorTupleAggMeasure m) {
     return new CompiledVectorTupleAggMeasure(
             m.alias,
-            m.fieldToAggregateAndAggFunc.stream().map(p -> Tuples.pair(resolveField(p.getOne()), p.getTwo())).toList(),
+            m.fieldToAggregateAndAggFunc.stream().map(p -> new CompiledFieldAndAggFunc(resolveField(p.field), p.aggFunc)).toList(),
             resolveField(m.vectorAxis),
             m.transformer);
   }
diff --git a/core/src/main/java/io/squashql/query/VectorTupleAggMeasure.java b/core/src/main/java/io/squashql/query/VectorTupleAggMeasure.java
index e2575b2..5f170c8 100644
--- a/core/src/main/java/io/squashql/query/VectorTupleAggMeasure.java
+++ b/core/src/main/java/io/squashql/query/VectorTupleAggMeasure.java
@@ -1,7 +1,6 @@
 package io.squashql.query;
 
 import lombok.*;
-import org.eclipse.collections.api.tuple.Pair;
 
 import java.util.List;
 import java.util.function.Function;
@@ -16,14 +15,14 @@ import java.util.function.Function;
 public class VectorTupleAggMeasure implements Measure {
 
   public String alias;
-  public List<Pair<Field, String>> fieldToAggregateAndAggFunc;
+  public List<FieldAndAggFunc> fieldToAggregateAndAggFunc;
   public Field vectorAxis;
   public Function<List<Object>, Object> transformer;
   @With
   public String expression;
 
   public VectorTupleAggMeasure(String alias,
-                               List<Pair<Field, String>> fieldToAggregateAndAggFunc,
+                               List<FieldAndAggFunc> fieldToAggregateAndAggFunc,
                                Field vectorAxis,
                                Function<List<Object>, Object> transformer) {
     this.alias = alias;
diff --git a/core/src/main/java/io/squashql/query/compiled/CompiledFieldAndAggFunc.java b/core/src/main/java/io/squashql/query/compiled/CompiledFieldAndAggFunc.java
new file mode 100644
index 0000000..19c00e7
--- /dev/null
+++ b/core/src/main/java/io/squashql/query/compiled/CompiledFieldAndAggFunc.java
@@ -0,0 +1,6 @@
+package io.squashql.query.compiled;
+
+import io.squashql.type.TypedField;
+
+public record CompiledFieldAndAggFunc(TypedField field, String aggFunc) {
+}
diff --git a/core/src/main/java/io/squashql/query/compiled/CompiledVectorTupleAggMeasure.java b/core/src/main/java/io/squashql/query/compiled/CompiledVectorTupleAggMeasure.java
index 4c684fc..4c7c975 100644
--- a/core/src/main/java/io/squashql/query/compiled/CompiledVectorTupleAggMeasure.java
+++ b/core/src/main/java/io/squashql/query/compiled/CompiledVectorTupleAggMeasure.java
@@ -2,13 +2,12 @@ package io.squashql.query.compiled;
 
 import io.squashql.query.database.QueryRewriter;
 import io.squashql.type.TypedField;
-import org.eclipse.collections.api.tuple.Pair;
 
 import java.util.List;
 import java.util.function.Function;
 
 public record CompiledVectorTupleAggMeasure(String alias,
-                                            List<Pair<TypedField, String>> fieldToAggregateAndAggFunc,
+                                            List<CompiledFieldAndAggFunc> fieldToAggregateAndAggFunc,
                                             TypedField vectorAxis,
                                             Function<List<Object>, Object> transformer) implements CompiledMeasure {
 
diff --git a/core/src/main/java/io/squashql/query/compiled/PrefetchVisitor.java b/core/src/main/java/io/squashql/query/compiled/PrefetchVisitor.java
index 264d387..3690775 100644
--- a/core/src/main/java/io/squashql/query/compiled/PrefetchVisitor.java
+++ b/core/src/main/java/io/squashql/query/compiled/PrefetchVisitor.java
@@ -7,9 +7,7 @@ import io.squashql.query.database.SqlUtils;
 import io.squashql.type.AliasedTypedField;
 import io.squashql.type.TypedField;
 import lombok.RequiredArgsConstructor;
-import org.eclipse.collections.api.tuple.Pair;
 import org.eclipse.collections.impl.set.mutable.MutableSetFactoryImpl;
-import org.eclipse.collections.impl.tuple.Tuples;
 
 import java.util.*;
 import java.util.stream.Stream;
@@ -112,7 +110,7 @@ public class PrefetchVisitor implements MeasureVisitor<Map<QueryScope, Set<Compi
      */
     return visit(new CompiledVectorTupleAggMeasure(
             vectorAggMeasure.alias(),
-            List.of(Tuples.pair(vectorAggMeasure.fieldToAggregate(), vectorAggMeasure.aggregationFunction())),
+            List.of(new CompiledFieldAndAggFunc(vectorAggMeasure.fieldToAggregate(), vectorAggMeasure.aggregationFunction())),
             vectorAggMeasure.vectorAxis(),
             null));
   }
@@ -120,8 +118,8 @@ public class PrefetchVisitor implements MeasureVisitor<Map<QueryScope, Set<Compi
   @Override
   public Map<QueryScope, Set<CompiledMeasure>> visit(CompiledVectorTupleAggMeasure vecMeasure) {
     TypedField vectorAxis = vecMeasure.vectorAxis();
-    List<TypedField> fieldToAggregates = vecMeasure.fieldToAggregateAndAggFunc().stream().map(Pair::getOne).toList();
-    List<String> vectorAggFuncs = vecMeasure.fieldToAggregateAndAggFunc().stream().map(Pair::getTwo).toList();
+    List<TypedField> fieldToAggregates = vecMeasure.fieldToAggregateAndAggFunc().stream().map(CompiledFieldAndAggFunc::field).toList();
+    List<String> vectorAggFuncs = vecMeasure.fieldToAggregateAndAggFunc().stream().map(CompiledFieldAndAggFunc::aggFunc).toList();
     if (this.originalQueryScope.columns().contains(vectorAxis)) {
       Set<CompiledMeasure> m = new HashSet<>();
       for (int i = 0; i < fieldToAggregates.size(); i++) {
diff --git a/pom.xml b/pom.xml
index 0c72cfd..21f90f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -123,6 +123,11 @@
         <artifactId>jackson-databind</artifactId>
         <version>${jackson.version}</version>
       </dependency>
+      <dependency>
+        <groupId>com.fasterxml.jackson.datatype</groupId>
+        <artifactId>jackson-datatype-jsr310</artifactId>
+        <version>${jackson.version}</version>
+      </dependency>
       <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>


Test patch:
diff --git a/core/src/test/java/io/squashql/jackson/TestJacksonUtil.java b/core/src/test/java/io/squashql/jackson/TestJacksonUtil.java
new file mode 100644
index 0000000..d8461e1
--- /dev/null
+++ b/core/src/test/java/io/squashql/jackson/TestJacksonUtil.java
@@ -0,0 +1,17 @@
+package io.squashql.jackson;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+
+public class TestJacksonUtil {
+
+  @Test
+  void testLocalDate() {
+    LocalDate d = LocalDate.of(2000, 3, 15);
+    String serialize = JacksonUtil.serialize(d);
+    LocalDate deserialize = JacksonUtil.deserialize(serialize, LocalDate.class);
+    Assertions.assertThat(deserialize).isEqualTo(d);
+  }
+}
diff --git a/core/src/test/java/io/squashql/query/ATestVectorAggregation.java b/core/src/test/java/io/squashql/query/ATestVectorAggregation.java
index cb3c0ae..048bb2c 100644
--- a/core/src/test/java/io/squashql/query/ATestVectorAggregation.java
+++ b/core/src/test/java/io/squashql/query/ATestVectorAggregation.java
@@ -11,7 +11,6 @@ import io.squashql.table.Table;
 import io.squashql.type.TableTypedField;
 import io.squashql.util.TestUtil;
 import org.assertj.core.api.Assertions;
-import org.eclipse.collections.impl.tuple.Tuples;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 
@@ -296,8 +295,8 @@ public abstract class ATestVectorAggregation extends ABaseTestQuery {
     this.queryCache.clear();
 
     // Do not use the same alia and the same transformer instance. We want the measure not to be equal.
-    Measure vectorWoTransformer = new VectorTupleAggMeasure("vectorWoTransformer", List.of(Tuples.pair(this.value, SUM)), this.date, a -> a.get(0));
-    Measure vectorWithTransformer = new VectorTupleAggMeasure("vectorWithTransformer", List.of(Tuples.pair(this.value, SUM)), this.date, a -> a.get(0));
+    Measure vectorWoTransformer = new VectorTupleAggMeasure("vectorWoTransformer", List.of(new FieldAndAggFunc(this.value, SUM)), this.date, a -> a.get(0));
+    Measure vectorWithTransformer = new VectorTupleAggMeasure("vectorWithTransformer", List.of(new FieldAndAggFunc(this.value, SUM)), this.date, a -> a.get(0));
     QueryDto q1 = Query
             .from(this.storeName)
             .select(List.of(this.ean, this.competitor), List.of(vectorWoTransformer))
diff --git a/core/src/test/java/io/squashql/query/ATestVectorOperation.java b/core/src/test/java/io/squashql/query/ATestVectorOperation.java
index 15dff7a..3b4d81c 100644
--- a/core/src/test/java/io/squashql/query/ATestVectorOperation.java
+++ b/core/src/test/java/io/squashql/query/ATestVectorOperation.java
@@ -11,7 +11,6 @@ import io.squashql.table.Table;
 import io.squashql.type.TableTypedField;
 import io.squashql.util.MultipleColumnsSorter;
 import org.assertj.core.api.Assertions;
-import org.eclipse.collections.impl.tuple.Tuples;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 
@@ -71,7 +70,7 @@ public abstract class ATestVectorOperation extends ABaseTestQuery {
 
   @Test
   void testWithoutTotals() {
-    Measure vector = new VectorTupleAggMeasure("vector", List.of(Tuples.pair(this.price, SUM), Tuples.pair(this.date, ANY_VALUE)), this.date, null);
+    Measure vector = new VectorTupleAggMeasure("vector", List.of(new FieldAndAggFunc(this.price, SUM), new FieldAndAggFunc(this.date, ANY_VALUE)), this.date, null);
     QueryDto query = Query
             .from(this.storeName)
             .select(List.of(this.competitor, this.ean), List.of(vector))
@@ -84,7 +83,7 @@ public abstract class ATestVectorOperation extends ABaseTestQuery {
 
   @Test
   void testTotals() {
-    Measure vector = new VectorTupleAggMeasure("vector", List.of(Tuples.pair(this.price, SUM), Tuples.pair(this.date, ANY_VALUE)), this.date, null);
+    Measure vector = new VectorTupleAggMeasure("vector", List.of(new FieldAndAggFunc(this.price, SUM), new FieldAndAggFunc(this.date, ANY_VALUE)), this.date, null);
     QueryDto query = Query
             .from(this.storeName)
             .select(List.of(this.competitor, this.ean), List.of(vector))
@@ -152,7 +151,7 @@ public abstract class ATestVectorOperation extends ABaseTestQuery {
 
     Measure vector = new VectorTupleAggMeasure(
             "price_at_2023_3_3",
-            List.of(Tuples.pair(this.price, SUM), Tuples.pair(this.date, ANY_VALUE)),
+            List.of(new FieldAndAggFunc(this.price, SUM), new FieldAndAggFunc(this.date, ANY_VALUE)),
             this.date,
             transformer);
     QueryDto query = Query
@@ -178,7 +177,7 @@ public abstract class ATestVectorOperation extends ABaseTestQuery {
 
   @Test
   void testParentComparison() {
-    Measure vector = new VectorTupleAggMeasure("vector", List.of(Tuples.pair(this.price, SUM), Tuples.pair(this.date, ANY_VALUE)), this.date, null);
+    Measure vector = new VectorTupleAggMeasure("vector", List.of(new FieldAndAggFunc(this.price, SUM), new FieldAndAggFunc(this.date, ANY_VALUE)), this.date, null);
     List<Field> fields = List.of(this.competitor, this.ean);
     BiFunction<Object, Object, Object> operator = (a, b) -> {
       DoubleList currentValue = (DoubleList) ((List) a).get(0);