Skip to content

Commit

Permalink
Work on #31
Browse files Browse the repository at this point in the history
  • Loading branch information
julgus committed May 23, 2023
1 parent b19e7f1 commit 8a080da
Show file tree
Hide file tree
Showing 12 changed files with 272 additions and 2 deletions.
4 changes: 4 additions & 0 deletions provider/renderer-standard/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@
<artifactId>jakarta.persistence-api</artifactId>
<version>${jakarta.version}</version>
</dependency>
<dependency>
<groupId>com.speedment.jpastreamer</groupId>
<artifactId>termopoptimizer</artifactId>
</dependency>


</dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -59,13 +61,15 @@ 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);
}

StandardRenderer(final EntityManager entityManager) {
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);
}

Expand Down Expand Up @@ -180,6 +184,7 @@ private <E, T, S extends BaseStream<T, S>> S replay(final Stream<E> stream, fina
}

private <T> void optimizePipeline(final Pipeline<T> pipeline) {
terminalOperationOptimizerFactory.get().optimize(pipeline);
intermediateOperationOptimizerFactory.stream().forEach(intermediateOperationOptimizer -> intermediateOperationOptimizer.optimize(pipeline));
}

Expand Down
1 change: 1 addition & 0 deletions provider/renderer-standard/src/main/java9/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions provider/termopoptimizer-standard/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@
<groupId>com.speedment.jpastreamer</groupId>
<artifactId>termopoptimizer</artifactId>
</dependency>

<dependency>
<groupId>com.speedment.jpastreamer</groupId>
<artifactId>pipeline</artifactId>
</dependency>

<dependency>
<groupId>com.speedment.jpastreamer</groupId>
<artifactId>rootfactory</artifactId>
</dependency>

<dependency>
<groupId>com.speedment.jpastreamer</groupId>
<artifactId>pipeline-standard</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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 <T> Pipeline<T> optimize(Pipeline<T> 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 <T> Pipeline<T> optimizeAnyMatch(Pipeline<T> pipeline) {
pipeline.intermediateOperations().add(iof.createLimit(1));
pipeline.intermediateOperations().add(
iof.createFilter(pipeline.terminatingOperation().predicate()));
pipeline.terminatingOperation(tof.createAnyMatch(p -> true));
return pipeline;
}

private <T> Pipeline<T> optimizeNoneMatch(Pipeline<T> 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 <T> Pipeline<T> optimizeFindFirst(Pipeline<T> pipeline) {
pipeline.intermediateOperations().add(iof.createLimit(1));
return pipeline;
}

private <T> Pipeline<T> optimizeFindAny(Pipeline<T> pipeline) {
pipeline.ordered(false);
pipeline.intermediateOperations().add(iof.createLimit(1));
return pipeline;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
module jpastreamer.termopoptimizer.standard {
requires transitive jpastreamer.termopoptimizer;
requires jpastreamer.rootfactory;

exports com.speedment.jpastreamer.termopoptimizer.standard;

Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}

}
Original file line number Diff line number Diff line change
@@ -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<String> {

@Override
Class<String> getEntityClass() {
return String.class;
}

@Override
protected Stream<PipelineTestCase<String>> pipelines() {
return Stream.of(
noAnyMatch(),
anyMatch()
);
}

private PipelineTestCase<String> noAnyMatch() {
final Pipeline<String> noAnyMatch = createPipeline(
tof.createAllMatch(s -> s.equals("test")),
iof.createLimit(1)
);

final Pipeline<String> noAnyMatchExpected = createPipeline(
tof.createAllMatch(s -> s.equals("test")),
iof.createLimit(1)
);

return new PipelineTestCase<>("No Any Match", noAnyMatch, noAnyMatchExpected);
}

private PipelineTestCase<String> anyMatch() {
final Predicate<String> p = s -> (s.equals("test"));

final Pipeline<String> anyMatch = createPipeline(
tof.createAnyMatch(p),
iof.createLimit(100)
);

final Pipeline<String> anyMatchExpected = createPipeline(
tof.createAnyMatch(s -> true),
iof.createLimit(100),
iof.createLimit(1),
iof.createFilter(p)
);

return new PipelineTestCase<>("Any Match", anyMatch, anyMatchExpected);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.speedment.jpastreamer.termopoptimizer.standard.internal;

public class FindAnyTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.speedment.jpastreamer.termopoptimizer.standard.internal;

public class FindFirstTest {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.speedment.jpastreamer.termopoptimizer.standard.internal;

public class NoneMatchTest {
}
Original file line number Diff line number Diff line change
@@ -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<ENTITY> {

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<ENTITY> getEntityClass();

@TestFactory
Stream<DynamicTest> optimize() {
return pipelines().map(testCase -> dynamicTest(testCase.getName(), () -> {
this.optimizer.optimize(testCase.getPipeline());

assertTestCase(testCase);
}));
}

protected abstract Stream<PipelineTestCase<ENTITY>> pipelines();

protected Pipeline<ENTITY> createPipeline(final TerminalOperation<?, ?> terminalOperation, final IntermediateOperation<?, ?>... operations) {
final Pipeline<ENTITY> pipeline = pipelineFactory.createPipeline(getEntityClass());

for (IntermediateOperation<?, ?> operation : operations) {
pipeline.intermediateOperations().add(operation);
}

pipeline.terminatingOperation(terminalOperation);

return pipeline;
}

private void assertTestCase(final PipelineTestCase<ENTITY> testCase) {
final Pipeline<ENTITY> unoptimized = testCase.getPipeline();
final Pipeline<ENTITY> optimized = testCase.getExpectedPipeline();

final List<IntermediateOperation<?, ?>> unoptimizedIntermediateOperations = unoptimized.intermediateOperations();
final List<IntermediateOperation<?, ?>> 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<ENTITY> {

private final String name;
private final Pipeline<ENTITY> pipeline;
private final Pipeline<ENTITY> expectedPipeline;

protected PipelineTestCase(
final String name,
final Pipeline<ENTITY> pipeline,
final Pipeline<ENTITY> expectedPipeline
) {
this.name = name;
this.pipeline = pipeline;
this.expectedPipeline = expectedPipeline;
}

public String getName() {
return name;
}

public Pipeline<ENTITY> getPipeline() {
return pipeline;
}

public Pipeline<ENTITY> getExpectedPipeline() {
return expectedPipeline;
}
}

}

0 comments on commit 8a080da

Please sign in to comment.