From 8a080da4456d6dcd833c5aa6359969357387062d Mon Sep 17 00:00:00 2001 From: Julia Gustafsson Date: Tue, 23 May 2023 10:20:24 +0200 Subject: [PATCH] Work on #31 --- provider/renderer-standard/pom.xml | 4 + .../standard/internal/StandardRenderer.java | 5 + .../src/main/java9/module-info.java | 1 + provider/termopoptimizer-standard/pom.xml | 17 +++ .../StandardTerminalOperationOptimizer.java | 58 +++++++++- .../src/main/java9/module-info.java | 1 + ...TerminalOperationOptimizerFactoryTest.java | 16 +++ .../standard/internal/AnyMatchTest.java | 55 +++++++++ .../standard/internal/FindAnyTest.java | 4 + .../standard/internal/FindFirstTest.java | 4 + .../standard/internal/NoneMatchTest.java | 4 + ...tandardTerminalOperationOptimizerTest.java | 105 ++++++++++++++++++ 12 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/StandardTerminalOperationOptimizerFactoryTest.java create mode 100644 provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/AnyMatchTest.java create mode 100644 provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/FindAnyTest.java create mode 100644 provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/FindFirstTest.java create mode 100644 provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/NoneMatchTest.java create mode 100644 provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizerTest.java diff --git a/provider/renderer-standard/pom.xml b/provider/renderer-standard/pom.xml index 8626d11fb..960b463a4 100644 --- a/provider/renderer-standard/pom.xml +++ b/provider/renderer-standard/pom.xml @@ -71,6 +71,10 @@ jakarta.persistence-api ${jakarta.version} + + com.speedment.jpastreamer + termopoptimizer + diff --git a/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java b/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java index abf7a94ec..e0a0eca28 100644 --- a/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java +++ b/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java @@ -29,6 +29,7 @@ import com.speedment.jpastreamer.rootfactory.RootFactory; import com.speedment.jpastreamer.streamconfiguration.StreamConfiguration; +import com.speedment.jpastreamer.termopoptimizer.TerminalOperationOptimizerFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.TypedQuery; @@ -48,6 +49,7 @@ final class StandardRenderer implements Renderer { private final CriteriaFactory criteriaFactory; private final IntermediateOperationOptimizerFactory intermediateOperationOptimizerFactory; + private final TerminalOperationOptimizerFactory terminalOperationOptimizerFactory; private final MergerFactory mergerFactory; @@ -59,6 +61,7 @@ final class StandardRenderer implements Renderer { this.entityManager = requireNonNull(entityManagerSupplier).get(); this.criteriaFactory = RootFactory.getOrThrow(CriteriaFactory.class, ServiceLoader::load); this.intermediateOperationOptimizerFactory = RootFactory.getOrThrow(IntermediateOperationOptimizerFactory.class, ServiceLoader::load); + this.terminalOperationOptimizerFactory = RootFactory.getOrThrow(TerminalOperationOptimizerFactory.class, ServiceLoader::load); this.mergerFactory = RootFactory.getOrThrow(MergerFactory.class, ServiceLoader::load); } @@ -66,6 +69,7 @@ final class StandardRenderer implements Renderer { this.entityManager = entityManager; this.criteriaFactory = RootFactory.getOrThrow(CriteriaFactory.class, ServiceLoader::load); this.intermediateOperationOptimizerFactory = RootFactory.getOrThrow(IntermediateOperationOptimizerFactory.class, ServiceLoader::load); + this.terminalOperationOptimizerFactory = RootFactory.getOrThrow(TerminalOperationOptimizerFactory.class, ServiceLoader::load); this.mergerFactory = RootFactory.getOrThrow(MergerFactory.class, ServiceLoader::load); } @@ -180,6 +184,7 @@ private > S replay(final Stream stream, fina } private void optimizePipeline(final Pipeline pipeline) { + terminalOperationOptimizerFactory.get().optimize(pipeline); intermediateOperationOptimizerFactory.stream().forEach(intermediateOperationOptimizer -> intermediateOperationOptimizer.optimize(pipeline)); } diff --git a/provider/renderer-standard/src/main/java9/module-info.java b/provider/renderer-standard/src/main/java9/module-info.java index 39e8bca53..a1d8ef152 100644 --- a/provider/renderer-standard/src/main/java9/module-info.java +++ b/provider/renderer-standard/src/main/java9/module-info.java @@ -19,6 +19,7 @@ requires jpastreamer.criteria; requires jpastreamer.merger; requires jpastreamer.interopoptimizer; + requires jpastreamer.termopoptimizer; exports com.speedment.jpastreamer.renderer.standard; // Todo: Enable this diff --git a/provider/termopoptimizer-standard/pom.xml b/provider/termopoptimizer-standard/pom.xml index ee7828504..2e12c4ccb 100644 --- a/provider/termopoptimizer-standard/pom.xml +++ b/provider/termopoptimizer-standard/pom.xml @@ -39,6 +39,23 @@ com.speedment.jpastreamer termopoptimizer + + + com.speedment.jpastreamer + pipeline + + + + com.speedment.jpastreamer + rootfactory + + + + com.speedment.jpastreamer + pipeline-standard + test + + diff --git a/provider/termopoptimizer-standard/src/main/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizer.java b/provider/termopoptimizer-standard/src/main/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizer.java index 3fd766c4f..5f5b0bd87 100644 --- a/provider/termopoptimizer-standard/src/main/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizer.java +++ b/provider/termopoptimizer-standard/src/main/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizer.java @@ -15,15 +15,69 @@ import static java.util.Objects.requireNonNull; import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationFactory; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperationFactory; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperationType; +import com.speedment.jpastreamer.rootfactory.RootFactory; import com.speedment.jpastreamer.termopoptimizer.TerminalOperationOptimizer; -final class StandardTerminalOperationOptimizer implements TerminalOperationOptimizer { +import java.util.ServiceLoader; +final class StandardTerminalOperationOptimizer implements TerminalOperationOptimizer { + + final IntermediateOperationFactory iof = RootFactory + .getOrThrow(IntermediateOperationFactory.class, ServiceLoader::load); + final TerminalOperationFactory tof = + RootFactory.getOrThrow(TerminalOperationFactory.class, ServiceLoader::load); + @Override public Pipeline optimize(Pipeline pipeline) { requireNonNull(pipeline); - // For now, just return whatever we get. + + final TerminalOperationType terminalOperationType = pipeline.terminatingOperation().type(); + + switch (terminalOperationType) { + case ANY_MATCH: + return optimizeAnyMatch(pipeline); + case NONE_MATCH: + return optimizeNoneMatch(pipeline); + case FIND_FIRST: + return optimizeFindFirst(pipeline); + case FIND_ANY: + return optimizeFindAny(pipeline); + default: + return pipeline; + } + + } + + private Pipeline optimizeAnyMatch(Pipeline pipeline) { + pipeline.intermediateOperations().add(iof.createLimit(1)); + pipeline.intermediateOperations().add( + iof.createFilter(pipeline.terminatingOperation().predicate())); + pipeline.terminatingOperation(tof.createAnyMatch(p -> true)); + return pipeline; + } + + private Pipeline optimizeNoneMatch(Pipeline pipeline) { + pipeline.intermediateOperations().add(iof.createLimit(1)); + pipeline.intermediateOperations().add( + iof.createFilter(pipeline.terminatingOperation().predicate())); + // NoneMatch() - If the stream is empty then true is returned and the predicate is not evaluated. + // If the expression is evaluated => There is a match and the expression is always false. + pipeline.terminatingOperation(tof.createNoneMatch(e -> false)); + return pipeline; + } + + private Pipeline optimizeFindFirst(Pipeline pipeline) { + pipeline.intermediateOperations().add(iof.createLimit(1)); return pipeline; } + private Pipeline optimizeFindAny(Pipeline pipeline) { + pipeline.ordered(false); + pipeline.intermediateOperations().add(iof.createLimit(1)); + return pipeline; + } + } diff --git a/provider/termopoptimizer-standard/src/main/java9/module-info.java b/provider/termopoptimizer-standard/src/main/java9/module-info.java index 46961365a..dcb9426ac 100644 --- a/provider/termopoptimizer-standard/src/main/java9/module-info.java +++ b/provider/termopoptimizer-standard/src/main/java9/module-info.java @@ -12,6 +12,7 @@ */ module jpastreamer.termopoptimizer.standard { requires transitive jpastreamer.termopoptimizer; + requires jpastreamer.rootfactory; exports com.speedment.jpastreamer.termopoptimizer.standard; diff --git a/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/StandardTerminalOperationOptimizerFactoryTest.java b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/StandardTerminalOperationOptimizerFactoryTest.java new file mode 100644 index 000000000..85c1d5cc6 --- /dev/null +++ b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/StandardTerminalOperationOptimizerFactoryTest.java @@ -0,0 +1,16 @@ +package com.speedment.jpastreamer.termopoptimizer.standard; + +import com.speedment.jpastreamer.termopoptimizer.TerminalOperationOptimizerFactory; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +final class StandardTerminalOperationOptimizerFactoryTest { + + @Test + void get() { + final TerminalOperationOptimizerFactory terminalOperationOptimizerFactory = new StandardTerminalOperationOptimizerFactory(); + assertNotNull(terminalOperationOptimizerFactory.get()); + } + +} diff --git a/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/AnyMatchTest.java b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/AnyMatchTest.java new file mode 100644 index 000000000..94e28c7e8 --- /dev/null +++ b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/AnyMatchTest.java @@ -0,0 +1,55 @@ +package com.speedment.jpastreamer.termopoptimizer.standard.internal; + +import com.speedment.jpastreamer.pipeline.Pipeline; + +import java.util.function.Predicate; +import java.util.stream.Stream; + +final class AnyMatchTest extends StandardTerminalOperationOptimizerTest { + + @Override + Class getEntityClass() { + return String.class; + } + + @Override + protected Stream> pipelines() { + return Stream.of( + noAnyMatch(), + anyMatch() + ); + } + + private PipelineTestCase noAnyMatch() { + final Pipeline noAnyMatch = createPipeline( + tof.createAllMatch(s -> s.equals("test")), + iof.createLimit(1) + ); + + final Pipeline noAnyMatchExpected = createPipeline( + tof.createAllMatch(s -> s.equals("test")), + iof.createLimit(1) + ); + + return new PipelineTestCase<>("No Any Match", noAnyMatch, noAnyMatchExpected); + } + + private PipelineTestCase anyMatch() { + final Predicate p = s -> (s.equals("test")); + + final Pipeline anyMatch = createPipeline( + tof.createAnyMatch(p), + iof.createLimit(100) + ); + + final Pipeline anyMatchExpected = createPipeline( + tof.createAnyMatch(s -> true), + iof.createLimit(100), + iof.createLimit(1), + iof.createFilter(p) + ); + + return new PipelineTestCase<>("Any Match", anyMatch, anyMatchExpected); + } + +} diff --git a/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/FindAnyTest.java b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/FindAnyTest.java new file mode 100644 index 000000000..78412ab19 --- /dev/null +++ b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/FindAnyTest.java @@ -0,0 +1,4 @@ +package com.speedment.jpastreamer.termopoptimizer.standard.internal; + +public class FindAnyTest { +} diff --git a/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/FindFirstTest.java b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/FindFirstTest.java new file mode 100644 index 000000000..979a74030 --- /dev/null +++ b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/FindFirstTest.java @@ -0,0 +1,4 @@ +package com.speedment.jpastreamer.termopoptimizer.standard.internal; + +public class FindFirstTest { +} diff --git a/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/NoneMatchTest.java b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/NoneMatchTest.java new file mode 100644 index 000000000..6140ee182 --- /dev/null +++ b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/NoneMatchTest.java @@ -0,0 +1,4 @@ +package com.speedment.jpastreamer.termopoptimizer.standard.internal; + +public class NoneMatchTest { +} diff --git a/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizerTest.java b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizerTest.java new file mode 100644 index 000000000..e8fc5ab0c --- /dev/null +++ b/provider/termopoptimizer-standard/src/test/java/com/speedment/jpastreamer/termopoptimizer/standard/internal/StandardTerminalOperationOptimizerTest.java @@ -0,0 +1,105 @@ +package com.speedment.jpastreamer.termopoptimizer.standard.internal; + +import com.speedment.jpastreamer.pipeline.Pipeline; +import com.speedment.jpastreamer.pipeline.PipelineFactory; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperation; +import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationFactory; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperation; +import com.speedment.jpastreamer.pipeline.terminal.TerminalOperationFactory; +import com.speedment.jpastreamer.rootfactory.RootFactory; +import com.speedment.jpastreamer.termopoptimizer.TerminalOperationOptimizer; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +abstract class StandardTerminalOperationOptimizerTest { + + protected final PipelineFactory pipelineFactory = RootFactory.getOrThrow(PipelineFactory.class, ServiceLoader::load); + protected final IntermediateOperationFactory iof = RootFactory.getOrThrow(IntermediateOperationFactory.class, ServiceLoader::load); + protected final TerminalOperationFactory tof = RootFactory.getOrThrow(TerminalOperationFactory.class, ServiceLoader::load); + protected final TerminalOperationOptimizer optimizer = new StandardTerminalOperationOptimizer(); + + abstract Class getEntityClass(); + + @TestFactory + Stream optimize() { + return pipelines().map(testCase -> dynamicTest(testCase.getName(), () -> { + this.optimizer.optimize(testCase.getPipeline()); + + assertTestCase(testCase); + })); + } + + protected abstract Stream> pipelines(); + + protected Pipeline createPipeline(final TerminalOperation terminalOperation, final IntermediateOperation... operations) { + final Pipeline pipeline = pipelineFactory.createPipeline(getEntityClass()); + + for (IntermediateOperation operation : operations) { + pipeline.intermediateOperations().add(operation); + } + + pipeline.terminatingOperation(terminalOperation); + + return pipeline; + } + + private void assertTestCase(final PipelineTestCase testCase) { + final Pipeline unoptimized = testCase.getPipeline(); + final Pipeline optimized = testCase.getExpectedPipeline(); + + final List> unoptimizedIntermediateOperations = unoptimized.intermediateOperations(); + final List> optimizedIntermediateOperations = optimized.intermediateOperations(); + + assertEquals(optimizedIntermediateOperations.size(), unoptimizedIntermediateOperations.size()); + + for (int i = 0; i < unoptimizedIntermediateOperations.size(); i++) { + final IntermediateOperation unoptimizedOperation = unoptimizedIntermediateOperations.get(i); + final IntermediateOperation optimizedOperation = optimizedIntermediateOperations.get(i); + + assertEquals(optimizedOperation.type(), unoptimizedOperation.type()); + assertArguments(optimizedOperation.arguments(), unoptimizedOperation.arguments()); + } + } + + protected void assertArguments(final Object[] expected, final Object[] actual) { + assertArrayEquals(expected, actual); + } + + protected static final class PipelineTestCase { + + private final String name; + private final Pipeline pipeline; + private final Pipeline expectedPipeline; + + protected PipelineTestCase( + final String name, + final Pipeline pipeline, + final Pipeline expectedPipeline + ) { + this.name = name; + this.pipeline = pipeline; + this.expectedPipeline = expectedPipeline; + } + + public String getName() { + return name; + } + + public Pipeline getPipeline() { + return pipeline; + } + + public Pipeline getExpectedPipeline() { + return expectedPipeline; + } + } + +}