From 8362de2329663041b6059a923bd667c26b8ee855 Mon Sep 17 00:00:00 2001 From: Jens Schulze Date: Thu, 13 Jul 2023 08:26:41 +0200 Subject: [PATCH 1/6] add search expression builder --- .../api/models/Identifiable.java | 4 + .../api/models/SimpleIdentifiable.java | 17 + .../api/models/common/CurrencyUtils.java | 2 +- .../CategoryTermFilterExpression.java | 68 +++ .../search/products/ConstantExpression.java | 23 + .../search/products/ContainerExpression.java | 49 ++ .../products/ExistsTermFilterExpression.java | 28 ++ .../ExistsTermFilterExpressionBuilder.java | 25 + .../api/search/products/FacetExpression.java | 11 + .../api/search/products/FilterExpression.java | 8 + .../api/search/products/PathExpression.java | 56 +++ .../ProductFacetExpressionBuilder.java | 53 +++ .../ProductFilterExpressionBuilder.java | 419 +++++++++++++++++ .../api/search/products/RangeExpression.java | 37 ++ .../products/RangeFilterExpression.java | 91 ++++ .../RangeFilterExpressionBuilder.java | 44 ++ .../ReferenceableTermFilterExpression.java | 54 +++ .../search/products/TermFilterExpression.java | 84 ++++ .../products/TermFilterExpressionBuilder.java | 40 ++ .../api/search/products/TermFormatter.java | 63 +++ .../commercetools/api/search/SearchTests.java | 435 ++++++++++++++++++ 21 files changed, 1610 insertions(+), 1 deletion(-) create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/SimpleIdentifiable.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFilterExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ConstantExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ExistsTermFilterExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ExistsTermFilterExpressionBuilder.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FacetExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FilterExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/PathExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFilterExpressionBuilder.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFilterExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFilterExpressionBuilder.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ReferenceableTermFilterExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpressionBuilder.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFormatter.java create mode 100644 commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/Identifiable.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/Identifiable.java index 90d3734adff..185284d5137 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/Identifiable.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/Identifiable.java @@ -11,4 +11,8 @@ public interface Identifiable { * @return ID */ String getId(); + + static Identifiable of(final String id) { + return new SimpleIdentifiable<>(id); + } } diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/SimpleIdentifiable.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/SimpleIdentifiable.java new file mode 100644 index 00000000000..40738b3a9bb --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/SimpleIdentifiable.java @@ -0,0 +1,17 @@ + +package com.commercetools.api.models; + +public class SimpleIdentifiable implements Identifiable { + + private final String id; + + public SimpleIdentifiable(String id) { + this.id = id; + } + + @Override + public String getId() { + return id; + } + +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/common/CurrencyUtils.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/common/CurrencyUtils.java index 8e8cd0ceb06..1c3e0018d94 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/common/CurrencyUtils.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/models/common/CurrencyUtils.java @@ -8,7 +8,7 @@ import org.javamoney.moneta.spi.JDKCurrencyProvider; -final class CurrencyUtils { +public final class CurrencyUtils { static final JDKCurrencyProvider CURRENCY_PROVIDER = new JDKCurrencyProvider(); public static CurrencyUnit ofCode(final String currencyCode) { diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFilterExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFilterExpression.java new file mode 100644 index 00000000000..5c2d7eea311 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFilterExpression.java @@ -0,0 +1,68 @@ + +package com.commercetools.api.search.products; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import com.commercetools.api.models.Identifiable; +import com.commercetools.api.models.category.Category; + +public class CategoryTermFilterExpression extends TermFilterExpression { + + public CategoryTermFilterExpression(PathExpression expression) { + super(expression, TermFormatter::format); + } + + public CategoryTermFilterExpression(PathExpression expression, List term) { + super(expression, term, TermFormatter::format); + } + + public CategoryTermFilterExpression is(String value) { + List terms = Optional.ofNullable(terms()).map(term -> { + List expressions = new ArrayList<>(terms()); + expressions.add(formatter().apply(value)); + return expressions; + }).orElse(Collections.singletonList(formatter().apply(value))); + return new CategoryTermFilterExpression(expression(), terms); + } + + public CategoryTermFilterExpression is(Identifiable value) { + return is(value.getId()); + } + + public CategoryTermFilterExpression subTree(String value) { + List terms = Optional.ofNullable(terms()).map(term -> { + List expressions = new ArrayList<>(terms()); + expressions.add( + ContainerExpression.of().parent(ConstantExpression.of("subTree")).inner(formatter().apply(value))); + return expressions; + }) + .orElse(Collections.singletonList( + ContainerExpression.of().parent(ConstantExpression.of("subTree")).inner(formatter().apply(value)))); + return new CategoryTermFilterExpression(expression(), terms); + } + + public CategoryTermFilterExpression subTree(Identifiable value) { + return subTree(value.getId()); + } + + public CategoryTermFilterExpression isIn(Iterable values) { + List terms = Optional.ofNullable(terms()).map(term -> { + List expressions = new ArrayList<>(terms()); + values.forEach(v -> expressions.add(formatter().apply(v))); + return expressions; + }).orElse(StreamSupport.stream(values.spliterator(), false).map(formatter()).collect(Collectors.toList())); + + return new CategoryTermFilterExpression(expression(), terms); + } + + public CategoryTermFilterExpression containsAny(Iterable> values) { + return isIn( + StreamSupport.stream(values.spliterator(), false).map(Identifiable::getId).collect(Collectors.toList())); + } + + public static CategoryTermFilterExpression of(PathExpression expression) { + return new CategoryTermFilterExpression(expression); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ConstantExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ConstantExpression.java new file mode 100644 index 00000000000..196f23854f9 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ConstantExpression.java @@ -0,0 +1,23 @@ + +package com.commercetools.api.search.products; + +public class ConstantExpression implements FilterExpression { + private final String value; + + public ConstantExpression(String value) { + this.value = value; + } + + public String value() { + return value; + } + + @Override + public String render() { + return value; + } + + public static ConstantExpression of(String value) { + return new ConstantExpression(value); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java new file mode 100644 index 00000000000..4ebf1a720a0 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java @@ -0,0 +1,49 @@ + +package com.commercetools.api.search.products; + +import java.util.Objects; + +public class ContainerExpression implements FilterExpression { + private final FilterExpression parent; + + private final FilterExpression inner; + + public ContainerExpression() { + parent = null; + inner = null; + } + + public ContainerExpression(FilterExpression parent, FilterExpression inner) { + this.parent = parent; + this.inner = inner; + } + + public FilterExpression parent() { + return parent; + } + + public FilterExpression inner() { + return inner; + } + + public ContainerExpression parent(FilterExpression parent) { + return new ContainerExpression(parent, inner); + } + + public ContainerExpression inner(FilterExpression element) { + return new ContainerExpression(parent, element); + } + + @Override + public String render() { + return parent().render() + "(" + Objects.requireNonNull(inner).render() + ")"; + } + + public static ContainerExpression of() { + return new ContainerExpression(); + } + + public static ContainerExpression of(FilterExpression parent, FilterExpression inner) { + return new ContainerExpression(parent, inner); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ExistsTermFilterExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ExistsTermFilterExpression.java new file mode 100644 index 00000000000..c9a47c60bd0 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ExistsTermFilterExpression.java @@ -0,0 +1,28 @@ + +package com.commercetools.api.search.products; + +import java.util.*; + +public class ExistsTermFilterExpression extends TermFilterExpression { + + public ExistsTermFilterExpression(PathExpression expression) { + super(expression, TermFormatter::format); + } + + public ExistsTermFilterExpression(PathExpression expression, List term) { + super(expression, term, TermFormatter::format); + } + + public FilterExpression missing() { + return new ExistsTermFilterExpression(expression(), + Collections.singletonList(ConstantExpression.of("missing"))); + } + + public FilterExpression exists() { + return new ExistsTermFilterExpression(expression(), Collections.singletonList(ConstantExpression.of("exists"))); + } + + public static ExistsTermFilterExpression of(PathExpression expression) { + return new ExistsTermFilterExpression(expression); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ExistsTermFilterExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ExistsTermFilterExpressionBuilder.java new file mode 100644 index 00000000000..b180f34dbcc --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ExistsTermFilterExpressionBuilder.java @@ -0,0 +1,25 @@ + +package com.commercetools.api.search.products; + +import java.util.Collections; + +public class ExistsTermFilterExpressionBuilder implements FilterExpression { + + private final PathExpression expression; + + public ExistsTermFilterExpressionBuilder(PathExpression expression) { + this.expression = expression; + } + + public FilterExpression missing() { + return new ExistsTermFilterExpression(expression, Collections.singletonList(ConstantExpression.of("missing"))); + } + + public FilterExpression exists() { + return new ExistsTermFilterExpression(expression, Collections.singletonList(ConstantExpression.of("exists"))); + } + + public static ExistsTermFilterExpressionBuilder of(PathExpression expression) { + return new ExistsTermFilterExpressionBuilder(expression); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FacetExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FacetExpression.java new file mode 100644 index 00000000000..e6a7392d0a7 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FacetExpression.java @@ -0,0 +1,11 @@ + +package com.commercetools.api.search.products; + +public interface FacetExpression { + + FilterExpression expression(); + + Boolean countingProducts(); + + String alias(); +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FilterExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FilterExpression.java new file mode 100644 index 00000000000..e45bef295d6 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FilterExpression.java @@ -0,0 +1,8 @@ + +package com.commercetools.api.search.products; + +public interface FilterExpression { + default String render() { + return ""; + }; +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/PathExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/PathExpression.java new file mode 100644 index 00000000000..e6a69c968ea --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/PathExpression.java @@ -0,0 +1,56 @@ + +package com.commercetools.api.search.products; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class PathExpression implements FilterExpression { + private final List paths; + + public PathExpression() { + paths = new ArrayList<>(); + } + + public PathExpression(FilterExpression path) { + this.paths = Collections.singletonList(path); + } + + public PathExpression(List paths) { + this.paths = paths; + } + + public List paths() { + return paths; + } + + public PathExpression add(String element) { + List p = new ArrayList<>(paths); + p.add(ConstantExpression.of(element)); + return new PathExpression(p); + } + + public PathExpression add(FilterExpression element) { + List p = new ArrayList<>(paths); + p.add(element); + return new PathExpression(p); + } + + @Override + public String render() { + return paths.stream().map(FilterExpression::render).collect(Collectors.joining(".")); + } + + public static PathExpression of() { + return new PathExpression(); + } + + public static PathExpression of(String parent) { + return new PathExpression(ConstantExpression.of(parent)); + } + + public static PathExpression of(FilterExpression parent) { + return new PathExpression(parent); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java new file mode 100644 index 00000000000..89008147ebf --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java @@ -0,0 +1,53 @@ + +package com.commercetools.api.search.products; + +public class ProductFacetExpressionBuilder { + public CategoriesFacetExpressionBuilder categories() { + return new CategoriesFacetExpressionBuilder(); + } + + public ReviewRatingStatisticsFacetExpressionBuilder reviewRatingStatistics() { + return new ReviewRatingStatisticsFacetExpressionBuilder(); + } + + public static ProductFacetExpressionBuilder of() { + return new ProductFacetExpressionBuilder(); + } + + public static class CategoriesFacetExpressionBuilder { + + PathExpression expression; + + public CategoriesFacetExpressionBuilder() { + this.expression = PathExpression.of("categories"); + } + + public CategoryTermFilterExpression id() { + return CategoryTermFilterExpression.of(expression.add("id")); + } + } + + public static class ReviewRatingStatisticsFacetExpressionBuilder { + PathExpression expression; + + public ReviewRatingStatisticsFacetExpressionBuilder() { + this.expression = PathExpression.of("reviewRatingStatistics"); + } + + public TermFilterExpression averageRating() { + return TermFilterExpression.of(expression.add("averageRating"), TermFormatter::format); + } + + public TermFilterExpression highestRating() { + return TermFilterExpression.of(expression.add("highestRating"), TermFormatter::format); + } + + public TermFilterExpression lowestRating() { + return TermFilterExpression.of(expression.add("lowestRating"), TermFormatter::format); + } + + public TermFilterExpression count() { + return TermFilterExpression.of(expression.add("count"), TermFormatter::format); + } + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFilterExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFilterExpressionBuilder.java new file mode 100644 index 00000000000..d32dd5396f8 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFilterExpressionBuilder.java @@ -0,0 +1,419 @@ + +package com.commercetools.api.search.products; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.Locale; + +import javax.money.CurrencyUnit; + +import com.commercetools.api.models.Identifiable; +import com.commercetools.api.models.category.Category; +import com.commercetools.api.models.channel.Channel; +import com.commercetools.api.models.product_type.ProductType; +import com.commercetools.api.models.state.State; +import com.commercetools.api.models.tax_category.TaxCategory; + +public class ProductFilterExpressionBuilder { + + public CategoriesFilterExpressionBuilder categories() { + return new CategoriesFilterExpressionBuilder(); + } + + public ReferencableFilterExpressionBuilder productType() { + return new ReferencableFilterExpressionBuilder<>("productType"); + } + + public ReferencableFilterExpressionBuilder taxCategory() { + return new ReferencableFilterExpressionBuilder<>("taxCategory"); + } + + public ReferencableFilterExpressionBuilder state() { + return new ReferencableFilterExpressionBuilder<>("state"); + } + + public TermFilterExpressionBuilder key() { + return TermFilterExpressionBuilder.of(PathExpression.of("key"), TermFormatter::format); + } + + public VariantsFilterExpressionBuilder variants() { + return new VariantsFilterExpressionBuilder(); + } + + public ReviewRatingStatisticsFilterExpressionBuilder reviewRatingStatistics() { + return new ReviewRatingStatisticsFilterExpressionBuilder(); + } + + public RangeFilterExpressionBuilder createdAt() { + return RangeFilterExpressionBuilder.of(PathExpression.of("createdAt"), TermFormatter::format); + } + + public RangeFilterExpressionBuilder lastModifiedAt() { + return RangeFilterExpressionBuilder.of(PathExpression.of("lastModifiedAt"), TermFormatter::format); + } + + public TermFilterExpressionBuilder searchKeywords(Locale locale) { + return searchKeywords(locale.toLanguageTag()); + } + + public TermFilterExpressionBuilder searchKeywords(String locale) { + return TermFilterExpressionBuilder.of(PathExpression.of("searchKeywords").add(locale).add("text"), + TermFormatter::format); + } + + public static ProductFilterExpressionBuilder of() { + return new ProductFilterExpressionBuilder(); + } + + public static class CategoriesFilterExpressionBuilder { + + PathExpression expression; + + public CategoriesFilterExpressionBuilder() { + this.expression = PathExpression.of("categories"); + } + + public CategoryTermFilterExpressionBuilder id() { + return CategoryTermFilterExpressionBuilder.of(expression.add("id")); + } + + public FilterExpression exists() { + return new ExistsTermFilterExpression(expression).exists(); + } + + public FilterExpression missing() { + return new ExistsTermFilterExpression(expression).missing(); + } + } + + public static class ReferencableFilterExpressionBuilder { + + PathExpression expression; + + public ReferencableFilterExpressionBuilder(String path) { + this.expression = PathExpression.of(ConstantExpression.of(path)); + } + + public ReferencableFilterExpressionBuilder(PathExpression expression) { + this.expression = PathExpression.of(expression); + } + + public ReferencableTermFilterExpressionBuilder id() { + return ReferencableTermFilterExpressionBuilder.of(expression.add("id")); + } + + public FilterExpression exists() { + return new ExistsTermFilterExpression(expression).exists(); + } + + public FilterExpression missing() { + return new ExistsTermFilterExpression(expression).missing(); + } + } + + public static class VariantsFilterExpressionBuilder { + PathExpression expression; + + public VariantsFilterExpressionBuilder() { + this.expression = PathExpression.of("variants"); + } + + public TermFilterExpressionBuilder key() { + return TermFilterExpressionBuilder.of(expression.add("key"), TermFormatter::format); + } + + public TermFilterExpressionBuilder sku() { + return TermFilterExpressionBuilder.of(expression.add("sku"), TermFormatter::format); + } + + public PriceFilterExpressionBuilder price() { + return new PriceFilterExpressionBuilder(expression.add("price")); + } + + public TermFilterExpressionBuilder scopedPriceDiscounted() { + return TermFilterExpressionBuilder.of(expression.add("scopedPriceDiscounted"), TermFormatter::format); + } + + public ScopedPriceFilterExpressionBuilder scopedPrice() { + return new ScopedPriceFilterExpressionBuilder(expression.add("scopedPrice")); + } + + public ExistsTermFilterExpressionBuilder prices() { + return ExistsTermFilterExpressionBuilder.of(expression.add("prices")); + } + + public AvailabilityFilterExpressionBuilder availability() { + return new AvailabilityFilterExpressionBuilder(expression.add("availability")); + } + + public AttributeFilterExpressionBuilder attribute(String name) { + return new AttributeFilterExpressionBuilder(expression.add("attributes").add(name)); + } + } + + public static class ReviewRatingStatisticsFilterExpressionBuilder { + PathExpression expression; + + public ReviewRatingStatisticsFilterExpressionBuilder() { + this.expression = PathExpression.of("reviewRatingStatistics"); + } + + public TermFilterExpressionBuilder averageRating() { + return TermFilterExpressionBuilder.of(expression.add("averageRating"), TermFormatter::format); + } + + public TermFilterExpressionBuilder highestRating() { + return TermFilterExpressionBuilder.of(expression.add("highestRating"), TermFormatter::format); + } + + public TermFilterExpressionBuilder lowestRating() { + return TermFilterExpressionBuilder.of(expression.add("lowestRating"), TermFormatter::format); + } + + public TermFilterExpressionBuilder count() { + return TermFilterExpressionBuilder.of(expression.add("count"), TermFormatter::format); + } + } + + public static class AvailabilityFilterExpressionBuilder { + PathExpression expression; + + public AvailabilityFilterExpressionBuilder(PathExpression expression) { + this.expression = expression; + } + + public TermFilterExpressionBuilder isOnStock() { + return TermFilterExpressionBuilder.of(expression.add("isOnStock"), TermFormatter::format); + } + + public RangeFilterExpressionBuilder availableQuantity() { + return RangeFilterExpressionBuilder.of(expression.add("availableQuantity"), TermFormatter::format); + } + + public AvailabilityFilterExpressionBuilder channel(Identifiable channel) { + return channel(channel.getId()); + } + + public AvailabilityFilterExpressionBuilder channel(String channel) { + return new AvailabilityFilterExpressionBuilder(expression.add("channels").add(channel)); + } + + public ReferencableTermFilterExpressionBuilder isOnStockInChannels() { + return ReferencableTermFilterExpressionBuilder.of(expression.add("isOnStockInChannels")); + } + } + + public static class PriceFilterExpressionBuilder { + PathExpression expression; + + public PriceFilterExpressionBuilder(PathExpression expression) { + this.expression = expression; + } + + public RangeFilterExpressionBuilder centAmount() { + return RangeFilterExpressionBuilder.of(expression.add("centAmount"), TermFormatter::format); + } + + public TermFilterExpression currencyCode() { + return TermFilterExpression.of(expression.add("currencyCode"), TermFormatter::format); + } + + public TermFilterExpression currency() { + return TermFilterExpression.of(expression.add("currencyCode"), TermFormatter::format); + } + } + + public static class ScopedPriceFilterExpressionBuilder { + PathExpression expression; + + public ScopedPriceFilterExpressionBuilder(PathExpression expression) { + this.expression = PathExpression.of(expression); + } + + public PriceFilterExpressionBuilder value() { + return new PriceFilterExpressionBuilder(expression.add("value")); + } + + public PriceFilterExpressionBuilder currentValue() { + return new PriceFilterExpressionBuilder(expression.add("currentValue")); + } + + public DiscountedPriceFilterExpressionBuilder discounted() { + return new DiscountedPriceFilterExpressionBuilder(expression.add("discounted")); + } + + public TermFilterExpression currency() { + return TermFilterExpression.of(expression.add("currencyCode"), TermFormatter::format); + } + + } + + public static class DiscountedPriceFilterExpressionBuilder { + PathExpression expression; + + public DiscountedPriceFilterExpressionBuilder(PathExpression expression) { + this.expression = PathExpression.of(expression); + } + + public PriceFilterExpressionBuilder value() { + return new PriceFilterExpressionBuilder(expression.add("value")); + } + + } + + public static class CategoryTermFilterExpressionBuilder { + + PathExpression expression; + + public CategoryTermFilterExpressionBuilder(PathExpression expression) { + this.expression = expression; + } + + public CategoryTermFilterExpression is(String value) { + return CategoryTermFilterExpression.of(expression).is(value); + } + + public CategoryTermFilterExpression is(Identifiable value) { + return is(value.getId()); + } + + public CategoryTermFilterExpression subTree(String value) { + return CategoryTermFilterExpression.of(expression).subTree(value); + } + + public CategoryTermFilterExpression subTree(Identifiable value) { + return subTree(value.getId()); + } + + public CategoryTermFilterExpression isIn(Iterable values) { + return CategoryTermFilterExpression.of(expression).isIn(values); + } + + public CategoryTermFilterExpression containsAny(Iterable> values) { + return CategoryTermFilterExpression.of(expression).containsAny(values); + } + + public static CategoryTermFilterExpressionBuilder of(PathExpression expression) { + return new CategoryTermFilterExpressionBuilder(expression); + } + } + + public static class ReferencableTermFilterExpressionBuilder { + + PathExpression expression; + + public ReferencableTermFilterExpressionBuilder(PathExpression expression) { + this.expression = expression; + } + + public ReferenceableTermFilterExpression is(String value) { + return ReferenceableTermFilterExpression. of(expression).is(value); + } + + public ReferenceableTermFilterExpression is(Identifiable value) { + return is(value.getId()); + } + + public ReferenceableTermFilterExpression isIn(Iterable values) { + return ReferenceableTermFilterExpression. of(expression).isIn(values); + } + + public ReferenceableTermFilterExpression containsAny(Iterable> values) { + return ReferenceableTermFilterExpression. of(expression).containsAny(values); + } + + public static ReferencableTermFilterExpressionBuilder of(PathExpression expression) { + return new ReferencableTermFilterExpressionBuilder<>(expression); + } + } + + public static class AttributeFilterExpressionBuilder { + PathExpression expression; + + public AttributeFilterExpressionBuilder(PathExpression expression) { + this.expression = expression; + } + + public FilterExpression exists() { + return new ExistsTermFilterExpression(expression).exists(); + } + + public FilterExpression missing() { + return new ExistsTermFilterExpression(expression).missing(); + } + + public TermFilterExpressionBuilder asBoolean() { + return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + } + + public TermFilterExpressionBuilder asLong() { + return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + } + + public TermFilterExpressionBuilder asDouble() { + return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + } + + public TermFilterExpressionBuilder asString() { + return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + } + + public TermFilterExpressionBuilder asDateTime() { + return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + } + + public TermFilterExpressionBuilder asDate() { + return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + } + + public TermFilterExpressionBuilder asTime() { + return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + } + + public PriceFilterExpressionBuilder asMoney() { + return new PriceFilterExpressionBuilder(expression); + } + + public AttributeReferenceTermFilterExpressionBuilder asReference() { + return new AttributeReferenceTermFilterExpressionBuilder(expression); + } + + public EnumTermFilterExpressionBuilder asEnum() { + return new EnumTermFilterExpressionBuilder(expression); + } + } + + public static class EnumTermFilterExpressionBuilder { + PathExpression expression; + + public EnumTermFilterExpressionBuilder(PathExpression expression) { + this.expression = PathExpression.of(expression); + } + + public TermFilterExpressionBuilder key() { + return new TermFilterExpressionBuilder<>(expression.add("key"), TermFormatter::format); + } + } + + public static class AttributeReferenceTermFilterExpressionBuilder { + + PathExpression expression; + + public AttributeReferenceTermFilterExpressionBuilder(PathExpression expression) { + this.expression = PathExpression.of(expression); + } + + public ReferencableTermFilterExpressionBuilder id() { + return new ReferencableFilterExpressionBuilder<>(expression).id(); + } + + public TermFilterExpressionBuilder typeId() { + return TermFilterExpressionBuilder.of(expression.add("typeId"), TermFormatter::format); + } + + public static AttributeReferenceTermFilterExpressionBuilder of(PathExpression expression) { + return new AttributeReferenceTermFilterExpressionBuilder(expression); + } + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeExpression.java new file mode 100644 index 00000000000..c2006f121de --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeExpression.java @@ -0,0 +1,37 @@ + +package com.commercetools.api.search.products; + +public class RangeExpression implements FilterExpression { + private final FilterExpression from; + private final FilterExpression to; + + public RangeExpression(FilterExpression from, FilterExpression to) { + this.from = from; + this.to = to; + } + + public FilterExpression from() { + return from; + } + + public FilterExpression to() { + return to; + } + + @Override + public String render() { + return String.format("(%s to %s)", from.render(), to.render()); + } + + public static RangeExpression from(FilterExpression from) { + return new RangeExpression(from, ConstantExpression.of("*")); + } + + public static RangeExpression to(FilterExpression to) { + return new RangeExpression(ConstantExpression.of("*"), to); + } + + public static RangeExpression of(FilterExpression from, FilterExpression to) { + return new RangeExpression(from, to); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFilterExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFilterExpression.java new file mode 100644 index 00000000000..8d36e8eca5d --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFilterExpression.java @@ -0,0 +1,91 @@ + +package com.commercetools.api.search.products; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class RangeFilterExpression implements FilterExpression { + + private final PathExpression expression; + + private final List ranges; + + private final Function formatter; + + public RangeFilterExpression(PathExpression expression, Function formatter) { + this.expression = expression; + this.ranges = null; + this.formatter = formatter; + } + + public RangeFilterExpression(PathExpression expression, List ranges, + Function formatter) { + this.expression = expression; + this.ranges = ranges; + this.formatter = formatter; + } + + PathExpression expression() { + return expression; + } + + List ranges() { + return ranges; + } + + Function formatter() { + return formatter; + } + + public RangeFilterExpression rangeFrom(T from) { + List terms = Optional.ofNullable(ranges).map(rangeList -> { + List expressions = new ArrayList<>(rangeList); + expressions.add(RangeExpression.from(formatter().apply(from))); + return expressions; + }).orElse(Collections.singletonList(RangeExpression.from(formatter().apply(from)))); + return RangeFilterExpression.of(expression, terms, formatter); + } + + public RangeFilterExpression rangeTo(T to) { + List terms = Optional.ofNullable(ranges).map(rangeList -> { + List expressions = new ArrayList<>(rangeList); + expressions.add(RangeExpression.to(formatter().apply(to))); + return expressions; + }).orElse(Collections.singletonList(RangeExpression.to(formatter().apply(to)))); + return RangeFilterExpression.of(expression, terms, formatter); + } + + public RangeFilterExpression range(T from, T to) { + List terms = Optional.ofNullable(ranges).map(rangeList -> { + List expressions = new ArrayList<>(rangeList); + expressions.add(RangeExpression.of(formatter().apply(from), formatter().apply(to))); + return expressions; + }).orElse(Collections.singletonList(RangeExpression.of(formatter().apply(from), formatter().apply(to)))); + return RangeFilterExpression.of(expression, terms, formatter); + } + + public static RangeFilterExpression of(PathExpression expression, Function formatter) { + return new RangeFilterExpression<>(expression, formatter); + } + + public static RangeFilterExpression of(PathExpression expression, List ranges, + Function formatter) { + return new RangeFilterExpression<>(expression, ranges, formatter); + } + + public FilterExpression exists() { + return new ExistsTermFilterExpression(expression).exists(); + } + + public FilterExpression missing() { + return new ExistsTermFilterExpression(expression).missing(); + } + + @Override + public String render() { + Objects.requireNonNull(ranges); + return expression.render() + ": range " + + ranges.stream().map(FilterExpression::render).collect(Collectors.joining(", ")); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFilterExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFilterExpressionBuilder.java new file mode 100644 index 00000000000..c77ece9288d --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFilterExpressionBuilder.java @@ -0,0 +1,44 @@ + +package com.commercetools.api.search.products; + +import java.util.function.Function; + +public class RangeFilterExpressionBuilder { + private final PathExpression expression; + private final Function formatter; + + public RangeFilterExpressionBuilder() { + this.expression = null; + this.formatter = null; + } + + public RangeFilterExpressionBuilder(PathExpression expression, Function formatter) { + this.expression = expression; + this.formatter = formatter; + } + + public TermFilterExpression is(T value) { + return TermFilterExpression.of(expression, formatter).is(value); + } + + public TermFilterExpression isIn(Iterable values) { + return TermFilterExpression.of(expression, formatter).isIn(values); + } + + public RangeFilterExpression rangeFrom(T from) { + return RangeFilterExpression.of(expression, formatter).rangeFrom(from); + } + + public RangeFilterExpression rangeTo(T to) { + return RangeFilterExpression.of(expression, formatter).rangeTo(to); + } + + public RangeFilterExpression range(T from, T to) { + return RangeFilterExpression.of(expression, formatter).range(from, to); + } + + public static RangeFilterExpressionBuilder of(PathExpression expression, + Function formatter) { + return new RangeFilterExpressionBuilder<>(expression, formatter); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ReferenceableTermFilterExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ReferenceableTermFilterExpression.java new file mode 100644 index 00000000000..b9bf74e1bc1 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ReferenceableTermFilterExpression.java @@ -0,0 +1,54 @@ + +package com.commercetools.api.search.products; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import com.commercetools.api.models.Identifiable; + +public class ReferenceableTermFilterExpression extends TermFilterExpression { + + public ReferenceableTermFilterExpression(PathExpression expression) { + super(expression, TermFormatter::format); + } + + public ReferenceableTermFilterExpression(PathExpression expression, List term) { + super(expression, term, TermFormatter::format); + } + + public ReferenceableTermFilterExpression is(String value) { + List terms = Optional.ofNullable(terms()).map(term -> { + List expressions = new ArrayList<>(terms()); + expressions.add(formatter().apply(value)); + return expressions; + }).orElse(Collections.singletonList(formatter().apply(value))); + return new ReferenceableTermFilterExpression<>(expression(), terms); + } + + public ReferenceableTermFilterExpression is(Identifiable value) { + return is(value.getId()); + } + + public ReferenceableTermFilterExpression isIn(Iterable values) { + List terms = Optional.ofNullable(terms()).map(term -> { + List expressions = new ArrayList<>(terms()); + values.forEach(v -> expressions.add(formatter().apply(v))); + return expressions; + }).orElse(StreamSupport.stream(values.spliterator(), false).map(formatter()).collect(Collectors.toList())); + + return new ReferenceableTermFilterExpression(expression(), terms); + } + + public ReferenceableTermFilterExpression containsAny(Iterable> values) { + return isIn( + StreamSupport.stream(values.spliterator(), false).map(Identifiable::getId).collect(Collectors.toList())); + } + + public static ReferenceableTermFilterExpression of(PathExpression expression) { + return new ReferenceableTermFilterExpression(expression); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpression.java new file mode 100644 index 00000000000..9c5c96ef74a --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpression.java @@ -0,0 +1,84 @@ + +package com.commercetools.api.search.products; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class TermFilterExpression implements FilterExpression { + + private final PathExpression expression; + + private final List terms; + + private final Function formatter; + + public TermFilterExpression(PathExpression expression, Function formatter) { + this.expression = expression; + this.terms = null; + this.formatter = formatter; + } + + public TermFilterExpression(PathExpression expression, List terms, + Function formatter) { + this.expression = expression; + this.terms = terms; + this.formatter = formatter; + } + + PathExpression expression() { + return expression; + } + + List terms() { + return terms; + } + + Function formatter() { + return formatter; + } + + public TermFilterExpression is(T value) { + List terms = Optional.ofNullable(terms()).map(termList -> { + List expressions = new ArrayList<>(termList); + expressions.add(formatter().apply(value)); + return expressions; + }).orElse(Collections.singletonList(formatter().apply(value))); + return TermFilterExpression.of(expression, terms, formatter); + } + + public TermFilterExpression isIn(Iterable values) { + List terms = Optional.ofNullable(terms()).map(termList -> { + List expressions = new ArrayList<>(termList); + values.forEach(v -> expressions.add(formatter().apply(v))); + return expressions; + }).orElse(StreamSupport.stream(values.spliterator(), false).map(formatter()).collect(Collectors.toList())); + + return TermFilterExpression.of(expression, terms, formatter); + } + + public static TermFilterExpression of(PathExpression expression, Function formatter) { + return new TermFilterExpression<>(expression, formatter); + } + + public static TermFilterExpression of(PathExpression expression, List terms, + Function formatter) { + return new TermFilterExpression<>(expression, terms, formatter); + } + + public FilterExpression exists() { + return new ExistsTermFilterExpression(expression()).exists(); + } + + public FilterExpression missing() { + return new ExistsTermFilterExpression(expression()).missing(); + } + + @Override + public String render() { + Objects.requireNonNull(terms); + return expression.render() + ": " + + terms.stream().map(FilterExpression::render).collect(Collectors.joining(", ")); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpressionBuilder.java new file mode 100644 index 00000000000..7d1c688c5b9 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpressionBuilder.java @@ -0,0 +1,40 @@ + +package com.commercetools.api.search.products; + +import java.util.function.Function; + +public class TermFilterExpressionBuilder { + private final PathExpression expression; + private final Function formatter; + + public TermFilterExpressionBuilder() { + this.expression = null; + this.formatter = null; + } + + public TermFilterExpressionBuilder(PathExpression expression, Function formatter) { + this.expression = expression; + this.formatter = formatter; + } + + public TermFilterExpression is(T value) { + return TermFilterExpression.of(expression, formatter).is(value); + } + + public TermFilterExpression isIn(Iterable values) { + return TermFilterExpression.of(expression, formatter).isIn(values); + } + + public FilterExpression exists() { + return new ExistsTermFilterExpression(expression).exists(); + } + + public FilterExpression missing() { + return new ExistsTermFilterExpression(expression).missing(); + } + + public static TermFilterExpressionBuilder of(PathExpression expression, + Function formatter) { + return new TermFilterExpressionBuilder<>(expression, formatter); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFormatter.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFormatter.java new file mode 100644 index 00000000000..5db3b23c77b --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFormatter.java @@ -0,0 +1,63 @@ + +package com.commercetools.api.search.products; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; + +import javax.money.CurrencyUnit; + +import com.commercetools.api.models.Identifiable; + +public class TermFormatter { + private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder().appendInstant(3).toFormatter(); + + public static ConstantExpression format(Boolean value) { + return ConstantExpression.of(String.format("%b", value)); + } + + public static ConstantExpression format(LocalDate value) { + return ConstantExpression.of(String.format("\"%s\"", value.format(DateTimeFormatter.ISO_LOCAL_DATE))); + } + + public static ConstantExpression format(ZonedDateTime value) { + return ConstantExpression + .of(String.format("\"%s\"", FORMATTER.format(value.withZoneSameInstant(ZoneOffset.UTC)))); + } + + public static ConstantExpression format(Double value) { + return ConstantExpression.of(value.toString()); + } + + public static ConstantExpression format(Long value) { + return ConstantExpression.of(String.format("%d", value)); + } + + public static ConstantExpression format(String value) { + return ConstantExpression.of(String.format("\"%s\"", escape(value))); + } + + public static ConstantExpression format(LocalTime value) { + return ConstantExpression.of(String.format("\"%s\"", value.format(DateTimeFormatter.ISO_LOCAL_TIME))); + } + + public static FilterExpression format(Identifiable value) { + return ConstantExpression.of(String.format("\"%s\"", escape(value.getId()))); + } + + public static FilterExpression format(CurrencyUnit currencyUnit) { + return ConstantExpression.of(String.format("\"%s\"", escape(currencyUnit.getCurrencyCode()))); + } + + /** + * Internal: Escapes Strings like that (Scala notation) """query by name " test name""" + * @param s the unescaped String + * @return the escaped string + */ + static String escape(final String s) { + return s.replace("\"", "\\\""); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java new file mode 100644 index 00000000000..339d5f2f5b0 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java @@ -0,0 +1,435 @@ + +package com.commercetools.api.search; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.Locale; + +import com.commercetools.api.models.Identifiable; +import com.commercetools.api.models.common.DefaultCurrencyUnits; +import com.commercetools.api.search.products.*; +import com.tngtech.junit.dataprovider.DataProvider; +import com.tngtech.junit.dataprovider.DataProviderExtension; +import com.tngtech.junit.dataprovider.UseDataProvider; +import com.tngtech.junit.dataprovider.UseDataProviderExtension; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.TestTemplate; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(UseDataProviderExtension.class) +@ExtendWith(DataProviderExtension.class) +public class SearchTests { + + @TestTemplate + @UseDataProvider("filterExpressions") + public void filterRender(FilterExpression searchExpression, String expectedPredicate) { + Assertions.assertThat(searchExpression.render()).isEqualTo(expectedPredicate); + } + + @TestTemplate + @UseDataProvider("facetExpressions") + public void facetRender(FilterExpression searchExpression, String expectedPredicate) { + Assertions.assertThat(searchExpression.render()).isEqualTo(expectedPredicate); + } + + @DataProvider + public static Object[][] filterExpressions() { + return new Object[][] { new Object[] { ProductFilterExpressionBuilder.of().key().is("foo"), "key: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().key().exists(), "key: exists" }, + new Object[] { ProductFilterExpressionBuilder.of().key().missing(), "key: missing" }, + new Object[] { ProductFilterExpressionBuilder.of().categories().id().is("foo"), + "categories.id: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().categories().id().is("foo").is("bar"), + "categories.id: \"foo\", \"bar\"" }, + new Object[] { ProductFilterExpressionBuilder.of().categories().id().subTree("foo"), + "categories.id: subTree(\"foo\")" }, + new Object[] { ProductFilterExpressionBuilder.of().categories().id().is("foo").subTree("bar"), + "categories.id: \"foo\", subTree(\"bar\")" }, + new Object[] { ProductFilterExpressionBuilder.of().categories().id().is("foo").subTree("bar").is("baz"), + "categories.id: \"foo\", subTree(\"bar\"), \"baz\"" }, + new Object[] { ProductFilterExpressionBuilder.of().categories().exists(), "categories: exists" }, + new Object[] { ProductFilterExpressionBuilder.of().categories().missing(), "categories: missing" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().key().is("foo"), + "variants.key: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().key().is("foo").is("bar"), + "variants.key: \"foo\", \"bar\"" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().key().exists(), "variants.key: exists" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().key().missing(), + "variants.key: missing" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().sku().is("foo"), + "variants.sku: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().sku().is("foo").is("bar"), + "variants.sku: \"foo\", \"bar\"" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().sku().exists(), "variants.sku: exists" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().sku().missing(), + "variants.sku: missing" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().prices().exists(), + "variants.prices: exists" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().prices().missing(), + "variants.prices: missing" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().price().centAmount().is(100L), + "variants.price.centAmount: 100" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().price().centAmount().rangeTo(100L), + "variants.price.centAmount: range (* to 100)" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().price().centAmount().rangeFrom(100L), + "variants.price.centAmount: range (100 to *)" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().price().centAmount().range(100L, 200L), + "variants.price.centAmount: range (100 to 200)" }, + new Object[] { + ProductFilterExpressionBuilder.of() + .variants() + .price() + .centAmount() + .rangeTo(100L) + .range(100L, 200L) + .rangeFrom(200L), + "variants.price.centAmount: range (* to 100), (100 to 200), (200 to *)" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().price().currencyCode().is("EUR"), + "variants.price.currencyCode: \"EUR\"" }, + new Object[] { + ProductFilterExpressionBuilder.of().variants().price().currency().is(DefaultCurrencyUnits.EUR), + "variants.price.currencyCode: \"EUR\"" }, + new Object[] { + ProductFilterExpressionBuilder.of().variants().scopedPrice().value().centAmount().is(100L), + "variants.scopedPrice.value.centAmount: 100" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .scopedPrice() + .currentValue() + .centAmount() + .is(100L), "variants.scopedPrice.currentValue.centAmount: 100" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .scopedPrice() + .discounted() + .value() + .centAmount() + .is(100L), "variants.scopedPrice.discounted.value.centAmount: 100" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().scopedPriceDiscounted().is(true), + "variants.scopedPriceDiscounted: true" }, + new Object[] { ProductFilterExpressionBuilder.of().productType().id().is("foo"), + "productType.id: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().taxCategory().id().is("foo"), + "taxCategory.id: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().taxCategory().exists(), "taxCategory: exists" }, + new Object[] { ProductFilterExpressionBuilder.of().taxCategory().missing(), "taxCategory: missing" }, + new Object[] { ProductFilterExpressionBuilder.of().state().id().is("foo"), "state.id: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().state().exists(), "state: exists" }, + new Object[] { ProductFilterExpressionBuilder.of().state().missing(), "state: missing" }, + new Object[] { ProductFilterExpressionBuilder.of().reviewRatingStatistics().averageRating().is(100L), + "reviewRatingStatistics.averageRating: 100" }, + new Object[] { ProductFilterExpressionBuilder.of().reviewRatingStatistics().lowestRating().is(100L), + "reviewRatingStatistics.lowestRating: 100" }, + new Object[] { ProductFilterExpressionBuilder.of().reviewRatingStatistics().highestRating().is(100L), + "reviewRatingStatistics.highestRating: 100" }, + new Object[] { ProductFilterExpressionBuilder.of().reviewRatingStatistics().count().is(100L), + "reviewRatingStatistics.count: 100" }, + new Object[] { + ProductFilterExpressionBuilder.of() + .createdAt() + .range(ZonedDateTime.parse("2015-06-04T12:27:55.344Z"), + ZonedDateTime.parse("2015-06-04T13:27:55.344Z")), + "createdAt: range (\"2015-06-04T12:27:55.344Z\" to \"2015-06-04T13:27:55.344Z\")" }, + new Object[] { + ProductFilterExpressionBuilder.of() + .lastModifiedAt() + .range(ZonedDateTime.parse("2015-06-04T12:27:55.344Z"), + ZonedDateTime.parse("2015-06-04T13:27:55.344Z")), + "lastModifiedAt: range (\"2015-06-04T12:27:55.344Z\" to \"2015-06-04T13:27:55.344Z\")" }, + new Object[] { ProductFilterExpressionBuilder.of().searchKeywords("en-US").is("foo"), + "searchKeywords.en-US.text: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().searchKeywords(Locale.US).is("foo"), + "searchKeywords.en-US.text: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().availability().isOnStock().is(true), + "variants.availability.isOnStock: true" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .availability() + .availableQuantity() + .range(100L, 200L), "variants.availability.availableQuantity: range (100 to 200)" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .availability() + .channel(Identifiable.of("foo")) + .isOnStock() + .is(true), "variants.availability.channels.foo.isOnStock: true" }, + new Object[] { + ProductFilterExpressionBuilder.of() + .variants() + .availability() + .channel(Identifiable.of("foo")) + .availableQuantity() + .range(100L, 200L), + "variants.availability.channels.foo.availableQuantity: range (100 to 200)" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .availability() + .channel("foo") + .isOnStock() + .is(true), "variants.availability.channels.foo.isOnStock: true" }, + new Object[] { + ProductFilterExpressionBuilder.of() + .variants() + .availability() + .channel("foo") + .availableQuantity() + .range(100L, 200L), + "variants.availability.channels.foo.availableQuantity: range (100 to 200)" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .availability() + .isOnStockInChannels() + .is("foo") + .is("bar"), "variants.availability.isOnStockInChannels: \"foo\", \"bar\"" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .availability() + .isOnStockInChannels() + .is(Identifiable.of("foo")) + .is("bar"), "variants.availability.isOnStockInChannels: \"foo\", \"bar\"" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").exists(), + "variants.attributes.foo: exists" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").missing(), + "variants.attributes.foo: missing" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asBoolean().is(true), + "variants.attributes.foo: true" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asLong().is(100L), + "variants.attributes.foo: 100" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asDouble().is(100.0), + "variants.attributes.foo: 100.0" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asDouble().is(100.0), + "variants.attributes.foo: 100.0" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asString().is("bar"), + "variants.attributes.foo: \"bar\"" }, + new Object[] { + ProductFilterExpressionBuilder.of() + .variants() + .attribute("foo") + .asDateTime() + .is(ZonedDateTime.parse("2015-06-04T12:27:55.344Z")), + "variants.attributes.foo: \"2015-06-04T12:27:55.344Z\"" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .attribute("foo") + .asDate() + .is(LocalDate.parse("2022-03-21")), "variants.attributes.foo: \"2022-03-21\"" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .attribute("foo") + .asTime() + .is(LocalTime.parse("12:34:32")), "variants.attributes.foo: \"12:34:32\"" }, + new Object[] { + ProductFilterExpressionBuilder.of().variants().attribute("foo").asMoney().centAmount().is(100L), + "variants.attributes.foo.centAmount: 100" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .attribute("foo") + .asMoney() + .currencyCode() + .is("EUR"), "variants.attributes.foo.currencyCode: \"EUR\"" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .attribute("foo") + .asMoney() + .currency() + .is(DefaultCurrencyUnits.EUR), "variants.attributes.foo.currencyCode: \"EUR\"" }, + new Object[] { + ProductFilterExpressionBuilder.of().variants().attribute("foo").asReference().id().is("foo"), + "variants.attributes.foo.id: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of() + .variants() + .attribute("foo") + .asReference() + .typeId() + .is("foo"), "variants.attributes.foo.typeId: \"foo\"" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asEnum().key().is("foo"), + "variants.attributes.foo.key: \"foo\"" }, + + }; + } + + @DataProvider + public static Object[][] facetExpressions() { + return new Object[][] { + new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo"), + "categories.id: \"foo\"" }, + new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo").is("bar"), + "categories.id: \"foo\", \"bar\"" }, + new Object[] { ProductFacetExpressionBuilder.of().categories().id().subTree("foo"), + "categories.id: subTree(\"foo\")" }, + new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo").subTree("bar"), + "categories.id: \"foo\", subTree(\"bar\")" }, + new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo").subTree("bar").is("baz"), + "categories.id: \"foo\", subTree(\"bar\"), \"baz\"" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().key().is("foo"), + // "variants.key: \"foo\"" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().key().is("foo").is("bar"), + // "variants.key: \"foo\", \"bar\"" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().key().exists(), "variants.key: exists" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().key().missing(), + // "variants.key: missing" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().sku().is("foo"), + // "variants.sku: \"foo\"" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().sku().is("foo").is("bar"), + // "variants.sku: \"foo\", \"bar\"" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().sku().exists(), "variants.sku: exists" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().sku().missing(), + // "variants.sku: missing" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().prices().exists(), + // "variants.prices: exists" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().prices().missing(), + // "variants.prices: missing" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().price().centAmount().is(100L), + // "variants.price.centAmount: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().price().centAmount().rangeTo(100L), + // "variants.price.centAmount: range (* to 100)" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().price().centAmount().rangeFrom(100L), + // "variants.price.centAmount: range (100 to *)" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().price().centAmount().range(100L, 200L), + // "variants.price.centAmount: range (100 to 200)" }, + // new Object[] { + // ProductFacetExpressionBuilder.of() + // .variants() + // .price() + // .centAmount() + // .rangeTo(100L) + // .range(100L, 200L) + // .rangeFrom(200L), + // "variants.price.centAmount: range (* to 100), (100 to 200), (200 to *)" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().price().currencyCode().is("EUR"), + // "variants.price.currencyCode: \"EUR\"" }, + // new Object[] { + // ProductFacetExpressionBuilder.of().variants().price().currency().is(DefaultCurrencyUnits.EUR), + // "variants.price.currencyCode: \"EUR\"" }, + // new Object[] { + // ProductFacetExpressionBuilder.of().variants().scopedPrice().value().centAmount().is(100L), + // "variants.scopedPrice.value.centAmount: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .scopedPrice() + // .currentValue() + // .centAmount() + // .is(100L), "variants.scopedPrice.currentValue.centAmount: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .scopedPrice() + // .discounted() + // .value() + // .centAmount() + // .is(100L), "variants.scopedPrice.discounted.value.centAmount: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().scopedPriceDiscounted().is(true), + // "variants.scopedPriceDiscounted: true" }, + // new Object[] { ProductFacetExpressionBuilder.of().reviewRatingStatistics().averageRating().is(100L), + // "reviewRatingStatistics.averageRating: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of().reviewRatingStatistics().lowestRating().is(100L), + // "reviewRatingStatistics.lowestRating: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of().reviewRatingStatistics().highestRating().is(100L), + // "reviewRatingStatistics.highestRating: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of().reviewRatingStatistics().count().is(100L), + // "reviewRatingStatistics.count: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().availability().isOnStock().is(true), + // "variants.availability.isOnStock: true" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .availability() + // .availableQuantity() + // .range(100L, 200L), "variants.availability.availableQuantity: range (100 to 200)" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .availability() + // .channel(Identifiable.of("foo")) + // .isOnStock() + // .is(true), "variants.availability.channels.foo.isOnStock: true" }, + // new Object[] { + // ProductFacetExpressionBuilder.of() + // .variants() + // .availability() + // .channel(Identifiable.of("foo")) + // .availableQuantity() + // .range(100L, 200L), + // "variants.availability.channels.foo.availableQuantity: range (100 to 200)" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .availability() + // .channel("foo") + // .isOnStock() + // .is(true), "variants.availability.channels.foo.isOnStock: true" }, + // new Object[] { + // ProductFacetExpressionBuilder.of() + // .variants() + // .availability() + // .channel("foo") + // .availableQuantity() + // .range(100L, 200L), + // "variants.availability.channels.foo.availableQuantity: range (100 to 200)" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .availability() + // .isOnStockInChannels() + // .is("foo") + // .is("bar"), "variants.availability.isOnStockInChannels: \"foo\", \"bar\"" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .availability() + // .isOnStockInChannels() + // .is(Identifiable.of("foo")) + // .is("bar"), "variants.availability.isOnStockInChannels: \"foo\", \"bar\"" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asBoolean().is(true), + // "variants.attributes.foo: true" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asLong().is(100L), + // "variants.attributes.foo: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asDouble().is(100.0), + // "variants.attributes.foo: 100.0" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asDouble().is(100.0), + // "variants.attributes.foo: 100.0" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asString().is("bar"), + // "variants.attributes.foo: \"bar\"" }, + // new Object[] { + // ProductFacetExpressionBuilder.of() + // .variants() + // .attribute("foo") + // .asDateTime() + // .is(ZonedDateTime.parse("2015-06-04T12:27:55.344Z")), + // "variants.attributes.foo: \"2015-06-04T12:27:55.344Z\"" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .attribute("foo") + // .asDate() + // .is(LocalDate.parse("2022-03-21")), "variants.attributes.foo: \"2022-03-21\"" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .attribute("foo") + // .asTime() + // .is(LocalTime.parse("12:34:32")), "variants.attributes.foo: \"12:34:32\"" }, + // new Object[] { + // ProductFacetExpressionBuilder.of().variants().attribute("foo").asMoney().centAmount().is(100L), + // "variants.attributes.foo.centAmount: 100" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .attribute("foo") + // .asMoney() + // .currencyCode() + // .is("EUR"), "variants.attributes.foo.currencyCode: \"EUR\"" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .attribute("foo") + // .asMoney() + // .currency() + // .is(DefaultCurrencyUnits.EUR), "variants.attributes.foo.currencyCode: \"EUR\"" }, + // new Object[] { + // ProductFacetExpressionBuilder.of().variants().attribute("foo").asReference().id().is("foo"), + // "variants.attributes.foo.id: \"foo\"" }, + // new Object[] { ProductFacetExpressionBuilder.of() + // .variants() + // .attribute("foo") + // .asReference() + // .typeId() + // .is("foo"), "variants.attributes.foo.typeId: \"foo\"" }, + // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asEnum().key().is("foo"), + // "variants.attributes.foo.key: \"foo\"" }, + + }; + } +} From 2c5df28d9c0162c2d02472144a588877c8adc508 Mon Sep 17 00:00:00 2001 From: Jens Schulze Date: Thu, 11 Jan 2024 10:47:35 +0100 Subject: [PATCH 2/6] WIP --- .../products/CategoryTermFacetExpression.java | 71 +++++++ .../search/products/ContainerExpression.java | 37 +++- .../ProductFacetExpressionBuilder.java | 96 ++++++++- .../search/products/TermFacetExpression.java | 83 ++++++++ .../commercetools/api/search/SearchTests.java | 191 +++--------------- 5 files changed, 303 insertions(+), 175 deletions(-) create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFacetExpression.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFacetExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFacetExpression.java new file mode 100644 index 00000000000..a988b4b233d --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFacetExpression.java @@ -0,0 +1,71 @@ + +package com.commercetools.api.search.products; + +import com.commercetools.api.models.Identifiable; +import com.commercetools.api.models.category.Category; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class CategoryTermFacetExpression extends TermFacetExpression { + + public CategoryTermFacetExpression(PathExpression expression) { + super(expression, TermFormatter::format); + } + + public CategoryTermFacetExpression(PathExpression expression, List term) { + super(expression, term, TermFormatter::format); + } + + public CategoryTermFacetExpression is(String value) { + List terms = Optional.ofNullable(terms()).map(term -> { + List expressions = new ArrayList<>(terms()); + expressions.add(formatter().apply(value)); + return expressions; + }).orElse(Collections.singletonList(formatter().apply(value))); + return new CategoryTermFacetExpression(expression(), terms); + } + + public CategoryTermFacetExpression is(Identifiable value) { + return is(value.getId()); + } + + public CategoryTermFacetExpression subTree(String value) { + List terms = Optional.ofNullable(terms()).map(term -> { + List expressions = new ArrayList<>(terms()); + expressions.add( + ContainerExpression.of().parent(ConstantExpression.of("subTree")).inner(formatter().apply(value))); + return expressions; + }) + .orElse(Collections.singletonList( + ContainerExpression.of().parent(ConstantExpression.of("subTree")).inner(formatter().apply(value)))); + return new CategoryTermFacetExpression(expression(), terms); + } + + public CategoryTermFacetExpression subTree(Identifiable value) { + return subTree(value.getId()); + } + + public CategoryTermFacetExpression isIn(Iterable values) { + List terms = Optional.ofNullable(terms()).map(term -> { + List expressions = new ArrayList<>(terms()); + values.forEach(v -> expressions.add(formatter().apply(v))); + return expressions; + }).orElse(StreamSupport.stream(values.spliterator(), false).map(formatter()).collect(Collectors.toList())); + + return new CategoryTermFacetExpression(expression(), terms); + } + + public CategoryTermFacetExpression containsAny(Iterable> values) { + return isIn( + StreamSupport.stream(values.spliterator(), false).map(Identifiable::getId).collect(Collectors.toList())); + } + + public static CategoryTermFacetExpression of(PathExpression expression) { + return new CategoryTermFacetExpression(expression); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java index 4ebf1a720a0..8d2b931a16f 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java @@ -8,14 +8,24 @@ public class ContainerExpression implements FilterExpression { private final FilterExpression inner; + private final boolean renderInnerWithoutParentheses; + public ContainerExpression() { parent = null; inner = null; + renderInnerWithoutParentheses = false; + } + + public ContainerExpression(final FilterExpression parent, final FilterExpression inner) { + this.parent = parent; + this.inner = inner; + this.renderInnerWithoutParentheses = false; } - public ContainerExpression(FilterExpression parent, FilterExpression inner) { + public ContainerExpression(final FilterExpression parent, final FilterExpression inner, final boolean renderInnerWithoutParentheses) { this.parent = parent; this.inner = inner; + this.renderInnerWithoutParentheses = renderInnerWithoutParentheses; } public FilterExpression parent() { @@ -26,16 +36,27 @@ public FilterExpression inner() { return inner; } - public ContainerExpression parent(FilterExpression parent) { - return new ContainerExpression(parent, inner); + public boolean renderInnerWithOutParentheses() { + return renderInnerWithoutParentheses; } - public ContainerExpression inner(FilterExpression element) { - return new ContainerExpression(parent, element); + public ContainerExpression parent(final FilterExpression parent) { + return new ContainerExpression(parent, inner, renderInnerWithoutParentheses); + } + + public ContainerExpression inner(final FilterExpression element) { + return new ContainerExpression(parent, element, renderInnerWithoutParentheses); + } + + public ContainerExpression renderInnerWithOutParentheses(final boolean renderInnerWithoutParentheses) { + return new ContainerExpression(parent, inner, renderInnerWithoutParentheses); } @Override public String render() { + if (renderInnerWithoutParentheses) { + return parent().render() + " " + Objects.requireNonNull(inner).render(); + } return parent().render() + "(" + Objects.requireNonNull(inner).render() + ")"; } @@ -43,7 +64,11 @@ public static ContainerExpression of() { return new ContainerExpression(); } - public static ContainerExpression of(FilterExpression parent, FilterExpression inner) { + public static ContainerExpression of(final FilterExpression parent, final FilterExpression inner) { return new ContainerExpression(parent, inner); } + + public static ContainerExpression of(final FilterExpression parent, final FilterExpression inner, final boolean renderInnerWithoutParentheses) { + return new ContainerExpression(parent, inner, renderInnerWithoutParentheses); + } } diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java index 89008147ebf..51d393dfaab 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java @@ -1,11 +1,19 @@ package com.commercetools.api.search.products; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZonedDateTime; + public class ProductFacetExpressionBuilder { public CategoriesFacetExpressionBuilder categories() { return new CategoriesFacetExpressionBuilder(); } + public VariantsFacetExpressionBuilder variants() { + return new VariantsFacetExpressionBuilder(); + } + public ReviewRatingStatisticsFacetExpressionBuilder reviewRatingStatistics() { return new ReviewRatingStatisticsFacetExpressionBuilder(); } @@ -22,8 +30,8 @@ public CategoriesFacetExpressionBuilder() { this.expression = PathExpression.of("categories"); } - public CategoryTermFilterExpression id() { - return CategoryTermFilterExpression.of(expression.add("id")); + public CategoryTermFacetExpression id() { + return CategoryTermFacetExpression.of(expression.add("id")); } } @@ -50,4 +58,88 @@ public TermFilterExpression count() { return TermFilterExpression.of(expression.add("count"), TermFormatter::format); } } + + public static class VariantsFacetExpressionBuilder { + + PathExpression expression; + + public VariantsFacetExpressionBuilder() { + this.expression = PathExpression.of("variants"); + } + + public AttributesFacetExpressionBuilder attribute() { + return new AttributesFacetExpressionBuilder(expression); + } + } + + public static class AttributesFacetExpressionBuilder { + PathExpression expression; + + public AttributesFacetExpressionBuilder(PathExpression expression) { + this.expression = expression.add("attributes"); + } + + public TermFacetExpression ofText(final String name) { + return TermFacetExpression.of(expression.add(name), TermFormatter::format); + } + public TermFacetExpression ofDate(final String name) { + return TermFacetExpression.of(expression.add(name), TermFormatter::format); + } + public TermFacetExpression ofTime(final String name) { + return TermFacetExpression.of(expression.add(name), TermFormatter::format); + } + public TermFacetExpression ofDatetime(final String name) { + return TermFacetExpression.of(expression.add(name), TermFormatter::format); + } + public TermFacetExpression ofBool(final String name) { + return TermFacetExpression.of(expression.add(name), TermFormatter::format); + } + public TermFacetExpression ofNumber(final String name) { + return TermFacetExpression.of(expression.add(name), TermFormatter::format); + } + public TermFacetExpression ofLong(final String name) { + return TermFacetExpression.of(expression.add(name), TermFormatter::format); + } + + public TermFacetExpression ofLocalizedString(final String name, final String language) { + return TermFacetExpression.of(expression.add(name).add(language), TermFormatter::format); + } + + public AttributesEnumFacetExpressionBuilder ofEnum(final String name) { + return new AttributesEnumFacetExpressionBuilder(expression); + } + } + + public static class AttributesEnumFacetExpressionBuilder { + PathExpression expression; + + public AttributesEnumFacetExpressionBuilder(final PathExpression expression) { + this.expression = expression; + } + + public TermFacetExpression key() { + return TermFacetExpression.of(expression.add("key"), TermFormatter::format); + } + public TermFacetExpression label() { + return TermFacetExpression.of(expression.add("label"), TermFormatter::format); + } + public TermFacetExpression label(final String language) { + return TermFacetExpression.of(expression.add("label").add(language), TermFormatter::format); + } + } + + public static class AttributesMoneyFacetExpressionBuilder { + PathExpression expression; + + public AttributesMoneyFacetExpressionBuilder(final PathExpression expression) { + this.expression = expression; + } + + public TermFacetExpression centAmount() { + return TermFacetExpression.of(expression.add("centAmount"), TermFormatter::format); + } + public TermFacetExpression currencyCode() { + return TermFacetExpression.of(expression.add("currencyCode"), TermFormatter::format); + } + } } diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java new file mode 100644 index 00000000000..b3388a0a199 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java @@ -0,0 +1,83 @@ + +package com.commercetools.api.search.products; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class TermFacetExpression implements FilterExpression { + + private final PathExpression expression; + + private final List terms; + + private final Function formatter; + + public TermFacetExpression(PathExpression expression, Function formatter) { + this.expression = expression; + this.terms = null; + this.formatter = formatter; + } + + public TermFacetExpression(PathExpression expression, List terms, + Function formatter) { + this.expression = expression; + this.terms = terms; + this.formatter = formatter; + } + + PathExpression expression() { + return expression; + } + + List terms() { + return terms; + } + + Function formatter() { + return formatter; + } + + public FilterExpression alias(final String alias) + { + return ContainerExpression.of(this, ConstantExpression.of(String.format("as %s", alias)), true); + } + + public TermFacetExpression is(T value) { + List terms = Optional.ofNullable(terms()).map(termList -> { + List expressions = new ArrayList<>(termList); + expressions.add(formatter().apply(value)); + return expressions; + }).orElse(Collections.singletonList(formatter().apply(value))); + return TermFacetExpression.of(expression, terms, formatter); + } + + public TermFacetExpression isIn(Iterable values) { + List terms = Optional.ofNullable(terms()).map(termList -> { + List expressions = new ArrayList<>(termList); + values.forEach(v -> expressions.add(formatter().apply(v))); + return expressions; + }).orElse(StreamSupport.stream(values.spliterator(), false).map(formatter()).collect(Collectors.toList())); + + return TermFacetExpression.of(expression, terms, formatter); + } + + public static TermFacetExpression of(PathExpression expression, Function formatter) { + return new TermFacetExpression<>(expression, formatter); + } + + public static TermFacetExpression of(PathExpression expression, List terms, + Function formatter) { + return new TermFacetExpression<>(expression, terms, formatter); + } + + @Override + public String render() { + if (terms == null || terms.isEmpty()) { + return expression.render(); + } + return expression.render() + ": " + + terms.stream().map(FilterExpression::render).collect(Collectors.joining(", ")); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java index 339d5f2f5b0..5c41173de1e 100644 --- a/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java +++ b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java @@ -253,6 +253,10 @@ public static Object[][] filterExpressions() { @DataProvider public static Object[][] facetExpressions() { return new Object[][] { + new Object[] { ProductFacetExpressionBuilder.of().categories().id().alias("cat"), + "categories.id as cat" }, + new Object[] { ProductFacetExpressionBuilder.of().categories().id(), + "categories.id" }, new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo"), "categories.id: \"foo\"" }, new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo").is("bar"), @@ -263,173 +267,26 @@ public static Object[][] facetExpressions() { "categories.id: \"foo\", subTree(\"bar\")" }, new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo").subTree("bar").is("baz"), "categories.id: \"foo\", subTree(\"bar\"), \"baz\"" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().key().is("foo"), - // "variants.key: \"foo\"" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().key().is("foo").is("bar"), - // "variants.key: \"foo\", \"bar\"" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().key().exists(), "variants.key: exists" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().key().missing(), - // "variants.key: missing" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().sku().is("foo"), - // "variants.sku: \"foo\"" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().sku().is("foo").is("bar"), - // "variants.sku: \"foo\", \"bar\"" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().sku().exists(), "variants.sku: exists" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().sku().missing(), - // "variants.sku: missing" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().prices().exists(), - // "variants.prices: exists" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().prices().missing(), - // "variants.prices: missing" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().price().centAmount().is(100L), - // "variants.price.centAmount: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().price().centAmount().rangeTo(100L), - // "variants.price.centAmount: range (* to 100)" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().price().centAmount().rangeFrom(100L), - // "variants.price.centAmount: range (100 to *)" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().price().centAmount().range(100L, 200L), - // "variants.price.centAmount: range (100 to 200)" }, - // new Object[] { - // ProductFacetExpressionBuilder.of() - // .variants() - // .price() - // .centAmount() - // .rangeTo(100L) - // .range(100L, 200L) - // .rangeFrom(200L), - // "variants.price.centAmount: range (* to 100), (100 to 200), (200 to *)" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().price().currencyCode().is("EUR"), - // "variants.price.currencyCode: \"EUR\"" }, - // new Object[] { - // ProductFacetExpressionBuilder.of().variants().price().currency().is(DefaultCurrencyUnits.EUR), - // "variants.price.currencyCode: \"EUR\"" }, - // new Object[] { - // ProductFacetExpressionBuilder.of().variants().scopedPrice().value().centAmount().is(100L), - // "variants.scopedPrice.value.centAmount: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .scopedPrice() - // .currentValue() - // .centAmount() - // .is(100L), "variants.scopedPrice.currentValue.centAmount: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .scopedPrice() - // .discounted() - // .value() - // .centAmount() - // .is(100L), "variants.scopedPrice.discounted.value.centAmount: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().scopedPriceDiscounted().is(true), - // "variants.scopedPriceDiscounted: true" }, - // new Object[] { ProductFacetExpressionBuilder.of().reviewRatingStatistics().averageRating().is(100L), - // "reviewRatingStatistics.averageRating: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of().reviewRatingStatistics().lowestRating().is(100L), - // "reviewRatingStatistics.lowestRating: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of().reviewRatingStatistics().highestRating().is(100L), - // "reviewRatingStatistics.highestRating: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of().reviewRatingStatistics().count().is(100L), - // "reviewRatingStatistics.count: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().availability().isOnStock().is(true), - // "variants.availability.isOnStock: true" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .availability() - // .availableQuantity() - // .range(100L, 200L), "variants.availability.availableQuantity: range (100 to 200)" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .availability() - // .channel(Identifiable.of("foo")) - // .isOnStock() - // .is(true), "variants.availability.channels.foo.isOnStock: true" }, - // new Object[] { - // ProductFacetExpressionBuilder.of() - // .variants() - // .availability() - // .channel(Identifiable.of("foo")) - // .availableQuantity() - // .range(100L, 200L), - // "variants.availability.channels.foo.availableQuantity: range (100 to 200)" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .availability() - // .channel("foo") - // .isOnStock() - // .is(true), "variants.availability.channels.foo.isOnStock: true" }, - // new Object[] { - // ProductFacetExpressionBuilder.of() - // .variants() - // .availability() - // .channel("foo") - // .availableQuantity() - // .range(100L, 200L), - // "variants.availability.channels.foo.availableQuantity: range (100 to 200)" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .availability() - // .isOnStockInChannels() - // .is("foo") - // .is("bar"), "variants.availability.isOnStockInChannels: \"foo\", \"bar\"" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .availability() - // .isOnStockInChannels() - // .is(Identifiable.of("foo")) - // .is("bar"), "variants.availability.isOnStockInChannels: \"foo\", \"bar\"" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asBoolean().is(true), - // "variants.attributes.foo: true" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asLong().is(100L), - // "variants.attributes.foo: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asDouble().is(100.0), - // "variants.attributes.foo: 100.0" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asDouble().is(100.0), - // "variants.attributes.foo: 100.0" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asString().is("bar"), - // "variants.attributes.foo: \"bar\"" }, - // new Object[] { - // ProductFacetExpressionBuilder.of() - // .variants() - // .attribute("foo") - // .asDateTime() - // .is(ZonedDateTime.parse("2015-06-04T12:27:55.344Z")), - // "variants.attributes.foo: \"2015-06-04T12:27:55.344Z\"" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .attribute("foo") - // .asDate() - // .is(LocalDate.parse("2022-03-21")), "variants.attributes.foo: \"2022-03-21\"" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .attribute("foo") - // .asTime() - // .is(LocalTime.parse("12:34:32")), "variants.attributes.foo: \"12:34:32\"" }, - // new Object[] { - // ProductFacetExpressionBuilder.of().variants().attribute("foo").asMoney().centAmount().is(100L), - // "variants.attributes.foo.centAmount: 100" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .attribute("foo") - // .asMoney() - // .currencyCode() - // .is("EUR"), "variants.attributes.foo.currencyCode: \"EUR\"" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .attribute("foo") - // .asMoney() - // .currency() - // .is(DefaultCurrencyUnits.EUR), "variants.attributes.foo.currencyCode: \"EUR\"" }, - // new Object[] { - // ProductFacetExpressionBuilder.of().variants().attribute("foo").asReference().id().is("foo"), - // "variants.attributes.foo.id: \"foo\"" }, - // new Object[] { ProductFacetExpressionBuilder.of() - // .variants() - // .attribute("foo") - // .asReference() - // .typeId() - // .is("foo"), "variants.attributes.foo.typeId: \"foo\"" }, - // new Object[] { ProductFacetExpressionBuilder.of().variants().attribute("foo").asEnum().key().is("foo"), - // "variants.attributes.foo.key: \"foo\"" }, - + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofText("test"), + "variants.attributes.test" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofText("test").is("foo"), + "variants.attributes.test: \"foo\"" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofText("test").is("foo").is("bar"), + "variants.attributes.test: \"foo\", \"bar\"" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDate("test"), + "variants.attributes.test" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDate("test").is(LocalDate.parse("2024-01-03")), + "variants.attributes.test: \"2024-01-03\"" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDate("test").is(LocalDate.parse("2024-01-03")).is(LocalDate.parse("2024-01-04")), + "variants.attributes.test: \"2024-01-03\", \"2024-01-04\"" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofTime("test"), + "variants.attributes.test" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofTime("test").is(LocalTime.parse("10:00")), + "variants.attributes.test: \"10:00:00\"" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofTime("test").is(LocalTime.parse("10:00")).is(LocalTime.parse("11:00")), + "variants.attributes.test: \"10:00:00\", \"11:00:00\"" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDatetime("test").is(ZonedDateTime.parse("2024-01-03T10:00Z")), + "variants.attributes.test: \"2024-01-03T10:00:00.000Z\"" }, }; } } From fe3109627392ebdb95186b899f08b00e54d84506 Mon Sep 17 00:00:00 2001 From: "ct-sdks[bot]" <153784748+ct-sdks[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 09:50:14 +0000 Subject: [PATCH 3/6] spotless: Fix code style --- .../products/CategoryTermFacetExpression.java | 6 +-- .../search/products/ContainerExpression.java | 6 ++- .../ProductFacetExpressionBuilder.java | 9 ++++ .../search/products/TermFacetExpression.java | 7 ++- .../commercetools/api/search/SearchTests.java | 46 +++++++++++++------ 5 files changed, 52 insertions(+), 22 deletions(-) diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFacetExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFacetExpression.java index a988b4b233d..634f9d3eb79 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFacetExpression.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/CategoryTermFacetExpression.java @@ -1,9 +1,6 @@ package com.commercetools.api.search.products; -import com.commercetools.api.models.Identifiable; -import com.commercetools.api.models.category.Category; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -11,6 +8,9 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import com.commercetools.api.models.Identifiable; +import com.commercetools.api.models.category.Category; + public class CategoryTermFacetExpression extends TermFacetExpression { public CategoryTermFacetExpression(PathExpression expression) { diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java index 8d2b931a16f..08418bb6fbb 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ContainerExpression.java @@ -22,7 +22,8 @@ public ContainerExpression(final FilterExpression parent, final FilterExpression this.renderInnerWithoutParentheses = false; } - public ContainerExpression(final FilterExpression parent, final FilterExpression inner, final boolean renderInnerWithoutParentheses) { + public ContainerExpression(final FilterExpression parent, final FilterExpression inner, + final boolean renderInnerWithoutParentheses) { this.parent = parent; this.inner = inner; this.renderInnerWithoutParentheses = renderInnerWithoutParentheses; @@ -68,7 +69,8 @@ public static ContainerExpression of(final FilterExpression parent, final Filter return new ContainerExpression(parent, inner); } - public static ContainerExpression of(final FilterExpression parent, final FilterExpression inner, final boolean renderInnerWithoutParentheses) { + public static ContainerExpression of(final FilterExpression parent, final FilterExpression inner, + final boolean renderInnerWithoutParentheses) { return new ContainerExpression(parent, inner, renderInnerWithoutParentheses); } } diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java index 51d393dfaab..6ad1e8e9c66 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java @@ -82,21 +82,27 @@ public AttributesFacetExpressionBuilder(PathExpression expression) { public TermFacetExpression ofText(final String name) { return TermFacetExpression.of(expression.add(name), TermFormatter::format); } + public TermFacetExpression ofDate(final String name) { return TermFacetExpression.of(expression.add(name), TermFormatter::format); } + public TermFacetExpression ofTime(final String name) { return TermFacetExpression.of(expression.add(name), TermFormatter::format); } + public TermFacetExpression ofDatetime(final String name) { return TermFacetExpression.of(expression.add(name), TermFormatter::format); } + public TermFacetExpression ofBool(final String name) { return TermFacetExpression.of(expression.add(name), TermFormatter::format); } + public TermFacetExpression ofNumber(final String name) { return TermFacetExpression.of(expression.add(name), TermFormatter::format); } + public TermFacetExpression ofLong(final String name) { return TermFacetExpression.of(expression.add(name), TermFormatter::format); } @@ -120,9 +126,11 @@ public AttributesEnumFacetExpressionBuilder(final PathExpression expression) { public TermFacetExpression key() { return TermFacetExpression.of(expression.add("key"), TermFormatter::format); } + public TermFacetExpression label() { return TermFacetExpression.of(expression.add("label"), TermFormatter::format); } + public TermFacetExpression label(final String language) { return TermFacetExpression.of(expression.add("label").add(language), TermFormatter::format); } @@ -138,6 +146,7 @@ public AttributesMoneyFacetExpressionBuilder(final PathExpression expression) { public TermFacetExpression centAmount() { return TermFacetExpression.of(expression.add("centAmount"), TermFormatter::format); } + public TermFacetExpression currencyCode() { return TermFacetExpression.of(expression.add("currencyCode"), TermFormatter::format); } diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java index b3388a0a199..27f993a7712 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java @@ -21,7 +21,7 @@ public TermFacetExpression(PathExpression expression, Function terms, - Function formatter) { + Function formatter) { this.expression = expression; this.terms = terms; this.formatter = formatter; @@ -39,8 +39,7 @@ Function formatter() { return formatter; } - public FilterExpression alias(final String alias) - { + public FilterExpression alias(final String alias) { return ContainerExpression.of(this, ConstantExpression.of(String.format("as %s", alias)), true); } @@ -68,7 +67,7 @@ public static TermFacetExpression of(PathExpression expression, Function< } public static TermFacetExpression of(PathExpression expression, List terms, - Function formatter) { + Function formatter) { return new TermFacetExpression<>(expression, terms, formatter); } diff --git a/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java index 5c41173de1e..e4424b60d78 100644 --- a/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java +++ b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java @@ -255,8 +255,7 @@ public static Object[][] facetExpressions() { return new Object[][] { new Object[] { ProductFacetExpressionBuilder.of().categories().id().alias("cat"), "categories.id as cat" }, - new Object[] { ProductFacetExpressionBuilder.of().categories().id(), - "categories.id" }, + new Object[] { ProductFacetExpressionBuilder.of().categories().id(), "categories.id" }, new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo"), "categories.id: \"foo\"" }, new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo").is("bar"), @@ -271,22 +270,43 @@ public static Object[][] facetExpressions() { "variants.attributes.test" }, new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofText("test").is("foo"), "variants.attributes.test: \"foo\"" }, - new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofText("test").is("foo").is("bar"), + new Object[] { + ProductFacetExpressionBuilder.of().variants().attribute().ofText("test").is("foo").is("bar"), "variants.attributes.test: \"foo\", \"bar\"" }, new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDate("test"), "variants.attributes.test" }, - new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDate("test").is(LocalDate.parse("2024-01-03")), - "variants.attributes.test: \"2024-01-03\"" }, - new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDate("test").is(LocalDate.parse("2024-01-03")).is(LocalDate.parse("2024-01-04")), + new Object[] { ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofDate("test") + .is(LocalDate.parse("2024-01-03")), "variants.attributes.test: \"2024-01-03\"" }, + new Object[] { + ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofDate("test") + .is(LocalDate.parse("2024-01-03")) + .is(LocalDate.parse("2024-01-04")), "variants.attributes.test: \"2024-01-03\", \"2024-01-04\"" }, new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofTime("test"), "variants.attributes.test" }, - new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofTime("test").is(LocalTime.parse("10:00")), - "variants.attributes.test: \"10:00:00\"" }, - new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofTime("test").is(LocalTime.parse("10:00")).is(LocalTime.parse("11:00")), - "variants.attributes.test: \"10:00:00\", \"11:00:00\"" }, - new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDatetime("test").is(ZonedDateTime.parse("2024-01-03T10:00Z")), - "variants.attributes.test: \"2024-01-03T10:00:00.000Z\"" }, - }; + new Object[] { ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofTime("test") + .is(LocalTime.parse("10:00")), "variants.attributes.test: \"10:00:00\"" }, + new Object[] { ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofTime("test") + .is(LocalTime.parse("10:00")) + .is(LocalTime.parse("11:00")), "variants.attributes.test: \"10:00:00\", \"11:00:00\"" }, + new Object[] { + ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofDatetime("test") + .is(ZonedDateTime.parse("2024-01-03T10:00Z")), + "variants.attributes.test: \"2024-01-03T10:00:00.000Z\"" }, }; } } From 38c118e292465405289986df4f4df46864de3d2b Mon Sep 17 00:00:00 2001 From: "ct-sdks[bot]" <153784748+ct-sdks[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 09:50:15 +0000 Subject: [PATCH 4/6] spotless: add commit to blame ignore revs file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 4365e4f6f83..9792c1acf43 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -3,3 +3,4 @@ a8ec45c8ea4ba559247b654d01b0d35b21a68865 430a1a0a5dd4efe78e21526c37bec9dbce036401 + From 8b154cf740c20081cc609a7792941368f8dfbde1 Mon Sep 17 00:00:00 2001 From: Jens Schulze Date: Tue, 23 Jan 2024 09:58:45 +0100 Subject: [PATCH 5/6] WIP search builder --- api-java-mixin.raml | 3 + ...ProjectKeyProductProjectionsSearchGet.java | 3 +- ...ctKeyProductProjectionsSearchGetMixin.java | 47 +++++++ ...tKeyProductProjectionsSearchPostMixin.java | 33 +++++ .../api/search/products/FacetExpression.java | 8 +- .../ProductFacetExpressionBuilder.java | 78 +++++++++++- .../search/products/RangeFacetExpression.java | 119 ++++++++++++++++++ .../search/products/TermFacetExpression.java | 64 +++++++--- .../commercetools/api/search/SearchTests.java | 91 +++++++++++++- 9 files changed, 423 insertions(+), 23 deletions(-) create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchGetMixin.java create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFacetExpression.java diff --git a/api-java-mixin.raml b/api-java-mixin.raml index e30fe47bd6f..5ebce4f350c 100644 --- a/api-java-mixin.raml +++ b/api-java-mixin.raml @@ -1602,6 +1602,9 @@ types: (java-implements): 'ByProjectKeyProductProjectionsGetMixin' /search: + get: + (java-implements): + 'ByProjectKeyProductProjectionsSearchGetMixin' post: (java-implements): 'ByProjectKeyProductProjectionsSearchPostMixin' /product-selections: diff --git a/commercetools/commercetools-sdk-java-api/src/main/java-generated/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchGet.java b/commercetools/commercetools-sdk-java-api/src/main/java-generated/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchGet.java index 2b4335266f1..e9e612c75de 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java-generated/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchGet.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java-generated/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchGet.java @@ -37,7 +37,8 @@ @Generated(value = "io.vrap.rmf.codegen.rendering.CoreCodeGenerator", comments = "https://github.com/commercetools/rmf-codegen") public class ByProjectKeyProductProjectionsSearchGet extends TypeApiMethod - implements com.commercetools.api.client.SortableTrait, + implements ByProjectKeyProductProjectionsSearchGetMixin, + com.commercetools.api.client.SortableTrait, com.commercetools.api.client.PagingTrait, com.commercetools.api.client.ProjectionselectingTrait, com.commercetools.api.client.PriceselectingTrait, diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchGetMixin.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchGetMixin.java new file mode 100644 index 00000000000..3cfbccf3c21 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchGetMixin.java @@ -0,0 +1,47 @@ + +package com.commercetools.api.client; + +import java.util.function.Function; + +import com.commercetools.api.search.products.FacetExpression; +import com.commercetools.api.search.products.FilterExpression; +import com.commercetools.api.search.products.ProductFacetExpressionBuilder; +import com.commercetools.api.search.products.ProductFilterExpressionBuilder; + +public interface ByProjectKeyProductProjectionsSearchGetMixin { + public ByProjectKeyProductProjectionsSearchGet addFilter(final TValue filter); + + public ByProjectKeyProductProjectionsSearchGet addFilterFacets(final TValue filterFacets); + + public ByProjectKeyProductProjectionsSearchGet addFilterQuery(final TValue filterQuery); + + public ByProjectKeyProductProjectionsSearchGet addFacet(final TValue facet); + + default ProductFilterExpressionBuilder filterDsl() { + return new ProductFilterExpressionBuilder(); + } + + default ProductFacetExpressionBuilder facetDsl() { + return new ProductFacetExpressionBuilder(); + } + + default ByProjectKeyProductProjectionsSearchGet filter( + Function fn) { + return addFilter(fn.apply(filterDsl()).render()); + } + + default ByProjectKeyProductProjectionsSearchGet filterFacets( + Function fn) { + return addFilterFacets(fn.apply(filterDsl()).render()); + } + + default ByProjectKeyProductProjectionsSearchGet filterQuery( + Function fn) { + return addFilterQuery(fn.apply(filterDsl()).render()); + } + + default ByProjectKeyProductProjectionsSearchGet facet( + Function> fn) { + return addFacet(fn.apply(facetDsl()).render()); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchPostMixin.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchPostMixin.java index a1d7bef5bce..a55a394414e 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchPostMixin.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/client/ByProjectKeyProductProjectionsSearchPostMixin.java @@ -7,6 +7,11 @@ import java.util.function.Supplier; import java.util.stream.Collectors; +import com.commercetools.api.search.products.FacetExpression; +import com.commercetools.api.search.products.FilterExpression; +import com.commercetools.api.search.products.ProductFacetExpressionBuilder; +import com.commercetools.api.search.products.ProductFilterExpressionBuilder; + import io.vrap.rmf.base.client.ApiMethod; public interface ByProjectKeyProductProjectionsSearchPostMixin { @@ -1654,4 +1659,32 @@ public default ByProjectKeyProductProjectionsSearchPost addText(final S .map(s -> new ApiMethod.ParamEntry<>(placeholderName, s.toString())) .collect(Collectors.toList())); } + + default ProductFilterExpressionBuilder filterDsl() { + return new ProductFilterExpressionBuilder(); + } + + default ProductFacetExpressionBuilder facetDsl() { + return new ProductFacetExpressionBuilder(); + } + + default ByProjectKeyProductProjectionsSearchPost filter( + Function fn) { + return addFilter(fn.apply(filterDsl()).render()); + } + + default ByProjectKeyProductProjectionsSearchPost filterFacets( + Function fn) { + return addFilterFacets(fn.apply(filterDsl()).render()); + } + + default ByProjectKeyProductProjectionsSearchPost filterQuery( + Function fn) { + return addFilterQuery(fn.apply(filterDsl()).render()); + } + + default ByProjectKeyProductProjectionsSearchPost facet( + Function> fn) { + return addFacet(fn.apply(facetDsl()).render()); + } } diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FacetExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FacetExpression.java index e6a7392d0a7..ceba69da100 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FacetExpression.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/FacetExpression.java @@ -1,11 +1,9 @@ package com.commercetools.api.search.products; -public interface FacetExpression { +public interface FacetExpression extends FilterExpression { - FilterExpression expression(); + FacetExpression countingProducts(); - Boolean countingProducts(); - - String alias(); + FacetExpression alias(String alias); } diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java index 6ad1e8e9c66..e1aed6c1df2 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java @@ -4,6 +4,12 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.ZonedDateTime; +import java.util.Locale; + +import javax.money.CurrencyUnit; + +import com.commercetools.api.models.Identifiable; +import com.commercetools.api.models.channel.Channel; public class ProductFacetExpressionBuilder { public CategoriesFacetExpressionBuilder categories() { @@ -70,6 +76,62 @@ public VariantsFacetExpressionBuilder() { public AttributesFacetExpressionBuilder attribute() { return new AttributesFacetExpressionBuilder(expression); } + + public AvailabilityFacetExpressionBuilder availability() { + return new AvailabilityFacetExpressionBuilder(expression.add("availability")); + } + + public PriceFacetExpressionBuilder price() { + return new PriceFacetExpressionBuilder(expression.add("price")); + } + + public PriceFacetExpressionBuilder scopedPrice() { + return new PriceFacetExpressionBuilder(expression.add("price")); + } + } + + public static class ScopedPriceFacetExpressionBuilder { + PathExpression expression; + + public ScopedPriceFacetExpressionBuilder(PathExpression expression) { + this.expression = expression; + } + + public PriceFacetExpressionBuilder currentValue() { + return new PriceFacetExpressionBuilder(expression.add("currentValue")); + } + } + + public static class PriceFacetExpressionBuilder { + PathExpression expression; + + public PriceFacetExpressionBuilder(PathExpression expression) { + this.expression = expression; + } + + public RangeFilterExpressionBuilder centAmount() { + return RangeFilterExpressionBuilder.of(expression.add("centAmount"), TermFormatter::format); + } + } + + public static class AvailabilityFacetExpressionBuilder { + PathExpression expression; + + public AvailabilityFacetExpressionBuilder(PathExpression expression) { + this.expression = expression; + } + + public RangeFilterExpressionBuilder availableQuantity() { + return RangeFilterExpressionBuilder.of(expression.add("availableQuantity"), TermFormatter::format); + } + + public AvailabilityFacetExpressionBuilder channel(Identifiable channel) { + return channel(channel.getId()); + } + + public AvailabilityFacetExpressionBuilder channel(String channel) { + return new AvailabilityFacetExpressionBuilder(expression.add("channels").add(channel)); + } } public static class AttributesFacetExpressionBuilder { @@ -112,7 +174,11 @@ public TermFacetExpression ofLocalizedString(final String name, final St } public AttributesEnumFacetExpressionBuilder ofEnum(final String name) { - return new AttributesEnumFacetExpressionBuilder(expression); + return new AttributesEnumFacetExpressionBuilder(expression.add(name)); + } + + public AttributesMoneyFacetExpressionBuilder ofMoney(final String name) { + return new AttributesMoneyFacetExpressionBuilder(expression.add(name)); } } @@ -134,6 +200,10 @@ public TermFacetExpression label() { public TermFacetExpression label(final String language) { return TermFacetExpression.of(expression.add("label").add(language), TermFormatter::format); } + + public TermFacetExpression label(final Locale locale) { + return TermFacetExpression.of(expression.add("label").add(locale.toLanguageTag()), TermFormatter::format); + } } public static class AttributesMoneyFacetExpressionBuilder { @@ -143,12 +213,16 @@ public AttributesMoneyFacetExpressionBuilder(final PathExpression expression) { this.expression = expression; } - public TermFacetExpression centAmount() { + public TermFacetExpression centAmount() { return TermFacetExpression.of(expression.add("centAmount"), TermFormatter::format); } public TermFacetExpression currencyCode() { return TermFacetExpression.of(expression.add("currencyCode"), TermFormatter::format); } + + public TermFacetExpression currency() { + return TermFacetExpression.of(expression.add("currencyCode"), TermFormatter::format); + } } } diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFacetExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFacetExpression.java new file mode 100644 index 00000000000..8b7d3690aa0 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFacetExpression.java @@ -0,0 +1,119 @@ + +package com.commercetools.api.search.products; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class RangeFacetExpression implements FacetExpression { + + private final PathExpression expression; + + private final List ranges; + + private final Function formatter; + + private final boolean countingProducts; + + private final String alias; + + RangeFacetExpression(final PathExpression expression, final Function formatter) { + this.expression = expression; + this.ranges = null; + this.formatter = formatter; + this.alias = null; + this.countingProducts = false; + } + + RangeFacetExpression(final PathExpression expression, final List ranges, + final Function formatter) { + this.expression = expression; + this.ranges = ranges; + this.formatter = formatter; + this.alias = null; + this.countingProducts = false; + } + + RangeFacetExpression(final PathExpression expression, final List ranges, + final Function formatter, final String alias, final boolean countingProducts) { + this.expression = expression; + this.ranges = ranges; + this.formatter = formatter; + this.alias = alias; + this.countingProducts = countingProducts; + } + + PathExpression expression() { + return expression; + } + + List ranges() { + return ranges; + } + + Function formatter() { + return formatter; + } + + public RangeFacetExpression alias(final String alias) { + return new RangeFacetExpression<>(expression, ranges, formatter, alias, countingProducts); + } + + public RangeFacetExpression countingProducts() { + return new RangeFacetExpression<>(expression, ranges, formatter, alias, true); + } + + public RangeFacetExpression rangeFrom(T from) { + List terms = Optional.ofNullable(ranges).map(rangeList -> { + List expressions = new ArrayList<>(rangeList); + expressions.add(RangeExpression.from(formatter().apply(from))); + return expressions; + }).orElse(Collections.singletonList(RangeExpression.from(formatter().apply(from)))); + return new RangeFacetExpression<>(expression, terms, formatter, alias, countingProducts); + } + + public RangeFacetExpression rangeTo(T to) { + List terms = Optional.ofNullable(ranges).map(rangeList -> { + List expressions = new ArrayList<>(rangeList); + expressions.add(RangeExpression.to(formatter().apply(to))); + return expressions; + }).orElse(Collections.singletonList(RangeExpression.to(formatter().apply(to)))); + return new RangeFacetExpression<>(expression, terms, formatter, alias, countingProducts); + } + + public RangeFacetExpression range(T from, T to) { + List terms = Optional.ofNullable(ranges).map(rangeList -> { + List expressions = new ArrayList<>(rangeList); + expressions.add(RangeExpression.of(formatter().apply(from), formatter().apply(to))); + return expressions; + }).orElse(Collections.singletonList(RangeExpression.of(formatter().apply(from), formatter().apply(to)))); + return new RangeFacetExpression<>(expression, terms, formatter, alias, countingProducts); + } + + public static RangeFacetExpression of(final PathExpression expression, + final Function formatter) { + return new RangeFacetExpression<>(expression, formatter); + } + + public static RangeFacetExpression of(final PathExpression expression, final List ranges, + Function formatter) { + return new RangeFacetExpression<>(expression, ranges, formatter); + } + + public static RangeFacetExpression of(final PathExpression expression, final List ranges, + final Function formatter, final String alias, final boolean countingProducts) { + return new RangeFacetExpression<>(expression, ranges, formatter, alias, countingProducts); + } + + @Override + public String render() { + Objects.requireNonNull(ranges); + return expression.render() + ": range " + + ranges.stream().map(FilterExpression::render).collect(Collectors.joining(", ")) + renderMeta(); + } + + private String renderMeta() { + return Optional.ofNullable(alias).map(s -> String.format(" as %s", alias)).orElse("") + + (countingProducts ? " counting products" : ""); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java index 27f993a7712..76fa833b7bf 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFacetExpression.java @@ -6,7 +6,7 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; -public class TermFacetExpression implements FilterExpression { +public class TermFacetExpression implements FacetExpression { private final PathExpression expression; @@ -14,17 +14,34 @@ public class TermFacetExpression implements FilterExpression { private final Function formatter; - public TermFacetExpression(PathExpression expression, Function formatter) { + private final boolean countingProducts; + + private final String alias; + + TermFacetExpression(final PathExpression expression, final Function formatter) { this.expression = expression; this.terms = null; this.formatter = formatter; + this.alias = null; + this.countingProducts = false; } - public TermFacetExpression(PathExpression expression, List terms, - Function formatter) { + TermFacetExpression(final PathExpression expression, final List terms, + final Function formatter) { + this.expression = expression; + this.terms = terms; + this.formatter = formatter; + this.alias = null; + this.countingProducts = false; + } + + TermFacetExpression(final PathExpression expression, final List terms, + final Function formatter, final String alias, final boolean countingProducts) { this.expression = expression; this.terms = terms; this.formatter = formatter; + this.alias = alias; + this.countingProducts = countingProducts; } PathExpression expression() { @@ -39,44 +56,63 @@ Function formatter() { return formatter; } - public FilterExpression alias(final String alias) { - return ContainerExpression.of(this, ConstantExpression.of(String.format("as %s", alias)), true); + public TermFacetExpression alias(final String alias) { + return new TermFacetExpression<>(expression, terms, formatter, alias, countingProducts); } - public TermFacetExpression is(T value) { + public TermFacetExpression countingProducts() { + return new TermFacetExpression<>(expression, terms, formatter, alias, true); + } + + public TermFacetExpression is(final T value) { List terms = Optional.ofNullable(terms()).map(termList -> { List expressions = new ArrayList<>(termList); expressions.add(formatter().apply(value)); return expressions; }).orElse(Collections.singletonList(formatter().apply(value))); - return TermFacetExpression.of(expression, terms, formatter); + return new TermFacetExpression<>(expression, terms, formatter, alias, countingProducts); } - public TermFacetExpression isIn(Iterable values) { + public TermFacetExpression isIn(final Iterable values) { List terms = Optional.ofNullable(terms()).map(termList -> { List expressions = new ArrayList<>(termList); values.forEach(v -> expressions.add(formatter().apply(v))); return expressions; }).orElse(StreamSupport.stream(values.spliterator(), false).map(formatter()).collect(Collectors.toList())); - return TermFacetExpression.of(expression, terms, formatter); + return new TermFacetExpression<>(expression, terms, formatter, alias, countingProducts); + } + + public RangeFacetExpression ranges() { + return new RangeFacetExpression<>(expression, null, formatter, alias, countingProducts); } - public static TermFacetExpression of(PathExpression expression, Function formatter) { + public static TermFacetExpression of(final PathExpression expression, + final Function formatter) { return new TermFacetExpression<>(expression, formatter); } - public static TermFacetExpression of(PathExpression expression, List terms, + public static TermFacetExpression of(final PathExpression expression, final List terms, Function formatter) { return new TermFacetExpression<>(expression, terms, formatter); } + public static TermFacetExpression of(final PathExpression expression, final List terms, + final Function formatter, final String alias, final boolean countingProducts) { + return new TermFacetExpression<>(expression, terms, formatter, alias, countingProducts); + } + @Override public String render() { if (terms == null || terms.isEmpty()) { - return expression.render(); + return expression.render() + renderMeta(); } return expression.render() + ": " - + terms.stream().map(FilterExpression::render).collect(Collectors.joining(", ")); + + terms.stream().map(FilterExpression::render).collect(Collectors.joining(", ")) + renderMeta(); + } + + private String renderMeta() { + return Optional.ofNullable(alias).map(s -> String.format(" as %s", alias)).orElse("") + + (countingProducts ? " counting products" : ""); } } diff --git a/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java index e4424b60d78..393f422c476 100644 --- a/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java +++ b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java @@ -6,6 +6,9 @@ import java.time.ZonedDateTime; import java.util.Locale; +import com.commercetools.api.client.ByProjectKeyProductProjectionsSearchGet; +import com.commercetools.api.client.ByProjectKeyProductProjectionsSearchPost; +import com.commercetools.api.client.ProjectApiRoot; import com.commercetools.api.models.Identifiable; import com.commercetools.api.models.common.DefaultCurrencyUnits; import com.commercetools.api.search.products.*; @@ -15,6 +18,7 @@ import com.tngtech.junit.dataprovider.UseDataProviderExtension; import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; @@ -22,6 +26,38 @@ @ExtendWith(DataProviderExtension.class) public class SearchTests { + @Test + public void getRequest() { + ByProjectKeyProductProjectionsSearchGet searchResponse = ProjectApiRoot.of("test") + .productProjections() + .search() + .get() + .filter(f -> f.categories().id().is("abc")) + .filterFacets(f -> f.categories().id().is("def")) + .filterQuery(f -> f.categories().id().is("ghi")) + .facet(f -> f.categories().id()); + + Assertions.assertThat(searchResponse.createHttpRequest().getUri().getRawQuery()) + .isEqualTo( + "filter=categories.id%3A+%22abc%22&filter.facets=categories.id%3A+%22def%22&filter.query=categories.id%3A+%22ghi%22&facet=categories.id"); + } + + @Test + public void postRequest() { + ByProjectKeyProductProjectionsSearchPost searchResponse = ProjectApiRoot.of("test") + .productProjections() + .search() + .post() + .filter(f -> f.categories().id().is("abc")) + .filterFacets(f -> f.categories().id().is("def")) + .filterQuery(f -> f.categories().id().is("ghi")) + .facet(f -> f.categories().id()); + + Assertions.assertThat(searchResponse.createHttpRequest().getSecuredBody()) + .isEqualTo( + "filter=categories.id%3A+%22abc%22&filter.facets=categories.id%3A+%22def%22&filter.query=categories.id%3A+%22ghi%22&facet=categories.id"); + } + @TestTemplate @UseDataProvider("filterExpressions") public void filterRender(FilterExpression searchExpression, String expectedPredicate) { @@ -255,6 +291,13 @@ public static Object[][] facetExpressions() { return new Object[][] { new Object[] { ProductFacetExpressionBuilder.of().categories().id().alias("cat"), "categories.id as cat" }, + new Object[] { ProductFacetExpressionBuilder.of().categories().id().alias("cat").countingProducts(), + "categories.id as cat counting products" }, + new Object[] { ProductFacetExpressionBuilder.of().categories().id().countingProducts().alias("cat"), + "categories.id as cat counting products" }, + new Object[] { + ProductFacetExpressionBuilder.of().categories().id().countingProducts().alias("cat").is("foo"), + "categories.id: \"foo\" as cat counting products" }, new Object[] { ProductFacetExpressionBuilder.of().categories().id(), "categories.id" }, new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo"), "categories.id: \"foo\"" }, @@ -307,6 +350,52 @@ public static Object[][] facetExpressions() { .attribute() .ofDatetime("test") .is(ZonedDateTime.parse("2024-01-03T10:00Z")), - "variants.attributes.test: \"2024-01-03T10:00:00.000Z\"" }, }; + "variants.attributes.test: \"2024-01-03T10:00:00.000Z\"" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofEnum("test").key(), + "variants.attributes.test.key" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofEnum("test").label(), + "variants.attributes.test.label" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofEnum("test").label("en"), + "variants.attributes.test.label.en" }, + new Object[] { + ProductFacetExpressionBuilder.of().variants().attribute().ofEnum("test").label(Locale.US), + "variants.attributes.test.label.en-US" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofMoney("test").centAmount(), + "variants.attributes.test.centAmount" }, + new Object[] { + ProductFacetExpressionBuilder.of().variants().attribute().ofMoney("test").centAmount().is(100L), + "variants.attributes.test.centAmount: 100" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofMoney("test").currencyCode(), + "variants.attributes.test.currencyCode" }, + new Object[] { ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofMoney("test") + .currencyCode() + .is("EUR"), "variants.attributes.test.currencyCode: \"EUR\"" }, + new Object[] { ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofMoney("test") + .currency() + .is(DefaultCurrencyUnits.EUR), "variants.attributes.test.currencyCode: \"EUR\"" }, + new Object[] { ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofLong("test") + .ranges() + .range(0L, 100L) + .rangeFrom(100L), "variants.attributes.test: range (0 to 100), (100 to *)" }, + new Object[] { + ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofLong("test") + .ranges() + .range(0L, 100L) + .rangeFrom(100L) + .alias("range_test") + .countingProducts(), + "variants.attributes.test: range (0 to 100), (100 to *) as range_test counting products" }, }; } } From a5f04813567d040aa47d4f2c753b773a78411caf Mon Sep 17 00:00:00 2001 From: Jens Schulze Date: Tue, 23 Jan 2024 11:31:44 +0100 Subject: [PATCH 6/6] refactor ranges --- .../ProductFacetExpressionBuilder.java | 24 ++++----- .../ProductFilterExpressionBuilder.java | 36 ++++++------- .../products/RangeFacetExpressionBuilder.java | 51 +++++++++++++++++++ .../products/TermFilterExpressionBuilder.java | 4 ++ .../commercetools/api/search/SearchTests.java | 29 ++++++++--- 5 files changed, 106 insertions(+), 38 deletions(-) create mode 100644 commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFacetExpressionBuilder.java diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java index e1aed6c1df2..6c9276190a4 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFacetExpressionBuilder.java @@ -145,28 +145,28 @@ public TermFacetExpression ofText(final String name) { return TermFacetExpression.of(expression.add(name), TermFormatter::format); } - public TermFacetExpression ofDate(final String name) { - return TermFacetExpression.of(expression.add(name), TermFormatter::format); + public RangeFacetExpressionBuilder ofDate(final String name) { + return RangeFacetExpressionBuilder.of(expression.add(name), TermFormatter::format); } - public TermFacetExpression ofTime(final String name) { - return TermFacetExpression.of(expression.add(name), TermFormatter::format); + public RangeFacetExpressionBuilder ofTime(final String name) { + return RangeFacetExpressionBuilder.of(expression.add(name), TermFormatter::format); } - public TermFacetExpression ofDatetime(final String name) { - return TermFacetExpression.of(expression.add(name), TermFormatter::format); + public RangeFacetExpressionBuilder ofDatetime(final String name) { + return RangeFacetExpressionBuilder.of(expression.add(name), TermFormatter::format); } public TermFacetExpression ofBool(final String name) { return TermFacetExpression.of(expression.add(name), TermFormatter::format); } - public TermFacetExpression ofNumber(final String name) { - return TermFacetExpression.of(expression.add(name), TermFormatter::format); + public RangeFacetExpressionBuilder ofNumber(final String name) { + return RangeFacetExpressionBuilder.of(expression.add(name), TermFormatter::format); } - public TermFacetExpression ofLong(final String name) { - return TermFacetExpression.of(expression.add(name), TermFormatter::format); + public RangeFacetExpressionBuilder ofLong(final String name) { + return RangeFacetExpressionBuilder.of(expression.add(name), TermFormatter::format); } public TermFacetExpression ofLocalizedString(final String name, final String language) { @@ -213,8 +213,8 @@ public AttributesMoneyFacetExpressionBuilder(final PathExpression expression) { this.expression = expression; } - public TermFacetExpression centAmount() { - return TermFacetExpression.of(expression.add("centAmount"), TermFormatter::format); + public RangeFacetExpressionBuilder centAmount() { + return RangeFacetExpressionBuilder.of(expression.add("centAmount"), TermFormatter::format); } public TermFacetExpression currencyCode() { diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFilterExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFilterExpressionBuilder.java index d32dd5396f8..0b0d31c22f0 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFilterExpressionBuilder.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/ProductFilterExpressionBuilder.java @@ -159,20 +159,20 @@ public ReviewRatingStatisticsFilterExpressionBuilder() { this.expression = PathExpression.of("reviewRatingStatistics"); } - public TermFilterExpressionBuilder averageRating() { - return TermFilterExpressionBuilder.of(expression.add("averageRating"), TermFormatter::format); + public RangeFilterExpressionBuilder averageRating() { + return RangeFilterExpressionBuilder.of(expression.add("averageRating"), TermFormatter::format); } - public TermFilterExpressionBuilder highestRating() { - return TermFilterExpressionBuilder.of(expression.add("highestRating"), TermFormatter::format); + public RangeFilterExpressionBuilder highestRating() { + return RangeFilterExpressionBuilder.of(expression.add("highestRating"), TermFormatter::format); } - public TermFilterExpressionBuilder lowestRating() { - return TermFilterExpressionBuilder.of(expression.add("lowestRating"), TermFormatter::format); + public RangeFilterExpressionBuilder lowestRating() { + return RangeFilterExpressionBuilder.of(expression.add("lowestRating"), TermFormatter::format); } - public TermFilterExpressionBuilder count() { - return TermFilterExpressionBuilder.of(expression.add("count"), TermFormatter::format); + public RangeFilterExpressionBuilder count() { + return RangeFilterExpressionBuilder.of(expression.add("count"), TermFormatter::format); } } @@ -347,28 +347,28 @@ public TermFilterExpressionBuilder asBoolean() { return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); } - public TermFilterExpressionBuilder asLong() { - return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + public RangeFilterExpressionBuilder asLong() { + return new RangeFilterExpressionBuilder<>(expression, TermFormatter::format); } - public TermFilterExpressionBuilder asDouble() { - return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + public RangeFilterExpressionBuilder asDouble() { + return new RangeFilterExpressionBuilder<>(expression, TermFormatter::format); } public TermFilterExpressionBuilder asString() { return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); } - public TermFilterExpressionBuilder asDateTime() { - return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + public RangeFilterExpressionBuilder asDateTime() { + return new RangeFilterExpressionBuilder<>(expression, TermFormatter::format); } - public TermFilterExpressionBuilder asDate() { - return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + public RangeFilterExpressionBuilder asDate() { + return new RangeFilterExpressionBuilder<>(expression, TermFormatter::format); } - public TermFilterExpressionBuilder asTime() { - return new TermFilterExpressionBuilder<>(expression, TermFormatter::format); + public RangeFilterExpressionBuilder asTime() { + return new RangeFilterExpressionBuilder<>(expression, TermFormatter::format); } public PriceFilterExpressionBuilder asMoney() { diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFacetExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFacetExpressionBuilder.java new file mode 100644 index 00000000000..767c98f30f2 --- /dev/null +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/RangeFacetExpressionBuilder.java @@ -0,0 +1,51 @@ + +package com.commercetools.api.search.products; + +import java.util.Objects; +import java.util.function.Function; + +public class RangeFacetExpressionBuilder implements FilterExpression { + private final PathExpression expression; + private final Function formatter; + + public RangeFacetExpressionBuilder() { + this.expression = null; + this.formatter = null; + } + + public RangeFacetExpressionBuilder(PathExpression expression, Function formatter) { + this.expression = expression; + this.formatter = formatter; + } + + public TermFacetExpression is(T value) { + return TermFacetExpression.of(expression, formatter).is(value); + } + + public TermFacetExpression isIn(Iterable values) { + return TermFacetExpression.of(expression, formatter).isIn(values); + } + + public RangeFacetExpression rangeFrom(T from) { + return RangeFacetExpression.of(expression, formatter).rangeFrom(from); + } + + public RangeFacetExpression rangeTo(T to) { + return RangeFacetExpression.of(expression, formatter).rangeTo(to); + } + + public RangeFacetExpression range(T from, T to) { + return RangeFacetExpression.of(expression, formatter).range(from, to); + } + + @Override + public String render() { + Objects.requireNonNull(expression); + return expression.render(); + } + + public static RangeFacetExpressionBuilder of(PathExpression expression, + Function formatter) { + return new RangeFacetExpressionBuilder<>(expression, formatter); + } +} diff --git a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpressionBuilder.java b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpressionBuilder.java index 7d1c688c5b9..5be9d1b1ee8 100644 --- a/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpressionBuilder.java +++ b/commercetools/commercetools-sdk-java-api/src/main/java/com/commercetools/api/search/products/TermFilterExpressionBuilder.java @@ -33,6 +33,10 @@ public FilterExpression missing() { return new ExistsTermFilterExpression(expression).missing(); } + public RangeFilterExpression ranges() { + return new RangeFilterExpression<>(expression, null, formatter); + } + public static TermFilterExpressionBuilder of(PathExpression expression, Function formatter) { return new TermFilterExpressionBuilder<>(expression, formatter); diff --git a/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java index 393f422c476..af6f2742a2a 100644 --- a/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java +++ b/commercetools/commercetools-sdk-java-api/src/test/java/com/commercetools/api/search/SearchTests.java @@ -156,6 +156,9 @@ public static Object[][] filterExpressions() { new Object[] { ProductFilterExpressionBuilder.of().state().missing(), "state: missing" }, new Object[] { ProductFilterExpressionBuilder.of().reviewRatingStatistics().averageRating().is(100L), "reviewRatingStatistics.averageRating: 100" }, + new Object[] { + ProductFilterExpressionBuilder.of().reviewRatingStatistics().averageRating().range(0L, 100L), + "reviewRatingStatistics.averageRating: range (0 to 100)" }, new Object[] { ProductFilterExpressionBuilder.of().reviewRatingStatistics().lowestRating().is(100L), "reviewRatingStatistics.lowestRating: 100" }, new Object[] { ProductFilterExpressionBuilder.of().reviewRatingStatistics().highestRating().is(100L), @@ -233,6 +236,8 @@ public static Object[][] filterExpressions() { "variants.attributes.foo: true" }, new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asLong().is(100L), "variants.attributes.foo: 100" }, + new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asLong().range(0L, 100L), + "variants.attributes.foo: range (0 to 100)" }, new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asDouble().is(100.0), "variants.attributes.foo: 100.0" }, new Object[] { ProductFilterExpressionBuilder.of().variants().attribute("foo").asDouble().is(100.0), @@ -309,15 +314,16 @@ public static Object[][] facetExpressions() { "categories.id: \"foo\", subTree(\"bar\")" }, new Object[] { ProductFacetExpressionBuilder.of().categories().id().is("foo").subTree("bar").is("baz"), "categories.id: \"foo\", subTree(\"bar\"), \"baz\"" }, - new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofText("test"), - "variants.attributes.test" }, - new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofText("test").is("foo"), - "variants.attributes.test: \"foo\"" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofText("test-text"), + "variants.attributes.test-text" }, + new Object[] { + ProductFacetExpressionBuilder.of().variants().attribute().ofText("test-text-foo").is("foo"), + "variants.attributes.test-text-foo: \"foo\"" }, new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofText("test").is("foo").is("bar"), "variants.attributes.test: \"foo\", \"bar\"" }, - new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDate("test"), - "variants.attributes.test" }, + new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofDate("test-date"), + "variants.attributes.test-date" }, new Object[] { ProductFacetExpressionBuilder.of() .variants() .attribute() @@ -365,6 +371,15 @@ public static Object[][] facetExpressions() { new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofMoney("test").centAmount().is(100L), "variants.attributes.test.centAmount: 100" }, + new Object[] { + ProductFacetExpressionBuilder.of() + .variants() + .attribute() + .ofMoney("test") + .centAmount() + .range(0L, 100L) + .range(101L, 200L), + "variants.attributes.test.centAmount: range (0 to 100), (101 to 200)" }, new Object[] { ProductFacetExpressionBuilder.of().variants().attribute().ofMoney("test").currencyCode(), "variants.attributes.test.currencyCode" }, new Object[] { ProductFacetExpressionBuilder.of() @@ -383,7 +398,6 @@ public static Object[][] facetExpressions() { .variants() .attribute() .ofLong("test") - .ranges() .range(0L, 100L) .rangeFrom(100L), "variants.attributes.test: range (0 to 100), (100 to *)" }, new Object[] { @@ -391,7 +405,6 @@ public static Object[][] facetExpressions() { .variants() .attribute() .ofLong("test") - .ranges() .range(0L, 100L) .rangeFrom(100L) .alias("range_test")