Skip to content

Commit

Permalink
Merge pull request #837 from commercetools/query_all_improvement
Browse files Browse the repository at this point in the history
[DEVX-322] QueryAll allows different field than id to be used
  • Loading branch information
jenschude authored Feb 25, 2025
2 parents 88f5536 + f7c3ae9 commit 21dba51
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,48 @@ final class QueryAll<TMethod extends SimplePagedQueryResourceRequest<TMethod, TR

private Consumer<List<TElement>> pageConsumer;

private QueryAll(@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> baseQuery,
final int pageSize) {
private final String sortField;

this.baseQuery = withDefaults(baseQuery, pageSize);
private final QueryUtils.SortOrder sortOrder;

private final Function<TElement, String> elementIdentifier;

private QueryAll(@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> baseQuery, final int pageSize,
final String sortField, final QueryUtils.SortOrder sortOrder,
final Function<TElement, String> elementIdentifier) {

this.sortField = sortField;
this.sortOrder = sortOrder;
this.baseQuery = withDefaults(baseQuery, pageSize, sortField, sortOrder);
this.pageSize = pageSize;
this.mappedResultsTillNow = new ArrayList<>();
this.elementIdentifier = elementIdentifier;
}

@Nonnull
private static <TMethod extends SimplePagedQueryResourceRequest<TMethod, TResult, ?>, TResult extends ResourcePagedQueryResponse<TElement>, TElement extends DomainResource<TElement>> SimplePagedQueryResourceRequest<TMethod, TResult, ?> withDefaults(
@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> request, final int pageSize) {
@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> request, final int pageSize,
final String sortField, final QueryUtils.SortOrder sortOrder) {

final SimplePagedQueryResourceRequest<TMethod, TResult, ?> withLimit = request.withLimit(pageSize)
.withWithTotal(false);
return !withLimit.getQueryParam("sort").isEmpty() ? withLimit : withLimit.withSort("id asc");
return !withLimit.getQueryParam("sort").isEmpty() ? withLimit
: withLimit.withSort(sortField + " " + sortOrder.getValue());
}

@Nonnull
static <TMethod extends SimplePagedQueryResourceRequest<TMethod, TResult, ?>, TResult extends ResourcePagedQueryResponse<TElement>, TElement extends DomainResource<TElement>, S> QueryAll<TMethod, TResult, TElement, S> of(
@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> baseQuery, final int pageSize) {
return of(baseQuery, pageSize, "id", QueryUtils.SortOrder.ASCENDING, DomainResource::getId);
}

@Nonnull
static <TMethod extends SimplePagedQueryResourceRequest<TMethod, TResult, ?>, TResult extends ResourcePagedQueryResponse<TElement>, TElement extends DomainResource<TElement>, S> QueryAll<TMethod, TResult, TElement, S> of(
@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> baseQuery, final int pageSize,
final String sortField, final QueryUtils.SortOrder sortOrder,
final Function<TElement, String> elementIdentifier) {

return new QueryAll<>(baseQuery, pageSize);
return new QueryAll<>(baseQuery, pageSize, sortField, sortOrder, elementIdentifier);
}

/**
Expand Down Expand Up @@ -157,8 +177,14 @@ private void mapOrConsume(@Nonnull final List<TElement> pageElements) {
@Nonnull
private CompletionStage<ApiHttpResponse<TResult>> getNextPageStage(@Nonnull final List<TElement> pageElements) {
if (pageElements.size() == pageSize) {
final String lastElementId = pageElements.get(pageElements.size() - 1).getId();
return baseQuery.addWhere("id > :lastId", "lastId", lastElementId).execute();
final String lastElementId = elementIdentifier.apply(pageElements.get(pageElements.size() - 1));
if (sortOrder == QueryUtils.SortOrder.ASCENDING) {
return baseQuery.addWhere(sortField + " > :lastValue", "lastValue", lastElementId).execute();
}
else {
return baseQuery.addWhere(sortField + " < :lastValue", "lastValue", lastElementId).execute();

}
}
return completedFuture(null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@
import com.commercetools.api.models.ResourcePagedQueryResponse;

public class QueryUtils {

public enum SortOrder {
ASCENDING("asc"), DESCENDING("desc");

private final String value;

SortOrder(final String value) {
this.value = value;
}

public String getValue() {
return value;
}
}

static int DEFAULT_PAGE_SIZE = 500;
/**
* <p>Queries all elements matching a query by using a limit based pagination with a combination of
Expand All @@ -26,6 +41,8 @@ public class QueryUtils {
* <p>NOTE: This method fetches all paged results sequentially as opposed to fetching the pages in
* parallel.
*
* <p>NOTE: Please be aware that using a non-unique field for sorting may return incomplete results</p>
*
* @param query query containing predicates and expansion paths
* @param pageMapper callback function that is called on every page queried
* @param <TElement> type of one query result element
Expand All @@ -41,6 +58,40 @@ public class QueryUtils {
return queryAll(query, pageMapper, DEFAULT_PAGE_SIZE);
}

/**
* <p>Queries all elements matching a query by using a limit based pagination with a combination of
* id sorting and a page size 500. More on the algorithm can be found here:
* <a href="https://docs.commercetools.com/api/general-concepts#iterating-over-all-elements">Iterating all elements</a>.</p>
*
* <p>The method takes a callback {@link java.util.function.Function} that returns a result of type {@code <S>} that
* is returned on every page of elements queried. Eventually, the method returns a {@link
* java.util.concurrent.CompletionStage} that contains a list of all the results of the callbacks returned from every
* page.
*
* <p>NOTE: This method fetches all paged results sequentially as opposed to fetching the pages in
* parallel.
*
* <p>NOTE: Please be aware that using a non-unique field for sorting may return incomplete results</p>
*
* @param query query containing predicates and expansion paths
* @param pageMapper callback function that is called on every page queried
* @param sortField field the results should sort on
* @param sortOrder order of the results
* @param elementIdentifierFn function to retrieve the sort order field from an element
* @param <TElement> type of one query result element
* @param <TMethod> type of the query
* @param <TResult> type of the result
* @param <S> type of the returned result of the callback function on every page.
* @return a completion stage containing a list of mapped pages as a result.
*/
@Nonnull
public static <TMethod extends SimplePagedQueryResourceRequest<TMethod, TResult, ?>, TResult extends ResourcePagedQueryResponse<TElement>, TElement extends DomainResource<TElement>, S> CompletionStage<List<S>> queryAll(
@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> query,
@Nonnull final Function<List<TElement>, S> pageMapper, final String sortField, final SortOrder sortOrder,
final Function<TElement, String> elementIdentifierFn) {
return queryAll(query, pageMapper, DEFAULT_PAGE_SIZE, sortField, sortOrder, elementIdentifierFn);
}

/**
* <p>Queries all elements matching a query by using a limit based pagination with a combination of
* id sorting and a page size 500. More on the algorithm can be found here:
Expand All @@ -52,6 +103,8 @@ public class QueryUtils {
* <p>NOTE: This method fetches all paged results sequentially as opposed to fetching the pages in
* parallel.
*
* <p>NOTE: Please be aware that using a non-unique field for sorting may return incomplete results</p>
*
* @param query query containing predicates and expansion paths
* @param pageConsumer consumer applied on every page queried
* @param <TElement> type of one query result element
Expand All @@ -67,6 +120,38 @@ public class QueryUtils {
return queryAll(query, pageConsumer, DEFAULT_PAGE_SIZE);
}

/**
* <p>Queries all elements matching a query by using a limit based pagination with a combination of
* id sorting and a page size 500. More on the algorithm can be found here:
* <a href="https://docs.commercetools.com/api/general-concepts#iterating-over-all-elements">Iterating all elements</a>.</p>
*
* <p>The method takes a consumer {@link Consumer} that is applied on every page of elements
* queried.
*
* <p>NOTE: This method fetches all paged results sequentially as opposed to fetching the pages in
* parallel.
*
* <p>NOTE: Please be aware that using a non-unique field for sorting may return incomplete results</p>
*
* @param query query containing predicates and expansion paths
* @param pageConsumer consumer applied on every page queried
* @param sortField field the results should sort on
* @param sortOrder order of the results
* @param elementIdentifierFn function to retrieve the sort order field from an element
* @param <TElement> type of one query result element
* @param <TMethod> type of the query
* @param <TResult> type of the result
* @return a completion stage containing void as a result after the consumer was applied on all
* pages.
*/
@Nonnull
public static <TMethod extends SimplePagedQueryResourceRequest<TMethod, TResult, ?>, TResult extends ResourcePagedQueryResponse<TElement>, TElement extends DomainResource<TElement>> CompletionStage<Void> queryAll(
@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> query,
@Nonnull final Consumer<List<TElement>> pageConsumer, final String sortField, final SortOrder sortOrder,
final Function<TElement, String> elementIdentifierFn) {
return queryAll(query, pageConsumer, DEFAULT_PAGE_SIZE, sortField, sortOrder, elementIdentifierFn);
}

/**
* <p>Queries all elements matching a query by using a limit based pagination with a combination of
* id sorting and the supplied {@code pageSize}. More on the algorithm can be found here:
Expand All @@ -80,6 +165,8 @@ public class QueryUtils {
* <p>NOTE: This method fetches all paged results sequentially as opposed to fetching the pages in
* parallel.
*
* <p>NOTE: Please be aware that using a non-unique field for sorting may return incomplete results</p>
*
* @param query query containing predicates and expansion paths
* @param pageMapper callback function that is called on every page queried
* @param <TElement> type of one query result element
Expand All @@ -97,6 +184,43 @@ public class QueryUtils {
return queryAll.run(pageMapper);
}

/**
* <p>Queries all elements matching a query by using a limit based pagination with a combination of
* id sorting and the supplied {@code pageSize}. More on the algorithm can be found here:
* <a href="https://docs.commercetools.com/api/general-concepts#iterating-over-all-elements">Iterating all elements</a>.</p>
*
* <p>The method takes a callback {@link Function} that returns a result of type {@code <S>} that
* is returned on every page of elements queried. Eventually, the method returns a {@link
* CompletionStage} that contains a list of all the results of the callbacks returned from every
* page.
*
* <p>NOTE: This method fetches all paged results sequentially as opposed to fetching the pages in
* parallel.
*
* <p>NOTE: Please be aware that using a non-unique field for sorting may return incomplete results</p>
*
* @param query query containing predicates and expansion paths
* @param pageMapper callback function that is called on every page queried
* @param sortField field the results should sort on
* @param sortOrder order of the results
* @param elementIdentifierFn function to retrieve the sort order field from an element
* @param <TElement> type of one query result element
* @param <TMethod> type of the query
* @param <TResult> type of the result
* @param <S> type of the returned result of the callback function on every page.
* @param pageSize the page size.
* @return a completion stage containing a list of mapped pages as a result.
*/
@Nonnull
public static <TMethod extends SimplePagedQueryResourceRequest<TMethod, TResult, ?>, TResult extends ResourcePagedQueryResponse<TElement>, TElement extends DomainResource<TElement>, S> CompletionStage<List<S>> queryAll(
@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> query,
@Nonnull final Function<List<TElement>, S> pageMapper, final int pageSize, final String sortField,
final SortOrder sortOrder, final Function<TElement, String> elementIdentifierFn) {
final QueryAll<TMethod, TResult, TElement, S> queryAll = QueryAll.of(query, pageSize, sortField, sortOrder,
elementIdentifierFn);
return queryAll.run(pageMapper);
}

/**
* <p>Queries all elements matching a query by using a limit based pagination with a combination of
* id sorting and the supplied {@code pageSize}. More on the algorithm can be found here:
Expand All @@ -123,4 +247,38 @@ public class QueryUtils {
final QueryAll<TMethod, TResult, TElement, Void> queryAll = QueryAll.of(query, pageSize);
return queryAll.run(pageConsumer);
}

/**
* <p>Queries all elements matching a query by using a limit based pagination with a combination of
* id sorting and the supplied {@code pageSize}. More on the algorithm can be found here:
* <a href="https://docs.commercetools.com/api/general-concepts#iterating-over-all-elements">Iterating all elements</a>.</p>
*
* <p>The method takes a {@link java.util.function.Consumer} that is applied on every page of the queried elements.
*
* <p>NOTE: This method fetches all paged results sequentially as opposed to fetching the pages in
* parallel.
*
* <p>NOTE: Please be aware that using a non-unique field for sorting may return incomplete results</p>
*
* @param query query containing predicates and expansion paths
* @param pageConsumer consumer applied on every page queried
* @param sortField field the results should sort on
* @param sortOrder order of the results
* @param elementIdentifierFn function to retrieve the sort order field from an element
* @param <TElement> type of one query result element
* @param <TMethod> type of the query
* @param <TResult> type of the result
* @param pageSize the page size
* @return a completion stage containing void as a result after the consumer was applied on all
* pages.
*/
@Nonnull
public static <TMethod extends SimplePagedQueryResourceRequest<TMethod, TResult, ?>, TResult extends ResourcePagedQueryResponse<TElement>, TElement extends DomainResource<TElement>> CompletionStage<Void> queryAll(
@Nonnull final SimplePagedQueryResourceRequest<TMethod, TResult, ?> query,
@Nonnull final Consumer<List<TElement>> pageConsumer, final int pageSize, final String sortField,
final SortOrder sortOrder, final Function<TElement, String> elementIdentifierFn) {
final QueryAll<TMethod, TResult, TElement, Void> queryAll = QueryAll.of(query, pageSize, sortField, sortOrder,
elementIdentifierFn);
return queryAll.run(pageConsumer);
}
}

0 comments on commit 21dba51

Please sign in to comment.