All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sphere.sdk.queries.QueryAllImpl Maven / Gradle / Ivy

The newest version!
package io.sphere.sdk.queries;

import io.sphere.sdk.client.SphereClient;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.LongStream;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Stream.concat;

final class QueryAllImpl> {
    private final QueryDsl baseQuery;
    private final long pageSize;

    private QueryAllImpl(final QueryDsl baseQuery, final long pageSize) {
        this.baseQuery = !baseQuery.sort().isEmpty() ? baseQuery : baseQuery.withSort(QuerySort.of("id asc"));
        this.pageSize = pageSize;
    }

    /**
     * Execute the {@link #baseQuery} on the {@code client} and return completion stage with all pages results
     * aggregated to list.
     * @param client the CTP client that the query is run on.
     * @return {@link CompletionStage} with the {@link #baseQuery} results aggregated from all pages to list.
     */
    public CompletionStage> run(final SphereClient client) {
        return run(client, entry -> entry);
    }

    /**
     * Given a {@code resultsMapper} {@link Function}, this method applies this callback on each item of the results
     * from {@link this} instance's {@link #baseQuery} and returns a future containing a list containing the results
     * of this {@code resultsMapper} on each item.
     *
     * @param client        the CTP client that the query is run on.
     * @param resultsMapper the callback that gets called on each item of results.
     * @param            the type of the result mapped values of the callback on each requested {@code T} item.
     * @return a future containing a list of results of the {@code resultsMapper} on each item.
     */
    @Nonnull
     CompletionStage> run(final SphereClient client, final Function resultsMapper) {
        return queryPage(client, 0)
                .thenApply(result -> concatPagedResultToList(result, client, resultsMapper));
    }

    /**
     * Given a {@link Consumer resultsConsumer}, this method applies this consumer on each item of the results from
     * {@link this} instance's {@link #baseQuery}.
     *
     * @param client          the CTP client that the query is run on.
     * @param resultsConsumer the consumer that gets called on each item of results.
     * @return an empty future which when is completed the {@code resultsConsumer} is executed on every item from the
     * {@link #baseQuery}
     */
    @Nonnull
    CompletionStage run(final SphereClient client, final Consumer resultsConsumer) {
        return queryPage(client, 0)
                .thenAccept(result -> consumePagedResult(result, client, resultsConsumer));
    }

    private  List concatPagedResultToList(@Nonnull PagedQueryResult result,
                                                @Nonnull SphereClient client,
                                                @Nonnull Function resultsMapper) {
        final Stream firstStream = result.getResults().stream().map(resultsMapper);

        Stream nextStream = queryNextPages(client, result.getTotal(), resultsMapper)
                .flatMap(stage -> stage.toCompletableFuture().join());

        return concat(firstStream, nextStream)
                .collect(toList());
    }

    private void consumePagedResult(@Nonnull PagedQueryResult result,
                                    @Nonnull SphereClient client,
                                    @Nonnull Consumer resultsConsumer) {
        result.getResults().forEach(resultsConsumer);
        queryNextPages(client, result.getTotal(), resultsConsumer)
                .forEach(stageStreamS -> stageStreamS.toCompletableFuture().join());
    }

    /**
     * Given the {@code resultsMapper} function, this method first calculates the total number of pages
     * resulting from the query, then it applies this {@code resultsMapper} on each item of the results
     * from {@link this} instance's {@link #baseQuery}.
     *
     * @param client        the CTP client that the query is run on.
     * @param totalElements the total number of elements resulting from the query.
     * @param resultsMapper the callback to apply on each item of results.
     * @return a stream of stages containing stream of mapped results using {@code resultsMapper}
     */
    @Nonnull
    private  Stream>> queryNextPages(final SphereClient client, final long totalElements,
                                                          final Function resultsMapper) {
        final long totalPages = getTotalNumberOfPages(totalElements);
        return LongStream.range(1, totalPages)
                         .mapToObj(page -> queryPage(client, page)
                             .thenApply(results -> results.getResults().stream().map(resultsMapper)));
    }

    /**
     * Given a {@link Consumer resultsConsumer}, this method first calculates the total number of pages
     * resulting from the query, then it accepts the results from {@link this} instance's {@link #baseQuery} on this
     * consumer and returns a stream of stages with accepting all the result items by {@code resultsConsumer}.
     *
     * @param client        the CTP client that the query is run on.
     * @param totalElements the total number of elements resulting from the query.
     * @param resultsConsumer      the consumer to apply on each page of results.
     * @return a list of futures of applying the consumer on each page of results.
     */
    @Nonnull
    private Stream> queryNextPages(final SphereClient client, final long totalElements,
                                                         final Consumer resultsConsumer) {
        final long totalPages = getTotalNumberOfPages(totalElements);
        return LongStream.range(1, totalPages)
                .mapToObj(page -> queryPage(client, page)
                        .thenAccept(results -> results.getResults().forEach(resultsConsumer)));
    }

    /**
     * Given a total number of elements {@code totalElements} this method calculates the number of pages needed that
     * would be needed to cover all the elements with set {@link #pageSize} of {@link this} instance.
     *
     * @param totalElements number of elements to get the number of pages for.
     * @return the total number of pages.
     */
    long getTotalNumberOfPages(final long totalElements) {
        return (totalElements - 1 + pageSize) / pageSize;
    }

    /**
     * Gets the results of {@link this} instance's query for a specific page with {@code pageNumber}.
     *
     * @param client     the CTP client that the query is run on.
     * @param pageNumber the page number to get the results for.
     * @return a future containing the results of the requested page of applying the query.
     */
    @Nonnull
    private CompletionStage> queryPage(final SphereClient client, final long pageNumber) {
        final QueryDsl query = baseQuery
                .withOffset(pageNumber * pageSize)
                .withLimit(pageSize);
        return client.execute(query);
    }

    @Nonnull
    static > QueryAllImpl of(@Nonnull final QueryDsl baseQuery, final int pageSize) {
        return new QueryAllImpl<>(baseQuery, pageSize);
    }
}