├── src └── org │ └── bitbucket │ └── andriichukandrii │ └── hybris │ └── flexiblesearchbuilder │ ├── ConditionType.java │ ├── QueryConditionType.java │ ├── ParameterConditionType.java │ ├── FieldRepresentation.java │ ├── FlexibleSearchQueryChainElement.java │ ├── AbstractFromClauseElement.java │ ├── OrderBySortingType.java │ ├── SqlFunctionType.java │ ├── CombineConditionType.java │ ├── Field.java │ ├── UnaryQueryConditionType.java │ ├── ParameterlessConditionType.java │ ├── CollectionAndQueryConditionType.java │ ├── ModelSelectClause.java │ ├── RegularParameterConditionType.java │ ├── AbstractFlexibleSearchQueryChainElement.java │ ├── VarargCollectionUtils.java │ ├── InnerJoinElement.java │ ├── LeftJoinElement.java │ ├── RightJoinElement.java │ ├── SimpleField.java │ ├── TwoParameterConditionType.java │ ├── AliasElement.java │ ├── ValueWithType.java │ ├── AbstractCondition.java │ ├── JoinOnElement.java │ ├── FromClauseElements.java │ ├── GroupByAcceptable.java │ ├── CustomSelectClause.java │ ├── FunctionWithType.java │ ├── TableFromClauseElement.java │ ├── CustomCondition.java │ ├── SqlFunction.java │ ├── ParameterlessFieldCondition.java │ ├── AliasedField.java │ ├── SelectClause.java │ ├── FlexibleSearchQueryStartChainElement.java │ ├── AbstractFieldCondition.java │ ├── FlexibleSearchQueryInnerChainElement.java │ ├── FlexibleSearchBuilderFieldUtils.java │ ├── GroupByClause.java │ ├── FieldToFieldCondition.java │ ├── FieldWithType.java │ ├── AbstractSelectClause.java │ ├── AbstractTableFromClauseElement.java │ ├── WrapperCondition.java │ ├── HavingClause.java │ ├── AbstractJoinElement.java │ ├── FlexibleSearchQueryConstants.java │ ├── WhereClause.java │ ├── TerminateQueryChainElement.java │ ├── OrderByClause.java │ ├── Alias.java │ ├── OrderByAcceptable.java │ ├── BraceConditionWrapper.java │ ├── JoinAliasElement.java │ ├── InnerQueryFieldCondition.java │ ├── FromClause.java │ ├── InnerQueryUnaryCondition.java │ ├── JoinableFromClauseElement.java │ ├── ParameterFieldCondition.java │ ├── FlexibleSearchQueryBuilder.java │ ├── TwoParameterFieldCondition.java │ ├── SqlFunctions.java │ ├── Conditions.java │ └── CombineConditionElement.java ├── resources ├── flexiblesearchbuilder.build.number └── flexiblesearchbuilder │ └── flexiblesearchbuilder-testclasses.xml ├── .gitignore ├── extensioninfo.xml ├── testsrc └── org │ └── bitbucket │ └── andriichukandrii │ └── hybris │ └── flexiblesearchbuilder │ ├── HavingClauseTest.java │ ├── ParameterLessFieldConditionsTest.java │ ├── UnaryQueryConditionsTest.java │ ├── FlexibleSearchQueryBuilderTest.java │ ├── GroupByClauseTest.java │ ├── SqlFunctionsTest.java │ ├── CollectionAndQueryConditionsTest.java │ ├── JoinsTest.java │ ├── ParameterFieldConditionsTest.java │ ├── ParameterAliasedFieldConditionsTest.java │ ├── CombineConditionsTest.java │ └── FlexibleSearchBuilderBasicUsageTest.java └── README.md /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ConditionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | @FunctionalInterface 4 | public interface ConditionType 5 | { 6 | String getOperator(); 7 | } 8 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/QueryConditionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | @FunctionalInterface 4 | public interface QueryConditionType extends ConditionType 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ParameterConditionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | @FunctionalInterface 4 | public interface ParameterConditionType extends ConditionType 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /resources/flexiblesearchbuilder.build.number: -------------------------------------------------------------------------------- 1 | #Ant properties 2 | #Thu Jul 19 07:59:59 GMT 2018 3 | builddate=20180719 0759 4 | description=flexiblesearchbuilder 5 | group.id=de.hybris.platform 6 | module.name=platform-module 7 | name=flexiblesearchbuilder 8 | vendor=hybris 9 | version=18.08 10 | version.api=18.08.0 11 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FieldRepresentation.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | /** 4 | * Marker interface for fields and functions, so that they can be put into certain places, like "select" or "order by". 5 | */ 6 | public interface FieldRepresentation 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FlexibleSearchQueryChainElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | /** 4 | * Represents an element of a flexible search query, e.g. 'SELECT' clause, 'FROM' clause, condition, etc. 5 | */ 6 | public interface FlexibleSearchQueryChainElement 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/AbstractFromClauseElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public abstract class AbstractFromClauseElement extends FlexibleSearchQueryInnerChainElement 4 | { 5 | 6 | AbstractFromClauseElement(final AbstractFlexibleSearchQueryChainElement parent) 7 | { 8 | super(parent); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/OrderBySortingType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public enum OrderBySortingType 4 | { 5 | ASC("ASC"), DESC("DESC"); 6 | 7 | 8 | private final String operator; 9 | 10 | OrderBySortingType(final String operator) 11 | { 12 | this.operator = operator; 13 | } 14 | 15 | public String getOperator() 16 | { 17 | return operator; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/SqlFunctionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public enum SqlFunctionType 4 | { 5 | MIN("MIN"), MAX("MAX"), AVG("AVG"), SUM("SUM"), COUNT("COUNT"); 6 | 7 | 8 | private final String name; 9 | 10 | SqlFunctionType(final String name) 11 | { 12 | this.name = name; 13 | } 14 | 15 | public String getName() 16 | { 17 | return name; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/CombineConditionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public enum CombineConditionType 4 | { 5 | AND("AND"), OR("OR"); 6 | 7 | private final String operator; 8 | 9 | CombineConditionType(final String operator) 10 | { 11 | this.operator = operator; 12 | } 13 | 14 | @Override 15 | public String toString() 16 | { 17 | return operator; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDEs and editors 2 | .project 3 | .classpath 4 | .springBeans 5 | .settings/ 6 | /eclipsebin 7 | .externalToolBuilders/ 8 | 9 | # System Files 10 | .DS_Store 11 | Thumbs.db 12 | 13 | # Libraries and generated sources 14 | lib/ 15 | gensrc/ 16 | classes/ 17 | build.xml 18 | 19 | # Others 20 | platformhome.properties 21 | extensioninfo.xsd 22 | /src/org/bitbucket/andriichukandrii/constants/FlexiblesearchbuilderConstants.java 23 | /src/org/bitbucket/andriichukandrii/jalo/FlexiblesearchbuilderManager.java 24 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/Field.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | @FunctionalInterface 4 | public interface Field 5 | { 6 | /** 7 | * Returns this field's name 8 | * 9 | * @return field name 10 | */ 11 | String getFieldName(); 12 | 13 | /** 14 | * Returns representation of this field, which is used in queries generation. 15 | * 16 | * @return representation of this field 17 | */ 18 | String toString(); 19 | } 20 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/UnaryQueryConditionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public enum UnaryQueryConditionType implements QueryConditionType 4 | { 5 | EXISTS("EXISTS"), NOT_EXISTS("NOT EXISTS"); 6 | 7 | 8 | private final String operator; 9 | 10 | UnaryQueryConditionType(final String operator) 11 | { 12 | this.operator = operator; 13 | } 14 | 15 | @Override 16 | public String getOperator() 17 | { 18 | return operator; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ParameterlessConditionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public enum ParameterlessConditionType implements ConditionType 4 | { 5 | IS_NULL("IS NULL"), IS_NOT_NULL("IS NOT NULL"); 6 | 7 | 8 | private final String operator; 9 | 10 | ParameterlessConditionType(final String operator) 11 | { 12 | this.operator = operator; 13 | } 14 | 15 | @Override 16 | public String getOperator() 17 | { 18 | return operator; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/CollectionAndQueryConditionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public enum CollectionAndQueryConditionType implements QueryConditionType, ParameterConditionType 4 | { 5 | IN(" IN "), NOT_IN(" NOT IN "); 6 | 7 | 8 | private final String operator; 9 | 10 | CollectionAndQueryConditionType(final String operator) 11 | { 12 | this.operator = operator; 13 | } 14 | 15 | @Override 16 | public String getOperator() 17 | { 18 | return operator; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ModelSelectClause.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.core.model.ItemModel; 4 | 5 | 6 | public class ModelSelectClause extends AbstractSelectClause 7 | { 8 | private final Field field; 9 | 10 | ModelSelectClause() 11 | { 12 | field = new SimpleField(ItemModel.PK); 13 | } 14 | 15 | ModelSelectClause(final Field aliasedField) 16 | { 17 | this.field = aliasedField; 18 | } 19 | 20 | @Override 21 | protected void appendFieldsPart(final StringBuilder sb) 22 | { 23 | sb.append(field); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/RegularParameterConditionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public enum RegularParameterConditionType implements ParameterConditionType 4 | { 5 | IS_EQUAL_TO("="), IS_NOT_EQUAL_TO("<>"), IS_LESS_THAN("<"), IS_LESS_OR_EQUAL("<="), IS_GREATER_THAN(">"), IS_GREATER_OR_EQUAL( 6 | ">="), LIKE(" LIKE "), NOT_LIKE(" NOT LIKE "); 7 | 8 | 9 | private final String operator; 10 | 11 | RegularParameterConditionType(final String operator) 12 | { 13 | this.operator = operator; 14 | } 15 | 16 | @Override 17 | public String getOperator() 18 | { 19 | return operator; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/AbstractFlexibleSearchQueryChainElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 4 | 5 | import java.util.Map; 6 | 7 | 8 | /** 9 | * An abstract element of the flexible search query. 10 | */ 11 | public abstract class AbstractFlexibleSearchQueryChainElement implements FlexibleSearchQueryChainElement 12 | { 13 | protected abstract void appendQuery(final StringBuilder sb); 14 | 15 | protected abstract void addParameters(final Map parameterMap); 16 | 17 | protected abstract void configureQuery(final FlexibleSearchQuery flexibleSearchQuery); 18 | } 19 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/VarargCollectionUtils.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.Stream; 7 | 8 | 9 | public final class VarargCollectionUtils 10 | { 11 | private VarargCollectionUtils() 12 | { 13 | } 14 | 15 | @SafeVarargs 16 | public static List toList(final T first, T... rest) 17 | { 18 | return toStream(first, rest).collect(Collectors.toList()); 19 | } 20 | 21 | @SafeVarargs 22 | public static Stream toStream(final T first, final T... rest) 23 | { 24 | return Stream.concat(Stream.of(first), Arrays.stream(rest)); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/InnerJoinElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.JOIN; 4 | 5 | import de.hybris.platform.core.model.ItemModel; 6 | 7 | 8 | /** 9 | * 'JOIN' (i.e. inner join) element of the flexible search query. 10 | */ 11 | public class InnerJoinElement extends AbstractJoinElement 12 | { 13 | 14 | InnerJoinElement(final AbstractFlexibleSearchQueryChainElement parent, final Class clazz) 15 | { 16 | super(parent, clazz, JOIN); 17 | } 18 | 19 | InnerJoinElement(final AbstractFlexibleSearchQueryChainElement parent, final String typeCode) 20 | { 21 | super(parent, typeCode, JOIN); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/LeftJoinElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.LEFT_JOIN; 4 | 5 | import de.hybris.platform.core.model.ItemModel; 6 | 7 | 8 | /** 9 | * 'LEFT JOIN' element of the flexible search query. 10 | */ 11 | public class LeftJoinElement extends AbstractJoinElement 12 | { 13 | 14 | LeftJoinElement(final AbstractFlexibleSearchQueryChainElement parent, final Class clazz) 15 | { 16 | super(parent, clazz, LEFT_JOIN); 17 | } 18 | 19 | LeftJoinElement(final AbstractFlexibleSearchQueryChainElement parent, final String typeCode) 20 | { 21 | super(parent, typeCode, LEFT_JOIN); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/RightJoinElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.RIGHT_JOIN; 4 | 5 | import de.hybris.platform.core.model.ItemModel; 6 | 7 | 8 | /** 9 | * 'RIGHT JOIN' element of the flexible search query. 10 | */ 11 | public class RightJoinElement extends AbstractJoinElement 12 | { 13 | 14 | RightJoinElement(final AbstractFlexibleSearchQueryChainElement parent, final Class clazz) 15 | { 16 | super(parent, clazz, RIGHT_JOIN); 17 | } 18 | 19 | RightJoinElement(final AbstractFlexibleSearchQueryChainElement parent, final String typeCode) 20 | { 21 | super(parent, typeCode, RIGHT_JOIN); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/SimpleField.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.CLOSING_BRACKET; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.OPENING_BRACKET; 5 | 6 | 7 | class SimpleField implements Field, FieldRepresentation 8 | { 9 | private final String fieldName; 10 | 11 | SimpleField(final String fieldName) 12 | { 13 | this.fieldName = fieldName; 14 | } 15 | 16 | @Override 17 | public String getFieldName() 18 | { 19 | return fieldName; 20 | } 21 | 22 | @Override 23 | public String toString() 24 | { 25 | return OPENING_BRACKET + fieldName + CLOSING_BRACKET; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/TwoParameterConditionType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | @SuppressWarnings("SameParameterValue") 4 | public enum TwoParameterConditionType implements ConditionType 5 | { 6 | BETWEEN("BETWEEN", "AND"), NOT_BETWEEN("NOT BETWEEN", "AND"); 7 | 8 | 9 | private final String operator; 10 | private final String parameterJoinOperation; 11 | 12 | TwoParameterConditionType(final String operator, final String parameterJoinOperation) 13 | { 14 | this.operator = operator; 15 | this.parameterJoinOperation = parameterJoinOperation; 16 | } 17 | 18 | @Override 19 | public String getOperator() 20 | { 21 | return operator; 22 | } 23 | 24 | public String getParameterJoinOperation() 25 | { 26 | return parameterJoinOperation; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/AliasElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.AS; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 6 | 7 | 8 | public class AliasElement extends JoinableFromClauseElement 9 | { 10 | private final Alias alias; 11 | 12 | AliasElement(final AbstractFlexibleSearchQueryChainElement parent, final Alias alias) 13 | { 14 | super(parent); 15 | this.alias = alias; 16 | } 17 | 18 | @Override 19 | protected void appendQuery(final StringBuilder sb) 20 | { 21 | super.appendQuery(sb); 22 | 23 | sb.append(SPACE).append(AS).append(SPACE).append(alias.getAliasValue()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ValueWithType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | /** 4 | * Represents value to result type mapping, see 5 | * org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FieldWithType and 6 | * org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FunctionWithType 7 | */ 8 | public abstract class ValueWithType 9 | { 10 | private final Class type; 11 | 12 | ValueWithType(final Class type) 13 | { 14 | this.type = type; 15 | } 16 | 17 | /** 18 | * Gets value type. 19 | * 20 | * @return type 21 | */ 22 | public Class getType() 23 | { 24 | return type; 25 | } 26 | 27 | /** 28 | * Gets string value of this class, mapped to type. 29 | * 30 | * @return string value 31 | */ 32 | public abstract String getValue(); 33 | } 34 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/AbstractCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.CombineConditionType.AND; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.CombineConditionType.OR; 5 | 6 | 7 | /** 8 | * Abstract condition of the flexible search query. 9 | */ 10 | public abstract class AbstractCondition extends FlexibleSearchQueryInnerChainElement 11 | { 12 | 13 | AbstractCondition(final AbstractFlexibleSearchQueryChainElement parent) 14 | { 15 | super(parent); 16 | } 17 | 18 | public CombineConditionElement and() 19 | { 20 | return new CombineConditionElement(this, AND); 21 | } 22 | 23 | public CombineConditionElement or() 24 | { 25 | return new CombineConditionElement(this, OR); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/JoinOnElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.ON; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 6 | 7 | 8 | public class JoinOnElement extends JoinableFromClauseElement 9 | { 10 | private final AbstractCondition condition; 11 | 12 | JoinOnElement(final AbstractFlexibleSearchQueryChainElement parent, final AbstractCondition condition) 13 | { 14 | super(parent); 15 | this.condition = condition; 16 | } 17 | 18 | @Override 19 | protected void appendQuery(final StringBuilder sb) 20 | { 21 | super.appendQuery(sb); 22 | 23 | sb.append(SPACE).append(ON).append(SPACE); 24 | condition.appendQuery(sb); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FromClauseElements.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.core.model.ItemModel; 4 | 5 | 6 | public final class FromClauseElements 7 | { 8 | private FromClauseElements() 9 | { 10 | } 11 | 12 | /** 13 | * Creates table element by given model type. 14 | * 15 | * @param clazz 16 | * model type 17 | * @return table element of the query 18 | */ 19 | public static TableFromClauseElement table(final Class clazz) 20 | { 21 | return new TableFromClauseElement(clazz); 22 | } 23 | 24 | /** 25 | * Creates table element by given type code. 26 | * 27 | * @param typeCode 28 | * model type code 29 | * @return table element of the query 30 | */ 31 | public static TableFromClauseElement table(final String typeCode) 32 | { 33 | return new TableFromClauseElement(typeCode); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/GroupByAcceptable.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.VarargCollectionUtils.toList; 4 | 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | 9 | public interface GroupByAcceptable 10 | { 11 | default GroupByClause groupBy(final String firstField, final String... restFields) 12 | { 13 | final List fields = toList(firstField, restFields).stream().map(SimpleField::new).collect(Collectors.toList()); 14 | return new GroupByClause((AbstractFlexibleSearchQueryChainElement) this, fields); 15 | } 16 | 17 | default GroupByClause groupBy(final AliasedField firstField, final AliasedField... restFields) 18 | { 19 | final List fields = toList(firstField, restFields); 20 | return new GroupByClause((AbstractFlexibleSearchQueryChainElement) this, fields); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/CustomSelectClause.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 4 | 5 | import java.util.List; 6 | 7 | 8 | public class CustomSelectClause extends AbstractSelectClause 9 | { 10 | private final String customFieldsPart; 11 | private final List> resultTypes; 12 | 13 | CustomSelectClause(final String customFieldsPart, final List> resultTypes) 14 | { 15 | this.customFieldsPart = customFieldsPart; 16 | this.resultTypes = resultTypes; 17 | } 18 | 19 | @Override 20 | protected void appendFieldsPart(final StringBuilder sb) 21 | { 22 | sb.append(customFieldsPart); 23 | } 24 | 25 | @Override 26 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 27 | { 28 | super.configureQuery(flexibleSearchQuery); 29 | 30 | flexibleSearchQuery.setResultClassList(resultTypes); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FunctionWithType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | 4 | /** 5 | * Maps function on a field to result type. 6 | */ 7 | public final class FunctionWithType extends ValueWithType 8 | { 9 | private final SqlFunction function; 10 | 11 | private FunctionWithType(final SqlFunction function, final Class type) 12 | { 13 | super(type); 14 | this.function = function; 15 | } 16 | 17 | /** 18 | * Creates this function-to-type mapping class. 19 | * 20 | * @param function 21 | * sql function 22 | * @param type 23 | * field type 24 | * @return new FieldWithType instance of given parameters 25 | */ 26 | public static FunctionWithType of(final SqlFunction function, final Class type) 27 | { 28 | return new FunctionWithType(function, type); 29 | } 30 | 31 | @Override 32 | public String getValue() 33 | { 34 | return function.toString(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/TableFromClauseElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.core.model.ItemModel; 4 | 5 | 6 | public class TableFromClauseElement extends AbstractTableFromClauseElement 7 | { 8 | TableFromClauseElement(final Class clazz) 9 | { 10 | super(FlexibleSearchQueryStartChainElement.INSTANCE, clazz); 11 | } 12 | 13 | TableFromClauseElement(final String typeCode) 14 | { 15 | super(FlexibleSearchQueryStartChainElement.INSTANCE, typeCode); 16 | } 17 | 18 | /** 19 | * Marks the table with given alias. 20 | * 21 | * @param alias 22 | * alias 23 | * @return alias query element 24 | */ 25 | public AliasElement as(final Alias alias) 26 | { 27 | return new AliasElement(this, alias); 28 | } 29 | 30 | @Override 31 | protected void appendQuery(final StringBuilder sb) 32 | { 33 | super.appendQuery(sb); 34 | sb.append(getTypecode()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/CustomCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 4 | 5 | 6 | public class CustomCondition extends AbstractCondition 7 | { 8 | private final String conditionValue; 9 | 10 | CustomCondition(final String conditionValue) 11 | { 12 | super(FlexibleSearchQueryStartChainElement.INSTANCE); 13 | this.conditionValue = conditionValue; 14 | } 15 | 16 | CustomCondition(final AbstractFlexibleSearchQueryChainElement parent, final String conditionValue) 17 | { 18 | super(parent); 19 | this.conditionValue = conditionValue; 20 | } 21 | 22 | @Override 23 | protected void appendQuery(final StringBuilder sb) 24 | { 25 | super.appendQuery(sb); 26 | 27 | if (!FlexibleSearchQueryStartChainElement.INSTANCE.equals(parent)) 28 | { 29 | sb.append(SPACE); 30 | } 31 | sb.append(conditionValue); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/SqlFunction.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.CLOSING_BRACE; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.OPENING_BRACE; 5 | 6 | 7 | public class SqlFunction implements FieldRepresentation 8 | { 9 | private final SqlFunctionType functionType; 10 | private final Field field; 11 | 12 | SqlFunction(final SqlFunctionType functionType, final String fieldName) 13 | { 14 | this.functionType = functionType; 15 | this.field = new SimpleField(fieldName); 16 | } 17 | 18 | SqlFunction(final SqlFunctionType functionType, final Field field) 19 | { 20 | this.functionType = functionType; 21 | this.field = field; 22 | } 23 | 24 | @Override 25 | public String toString() 26 | { 27 | return functionType.getName() + OPENING_BRACE + field + CLOSING_BRACE; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ParameterlessFieldCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 5 | 6 | 7 | public class ParameterlessFieldCondition extends AbstractFieldCondition 8 | { 9 | private final ParameterlessConditionType conditionType; 10 | 11 | ParameterlessFieldCondition(final Field field, final ParameterlessConditionType conditionType) 12 | { 13 | super(field); 14 | this.conditionType = conditionType; 15 | } 16 | 17 | ParameterlessFieldCondition(final AbstractFlexibleSearchQueryChainElement parent, final Field field, 18 | final ParameterlessConditionType conditionType) 19 | { 20 | super(parent, field); 21 | this.conditionType = conditionType; 22 | } 23 | 24 | @Override 25 | protected void appendQuery(final StringBuilder sb) 26 | { 27 | super.appendQuery(sb); 28 | 29 | sb.append(SPACE).append(conditionType.getOperator()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/AliasedField.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.ALIAS_AND_FIELD_SEPARATOR; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.CLOSING_BRACKET; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.OPENING_BRACKET; 7 | 8 | 9 | /** 10 | * Represents field reference with a given alias. 11 | */ 12 | public class AliasedField extends SimpleField 13 | { 14 | private final Alias alias; 15 | 16 | AliasedField(final Alias alias, final String fieldName) 17 | { 18 | super(fieldName); 19 | this.alias = alias; 20 | } 21 | 22 | Alias getAlias() 23 | { 24 | return alias; 25 | } 26 | 27 | @Override 28 | public String toString() 29 | { 30 | return OPENING_BRACKET + alias.getAliasValue() + ALIAS_AND_FIELD_SEPARATOR + getFieldName() + CLOSING_BRACKET; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/SelectClause.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchBuilderFieldUtils.buildQueryString; 4 | 5 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 6 | 7 | import java.util.List; 8 | 9 | 10 | public class SelectClause extends AbstractSelectClause 11 | { 12 | private final List parameters; 13 | private final List> resultTypes; 14 | 15 | SelectClause(final List parameters, final List> resultTypes) 16 | { 17 | this.parameters = parameters; 18 | this.resultTypes = resultTypes; 19 | } 20 | 21 | @Override 22 | protected void appendFieldsPart(final StringBuilder sb) 23 | { 24 | sb.append(buildQueryString(parameters)); 25 | } 26 | 27 | @Override 28 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 29 | { 30 | super.configureQuery(flexibleSearchQuery); 31 | 32 | flexibleSearchQuery.setResultClassList(resultTypes); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FlexibleSearchQueryStartChainElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 4 | 5 | import java.util.Map; 6 | 7 | 8 | /** 9 | * First chain element of the query. Helps to enable a no-null policy. 10 | */ 11 | public final class FlexibleSearchQueryStartChainElement extends AbstractFlexibleSearchQueryChainElement 12 | { 13 | public static final FlexibleSearchQueryStartChainElement INSTANCE = new FlexibleSearchQueryStartChainElement(); 14 | 15 | private FlexibleSearchQueryStartChainElement() 16 | { 17 | } 18 | 19 | @Override 20 | protected void appendQuery(final StringBuilder sb) 21 | { 22 | //nothing to do for this class 23 | } 24 | 25 | @Override 26 | protected void addParameters(final Map parameterMap) 27 | { 28 | //nothing to do for this class 29 | } 30 | 31 | @Override 32 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 33 | { 34 | //nothing to do for this class 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/AbstractFieldCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 5 | 6 | 7 | /** 8 | * Abstract condition on the model field (DB table column) of the flexible search query. 9 | */ 10 | public abstract class AbstractFieldCondition extends AbstractCondition 11 | { 12 | protected final Field field; 13 | 14 | protected AbstractFieldCondition(final Field field) 15 | { 16 | super(FlexibleSearchQueryStartChainElement.INSTANCE); 17 | this.field = field; 18 | } 19 | 20 | protected AbstractFieldCondition(final AbstractFlexibleSearchQueryChainElement parent, final Field field) 21 | { 22 | super(parent); 23 | this.field = field; 24 | } 25 | 26 | @Override 27 | protected void appendQuery(final StringBuilder sb) 28 | { 29 | super.appendQuery(sb); 30 | 31 | if (!FlexibleSearchQueryStartChainElement.INSTANCE.equals(parent)) 32 | { 33 | sb.append(SPACE); 34 | } 35 | sb.append(field); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FlexibleSearchQueryInnerChainElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 4 | 5 | import java.util.Map; 6 | 7 | 8 | /** 9 | * Inner chain element, which always has a parent. 10 | */ 11 | public abstract class FlexibleSearchQueryInnerChainElement extends AbstractFlexibleSearchQueryChainElement 12 | { 13 | protected final AbstractFlexibleSearchQueryChainElement parent; 14 | 15 | FlexibleSearchQueryInnerChainElement(final AbstractFlexibleSearchQueryChainElement parent) 16 | { 17 | this.parent = parent; 18 | } 19 | 20 | @Override 21 | protected void appendQuery(final StringBuilder sb) 22 | { 23 | parent.appendQuery(sb); 24 | } 25 | 26 | @Override 27 | protected void addParameters(final Map parameterMap) 28 | { 29 | parent.addParameters(parameterMap); 30 | } 31 | 32 | @Override 33 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 34 | { 35 | parent.configureQuery(flexibleSearchQuery); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FlexibleSearchBuilderFieldUtils.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.FIELD_SEPARATOR; 4 | 5 | import java.util.Collection; 6 | import java.util.Map; 7 | import java.util.stream.Collectors; 8 | 9 | 10 | final class FlexibleSearchBuilderFieldUtils 11 | { 12 | private FlexibleSearchBuilderFieldUtils() 13 | { 14 | } 15 | 16 | static String buildFieldsQueryString(final Collection fields) 17 | { 18 | return fields.stream().map(Object::toString).collect(Collectors.joining(FIELD_SEPARATOR)); 19 | } 20 | 21 | static String buildQueryString(final Iterable parts) 22 | { 23 | return String.join(FIELD_SEPARATOR, parts); 24 | } 25 | 26 | static String createUniqueParameterCode(final Map parameterMap, final String fieldName) 27 | { 28 | String uniqueCode; 29 | int counter = 0; 30 | do 31 | { 32 | counter++; 33 | uniqueCode = fieldName + counter; 34 | } 35 | while (parameterMap.containsKey(uniqueCode)); 36 | 37 | return uniqueCode; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/GroupByClause.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchBuilderFieldUtils.buildFieldsQueryString; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.GROUP_BY; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 6 | 7 | import java.util.List; 8 | 9 | 10 | public class GroupByClause extends TerminateQueryChainElement implements OrderByAcceptable 11 | { 12 | private final List fields; 13 | 14 | GroupByClause(final AbstractFlexibleSearchQueryChainElement parent, final List fields) 15 | { 16 | super(parent); 17 | this.fields = fields; 18 | } 19 | 20 | public HavingClause having(final AbstractCondition condition) 21 | { 22 | return new HavingClause(this, condition); 23 | } 24 | 25 | @Override 26 | protected void appendQuery(final StringBuilder sb) 27 | { 28 | super.appendQuery(sb); 29 | 30 | sb.append(SPACE).append(GROUP_BY).append(SPACE).append(buildFieldsQueryString(fields)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FieldToFieldCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | /** 4 | * Condition on two model fields (DB table columns) of the flexible search query, e.g. condition to check if field 5 | * equals to another field. 6 | */ 7 | public class FieldToFieldCondition extends AbstractFieldCondition 8 | { 9 | private final RegularParameterConditionType conditionType; 10 | private final Field secondField; 11 | 12 | FieldToFieldCondition(final Field field, final RegularParameterConditionType conditionType, final Field secondField) 13 | { 14 | super(field); 15 | this.conditionType = conditionType; 16 | this.secondField = secondField; 17 | } 18 | 19 | FieldToFieldCondition(final AbstractFlexibleSearchQueryChainElement parent, final Field field, 20 | final RegularParameterConditionType conditionType, final Field secondField) 21 | { 22 | super(parent, field); 23 | this.conditionType = conditionType; 24 | this.secondField = secondField; 25 | } 26 | 27 | @Override 28 | protected void appendQuery(final StringBuilder sb) 29 | { 30 | super.appendQuery(sb); 31 | 32 | sb.append(conditionType.getOperator()).append(secondField); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FieldWithType.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | /** 4 | * Maps field to it's type. 5 | */ 6 | public final class FieldWithType extends ValueWithType 7 | { 8 | private final Field field; 9 | 10 | private FieldWithType(final Field field, final Class type) 11 | { 12 | super(type); 13 | this.field = field; 14 | } 15 | 16 | /** 17 | * Creates this field-to-type mapped class. 18 | * 19 | * @param field 20 | * field 21 | * @param type 22 | * field type 23 | * @return new FieldWithType instance of given parameters 24 | */ 25 | public static FieldWithType of(final String field, final Class type) 26 | { 27 | return new FieldWithType(new SimpleField(field), type); 28 | } 29 | 30 | /** 31 | * Creates this field-to-type mapping class. 32 | * 33 | * @param field 34 | * field 35 | * @param type 36 | * field type 37 | * @return new FieldWithType instance of given parameters 38 | */ 39 | public static FieldWithType of(final AliasedField field, final Class type) 40 | { 41 | return new FieldWithType(field, type); 42 | } 43 | 44 | @Override 45 | public String getValue() 46 | { 47 | return field.toString(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/AbstractSelectClause.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SELECT; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 6 | 7 | 8 | /** 9 | * 'SELECT' clause of the flexible search query, used to query the models. 10 | */ 11 | public abstract class AbstractSelectClause extends FlexibleSearchQueryInnerChainElement 12 | { 13 | 14 | AbstractSelectClause() 15 | { 16 | super(FlexibleSearchQueryStartChainElement.INSTANCE); 17 | } 18 | 19 | /** 20 | * Builds "FROM" statement of the query. 21 | * 22 | * @param lastClauseElement 23 | * ending from clause element, which is wrapping all others (if any) 24 | * @return "FROM" clause of the query 25 | */ 26 | public FromClause from(final AbstractFromClauseElement lastClauseElement) 27 | { 28 | return new FromClause(this, lastClauseElement); 29 | } 30 | 31 | @Override 32 | protected void appendQuery(final StringBuilder sb) 33 | { 34 | super.appendQuery(sb); 35 | sb.append(SELECT).append(SPACE); 36 | appendFieldsPart(sb); 37 | } 38 | 39 | protected abstract void appendFieldsPart(StringBuilder sb); 40 | } 41 | -------------------------------------------------------------------------------- /extensioninfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /resources/flexiblesearchbuilder/flexiblesearchbuilder-testclasses.xml: -------------------------------------------------------------------------------- 1 | org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.JoinsTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.UnaryQueryConditionsTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.CombineConditionsTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.CollectionAndQueryConditionsTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchBuilderBasicUsageTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.HavingClauseTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterFieldConditionsTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterAliasedFieldConditionsTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.SqlFunctionsTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.GroupByClauseTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterLessFieldConditionsTestorg.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilderTest -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/AbstractTableFromClauseElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.core.model.ItemModel; 4 | 5 | import java.util.Optional; 6 | 7 | 8 | /** 9 | * Element of the 'FROM' clause that holds a model type, marking the database table. 10 | */ 11 | public abstract class AbstractTableFromClauseElement extends AbstractFromClauseElement 12 | { 13 | private final String typeCode; 14 | 15 | AbstractTableFromClauseElement(final AbstractFlexibleSearchQueryChainElement parent, final Class clazz) 16 | { 17 | super(parent); 18 | this.typeCode = fetchTypeCode(clazz); 19 | } 20 | 21 | AbstractTableFromClauseElement(final AbstractFlexibleSearchQueryChainElement parent, final String typeCode) 22 | { 23 | super(parent); 24 | this.typeCode = typeCode; 25 | } 26 | 27 | protected String getTypecode() 28 | { 29 | return typeCode; 30 | } 31 | 32 | private static String fetchTypeCode(final Class clazz) 33 | { 34 | try 35 | { 36 | return clazz.getField("_TYPECODE").get(Optional.empty()).toString(); 37 | } 38 | catch (final IllegalAccessException | NoSuchFieldException e) 39 | { 40 | throw new IllegalStateException( 41 | "Failed to get _TYPECODE field from class " + clazz.getName() + " during building flexible search query.", e); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/WrapperCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 4 | 5 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 6 | 7 | import java.util.Map; 8 | 9 | 10 | /** 11 | * A proxy for a field condition. 12 | */ 13 | public class WrapperCondition extends AbstractCondition 14 | { 15 | private final AbstractCondition original; 16 | 17 | 18 | WrapperCondition(final AbstractFlexibleSearchQueryChainElement parent, final AbstractCondition condition) 19 | { 20 | super(parent); 21 | this.original = condition; 22 | } 23 | 24 | @Override 25 | protected void appendQuery(final StringBuilder sb) 26 | { 27 | super.appendQuery(sb); 28 | 29 | sb.append(SPACE);//needed because wrapped condition doesn't have a parent and thus doesn't add space 30 | original.appendQuery(sb); 31 | } 32 | 33 | @Override 34 | protected void addParameters(final Map parameterMap) 35 | { 36 | super.addParameters(parameterMap); 37 | 38 | original.addParameters(parameterMap); 39 | } 40 | 41 | @Override 42 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 43 | { 44 | super.configureQuery(flexibleSearchQuery); 45 | 46 | original.configureQuery(flexibleSearchQuery); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/HavingClause.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.HAVING; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 5 | 6 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 7 | 8 | import java.util.Map; 9 | 10 | 11 | public class HavingClause extends TerminateQueryChainElement implements OrderByAcceptable 12 | { 13 | private final AbstractCondition condition; 14 | 15 | HavingClause(final AbstractFlexibleSearchQueryChainElement parent, final AbstractCondition condition) 16 | { 17 | super(parent); 18 | this.condition = condition; 19 | } 20 | 21 | @Override 22 | protected void appendQuery(final StringBuilder sb) 23 | { 24 | super.appendQuery(sb); 25 | 26 | sb.append(SPACE).append(HAVING).append(SPACE); 27 | condition.appendQuery(sb); 28 | } 29 | 30 | @Override 31 | protected void addParameters(final Map parameterMap) 32 | { 33 | super.addParameters(parameterMap); 34 | 35 | condition.addParameters(parameterMap); 36 | } 37 | 38 | @Override 39 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 40 | { 41 | super.configureQuery(flexibleSearchQuery); 42 | 43 | condition.configureQuery(flexibleSearchQuery); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/HavingClauseTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.customCondition; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectCustom; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 6 | import static org.junit.Assert.assertEquals; 7 | 8 | import de.hybris.platform.core.model.product.ProductModel; 9 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 10 | 11 | import java.util.Arrays; 12 | 13 | import org.junit.Test; 14 | 15 | public class HavingClauseTest 16 | { 17 | @Test 18 | public void testHavingClause() 19 | { 20 | final FlexibleSearchQuery fQuery = 21 | selectCustom( 22 | "COUNT({pk}),{name}", Long.class, String.class 23 | ) 24 | .from(table(ProductModel.class)) 25 | .groupBy(ProductModel.NAME) 26 | .having(customCondition("COUNT({pk})>2")) 27 | .build(); 28 | assertEquals("Query does not match", "SELECT COUNT({pk}),{name} FROM {Product} GROUP BY {name} HAVING COUNT({pk})>2", fQuery.getQuery()); 29 | assertEquals("Wrong number of query parameters", 0, fQuery.getQueryParameters().size()); 30 | assertEquals("Result classes don't match", Arrays.asList(Long.class, String.class), fQuery.getResultClassList()); 31 | } 32 | } -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/AbstractJoinElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 4 | 5 | import de.hybris.platform.core.model.ItemModel; 6 | 7 | 8 | /** 9 | * Abstract 'JOIN' element of the flexible search query. 10 | */ 11 | public abstract class AbstractJoinElement extends AbstractTableFromClauseElement 12 | { 13 | private final String joinStatement; 14 | 15 | AbstractJoinElement(final AbstractFlexibleSearchQueryChainElement parent, final Class clazz, 16 | final String joinStatement) 17 | { 18 | super(parent, clazz); 19 | this.joinStatement = joinStatement; 20 | } 21 | 22 | AbstractJoinElement(final AbstractFlexibleSearchQueryChainElement parent, final String typeCode, final String joinStatement) 23 | { 24 | super(parent, typeCode); 25 | this.joinStatement = joinStatement; 26 | } 27 | 28 | /** 29 | * Marks the table with given alias. 30 | * 31 | * @param alias 32 | * alias 33 | * @return alias query element 34 | */ 35 | public JoinAliasElement as(final Alias alias) 36 | { 37 | return new JoinAliasElement(this, alias); 38 | } 39 | 40 | @Override 41 | protected void appendQuery(final StringBuilder sb) 42 | { 43 | super.appendQuery(sb); 44 | 45 | sb.append(SPACE).append(joinStatement).append(SPACE).append(getTypecode()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FlexibleSearchQueryConstants.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public final class FlexibleSearchQueryConstants 4 | { 5 | public static final String SPACE = " "; 6 | public static final String QUESTION_MARK = "?"; 7 | public static final String OPENING_BRACKET = "{"; 8 | public static final String CLOSING_BRACKET = "}"; 9 | public static final String OPENING_BRACE = "("; 10 | public static final String CLOSING_BRACE = ")"; 11 | public static final String INNER_QUERY_OPENING_BRACKETS = OPENING_BRACE + OPENING_BRACKET + OPENING_BRACKET; 12 | public static final String INNER_QUERY_CLOSING_BRACKETS = CLOSING_BRACKET + CLOSING_BRACKET + CLOSING_BRACE; 13 | public static final String ALIAS_AND_FIELD_SEPARATOR = "."; 14 | public static final String FIELD_SEPARATOR = ","; 15 | public static final String SELECT = "SELECT"; 16 | public static final String FROM = "FROM"; 17 | public static final String AS = "AS"; 18 | public static final String JOIN = "JOIN"; 19 | public static final String ON = "ON"; 20 | public static final String LEFT_JOIN = "LEFT JOIN"; 21 | public static final String RIGHT_JOIN = "RIGHT JOIN"; 22 | public static final String WHERE = "WHERE"; 23 | public static final String ORDER_BY = "ORDER BY"; 24 | public static final String GROUP_BY = "GROUP BY"; 25 | public static final String HAVING = "HAVING"; 26 | 27 | 28 | private FlexibleSearchQueryConstants() 29 | { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/WhereClause.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.WHERE; 5 | 6 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 7 | 8 | import java.util.Map; 9 | 10 | 11 | /** 12 | * 'WHERE' clause of a flexible search query. 13 | */ 14 | public class WhereClause extends TerminateQueryChainElement implements OrderByAcceptable, GroupByAcceptable 15 | { 16 | private final AbstractCondition lastCondition; 17 | 18 | WhereClause(final FromClause fromClause, final AbstractCondition condition) 19 | { 20 | super(fromClause); 21 | this.lastCondition = condition; 22 | } 23 | 24 | @Override 25 | protected void appendQuery(final StringBuilder sb) 26 | { 27 | super.appendQuery(sb); 28 | 29 | sb.append(SPACE).append(WHERE).append(SPACE); 30 | lastCondition.appendQuery(sb); 31 | } 32 | 33 | @Override 34 | protected void addParameters(final Map parameterMap) 35 | { 36 | super.addParameters(parameterMap); 37 | 38 | lastCondition.addParameters(parameterMap); 39 | } 40 | 41 | @Override 42 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 43 | { 44 | super.configureQuery(flexibleSearchQuery); 45 | 46 | lastCondition.configureQuery(flexibleSearchQuery); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/TerminateQueryChainElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | 9 | /** 10 | * Part of the flexible search query, that can be the last one, thus having the build() method. 11 | */ 12 | public abstract class TerminateQueryChainElement extends FlexibleSearchQueryInnerChainElement 13 | { 14 | TerminateQueryChainElement(final AbstractFlexibleSearchQueryChainElement parent) 15 | { 16 | super(parent); 17 | } 18 | 19 | /** 20 | * Builds the flexible search query with all chained elements inside current element. Puts all parameters (if any) into 21 | * query as well. 22 | * 23 | * @return flexible search query 24 | */ 25 | public FlexibleSearchQuery build() 26 | { 27 | final Map parameters = buildParameters(); 28 | final String query = buildQuery(); 29 | final FlexibleSearchQuery fQuery = new FlexibleSearchQuery(query, parameters); 30 | 31 | configureQuery(fQuery); 32 | 33 | return fQuery; 34 | } 35 | 36 | private Map buildParameters() 37 | { 38 | final Map parameterMap = new HashMap<>(); 39 | addParameters(parameterMap); 40 | return parameterMap; 41 | } 42 | 43 | private String buildQuery() 44 | { 45 | final StringBuilder sb = new StringBuilder(); 46 | appendQuery(sb);//applies all clauses internally 47 | return sb.toString(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/OrderByClause.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.FIELD_SEPARATOR; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.ORDER_BY; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | 11 | public class OrderByClause extends TerminateQueryChainElement implements OrderByAcceptable 12 | { 13 | private final List fields; 14 | private final OrderBySortingType sortingType; 15 | 16 | OrderByClause(final AbstractFlexibleSearchQueryChainElement parent, final List fields, 17 | final OrderBySortingType sortingType) 18 | { 19 | super(parent); 20 | this.fields = fields; 21 | this.sortingType = sortingType; 22 | } 23 | 24 | @Override 25 | protected void appendQuery(final StringBuilder sb) 26 | { 27 | super.appendQuery(sb); 28 | 29 | if ((parent instanceof OrderByClause)) 30 | { 31 | sb.append(FIELD_SEPARATOR); 32 | } else 33 | { 34 | sb.append(SPACE).append(ORDER_BY).append(SPACE); 35 | } 36 | sb.append(joinFields()).append(SPACE).append(sortingType.getOperator()); 37 | } 38 | 39 | private String joinFields() 40 | { 41 | final String delimiter = SPACE + sortingType.getOperator() + FIELD_SEPARATOR; 42 | return fields.stream().map(Object::toString).collect(Collectors.joining(delimiter)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/Alias.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.core.model.ItemModel; 4 | 5 | 6 | /** 7 | * Represents flexible search query alias (usually used for tables). 8 | */ 9 | public class Alias 10 | { 11 | private static final String TARGET = "target"; 12 | private static final String SOURCE = "source"; 13 | 14 | private final String aliasValue; 15 | 16 | public Alias(final String aliasValue) 17 | { 18 | this.aliasValue = aliasValue; 19 | } 20 | 21 | /** 22 | * Creates alias from a given string value. 23 | * 24 | * @param aliasValue 25 | * alias string 26 | * @return alias 27 | */ 28 | public static Alias alias(final String aliasValue) 29 | { 30 | return new Alias(aliasValue); 31 | } 32 | 33 | /** 34 | * Creates field reference with this alias. 35 | * 36 | * @param fieldName 37 | * field name to be used 38 | * @return field reference using this alias 39 | */ 40 | public AliasedField field(final String fieldName) 41 | { 42 | return new AliasedField(this, fieldName); 43 | } 44 | 45 | protected String getAliasValue() 46 | { 47 | return aliasValue; 48 | } 49 | 50 | /** 51 | * Creates PK field reference with this alias. 52 | * 53 | * @return PK field reference using this alias 54 | */ 55 | public AliasedField pk() 56 | { 57 | return new AliasedField(this, ItemModel.PK); 58 | } 59 | 60 | /** 61 | * Creates aliased "target" field. 62 | * 63 | * @return "target" aliased field 64 | */ 65 | public AliasedField target() 66 | { 67 | return new AliasedField(this, TARGET); 68 | } 69 | 70 | /** 71 | * Creates aliased "source" field. 72 | * 73 | * @return "source" aliased field 74 | */ 75 | public AliasedField source() 76 | { 77 | return new AliasedField(this, SOURCE); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/OrderByAcceptable.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.OrderBySortingType.ASC; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.OrderBySortingType.DESC; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.VarargCollectionUtils.toList; 6 | 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | 11 | public interface OrderByAcceptable 12 | { 13 | default OrderByClause orderByAsc(final String firstField, final String... restFields) 14 | { 15 | final List fields = toList(firstField, restFields).stream().map(SimpleField::new) 16 | .collect(Collectors.toList()); 17 | return new OrderByClause((AbstractFlexibleSearchQueryChainElement) this, fields, ASC); 18 | } 19 | 20 | default OrderByClause orderByAsc(final FieldRepresentation firstField, final FieldRepresentation... restFields) 21 | { 22 | final List fields = toList(firstField, restFields); 23 | return new OrderByClause((AbstractFlexibleSearchQueryChainElement) this, fields, ASC); 24 | } 25 | 26 | default OrderByClause orderByDesc(final String firstField, final String... restFields) 27 | { 28 | final List fields = toList(firstField, restFields).stream().map(SimpleField::new) 29 | .collect(Collectors.toList()); 30 | return new OrderByClause((AbstractFlexibleSearchQueryChainElement) this, fields, DESC); 31 | } 32 | 33 | default OrderByClause orderByDesc(final FieldRepresentation firstField, final FieldRepresentation... restFields) 34 | { 35 | final List fields = toList(firstField, restFields); 36 | return new OrderByClause((AbstractFlexibleSearchQueryChainElement) this, fields, DESC); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/BraceConditionWrapper.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.CLOSING_BRACE; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.OPENING_BRACE; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 6 | 7 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 8 | 9 | import java.util.Map; 10 | 11 | 12 | public class BraceConditionWrapper extends AbstractCondition 13 | { 14 | private final AbstractCondition wrappedCondition; 15 | 16 | BraceConditionWrapper(final AbstractFlexibleSearchQueryChainElement parent, final AbstractCondition wrappedCondition) 17 | { 18 | super(parent); 19 | this.wrappedCondition = wrappedCondition; 20 | } 21 | 22 | BraceConditionWrapper(final AbstractCondition wrappedCondition) 23 | { 24 | super(FlexibleSearchQueryStartChainElement.INSTANCE); 25 | this.wrappedCondition = wrappedCondition; 26 | } 27 | 28 | @Override 29 | protected void appendQuery(final StringBuilder sb) 30 | { 31 | super.appendQuery(sb); 32 | 33 | if (!FlexibleSearchQueryStartChainElement.INSTANCE.equals(parent)) 34 | { 35 | sb.append(SPACE); 36 | } 37 | sb.append(OPENING_BRACE); 38 | wrappedCondition.appendQuery(sb); 39 | sb.append(CLOSING_BRACE); 40 | } 41 | 42 | @Override 43 | protected void addParameters(final Map parameterMap) 44 | { 45 | super.addParameters(parameterMap); 46 | 47 | wrappedCondition.addParameters(parameterMap); 48 | } 49 | 50 | @Override 51 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 52 | { 53 | super.configureQuery(flexibleSearchQuery); 54 | 55 | wrappedCondition.configureQuery(flexibleSearchQuery); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ParameterLessFieldConditionsTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static de.hybris.platform.core.model.product.ProductModel.NAME; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.condition; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectFrom; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterlessConditionType.IS_NOT_NULL; 7 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterlessConditionType.IS_NULL; 8 | import static org.junit.Assert.assertEquals; 9 | import static org.junit.Assert.assertTrue; 10 | 11 | import de.hybris.bootstrap.annotations.UnitTest; 12 | import de.hybris.platform.core.model.product.ProductModel; 13 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 14 | 15 | import org.junit.Test; 16 | 17 | 18 | @UnitTest 19 | public class ParameterLessFieldConditionsTest 20 | { 21 | 22 | @Test 23 | public void testIsNullCondition() 24 | { 25 | final FlexibleSearchQuery query = 26 | selectFrom(ProductModel.class) 27 | .where( 28 | condition(NAME, IS_NULL) 29 | ) 30 | .build(); 31 | 32 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NULL", query.getQuery()); 33 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 34 | } 35 | 36 | @Test 37 | public void testIsNotNullCondition() 38 | { 39 | final FlexibleSearchQuery query = 40 | selectFrom(ProductModel.class) 41 | .where( 42 | condition(NAME, IS_NOT_NULL) 43 | ) 44 | .build(); 45 | 46 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NOT NULL", query.getQuery()); 47 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/JoinAliasElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.AS; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_EQUAL_TO; 6 | 7 | 8 | public class JoinAliasElement extends FlexibleSearchQueryInnerChainElement 9 | { 10 | private final Alias alias; 11 | 12 | JoinAliasElement(final AbstractFlexibleSearchQueryChainElement parent, final Alias alias) 13 | { 14 | super(parent); 15 | this.alias = alias; 16 | } 17 | 18 | /** 19 | * Marks on which fields table joining should happen. 20 | * 21 | * @param field1 22 | * first aliased field 23 | * @param condition 24 | * condition 25 | * @param field2 26 | * second aliased field 27 | * @return "join on" query element 28 | */ 29 | public JoinOnElement on(final AliasedField field1, final RegularParameterConditionType condition, final AliasedField field2) 30 | { 31 | return new JoinOnElement(this, new FieldToFieldCondition(field1, condition, field2)); 32 | } 33 | 34 | /** 35 | * Marks on which fields table joining should happen. Takes RegularParameterConditionType.IS_EQUAL_TO as the default 36 | * condition type. 37 | * 38 | * @param field1 39 | * first aliased field 40 | * @param field2 41 | * second aliased field 42 | * @return "join on" query element 43 | */ 44 | public JoinOnElement on(final AliasedField field1, final AliasedField field2) 45 | { 46 | return new JoinOnElement(this, new FieldToFieldCondition(field1, IS_EQUAL_TO, field2)); 47 | } 48 | 49 | @Override 50 | protected void appendQuery(final StringBuilder sb) 51 | { 52 | super.appendQuery(sb); 53 | sb.append(SPACE).append(AS).append(SPACE).append(alias.getAliasValue()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/InnerQueryFieldCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.INNER_QUERY_CLOSING_BRACKETS; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.INNER_QUERY_OPENING_BRACKETS; 5 | 6 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 7 | 8 | import java.util.Map; 9 | 10 | 11 | public class InnerQueryFieldCondition extends AbstractFieldCondition 12 | { 13 | private final CollectionAndQueryConditionType conditionType; 14 | private final TerminateQueryChainElement innerQuery; 15 | 16 | protected InnerQueryFieldCondition(final Field field, final CollectionAndQueryConditionType conditionType, 17 | final TerminateQueryChainElement innerQuery) 18 | { 19 | super(field); 20 | this.conditionType = conditionType; 21 | this.innerQuery = innerQuery; 22 | } 23 | 24 | protected InnerQueryFieldCondition(final AbstractFlexibleSearchQueryChainElement parent, final Field field, 25 | final CollectionAndQueryConditionType conditionType, final TerminateQueryChainElement innerQuery) 26 | { 27 | super(parent, field); 28 | this.conditionType = conditionType; 29 | this.innerQuery = innerQuery; 30 | } 31 | 32 | @Override 33 | protected void appendQuery(final StringBuilder sb) 34 | { 35 | super.appendQuery(sb); 36 | 37 | sb.append(conditionType.getOperator()).append(INNER_QUERY_OPENING_BRACKETS); 38 | innerQuery.appendQuery(sb); 39 | sb.append(INNER_QUERY_CLOSING_BRACKETS); 40 | } 41 | 42 | @Override 43 | protected void addParameters(final Map parameterMap) 44 | { 45 | super.addParameters(parameterMap); 46 | 47 | innerQuery.addParameters(parameterMap); 48 | } 49 | 50 | @Override 51 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 52 | { 53 | super.configureQuery(flexibleSearchQuery); 54 | 55 | innerQuery.configureQuery(flexibleSearchQuery); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FromClause.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.CLOSING_BRACKET; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.FROM; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.OPENING_BRACKET; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 7 | 8 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 9 | 10 | import java.util.Map; 11 | 12 | 13 | /** 14 | * 'FROM' clause of the flexible search query. 15 | */ 16 | public class FromClause extends TerminateQueryChainElement implements OrderByAcceptable, GroupByAcceptable 17 | { 18 | private final AbstractFromClauseElement lastElement; 19 | 20 | FromClause(final AbstractFlexibleSearchQueryChainElement parent, final AbstractFromClauseElement lastElement) 21 | { 22 | super(parent); 23 | this.lastElement = lastElement; 24 | } 25 | 26 | /** 27 | * Creates 'WHERE' clause of the query. 28 | * 29 | * @param condition 30 | * condition (last condition in condition chain) 31 | * @return 'WHERE' clause 32 | */ 33 | public WhereClause where(final AbstractCondition condition) 34 | { 35 | return new WhereClause(this, condition); 36 | } 37 | 38 | @Override 39 | protected void appendQuery(final StringBuilder sb) 40 | { 41 | super.appendQuery(sb); 42 | 43 | sb.append(SPACE).append(FROM).append(SPACE).append(OPENING_BRACKET); 44 | lastElement.appendQuery(sb); 45 | sb.append(CLOSING_BRACKET); 46 | } 47 | 48 | @Override 49 | protected void addParameters(final Map parameterMap) 50 | { 51 | super.addParameters(parameterMap); 52 | 53 | lastElement.addParameters(parameterMap); 54 | } 55 | 56 | @Override 57 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 58 | { 59 | super.configureQuery(flexibleSearchQuery); 60 | 61 | lastElement.configureQuery(flexibleSearchQuery); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/InnerQueryUnaryCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.INNER_QUERY_CLOSING_BRACKETS; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.INNER_QUERY_OPENING_BRACKETS; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 6 | 7 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 8 | 9 | import java.util.Map; 10 | 11 | 12 | public class InnerQueryUnaryCondition extends AbstractCondition 13 | { 14 | private final UnaryQueryConditionType queryConditionType; 15 | private final TerminateQueryChainElement innerQuery; 16 | 17 | InnerQueryUnaryCondition(final UnaryQueryConditionType queryConditionType, final TerminateQueryChainElement innerQuery) 18 | { 19 | super(FlexibleSearchQueryStartChainElement.INSTANCE); 20 | this.innerQuery = innerQuery; 21 | this.queryConditionType = queryConditionType; 22 | } 23 | 24 | InnerQueryUnaryCondition(final AbstractFlexibleSearchQueryChainElement parentElement, 25 | final UnaryQueryConditionType queryConditionType, final TerminateQueryChainElement innerQuery) 26 | { 27 | super(parentElement); 28 | this.innerQuery = innerQuery; 29 | this.queryConditionType = queryConditionType; 30 | } 31 | 32 | @Override 33 | protected void appendQuery(final StringBuilder sb) 34 | { 35 | super.appendQuery(sb); 36 | 37 | if (!FlexibleSearchQueryStartChainElement.INSTANCE.equals(parent)) 38 | { 39 | sb.append(SPACE); 40 | } 41 | sb.append(queryConditionType.getOperator()).append(SPACE).append(INNER_QUERY_OPENING_BRACKETS); 42 | innerQuery.appendQuery(sb); 43 | sb.append(INNER_QUERY_CLOSING_BRACKETS); 44 | } 45 | 46 | @Override 47 | protected void addParameters(final Map parameterMap) 48 | { 49 | super.addParameters(parameterMap); 50 | 51 | innerQuery.addParameters(parameterMap); 52 | } 53 | 54 | @Override 55 | protected void configureQuery(final FlexibleSearchQuery flexibleSearchQuery) 56 | { 57 | super.configureQuery(flexibleSearchQuery); 58 | 59 | innerQuery.configureQuery(flexibleSearchQuery); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/UnaryQueryConditionsTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.condition; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectCustom; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectFrom; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 7 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.UnaryQueryConditionType.EXISTS; 8 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.UnaryQueryConditionType.NOT_EXISTS; 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertTrue; 11 | 12 | import de.hybris.bootstrap.annotations.UnitTest; 13 | import de.hybris.platform.core.model.product.ProductModel; 14 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 15 | 16 | import org.junit.Test; 17 | 18 | 19 | @UnitTest 20 | public class UnaryQueryConditionsTest 21 | { 22 | private final TerminateQueryChainElement commonInnerQuery = 23 | selectCustom("UPPER({code})") 24 | .from( 25 | table(ProductModel.class) 26 | ); 27 | 28 | @Test 29 | public void testNotExistsQueryCondition() 30 | { 31 | final FlexibleSearchQuery query = 32 | selectFrom(ProductModel.class) 33 | .where( 34 | condition(NOT_EXISTS, commonInnerQuery) 35 | ) 36 | .build(); 37 | 38 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE NOT EXISTS " + 39 | "({{SELECT UPPER({code}) FROM {Product}}})", query.getQuery()); 40 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 41 | } 42 | 43 | @Test 44 | public void testExistsQueryCondition() 45 | { 46 | final FlexibleSearchQuery query = 47 | selectFrom(ProductModel.class) 48 | .where( 49 | condition(EXISTS, commonInnerQuery) 50 | ) 51 | .build(); 52 | 53 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE EXISTS " + 54 | "({{SELECT UPPER({code}) FROM {Product}}})", query.getQuery()); 55 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/JoinableFromClauseElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import de.hybris.platform.core.model.ItemModel; 4 | 5 | 6 | public abstract class JoinableFromClauseElement extends AbstractFromClauseElement 7 | { 8 | JoinableFromClauseElement(final AbstractFlexibleSearchQueryChainElement parent) 9 | { 10 | super(parent); 11 | } 12 | 13 | /** 14 | * Join another table using given item type. 15 | * 16 | * @param clazz 17 | * model type 18 | * @return join query element 19 | */ 20 | public InnerJoinElement join(final Class clazz) 21 | { 22 | return new InnerJoinElement(this, clazz); 23 | } 24 | 25 | /** 26 | * Join another table using given typecode. This method is designed in order to support relations, but regular model 27 | * typecodes can be used as well. 28 | * 29 | * @param typeCode 30 | * type code, e.g. 31 | * @return join query element 32 | */ 33 | public InnerJoinElement join(final String typeCode) 34 | { 35 | return new InnerJoinElement(this, typeCode); 36 | } 37 | 38 | /** 39 | * Left join another table using given item type. 40 | * 41 | * @param clazz 42 | * model type 43 | * @return join query element 44 | */ 45 | public LeftJoinElement leftJoin(final Class clazz) 46 | { 47 | return new LeftJoinElement(this, clazz); 48 | } 49 | 50 | /** 51 | * Left join another table using given item type. This method is designed in order to support relations, but regular 52 | * model typecodes can be used as well. 53 | * 54 | * @param typeCode 55 | * type code, e.g. 56 | * @return join query element 57 | */ 58 | public LeftJoinElement leftJoin(final String typeCode) 59 | { 60 | return new LeftJoinElement(this, typeCode); 61 | } 62 | 63 | /** 64 | * Right join another table using given item type. 65 | * 66 | * @param clazz 67 | * model type 68 | * @return join query element 69 | */ 70 | public RightJoinElement rightJoin(final Class clazz) 71 | { 72 | return new RightJoinElement(this, clazz); 73 | } 74 | 75 | /** 76 | * Right join another table using given item type. 77 | * 78 | * @param typeCode 79 | * type code, e.g. 80 | * @return join query element 81 | */ 82 | public RightJoinElement rightJoin(final String typeCode) 83 | { 84 | return new RightJoinElement(this, typeCode); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ParameterFieldCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchBuilderFieldUtils.createUniqueParameterCode; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.CLOSING_BRACE; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.OPENING_BRACE; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.QUESTION_MARK; 7 | 8 | import java.util.Collection; 9 | import java.util.Map; 10 | 11 | 12 | /** 13 | * Condition on the model field (DB table column) of the flexible search query, that has one parameter, e.g. condition 14 | * to check if field equals to given value. 15 | */ 16 | public class ParameterFieldCondition extends AbstractFieldCondition 17 | { 18 | private final ParameterConditionType conditionType; 19 | private final Object conditionParameter; 20 | private String parameterCode; 21 | 22 | ParameterFieldCondition(final Field field, final ParameterConditionType conditionType, final Object conditionParameter) 23 | { 24 | super(field); 25 | this.conditionType = conditionType; 26 | this.conditionParameter = conditionParameter; 27 | } 28 | 29 | ParameterFieldCondition(final AbstractFlexibleSearchQueryChainElement parent, final Field field, 30 | final ParameterConditionType conditionType, final Object conditionParameter) 31 | { 32 | super(parent, field); 33 | this.conditionType = conditionType; 34 | this.conditionParameter = conditionParameter; 35 | } 36 | 37 | @Override 38 | protected void appendQuery(final StringBuilder sb) 39 | { 40 | super.appendQuery(sb); 41 | 42 | final boolean collectionParameter = conditionParameter instanceof Collection; 43 | 44 | sb.append(conditionType.getOperator()); 45 | if (collectionParameter) 46 | { 47 | sb.append(OPENING_BRACE); 48 | } 49 | sb.append(QUESTION_MARK).append(parameterCode); 50 | if (collectionParameter) 51 | { 52 | sb.append(CLOSING_BRACE); 53 | } 54 | } 55 | 56 | @Override 57 | protected void addParameters(final Map parameterMap) 58 | { 59 | super.addParameters(parameterMap); 60 | 61 | this.parameterCode = createUniqueParameterCode(parameterMap, this.field.getFieldName()); 62 | parameterMap.put(parameterCode, conditionParameter); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FlexibleSearchQueryBuilderTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FieldWithType.of; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.select; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectCustom; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectFrom; 7 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 8 | import static org.junit.Assert.assertEquals; 9 | 10 | import de.hybris.bootstrap.annotations.UnitTest; 11 | import de.hybris.platform.core.model.product.ProductModel; 12 | import de.hybris.platform.core.model.product.UnitModel; 13 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 14 | 15 | import java.util.Arrays; 16 | import java.util.Collections; 17 | 18 | import org.junit.Test; 19 | 20 | 21 | @UnitTest 22 | public class FlexibleSearchQueryBuilderTest 23 | { 24 | 25 | @Test 26 | public void testSelectFrom() 27 | { 28 | final FlexibleSearchQuery query = selectFrom(ProductModel.class).build(); 29 | 30 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product}", query.getQuery()); 31 | } 32 | 33 | @Test 34 | public void testSelectFieldsWithType() 35 | { 36 | final FlexibleSearchQuery query = 37 | select(of(ProductModel.CODE, String.class), of(ProductModel.UNIT, UnitModel.class)) 38 | .from(table(ProductModel.class)) 39 | .build(); 40 | 41 | assertEquals("Query is not as expected", "SELECT {code},{unit} FROM {Product}", query.getQuery()); 42 | assertEquals("Result types don't match", Arrays.asList(String.class, UnitModel.class), query.getResultClassList()); 43 | } 44 | 45 | @Test 46 | public void testSelectCustom() 47 | { 48 | final FlexibleSearchQuery query = 49 | selectCustom("DISTINCT {name}", String.class) 50 | .from(table(ProductModel.class)) 51 | .build(); 52 | 53 | assertEquals("Query is not as expected", "SELECT DISTINCT {name} FROM {Product}", query.getQuery()); 54 | assertEquals("Result types don't match", Collections.singletonList(String.class), query.getResultClassList()); 55 | } 56 | 57 | @Test 58 | public void testSelectWithAlias() 59 | { 60 | final Alias p = new Alias("p"); 61 | final FlexibleSearchQuery query = select(p).from(table(ProductModel.class).as(p)).build(); 62 | 63 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p}", query.getQuery()); 64 | } 65 | } -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FlexibleSearchQueryBuilder.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.VarargCollectionUtils.toStream; 5 | 6 | import de.hybris.platform.core.model.ItemModel; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | 13 | public final class FlexibleSearchQueryBuilder 14 | { 15 | private FlexibleSearchQueryBuilder() 16 | { 17 | } 18 | 19 | /** 20 | * Builds select statement from given model type. 21 | * 22 | * @param clazz 23 | * model type 24 | * @return from clause with given model type (which is wrapping select clause inside it) 25 | */ 26 | public static FromClause selectFrom(final Class clazz) 27 | { 28 | return new FromClause(new ModelSelectClause(), table(clazz)); 29 | } 30 | 31 | /** 32 | * Builds select clause of given fields. Applies field types to flexible search query. 33 | * 34 | * @param firstValueWithType 35 | * first field-to-type mapping (which requires at least one argument) 36 | * @param restFieldsWithTypes 37 | * rest fields and their types 38 | * @return select clause 39 | */ 40 | public static SelectClause select(final ValueWithType firstValueWithType, final ValueWithType... restFieldsWithTypes) 41 | { 42 | final List selectParameters = toStream(firstValueWithType, restFieldsWithTypes).map(ValueWithType::getValue) 43 | .collect(Collectors.toList()); 44 | final List> types = toStream(firstValueWithType, restFieldsWithTypes).map(ValueWithType::getType) 45 | .collect(Collectors.toList()); 46 | return new SelectClause(selectParameters, types); 47 | } 48 | 49 | /** 50 | * Builds select clause with given custom fields statement. Puts given result types (if any) into flexible search query. 51 | * 52 | * @param customFieldsStatement 53 | * custom statement to put into select clause, e.g. "COUNT({pk})" 54 | * @param resultTypes 55 | * result types, may be left empty 56 | * @return customized select clause 57 | */ 58 | public static CustomSelectClause selectCustom(final String customFieldsStatement, Class... resultTypes) 59 | { 60 | return new CustomSelectClause(customFieldsStatement, Arrays.asList(resultTypes)); 61 | } 62 | 63 | /** 64 | * Builds default select statement (selecting PK, i.e. model selection) with given alias. 65 | * 66 | * @param alias 67 | * alias 68 | * @return select statement 69 | */ 70 | public static ModelSelectClause select(final Alias alias) 71 | { 72 | return new ModelSelectClause(alias.pk()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/GroupByClauseTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.condition; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectCustom; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.LIKE; 7 | import static org.junit.Assert.assertEquals; 8 | 9 | import de.hybris.platform.core.model.product.ProductModel; 10 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 11 | 12 | import java.util.Arrays; 13 | 14 | import org.junit.Test; 15 | 16 | public class GroupByClauseTest 17 | { 18 | @Test 19 | public void testGroupByClauseWithOneField() 20 | { 21 | final FlexibleSearchQuery fQuery = 22 | selectCustom( 23 | "COUNT({pk}),{name}", Long.class, String.class 24 | ) 25 | .from(table(ProductModel.class)) 26 | .groupBy(ProductModel.NAME) 27 | .build(); 28 | assertEquals("Query does not match", "SELECT COUNT({pk}),{name} FROM {Product} GROUP BY {name}", fQuery.getQuery()); 29 | assertEquals("Wrong number of query parameters", 0, fQuery.getQueryParameters().size()); 30 | assertEquals("Result classes don't match", Arrays.asList(Long.class, String.class), fQuery.getResultClassList()); 31 | } 32 | 33 | @Test 34 | public void testGroupByClauseWithThreeFields() 35 | { 36 | final FlexibleSearchQuery fQuery = 37 | selectCustom( 38 | "COUNT({pk}),{name}", Long.class, String.class 39 | ) 40 | .from(table(ProductModel.class)) 41 | .groupBy(ProductModel.CODE, ProductModel.NAME, ProductModel.DESCRIPTION) 42 | .build(); 43 | assertEquals("Query does not match", "SELECT COUNT({pk}),{name} FROM {Product} GROUP BY {code},{name},{description}", fQuery.getQuery()); 44 | assertEquals("Wrong number of query parameters", 0, fQuery.getQueryParameters().size()); 45 | assertEquals("Result classes don't match", Arrays.asList(Long.class, String.class), fQuery.getResultClassList()); 46 | } 47 | 48 | @Test 49 | public void testGroupByClauseWithWhereClause() 50 | { 51 | final FlexibleSearchQuery fQuery = 52 | selectCustom( 53 | "COUNT({pk}),{name}", Long.class, String.class 54 | ) 55 | .from(table(ProductModel.class)) 56 | .where(condition(ProductModel.NAME, LIKE, "% for %")) 57 | .groupBy(ProductModel.NAME) 58 | .build(); 59 | assertEquals("Query does not match", "SELECT COUNT({pk}),{name} FROM {Product} WHERE {name} LIKE ?name1 GROUP BY {name}", fQuery.getQuery()); 60 | assertEquals("Wrong number of query parameters", 1, fQuery.getQueryParameters().size()); 61 | assertEquals("Parameter doesn't match", "% for %", fQuery.getQueryParameters().get("name1")); 62 | assertEquals("Result classes don't match", Arrays.asList(Long.class, String.class), fQuery.getResultClassList()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/SqlFunctionsTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.select; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.SqlFunctions.avg; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.SqlFunctions.count; 7 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.SqlFunctions.max; 8 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.SqlFunctions.min; 9 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.SqlFunctions.sum; 10 | import static org.junit.Assert.assertEquals; 11 | 12 | import de.hybris.bootstrap.annotations.UnitTest; 13 | import de.hybris.platform.core.model.product.ProductModel; 14 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 15 | 16 | import org.junit.Test; 17 | 18 | 19 | @UnitTest 20 | public class SqlFunctionsTest 21 | { 22 | 23 | @Test 24 | public void testCount() 25 | { 26 | final FlexibleSearchQuery query = 27 | select( 28 | FunctionWithType.of(count(ProductModel.PK), Long.class) 29 | ) 30 | .from( 31 | table(ProductModel.class) 32 | ) 33 | .build(); 34 | 35 | assertEquals("Query is not as expected", "SELECT COUNT({pk}) FROM {Product}", query.getQuery()); 36 | } 37 | 38 | @Test 39 | public void testSum() 40 | { 41 | final FlexibleSearchQuery query = 42 | select( 43 | FunctionWithType.of(sum(ProductModel.PK), Long.class) 44 | ) 45 | .from( 46 | table(ProductModel.class) 47 | ) 48 | .build(); 49 | 50 | assertEquals("Query is not as expected", "SELECT SUM({pk}) FROM {Product}", query.getQuery()); 51 | } 52 | 53 | @Test 54 | public void testMin() 55 | { 56 | final FlexibleSearchQuery query = 57 | select( 58 | FunctionWithType.of(min(ProductModel.PK), Long.class) 59 | ) 60 | .from( 61 | table(ProductModel.class) 62 | ) 63 | .build(); 64 | 65 | assertEquals("Query is not as expected", "SELECT MIN({pk}) FROM {Product}", query.getQuery()); 66 | } 67 | 68 | @Test 69 | public void testMax() 70 | { 71 | final FlexibleSearchQuery query = 72 | select( 73 | FunctionWithType.of(max(ProductModel.PK), Long.class) 74 | ) 75 | .from( 76 | table(ProductModel.class) 77 | ) 78 | .build(); 79 | 80 | assertEquals("Query is not as expected", "SELECT MAX({pk}) FROM {Product}", query.getQuery()); 81 | } 82 | 83 | @Test 84 | public void testAvg() 85 | { 86 | final FlexibleSearchQuery query = 87 | select( 88 | FunctionWithType.of(avg(ProductModel.PK), Long.class) 89 | ) 90 | .from( 91 | table(ProductModel.class) 92 | ) 93 | .build(); 94 | 95 | assertEquals("Query is not as expected", "SELECT AVG({pk}) FROM {Product}", query.getQuery()); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/TwoParameterFieldCondition.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchBuilderFieldUtils.createUniqueParameterCode; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.QUESTION_MARK; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 6 | 7 | import java.util.Map; 8 | 9 | 10 | /** 11 | * Condition on the model field (DB table column) of the flexible search query, that has one parameter, e.g. condition 12 | * to check if field equals to given value. 13 | */ 14 | public class TwoParameterFieldCondition extends AbstractFieldCondition 15 | { 16 | private final TwoParameterConditionType conditionType; 17 | private final CodeToValue firstParam; 18 | private final CodeToValue secondParam; 19 | 20 | TwoParameterFieldCondition(final Field field, final TwoParameterConditionType conditionType, final Object firstParamValue, 21 | final Object secondParamValue) 22 | { 23 | super(field); 24 | this.conditionType = conditionType; 25 | this.firstParam = new CodeToValue(firstParamValue); 26 | this.secondParam = new CodeToValue(secondParamValue); 27 | } 28 | 29 | TwoParameterFieldCondition(final AbstractFlexibleSearchQueryChainElement parent, final Field field, 30 | final TwoParameterConditionType conditionType, final Object firstParam, final Object secondParam) 31 | { 32 | super(parent, field); 33 | this.conditionType = conditionType; 34 | this.firstParam = new CodeToValue(firstParam); 35 | this.secondParam = new CodeToValue(secondParam); 36 | } 37 | 38 | @Override 39 | protected void appendQuery(final StringBuilder sb) 40 | { 41 | super.appendQuery(sb); 42 | 43 | sb.append(SPACE).append(conditionType.getOperator()).append(SPACE).append(QUESTION_MARK).append(firstParam.getCode()) 44 | .append(SPACE).append(conditionType.getParameterJoinOperation()).append(SPACE).append(QUESTION_MARK) 45 | .append(secondParam.getCode()); 46 | } 47 | 48 | @Override 49 | protected void addParameters(final Map parameterMap) 50 | { 51 | super.addParameters(parameterMap); 52 | 53 | firstParam.setCode(createUniqueParameterCode(parameterMap, field.getFieldName())); 54 | parameterMap.put(firstParam.getCode(), firstParam.getValue()); 55 | 56 | secondParam.setCode(createUniqueParameterCode(parameterMap, field.getFieldName())); 57 | parameterMap.put(secondParam.getCode(), secondParam.getValue()); 58 | } 59 | 60 | private static final class CodeToValue 61 | { 62 | private String code; 63 | private final Object value; 64 | 65 | CodeToValue(final Object value) 66 | { 67 | this.value = value; 68 | } 69 | 70 | public String getCode() 71 | { 72 | return code; 73 | } 74 | 75 | public void setCode(String code) 76 | { 77 | this.code = code; 78 | } 79 | 80 | public Object getValue() 81 | { 82 | return value; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/SqlFunctions.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | public final class SqlFunctions 4 | { 5 | private SqlFunctions() 6 | { 7 | } 8 | 9 | /** 10 | * Creates SQL SUM() functions with given field. 11 | * 12 | * @param field 13 | * field 14 | * @return SUM() function 15 | */ 16 | public static SqlFunction sum(final String field) 17 | { 18 | return new SqlFunction(SqlFunctionType.SUM, field); 19 | } 20 | 21 | /** 22 | * Creates SQL SUM() functions with given field. 23 | * 24 | * @param field 25 | * field 26 | * @return SUM() function 27 | */ 28 | public static SqlFunction sum(final AliasedField field) 29 | { 30 | return new SqlFunction(SqlFunctionType.SUM, field); 31 | } 32 | 33 | /** 34 | * Creates SQL COUNT() functions with given field. 35 | * 36 | * @param field 37 | * field 38 | * @return COUNT() function 39 | */ 40 | public static SqlFunction count(final String field) 41 | { 42 | return new SqlFunction(SqlFunctionType.COUNT, field); 43 | } 44 | 45 | /** 46 | * Creates SQL COUNT() functions with given field. 47 | * 48 | * @param field 49 | * field 50 | * @return COUNT() function 51 | */ 52 | public static SqlFunction count(final AliasedField field) 53 | { 54 | return new SqlFunction(SqlFunctionType.COUNT, field); 55 | } 56 | 57 | /** 58 | * Creates SQL AVG() functions with given field. 59 | * 60 | * @param field 61 | * field 62 | * @return AVG() function 63 | */ 64 | public static SqlFunction avg(final String field) 65 | { 66 | return new SqlFunction(SqlFunctionType.AVG, field); 67 | } 68 | 69 | /** 70 | * Creates SQL AVG() functions with given field. 71 | * 72 | * @param field 73 | * field 74 | * @return AVG() function 75 | */ 76 | public static SqlFunction avg(final AliasedField field) 77 | { 78 | return new SqlFunction(SqlFunctionType.AVG, field); 79 | } 80 | 81 | /** 82 | * Creates SQL MIN() functions with given field. 83 | * 84 | * @param field 85 | * field 86 | * @return MIN() function 87 | */ 88 | public static SqlFunction min(final String field) 89 | { 90 | return new SqlFunction(SqlFunctionType.MIN, field); 91 | } 92 | 93 | /** 94 | * Creates SQL MIN() functions with given field. 95 | * 96 | * @param field 97 | * field 98 | * @return MIN() function 99 | */ 100 | public static SqlFunction min(final AliasedField field) 101 | { 102 | return new SqlFunction(SqlFunctionType.MIN, field); 103 | } 104 | 105 | /** 106 | * Creates SQL MAX() functions with given field. 107 | * 108 | * @param field 109 | * field 110 | * @return MAX() function 111 | */ 112 | public static SqlFunction max(final String field) 113 | { 114 | return new SqlFunction(SqlFunctionType.MAX, field); 115 | } 116 | 117 | /** 118 | * Creates SQL MAX() functions with given field. 119 | * 120 | * @param field 121 | * field 122 | * @return MAX() function 123 | */ 124 | public static SqlFunction max(final AliasedField field) 125 | { 126 | return new SqlFunction(SqlFunctionType.MAX, field); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/CollectionAndQueryConditionsTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static de.hybris.platform.core.model.product.ProductModel.CODE; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.CollectionAndQueryConditionType.IN; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.CollectionAndQueryConditionType.NOT_IN; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.condition; 7 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectCustom; 8 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectFrom; 9 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertTrue; 12 | 13 | import de.hybris.bootstrap.annotations.UnitTest; 14 | import de.hybris.platform.core.model.product.ProductModel; 15 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 16 | 17 | import java.util.Arrays; 18 | import java.util.List; 19 | 20 | import org.junit.Test; 21 | 22 | 23 | @UnitTest 24 | public class CollectionAndQueryConditionsTest 25 | { 26 | private final TerminateQueryChainElement commonInnerQuery = 27 | selectCustom("UPPER({code})") 28 | .from( 29 | table(ProductModel.class) 30 | ); 31 | 32 | @Test 33 | public void testNotInQueryCondition() 34 | { 35 | final FlexibleSearchQuery query = 36 | selectFrom(ProductModel.class) 37 | .where( 38 | condition(CODE, NOT_IN, commonInnerQuery) 39 | ) 40 | .build(); 41 | 42 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {code} NOT IN " + 43 | "({{SELECT UPPER({code}) FROM {Product}}})", query.getQuery()); 44 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 45 | } 46 | 47 | @Test 48 | public void testInQueryCondition() 49 | { 50 | final FlexibleSearchQuery query = 51 | selectFrom(ProductModel.class) 52 | .where( 53 | condition(CODE, IN, commonInnerQuery) 54 | ) 55 | .build(); 56 | 57 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {code} IN " + 58 | "({{SELECT UPPER({code}) FROM {Product}}})", query.getQuery()); 59 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 60 | } 61 | 62 | @Test 63 | public void testNotInCollectionCondition() 64 | { 65 | final List codes = Arrays.asList("code1", "code2", "code3"); 66 | final FlexibleSearchQuery query = 67 | selectFrom(ProductModel.class) 68 | .where( 69 | condition(CODE, NOT_IN, codes) 70 | ) 71 | .build(); 72 | 73 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {code} NOT IN (?code1)", query.getQuery()); 74 | assertEquals("Query parameters don't match", codes, query.getQueryParameters().get("code1")); 75 | } 76 | 77 | @Test 78 | public void testInCollectionCondition() 79 | { 80 | final List codes = Arrays.asList("code1", "code2", "code3"); 81 | final FlexibleSearchQuery query = 82 | selectFrom(ProductModel.class) 83 | .where( 84 | condition(CODE, IN, codes) 85 | ) 86 | .build(); 87 | 88 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {code} IN (?code1)", query.getQuery()); 89 | assertEquals("Query parameters don't match", codes, query.getQueryParameters().get("code1")); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/JoinsTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static de.hybris.platform.core.model.product.ProductModel._STOCKLEVELPRODUCTRELATION; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.select; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | import de.hybris.bootstrap.annotations.UnitTest; 10 | import de.hybris.platform.core.model.order.OrderEntryModel; 11 | import de.hybris.platform.core.model.order.OrderModel; 12 | import de.hybris.platform.core.model.product.ProductModel; 13 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 14 | 15 | import org.junit.Test; 16 | import org.springframework.util.CollectionUtils; 17 | 18 | 19 | @UnitTest 20 | public class JoinsTest 21 | { 22 | private final Alias o = new Alias("o"); 23 | private final Alias e = new Alias("e"); 24 | private final Alias p = new Alias("p"); 25 | private final Alias sl2p = new Alias("sl2p"); 26 | 27 | 28 | @Test 29 | public void testSelectWithInnerJoins() 30 | { 31 | final FlexibleSearchQuery fQuery = 32 | select(e) 33 | .from( 34 | table(OrderModel.class).as(o) 35 | .join(OrderEntryModel.class).as(e) 36 | .on(e.field(OrderEntryModel.ORDER), o.pk()) 37 | .join(ProductModel.class).as(p) 38 | .on(e.field(OrderEntryModel.PRODUCT), p.pk()) 39 | .join(_STOCKLEVELPRODUCTRELATION).as(sl2p) 40 | .on(sl2p.target(), p.pk()) 41 | ) 42 | .build(); 43 | 44 | assertEquals("Query does not match", "SELECT {e.pk} FROM {Order AS o JOIN OrderEntry AS e " + 45 | "ON {e.order}={o.pk} JOIN Product AS p ON {e.product}={p.pk} " + 46 | "JOIN StockLevelProductRelation AS sl2p ON {sl2p.target}={p.pk}}", fQuery.getQuery()); 47 | assertTrue("Non-expected parameter(-s) found", CollectionUtils.isEmpty(fQuery.getQueryParameters())); 48 | } 49 | 50 | @Test 51 | public void testSelectTableByTypeCodeWithInnerJoins() 52 | { 53 | final FlexibleSearchQuery fQuery = 54 | select(e) 55 | .from( 56 | table(_STOCKLEVELPRODUCTRELATION).as(sl2p) 57 | .join(ProductModel.class).as(p) 58 | .on(sl2p.target(), p.pk()) 59 | .join(OrderEntryModel.class).as(e) 60 | .on(e.field(OrderEntryModel.PRODUCT), p.pk()) 61 | .join(OrderModel.class).as(o) 62 | .on(e.field(OrderEntryModel.ORDER), o.pk()) 63 | ) 64 | .build(); 65 | 66 | assertEquals("Query does not match", "SELECT {e.pk} FROM {StockLevelProductRelation AS sl2p " + 67 | "JOIN Product AS p ON {sl2p.target}={p.pk} " + 68 | "JOIN OrderEntry AS e ON {e.product}={p.pk} " + 69 | "JOIN Order AS o ON {e.order}={o.pk}}", fQuery.getQuery()); 70 | assertTrue("Non-expected parameter(-s) found", CollectionUtils.isEmpty(fQuery.getQueryParameters())); 71 | } 72 | 73 | @Test 74 | public void testSelectWithRightJoin() 75 | { 76 | final FlexibleSearchQuery fQuery = 77 | select(e) 78 | .from( 79 | table(OrderModel.class).as(o) 80 | .rightJoin(OrderEntryModel.class).as(e) 81 | .on(e.field(OrderEntryModel.ORDER), o.pk()) 82 | ) 83 | .build(); 84 | 85 | assertEquals("Query does not match", "SELECT {e.pk} FROM {Order AS o RIGHT JOIN OrderEntry AS e " + 86 | "ON {e.order}={o.pk}}", fQuery.getQuery()); 87 | assertTrue("Non-expected parameter(-s) found", CollectionUtils.isEmpty(fQuery.getQueryParameters())); 88 | } 89 | 90 | @Test 91 | public void testSelectWithLeftJoin() 92 | { 93 | final FlexibleSearchQuery fQuery = 94 | select(e) 95 | .from( 96 | table(OrderModel.class).as(o) 97 | .leftJoin(OrderEntryModel.class).as(e) 98 | .on(e.field(OrderEntryModel.ORDER), o.pk()) 99 | ) 100 | .build(); 101 | 102 | assertEquals("Query does not match", "SELECT {e.pk} FROM {Order AS o LEFT JOIN OrderEntry AS e " + 103 | "ON {e.order}={o.pk}}", fQuery.getQuery()); 104 | assertTrue("Non-expected parameter(-s) found", CollectionUtils.isEmpty(fQuery.getQueryParameters())); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ParameterFieldConditionsTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static de.hybris.platform.core.model.ItemModel.CREATIONTIME; 4 | import static de.hybris.platform.core.model.product.ProductModel.NAME; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.condition; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectFrom; 7 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_EQUAL_TO; 8 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_GREATER_OR_EQUAL; 9 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_GREATER_THAN; 10 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_LESS_OR_EQUAL; 11 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_LESS_THAN; 12 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_NOT_EQUAL_TO; 13 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.LIKE; 14 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.NOT_LIKE; 15 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.TwoParameterConditionType.BETWEEN; 16 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.TwoParameterConditionType.NOT_BETWEEN; 17 | import static org.junit.Assert.assertEquals; 18 | 19 | import de.hybris.bootstrap.annotations.UnitTest; 20 | import de.hybris.platform.core.model.product.ProductModel; 21 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 22 | 23 | import java.util.Date; 24 | 25 | import org.junit.Test; 26 | 27 | 28 | @UnitTest 29 | public class ParameterFieldConditionsTest 30 | { 31 | 32 | @Test 33 | public void testEqualCondition() 34 | { 35 | final FlexibleSearchQuery query = 36 | selectFrom(ProductModel.class) 37 | .where( 38 | condition(NAME, IS_EQUAL_TO, "TEST") 39 | ) 40 | .build(); 41 | 42 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name}=?name1", query.getQuery()); 43 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 44 | } 45 | 46 | @Test 47 | public void testNotEqualCondition() 48 | { 49 | final FlexibleSearchQuery query = 50 | selectFrom(ProductModel.class) 51 | .where( 52 | condition(NAME, IS_NOT_EQUAL_TO, "TEST") 53 | ) 54 | .build(); 55 | 56 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name}<>?name1", query.getQuery()); 57 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 58 | } 59 | 60 | @Test 61 | public void testLessThanCondition() 62 | { 63 | final FlexibleSearchQuery query = 64 | selectFrom(ProductModel.class) 65 | .where( 66 | condition(NAME, IS_LESS_THAN, "TEST") 67 | ) 68 | .build(); 69 | 70 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name}?name1", query.getQuery()); 99 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 100 | } 101 | 102 | @Test 103 | public void testGreaterOrEqualCondition() 104 | { 105 | final FlexibleSearchQuery query = 106 | selectFrom(ProductModel.class) 107 | .where( 108 | condition(NAME, IS_GREATER_OR_EQUAL, "TEST") 109 | ) 110 | .build(); 111 | 112 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name}>=?name1", query.getQuery()); 113 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 114 | } 115 | 116 | @Test 117 | public void testLikeCondition() 118 | { 119 | final FlexibleSearchQuery query = 120 | selectFrom(ProductModel.class) 121 | .where( 122 | condition(NAME, LIKE, "TEST") 123 | ) 124 | .build(); 125 | 126 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} LIKE ?name1", query.getQuery()); 127 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 128 | } 129 | 130 | @Test 131 | public void testNotLikeCondition() 132 | { 133 | final FlexibleSearchQuery query = 134 | selectFrom(ProductModel.class) 135 | .where( 136 | condition(NAME, NOT_LIKE, "TEST") 137 | ) 138 | .build(); 139 | 140 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} NOT LIKE ?name1", query.getQuery()); 141 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 142 | } 143 | 144 | @Test 145 | public void testBetweenCondition() 146 | { 147 | final Date dateStart = new Date(); 148 | final Date dateEnd = new Date(); 149 | final FlexibleSearchQuery query = 150 | selectFrom(ProductModel.class) 151 | .where( 152 | condition(CREATIONTIME, BETWEEN, dateStart, dateEnd) 153 | ) 154 | .build(); 155 | 156 | assertEquals("Query is not as expected", 157 | "SELECT {pk} FROM {Product} WHERE {creationtime} BETWEEN ?creationtime1 AND ?creationtime2", query.getQuery()); 158 | assertEquals("Query parameters size doesn't match", 2, query.getQueryParameters().size()); 159 | assertEquals("Query parameter 1 doesn't match", dateStart, query.getQueryParameters().get("creationtime1")); 160 | assertEquals("Query parameter 2 doesn't match", dateEnd, query.getQueryParameters().get("creationtime2")); 161 | } 162 | 163 | @Test 164 | public void testNotBetweenCondition() 165 | { 166 | final Date dateStart = new Date(); 167 | final Date dateEnd = new Date(); 168 | final FlexibleSearchQuery query = 169 | selectFrom(ProductModel.class) 170 | .where( 171 | condition(CREATIONTIME, NOT_BETWEEN, dateStart, dateEnd) 172 | ) 173 | .build(); 174 | 175 | assertEquals("Query is not as expected", 176 | "SELECT {pk} FROM {Product} WHERE {creationtime} NOT BETWEEN ?creationtime1 AND ?creationtime2", query.getQuery()); 177 | assertEquals("Query parameters size doesn't match", 2, query.getQueryParameters().size()); 178 | assertEquals("Query parameter 1 doesn't match", dateStart, query.getQueryParameters().get("creationtime1")); 179 | assertEquals("Query parameter 2 doesn't match", dateEnd, query.getQueryParameters().get("creationtime2")); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flexible search query builder 2 | 3 | Flexible search query builder is SAP Hybris Commerce extension (released as a library) that provides developer-friendly way to build flexible search queries. 4 | The aim of this extension is to `write().flexibleSearchQueries().easily()` with the help of IDE in a compile-time safe manner. 5 | 6 | ## Installation 7 | 8 | It is released as a Java library. 9 | You can find jars at https://bitbucket.org/andriichukandrii/flexiblesearchbuilder/downloads/ 10 | It is now also possible to use following Maven dependency: 11 | ```xml 12 | 13 | org.bitbucket.andriichukandrii 14 | flexiblesearchbuilder 15 | 3.2.2 16 | 17 | ``` 18 | 19 | For development purposes it can be added as an extension either to localextensions.xml or to other extension dependencies. 20 | 21 | ## Usage 22 | 23 | All the elements of the builder chain are immutable (unless you pass a mutable parameter and then modify it), 24 | thus they can be safely reused among different queries. 25 | 26 | Here are some examples of using the flexible search query builder (executable in groovy scripting console) 27 | ```java 28 | import static de.hybris.platform.core.model.order.AbstractOrderEntryModel.ORDER; 29 | import static de.hybris.platform.core.model.order.AbstractOrderEntryModel.PRODUCT; 30 | import static de.hybris.platform.core.model.order.AbstractOrderModel.USER; 31 | 32 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.braces; 33 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.condition; 34 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.select; 35 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectFrom; 36 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 37 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterlessConditionType.IS_NOT_NULL; 38 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterlessConditionType.IS_NULL; 39 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.TwoParameterConditionType.BETWEEN; 40 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_EQUAL_TO; 41 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_GREATER_THAN; 42 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.LIKE; 43 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.SqlFunctions.count; 44 | 45 | import de.hybris.platform.catalog.model.CatalogUnawareMediaModel; 46 | import de.hybris.platform.catalog.model.ProductReferenceModel; 47 | import de.hybris.platform.category.constants.CategoryConstants; 48 | import de.hybris.platform.category.model.CategoryModel; 49 | import de.hybris.platform.core.model.order.OrderEntryModel; 50 | import de.hybris.platform.core.model.order.OrderModel; 51 | import de.hybris.platform.core.model.product.ProductModel; 52 | import de.hybris.platform.core.model.user.UserModel; 53 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 54 | import de.hybris.platform.variants.model.VariantProductModel; 55 | 56 | import org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.AbstractFieldCondition; 57 | import org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Alias; 58 | import org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FieldWithType; 59 | import org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FunctionWithType; 60 | import org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.JoinOnElement; 61 | 62 | 63 | final FlexibleSearchQuery query1 = 64 | selectFrom(CatalogUnawareMediaModel.class) 65 | .where( 66 | condition(CatalogUnawareMediaModel.CODE, IS_EQUAL_TO, "someLogoCode")) 67 | .build(); 68 | 69 | final AbstractFieldCondition nameAndDescriptionNonNull = 70 | condition(ProductModel.NAME, IS_NOT_NULL) 71 | .and() 72 | .condition(ProductModel.DESCRIPTION, IS_NOT_NULL); 73 | 74 | final FlexibleSearchQuery query2 = 75 | selectFrom(ProductModel.class) 76 | .where( 77 | condition(ProductModel.CODE, LIKE, "p%") 78 | .and() 79 | .condition(nameAndDescriptionNonNull) 80 | ) 81 | .orderByAsc(ProductModel.NAME) 82 | .build(); 83 | 84 | final Alias p = new Alias("p"); 85 | final Alias v = new Alias("v"); 86 | final FlexibleSearchQuery query3 = 87 | select(p) 88 | .from( 89 | table(ProductModel.class).as(p) 90 | join(VariantProductModel.class).as(v) 91 | .on(p.pk(), IS_EQUAL_TO, v.field(VariantProductModel.BASEPRODUCT)) 92 | ) 93 | .where( 94 | condition(v.field(VariantProductModel.OFFLINEDATE), IS_GREATER_THAN, timeService.getCurrentTime()) 95 | ) 96 | .build(); 97 | 98 | final FlexibleSearchQuery query4 = 99 | select( 100 | FieldWithType.of(ProductModel.NAME, String.class), 101 | FieldWithType.of(ProductModel.DESCRIPTION, String.class), 102 | FieldWithType.of(ProductModel.PK, Long.class) 103 | ) 104 | .from( 105 | table(ProductModel.class) 106 | ) 107 | .where( 108 | braces( 109 | condition(ProductModel.SUMMARY, IS_NULL) 110 | .or() 111 | .condition(ProductModel.NAME, IS_NOT_NULL) 112 | ) 113 | .and() 114 | .condition(ProductModel.PK, BETWEEN, 8796093054977L, 8796193054977L) 115 | ) 116 | .build(); 117 | 118 | final UserModel user = userService.getAnonymousUser(); 119 | final CategoryModel category = new CategoryModel();//just for demonstration purposes 120 | 121 | final Alias r = new Alias("r"); 122 | final Alias e = new Alias("e"); 123 | final Alias o = new Alias("o"); 124 | final Alias c2p = new Alias("c2p"); 125 | final Alias c = new Alias("c"); 126 | 127 | final JoinOnElement joinTables = 128 | table(ProductModel.class).as(p) 129 | .leftJoin(ProductReferenceModel.class).as(r) 130 | .on(p.pk(), r.target()) 131 | .leftJoin(OrderEntryModel.class).as(e) 132 | .on(r.source(), e.field(PRODUCT)) 133 | .leftJoin(OrderModel.class).as(o) 134 | .on(o.pk(), e.field(ORDER)) 135 | .leftJoin(CategoryConstants.Relations.CATEGORYPRODUCTRELATION).as(c2p) 136 | .on(r.source(), c2p.target()) 137 | .leftJoin(CategoryModel.class).as(c) 138 | .on(c.pk(), c2p.source()); 139 | 140 | final FlexibleSearchQuery query5 = 141 | select(p) 142 | .from(joinTables) 143 | .where( 144 | condition(o.field(USER), IS_EQUAL_TO, user) 145 | .and() 146 | .condition(c.pk(), IS_EQUAL_TO, category) 147 | ) 148 | .orderByAsc(p.field(ProductModel.CODE)) 149 | .build(); 150 | 151 | final FlexibleSearchQuery query6 = 152 | select( 153 | FunctionWithType.of(count(o.field(OrderModel.CODE)), Long.class), 154 | FieldWithType.of(p.field(ProductModel.CODE), String.class) 155 | ) 156 | .from( 157 | table(ProductModel.class).as(p) 158 | .join(OrderEntryModel.class).as(e) 159 | .on(p.pk(), e.field(OrderEntryModel.PRODUCT)) 160 | .join(OrderModel.class).as(o) 161 | .on(o.pk(), e.field(OrderEntryModel.ORDER)) 162 | ) 163 | .groupBy(p.field(ProductModel.CODE)) 164 | .orderByDesc(count(o.field(OrderModel.CODE))) 165 | .build(); 166 | 167 | ``` 168 | 169 | ## Contributing 170 | Pull requests are welcome. If you're planning to contribute, drop me a [mail](mailto:92jo5eqfy@mozmail.com) as currently the process is not formal yet. 171 | 172 | ## License 173 | [MIT](https://choosealicense.com/licenses/mit/) 174 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/Conditions.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import java.util.Collection; 4 | 5 | 6 | public final class Conditions 7 | { 8 | private Conditions() 9 | { 10 | } 11 | 12 | /** 13 | * Creates field condition with a given parameter. 14 | * 15 | * @param fieldName 16 | * field name (from model item, e.g. ProductModel.NAME) 17 | * @param conditionType 18 | * type of condition 19 | * @param conditionParameter 20 | * parameter 21 | * @return new field condition 22 | */ 23 | public static AbstractFieldCondition condition(final String fieldName, final RegularParameterConditionType conditionType, 24 | final Object conditionParameter) 25 | { 26 | return new ParameterFieldCondition(new SimpleField(fieldName), conditionType, conditionParameter); 27 | } 28 | 29 | /** 30 | * Creates field condition with a given parameters. 31 | * 32 | * @param fieldName 33 | * field name (from model item, e.g. ProductModel.NAME) 34 | * @param conditionType 35 | * type of condition 36 | * @param firstParameter 37 | * first parameter 38 | * @param secondParameter 39 | * second parameter 40 | * @return new field condition 41 | */ 42 | public static AbstractFieldCondition condition(final String fieldName, final TwoParameterConditionType conditionType, 43 | final Object firstParameter, final Object secondParameter) 44 | { 45 | return new TwoParameterFieldCondition(new SimpleField(fieldName), conditionType, firstParameter, secondParameter); 46 | } 47 | 48 | /** 49 | * Creates field condition with a given collection parameter. 50 | * 51 | * @param fieldName 52 | * field name (from model item, e.g. ProductModel.NAME) 53 | * @param conditionType 54 | * type of condition (which supports collection as parameter) 55 | * @param collectionConditionParameter 56 | * collection parameter 57 | * @return new field condition 58 | */ 59 | public static AbstractFieldCondition condition(final String fieldName, final CollectionAndQueryConditionType conditionType, 60 | final Collection collectionConditionParameter) 61 | { 62 | return new ParameterFieldCondition(new SimpleField(fieldName), conditionType, collectionConditionParameter); 63 | } 64 | 65 | /** 66 | * Creates field condition with a given inner query. 67 | * 68 | * @param fieldName 69 | * field name (from model item, e.g. ProductModel.NAME) 70 | * @param conditionType 71 | * type of condition (which supports inner query as parameter) 72 | * @param innerQuery 73 | * inner query 74 | * @return new field condition 75 | */ 76 | public static AbstractFieldCondition condition(final String fieldName, final CollectionAndQueryConditionType conditionType, 77 | TerminateQueryChainElement innerQuery) 78 | { 79 | return new InnerQueryFieldCondition(new SimpleField(fieldName), conditionType, innerQuery); 80 | } 81 | 82 | /** 83 | * Creates field condition with a given parameter. 84 | * 85 | * @param aliasedField 86 | * field with alias 87 | * @param conditionType 88 | * type of condition 89 | * @param conditionParameter 90 | * parameter 91 | * @return new field condition 92 | */ 93 | public static AbstractFieldCondition condition(final AliasedField aliasedField, 94 | final RegularParameterConditionType conditionType, final Object conditionParameter) 95 | { 96 | return new ParameterFieldCondition(aliasedField, conditionType, conditionParameter); 97 | } 98 | 99 | /** 100 | * Creates field condition with a given parameter. 101 | * 102 | * @param aliasedField 103 | * field with alias 104 | * @param conditionType 105 | * type of condition 106 | * @param firstParameter 107 | * first parameter 108 | * @param secondParameter 109 | * second parameter 110 | * @return new field condition 111 | */ 112 | public static AbstractFieldCondition condition(final AliasedField aliasedField, final TwoParameterConditionType conditionType, 113 | final Object firstParameter, final Object secondParameter) 114 | { 115 | return new TwoParameterFieldCondition(aliasedField, conditionType, firstParameter, secondParameter); 116 | } 117 | 118 | /** 119 | * Creates field condition with a given collection parameter. 120 | * 121 | * @param aliasedField 122 | * field with alias 123 | * @param conditionType 124 | * type of condition (which supports collection as parameter) 125 | * @param collectionConditionParameter 126 | * collection parameter 127 | * @return new field condition 128 | */ 129 | public static AbstractFieldCondition condition(final AliasedField aliasedField, 130 | final CollectionAndQueryConditionType conditionType, Collection collectionConditionParameter) 131 | { 132 | return new ParameterFieldCondition(aliasedField, conditionType, collectionConditionParameter); 133 | } 134 | 135 | /** 136 | * Creates field condition with a given inner query. 137 | * 138 | * @param aliasedField 139 | * field with alias 140 | * @param conditionType 141 | * type of condition (which supports inner query as parameter) 142 | * @param innerQuery 143 | * inner query 144 | * @return new field condition 145 | */ 146 | public static AbstractFieldCondition condition(final AliasedField aliasedField, 147 | final CollectionAndQueryConditionType conditionType, TerminateQueryChainElement innerQuery) 148 | { 149 | return new InnerQueryFieldCondition(aliasedField, conditionType, innerQuery); 150 | } 151 | 152 | /** 153 | * Creates field condition with a given condition. 154 | * 155 | * @param fieldName 156 | * field name (from model item, e.g. ProductModel.NAME) 157 | * @param conditionType 158 | * type of condition, which doesn't require parameter (e.g. ParameterlessConditionType.IS_NULL) 159 | * @return new field condition 160 | */ 161 | public static AbstractFieldCondition condition(final String fieldName, final ParameterlessConditionType conditionType) 162 | { 163 | return new ParameterlessFieldCondition(new SimpleField(fieldName), conditionType); 164 | } 165 | 166 | /** 167 | * Creates field condition with a given condition. 168 | * 169 | * @param aliasedField 170 | * field with alias 171 | * @param conditionType 172 | * type of condition, which doesn't require parameter (e.g. ParameterlessConditionType.IS_NULL) 173 | * @return new field condition 174 | */ 175 | public static AbstractFieldCondition condition(final AliasedField aliasedField, final ParameterlessConditionType conditionType) 176 | { 177 | return new ParameterlessFieldCondition(aliasedField, conditionType); 178 | } 179 | 180 | /** 181 | * Puts given condition (with chained conditions if any) into braces. 182 | * 183 | * @param condition 184 | * condition to wrap 185 | * @return braced condition chain 186 | */ 187 | public static BraceConditionWrapper braces(final AbstractCondition condition) 188 | { 189 | return new BraceConditionWrapper(condition); 190 | } 191 | 192 | /** 193 | * Builds inner query condition. 194 | * 195 | * @param queryConditionType 196 | * condition type 197 | * @param innerQuery 198 | * inner query 199 | * @return inner query condition 200 | */ 201 | public static AbstractCondition condition(final UnaryQueryConditionType queryConditionType, 202 | TerminateQueryChainElement innerQuery) 203 | { 204 | return new InnerQueryUnaryCondition(queryConditionType, innerQuery); 205 | } 206 | 207 | /** 208 | * Creates custom condition of given string. Given string is trimmed to remove redundant spaces if any. 209 | * 210 | * @param customCondition 211 | * custom condition string, e.g. "UPPER({name})=?name" 212 | * @return new custom condition element 213 | */ 214 | public static AbstractCondition customCondition(final String customCondition) 215 | { 216 | return new CustomCondition(customCondition.trim()); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/ParameterAliasedFieldConditionsTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static de.hybris.platform.core.model.ItemModel.CREATIONTIME; 4 | import static de.hybris.platform.core.model.product.ProductModel.NAME; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Alias.alias; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.condition; 7 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.select; 8 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 9 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_EQUAL_TO; 10 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_GREATER_OR_EQUAL; 11 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_GREATER_THAN; 12 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_LESS_OR_EQUAL; 13 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_LESS_THAN; 14 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_NOT_EQUAL_TO; 15 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.LIKE; 16 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.NOT_LIKE; 17 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.TwoParameterConditionType.BETWEEN; 18 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.TwoParameterConditionType.NOT_BETWEEN; 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import de.hybris.bootstrap.annotations.UnitTest; 22 | import de.hybris.platform.core.model.product.ProductModel; 23 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 24 | 25 | import java.util.Date; 26 | 27 | import org.junit.Test; 28 | 29 | 30 | @UnitTest 31 | public class ParameterAliasedFieldConditionsTest 32 | { 33 | private final Alias p = alias("p"); 34 | 35 | @Test 36 | public void testEqualCondition() 37 | { 38 | final FlexibleSearchQuery query = 39 | select(p) 40 | .from(table(ProductModel.class).as(p)) 41 | .where( 42 | condition(p.field(NAME), IS_EQUAL_TO, "TEST") 43 | ) 44 | .build(); 45 | 46 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name}=?name1", query.getQuery()); 47 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 48 | } 49 | 50 | @Test 51 | public void testNotEqualCondition() 52 | { 53 | final FlexibleSearchQuery query = 54 | select(p) 55 | .from(table(ProductModel.class).as(p)) 56 | .where( 57 | condition(p.field(NAME), IS_NOT_EQUAL_TO, "TEST") 58 | ) 59 | .build(); 60 | 61 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name}<>?name1", query.getQuery()); 62 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 63 | } 64 | 65 | @Test 66 | public void testLessThanCondition() 67 | { 68 | final FlexibleSearchQuery query = 69 | select(p) 70 | .from(table(ProductModel.class).as(p)) 71 | .where( 72 | condition(p.field(NAME), IS_LESS_THAN, "TEST") 73 | ) 74 | .build(); 75 | 76 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name}?name1", query.getQuery()); 107 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 108 | } 109 | 110 | @Test 111 | public void testGreaterOrEqualCondition() 112 | { 113 | final FlexibleSearchQuery query = 114 | select(p) 115 | .from(table(ProductModel.class).as(p)) 116 | .where( 117 | condition(p.field(NAME), IS_GREATER_OR_EQUAL, "TEST") 118 | ) 119 | .build(); 120 | 121 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name}>=?name1", query.getQuery()); 122 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 123 | } 124 | 125 | @Test 126 | public void testLikeCondition() 127 | { 128 | final FlexibleSearchQuery query = 129 | select(p) 130 | .from(table(ProductModel.class).as(p)) 131 | .where( 132 | condition(p.field(NAME), LIKE, "TEST") 133 | ) 134 | .build(); 135 | 136 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name} LIKE ?name1", query.getQuery()); 137 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 138 | } 139 | 140 | @Test 141 | public void testNotLikeCondition() 142 | { 143 | final FlexibleSearchQuery query = 144 | select(p) 145 | .from(table(ProductModel.class).as(p)) 146 | .where( 147 | condition(p.field(NAME), NOT_LIKE, "TEST") 148 | ) 149 | .build(); 150 | 151 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name} NOT LIKE ?name1", query.getQuery()); 152 | assertEquals("Query parameters don't match", "TEST", query.getQueryParameters().get("name1")); 153 | } 154 | 155 | @Test 156 | public void testBetweenCondition() 157 | { 158 | final Date dateStart = new Date(); 159 | final Date dateEnd = new Date(); 160 | final FlexibleSearchQuery query = 161 | select(p) 162 | .from(table(ProductModel.class).as(p)) 163 | .where( 164 | condition(p.field(CREATIONTIME), BETWEEN, dateStart, dateEnd) 165 | ) 166 | .build(); 167 | 168 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.creationtime} BETWEEN ?creationtime1 AND ?creationtime2", query.getQuery()); 169 | assertEquals("Query parameter size is not 2", 2, query.getQueryParameters().size()); 170 | assertEquals("Query parameter 1 doesn't match", dateStart, query.getQueryParameters().get("creationtime1")); 171 | assertEquals("Query parameter 2 doesn't match", dateEnd, query.getQueryParameters().get("creationtime2")); 172 | } 173 | 174 | @Test 175 | public void testNotBetweenCondition() 176 | { 177 | final Date dateStart = new Date(); 178 | final Date dateEnd = new Date(); 179 | final FlexibleSearchQuery query = 180 | select(p) 181 | .from(table(ProductModel.class).as(p)) 182 | .where( 183 | condition(p.field(CREATIONTIME), NOT_BETWEEN, dateStart, dateEnd) 184 | ) 185 | .build(); 186 | 187 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.creationtime} NOT BETWEEN ?creationtime1 AND ?creationtime2", query.getQuery()); 188 | assertEquals("Query parameter size is not 2", 2, query.getQueryParameters().size()); 189 | assertEquals("Query parameter 1 doesn't match", dateStart, query.getQueryParameters().get("creationtime1")); 190 | assertEquals("Query parameter 2 doesn't match", dateEnd, query.getQueryParameters().get("creationtime2")); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/CombineConditionElement.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryConstants.SPACE; 5 | 6 | import java.util.Collection; 7 | 8 | 9 | public class CombineConditionElement extends FlexibleSearchQueryInnerChainElement 10 | { 11 | private final CombineConditionType combineOperator; 12 | 13 | 14 | CombineConditionElement(final AbstractCondition parent, final CombineConditionType combineOperator) 15 | { 16 | super(parent); 17 | this.combineOperator = combineOperator; 18 | } 19 | 20 | 21 | /** 22 | * Creates field condition with a given parameter. 23 | * 24 | * @param fieldName 25 | * field name (from model item, e.g. ProductModel.NAME) 26 | * @param conditionType 27 | * type of condition 28 | * @param conditionParameter 29 | * parameter 30 | * @return new field condition 31 | */ 32 | public AbstractFieldCondition condition(final String fieldName, final RegularParameterConditionType conditionType, 33 | final Object conditionParameter) 34 | { 35 | return new ParameterFieldCondition(this, new SimpleField(fieldName), conditionType, conditionParameter); 36 | } 37 | 38 | /** 39 | * Creates field condition with a given parameters. 40 | * 41 | * @param fieldName 42 | * field name (from model item, e.g. ProductModel.NAME) 43 | * @param conditionType 44 | * type of condition 45 | * @param firstParameter 46 | * first parameter 47 | * @param secondParameter 48 | * second parameter 49 | * @return new field condition 50 | */ 51 | public AbstractFieldCondition condition(final String fieldName, final TwoParameterConditionType conditionType, 52 | final Object firstParameter, final Object secondParameter) 53 | { 54 | return new TwoParameterFieldCondition(this, new SimpleField(fieldName), conditionType, firstParameter, secondParameter); 55 | } 56 | 57 | /** 58 | * Creates field condition with a given collection parameter. 59 | * 60 | * @param fieldName 61 | * field name (from model item, e.g. ProductModel.NAME) 62 | * @param conditionType 63 | * type of condition (which supports collection as parameter) 64 | * @param collectionConditionParameter 65 | * collection parameter 66 | * @return new field condition 67 | */ 68 | public AbstractFieldCondition condition(final String fieldName, final CollectionAndQueryConditionType conditionType, 69 | final Collection collectionConditionParameter) 70 | { 71 | return new ParameterFieldCondition(this, new SimpleField(fieldName), conditionType, collectionConditionParameter); 72 | } 73 | 74 | /** 75 | * Creates field condition with a given inner query. 76 | * 77 | * @param fieldName 78 | * field name (from model item, e.g. ProductModel.NAME) 79 | * @param conditionType 80 | * type of condition (which supports inner query as parameter) 81 | * @param innerQuery 82 | * inner query 83 | * @return new field condition 84 | */ 85 | public AbstractFieldCondition condition(final String fieldName, final CollectionAndQueryConditionType conditionType, 86 | TerminateQueryChainElement innerQuery) 87 | { 88 | return new InnerQueryFieldCondition(this, new SimpleField(fieldName), conditionType, innerQuery); 89 | } 90 | 91 | /** 92 | * Creates field condition with a given parameter. 93 | * 94 | * @param aliasedField 95 | * field with alias 96 | * @param conditionType 97 | * type of condition 98 | * @param conditionParameter 99 | * parameter 100 | * @return new field condition 101 | */ 102 | public AbstractFieldCondition condition(final AliasedField aliasedField, final RegularParameterConditionType conditionType, 103 | final Object conditionParameter) 104 | { 105 | return new ParameterFieldCondition(this, aliasedField, conditionType, conditionParameter); 106 | } 107 | 108 | /** 109 | * Creates field condition with a given parameter. 110 | * 111 | * @param aliasedField 112 | * field with alias 113 | * @param conditionType 114 | * type of condition 115 | * @param firstParameter 116 | * first parameter 117 | * @param secondParameter 118 | * second parameter 119 | * @return new field condition 120 | */ 121 | public AbstractFieldCondition condition(final AliasedField aliasedField, final TwoParameterConditionType conditionType, 122 | final Object firstParameter, final Object secondParameter) 123 | { 124 | return new TwoParameterFieldCondition(this, aliasedField, conditionType, firstParameter, secondParameter); 125 | } 126 | 127 | /** 128 | * Creates field condition with a given collection parameter. 129 | * 130 | * @param aliasedField 131 | * field with alias 132 | * @param conditionType 133 | * type of condition (which supports collection as parameter) 134 | * @param collectionConditionParameter 135 | * collection parameter 136 | * @return new field condition 137 | */ 138 | public AbstractFieldCondition condition(final AliasedField aliasedField, final CollectionAndQueryConditionType conditionType, 139 | Collection collectionConditionParameter) 140 | { 141 | return new ParameterFieldCondition(this, aliasedField, conditionType, collectionConditionParameter); 142 | } 143 | 144 | /** 145 | * Creates field condition with a given inner query. 146 | * 147 | * @param aliasedField 148 | * field with alias 149 | * @param conditionType 150 | * type of condition (which supports inner query as parameter) 151 | * @param innerQuery 152 | * inner query 153 | * @return new field condition 154 | */ 155 | public AbstractFieldCondition condition(final AliasedField aliasedField, final CollectionAndQueryConditionType conditionType, 156 | TerminateQueryChainElement innerQuery) 157 | { 158 | return new InnerQueryFieldCondition(this, aliasedField, conditionType, innerQuery); 159 | } 160 | 161 | /** 162 | * Creates field condition with a given parameter. 163 | * 164 | * @param field1 165 | * first field with alias 166 | * @param conditionType 167 | * type of condition 168 | * @param field2 169 | * second field with alias 170 | * @return new field condition 171 | */ 172 | public AbstractFieldCondition condition(final AliasedField field1, final RegularParameterConditionType conditionType, 173 | final AliasedField field2) 174 | { 175 | return new FieldToFieldCondition(this, field1, conditionType, field2); 176 | } 177 | 178 | /** 179 | * Creates field condition with a given condition. 180 | * 181 | * @param fieldName 182 | * field name (from model item, e.g. ProductModel.NAME) 183 | * @param conditionType 184 | * type of condition, which doesn't require parameter (e.g. ParameterlessConditionType.IS_NULL) 185 | * @return new field condition 186 | */ 187 | public AbstractFieldCondition condition(final String fieldName, final ParameterlessConditionType conditionType) 188 | { 189 | return new ParameterlessFieldCondition(this, new SimpleField(fieldName), conditionType); 190 | } 191 | 192 | /** 193 | * Creates field condition with a given condition. 194 | * 195 | * @param aliasedField 196 | * field with alias 197 | * @param conditionType 198 | * type of condition, which doesn't require parameter (e.g. ParameterlessConditionType.IS_NULL) 199 | * @return new field condition 200 | */ 201 | public AbstractFieldCondition condition(final AliasedField aliasedField, final ParameterlessConditionType conditionType) 202 | { 203 | return new ParameterlessFieldCondition(this, aliasedField, conditionType); 204 | } 205 | 206 | /** 207 | * Wraps given condition to be chained. 208 | * 209 | * @param condition 210 | * condition to wrap 211 | * @return wrapped condition 212 | */ 213 | public AbstractCondition condition(final AbstractCondition condition) 214 | { 215 | return new WrapperCondition(this, condition); 216 | } 217 | 218 | /** 219 | * Puts given condition (with chained conditions if any) into braces. 220 | * 221 | * @param condition 222 | * condition to wrap 223 | * @return braced condition chain 224 | */ 225 | public BraceConditionWrapper braces(final AbstractCondition condition) 226 | { 227 | return new BraceConditionWrapper(this, condition); 228 | } 229 | 230 | /** 231 | * Builds inner query condition. 232 | * 233 | * @param queryConditionType 234 | * condition type 235 | * @param innerQuery 236 | * inner query 237 | * @return inner query condition 238 | */ 239 | public AbstractCondition condition(final UnaryQueryConditionType queryConditionType, TerminateQueryChainElement innerQuery) 240 | { 241 | return new InnerQueryUnaryCondition(this, queryConditionType, innerQuery); 242 | } 243 | 244 | /** 245 | * Creates custom condition of given string. Given string is trimmed to remove redundant spaces if any. 246 | * 247 | * @param customCondition 248 | * custom condition string, e.g. "UPPER({name})=?name" 249 | * @return new custom condition element 250 | */ 251 | public AbstractCondition customCondition(final String customCondition) 252 | { 253 | return new CustomCondition(this, customCondition.trim()); 254 | } 255 | 256 | @Override 257 | protected void appendQuery(final StringBuilder sb) 258 | { 259 | super.appendQuery(sb); 260 | 261 | sb.append(SPACE).append(combineOperator); 262 | } 263 | 264 | } 265 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/CombineConditionsTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static de.hybris.platform.core.model.ItemModel.CREATIONTIME; 4 | import static de.hybris.platform.core.model.product.ProductModel.CODE; 5 | import static de.hybris.platform.core.model.product.ProductModel.NAME; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.CollectionAndQueryConditionType.IN; 7 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.condition; 8 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.select; 9 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectCustom; 10 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectFrom; 11 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 12 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterlessConditionType.IS_NOT_NULL; 13 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterlessConditionType.IS_NULL; 14 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.LIKE; 15 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.TwoParameterConditionType.BETWEEN; 16 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.TwoParameterConditionType.NOT_BETWEEN; 17 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.UnaryQueryConditionType.EXISTS; 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertTrue; 20 | 21 | import de.hybris.bootstrap.annotations.UnitTest; 22 | import de.hybris.platform.core.model.product.ProductModel; 23 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 24 | 25 | import java.util.Date; 26 | 27 | import org.junit.Test; 28 | 29 | 30 | @UnitTest 31 | public class CombineConditionsTest 32 | { 33 | private final TerminateQueryChainElement commonInnerQuery = selectCustom("UPPER({code})").from(table(ProductModel.class)); 34 | private final Alias p = new Alias("p"); 35 | 36 | @Test 37 | public void testParameterlessToParameterCondition() 38 | { 39 | final FlexibleSearchQuery query = 40 | selectFrom(ProductModel.class) 41 | .where( 42 | condition(NAME, IS_NULL) 43 | .or() 44 | .condition(NAME, LIKE, "%test%") 45 | ) 46 | .build(); 47 | 48 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NULL OR {name} LIKE ?name1", query.getQuery()); 49 | assertEquals("Query parameters don't match", "%test%", query.getQueryParameters().get("name1")); 50 | } 51 | 52 | @Test 53 | public void testParameterlessToWrappedCondition() 54 | { 55 | final AbstractFieldCondition nameLikeTest = condition(NAME, LIKE, "%test%"); 56 | 57 | final FlexibleSearchQuery query = 58 | selectFrom(ProductModel.class) 59 | .where( 60 | condition(NAME, IS_NULL) 61 | .or() 62 | .condition(nameLikeTest) 63 | ) 64 | .build(); 65 | 66 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NULL OR {name} LIKE ?name1", query.getQuery()); 67 | assertEquals("Query parameters don't match", "%test%", query.getQueryParameters().get("name1")); 68 | } 69 | 70 | @Test 71 | public void testParameterlessToParameterlessCondition() 72 | { 73 | final FlexibleSearchQuery query = 74 | selectFrom(ProductModel.class) 75 | .where( 76 | condition(NAME, IS_NULL) 77 | .and() 78 | .condition(CODE, IS_NULL) 79 | ) 80 | .build(); 81 | 82 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NULL AND {code} IS NULL", query.getQuery()); 83 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 84 | } 85 | 86 | @Test 87 | public void testParameterlessToInnerQueryCondition() 88 | { 89 | final FlexibleSearchQuery query = 90 | selectFrom(ProductModel.class) 91 | .where( 92 | condition(NAME, IS_NULL) 93 | .and() 94 | .condition(CODE, IN, commonInnerQuery) 95 | ) 96 | .build(); 97 | 98 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NULL AND {code} IN " + 99 | "({{SELECT UPPER({code}) FROM {Product}}})", query.getQuery()); 100 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 101 | } 102 | 103 | @Test 104 | public void testParameterlessToUnaryQueryCondition() 105 | { 106 | final FlexibleSearchQuery query = 107 | selectFrom(ProductModel.class) 108 | .where( 109 | condition(NAME, IS_NULL) 110 | .and() 111 | .condition(EXISTS, commonInnerQuery) 112 | ) 113 | .build(); 114 | 115 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NULL AND EXISTS " + 116 | "({{SELECT UPPER({code}) FROM {Product}}})", query.getQuery()); 117 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 118 | } 119 | 120 | @Test 121 | public void testParameterlessToCustomCondition() 122 | { 123 | final FlexibleSearchQuery query = 124 | selectFrom(ProductModel.class) 125 | .where( 126 | condition(NAME, IS_NULL) 127 | .and() 128 | .customCondition("UPPER({code})={code}") 129 | ) 130 | .build(); 131 | 132 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NULL AND UPPER({code})={code}", query.getQuery()); 133 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 134 | } 135 | 136 | @Test 137 | public void testParameterlessToBracedConditions() 138 | { 139 | final FlexibleSearchQuery query = 140 | selectFrom(ProductModel.class) 141 | .where( 142 | condition(NAME, IS_NULL) 143 | .and() 144 | .braces( 145 | condition(CODE, IN, commonInnerQuery) 146 | .or() 147 | .condition(NAME, IS_NOT_NULL) 148 | ) 149 | ) 150 | .build(); 151 | 152 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NULL AND ({code} IN " + 153 | "({{SELECT UPPER({code}) FROM {Product}}}) OR {name} IS NOT NULL)", query.getQuery()); 154 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 155 | } 156 | 157 | @Test 158 | public void testParameterlessToTwoParameterCondition() 159 | { 160 | final Date dateStart = new Date(); 161 | final Date dateEnd = new Date(); 162 | 163 | final FlexibleSearchQuery query = 164 | selectFrom(ProductModel.class) 165 | .where( 166 | condition(NAME, IS_NULL) 167 | .or() 168 | .condition(CREATIONTIME, BETWEEN, dateStart, dateEnd) 169 | ) 170 | .build(); 171 | 172 | assertEquals("Query is not as expected", "SELECT {pk} FROM {Product} WHERE {name} IS NULL OR " + 173 | "{creationtime} BETWEEN ?creationtime1 AND ?creationtime2", query.getQuery()); 174 | assertEquals("Query parameters size doesn't match", 2, query.getQueryParameters().size()); 175 | assertEquals("Query parameter 1 doesn't match", dateStart, query.getQueryParameters().get("creationtime1")); 176 | assertEquals("Query parameter 2 doesn't match", dateEnd, query.getQueryParameters().get("creationtime2")); 177 | } 178 | 179 | @Test 180 | public void testAliasedParameterlessToParameterCondition() 181 | { 182 | final FlexibleSearchQuery query = 183 | select(p) 184 | .from(table(ProductModel.class).as(p)) 185 | .where( 186 | condition(p.field(NAME), IS_NULL) 187 | .or() 188 | .condition(p.field(NAME), LIKE, "%test%") 189 | ) 190 | .build(); 191 | 192 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name} IS NULL OR {p.name} LIKE ?name1", query.getQuery()); 193 | assertEquals("Query parameters don't match", "%test%", query.getQueryParameters().get("name1")); 194 | } 195 | 196 | @Test 197 | public void testAliasedParameterlessToWrappedCondition() 198 | { 199 | final AbstractFieldCondition nameLikeTest = condition(p.field(NAME), LIKE, "%test%"); 200 | 201 | final FlexibleSearchQuery query = 202 | select(p) 203 | .from(table(ProductModel.class).as(p)) 204 | .where( 205 | condition(p.field(NAME), IS_NULL) 206 | .or() 207 | .condition(nameLikeTest) 208 | ) 209 | .build(); 210 | 211 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name} IS NULL OR {p.name} LIKE ?name1", query.getQuery()); 212 | assertEquals("Query parameters don't match", "%test%", query.getQueryParameters().get("name1")); 213 | } 214 | 215 | @Test 216 | public void testAliasedParameterlessToParameterlessCondition() 217 | { 218 | final FlexibleSearchQuery query = 219 | select(p) 220 | .from(table(ProductModel.class).as(p)) 221 | .where( 222 | condition(p.field(NAME), IS_NULL) 223 | .and() 224 | .condition(p.field(CODE), IS_NULL) 225 | ) 226 | .build(); 227 | 228 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name} IS NULL AND {p.code} IS NULL", query.getQuery()); 229 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 230 | } 231 | 232 | @Test 233 | public void testAliasedParameterlessToInnerQueryCondition() 234 | { 235 | final FlexibleSearchQuery query = 236 | select(p) 237 | .from(table(ProductModel.class).as(p)) 238 | .where( 239 | condition(p.field(NAME), IS_NULL) 240 | .and() 241 | .condition(p.field(CODE), IN, commonInnerQuery) 242 | ) 243 | .build(); 244 | 245 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name} IS NULL AND {p.code} IN " + 246 | "({{SELECT UPPER({code}) FROM {Product}}})", query.getQuery()); 247 | assertTrue("Query parameters don't match", query.getQueryParameters().isEmpty()); 248 | } 249 | 250 | @Test 251 | public void testAliasedParameterlessToTwoParameterCondition() 252 | { 253 | final Date dateStart = new Date(); 254 | final Date dateEnd = new Date(); 255 | 256 | final FlexibleSearchQuery query = 257 | select(p) 258 | .from(table(ProductModel.class).as(p)) 259 | .where( 260 | condition(p.field(NAME), IS_NULL) 261 | .and() 262 | .condition(p.field(CREATIONTIME), NOT_BETWEEN, dateStart, dateEnd) 263 | ) 264 | .build(); 265 | 266 | assertEquals("Query is not as expected", "SELECT {p.pk} FROM {Product AS p} WHERE {p.name} IS NULL AND " + 267 | "{p.creationtime} NOT BETWEEN ?creationtime1 AND ?creationtime2", query.getQuery()); 268 | assertEquals("Query parameters size doesn't match", 2, query.getQueryParameters().size()); 269 | assertEquals("Query parameter 1 doesn't match", dateStart, query.getQueryParameters().get("creationtime1")); 270 | assertEquals("Query parameter 2 doesn't match", dateEnd, query.getQueryParameters().get("creationtime2")); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /testsrc/org/bitbucket/andriichukandrii/hybris/flexiblesearchbuilder/FlexibleSearchBuilderBasicUsageTest.java: -------------------------------------------------------------------------------- 1 | package org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder; 2 | 3 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.CollectionAndQueryConditionType.IN; 4 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.braces; 5 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.condition; 6 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.Conditions.customCondition; 7 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.select; 8 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectCustom; 9 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FlexibleSearchQueryBuilder.selectFrom; 10 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.FromClauseElements.table; 11 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterlessConditionType.IS_NOT_NULL; 12 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.ParameterlessConditionType.IS_NULL; 13 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_EQUAL_TO; 14 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.RegularParameterConditionType.IS_GREATER_THAN; 15 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.SqlFunctions.count; 16 | import static org.bitbucket.andriichukandrii.hybris.flexiblesearchbuilder.UnaryQueryConditionType.EXISTS; 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertTrue; 19 | 20 | import de.hybris.bootstrap.annotations.UnitTest; 21 | import de.hybris.platform.core.enums.Gender; 22 | import de.hybris.platform.core.model.media.MediaModel; 23 | import de.hybris.platform.core.model.order.OrderEntryModel; 24 | import de.hybris.platform.core.model.order.OrderModel; 25 | import de.hybris.platform.core.model.product.ProductModel; 26 | import de.hybris.platform.servicelayer.search.FlexibleSearchQuery; 27 | import de.hybris.platform.variants.model.VariantProductModel; 28 | import de.hybris.platform.yacceleratorcore.model.ApparelProductModel; 29 | 30 | import java.util.Arrays; 31 | import java.util.Collections; 32 | import java.util.List; 33 | 34 | import org.junit.Test; 35 | import org.springframework.util.CollectionUtils; 36 | 37 | 38 | //This test relies on the constants and current implementation. However, it serves as a point of safety and a reference now. 39 | @UnitTest 40 | @SuppressWarnings({"LawOfDemeter"}) 41 | public class FlexibleSearchBuilderBasicUsageTest 42 | { 43 | 44 | @Test 45 | public void testSelectAllFromTableQuery() 46 | { 47 | final FlexibleSearchQuery fQuery = selectFrom(ProductModel.class).build(); 48 | 49 | assertEquals("Query does not match", "SELECT {pk} FROM {Product}", fQuery.getQuery()); 50 | assertTrue("Non-expected parameter(-s) found", CollectionUtils.isEmpty(fQuery.getQueryParameters())); 51 | } 52 | 53 | @Test 54 | public void testSelectWithParameterlessCondition() 55 | { 56 | final FlexibleSearchQuery fQuery = 57 | selectFrom(VariantProductModel.class) 58 | .where( 59 | condition(VariantProductModel.NAME, IS_NOT_NULL) 60 | ) 61 | .build(); 62 | 63 | assertEquals("Query does not match", "SELECT {pk} FROM {VariantProduct} WHERE {name} IS NOT NULL", fQuery.getQuery()); 64 | assertTrue("Non-expected parameter(-s) found", CollectionUtils.isEmpty(fQuery.getQueryParameters())); 65 | } 66 | 67 | @Test 68 | public void testSelectWithParameterCondition() 69 | { 70 | final FlexibleSearchQuery fQuery = 71 | selectFrom(OrderModel.class) 72 | .where( 73 | condition(OrderModel.CALCULATED, IS_EQUAL_TO, false) 74 | ) 75 | .build(); 76 | assertEquals("Query does not match", "SELECT {pk} FROM {Order} WHERE {calculated}=?calculated1", fQuery.getQuery()); 77 | assertEquals("Wrong number of query parameters", 1, fQuery.getQueryParameters().size()); 78 | assertEquals("Query parameter doesn't match", false, fQuery.getQueryParameters().get("calculated1")); 79 | } 80 | 81 | @Test 82 | public void testSelectWithCollectionParameterCondition() 83 | { 84 | final List genders = Collections.singletonList(Gender.MALE); 85 | 86 | final FlexibleSearchQuery fQuery = 87 | selectFrom(ApparelProductModel.class) 88 | .where( 89 | condition(ApparelProductModel.GENDERS, IN, genders) 90 | ) 91 | .build(); 92 | assertEquals("Query does not match", "SELECT {pk} FROM {ApparelProduct} WHERE {genders} IN (?genders1)", fQuery.getQuery()); 93 | assertEquals("Wrong number of query parameters", 1, fQuery.getQueryParameters().size()); 94 | assertEquals("Query parameter doesn't match", genders, fQuery.getQueryParameters().get("genders1")); 95 | } 96 | 97 | @Test 98 | public void testSelectWithCustomCondition() 99 | { 100 | final FlexibleSearchQuery fQuery = 101 | selectFrom(ProductModel.class) 102 | .where( 103 | customCondition("UPPER({name})={name}") 104 | ) 105 | .build(); 106 | assertEquals("Query does not match", "SELECT {pk} FROM {Product} WHERE UPPER({name})={name}", fQuery.getQuery()); 107 | assertEquals("Wrong number of query parameters", 0, fQuery.getQueryParameters().size()); 108 | } 109 | 110 | @Test 111 | public void testSelectWithBracesAndParameterConditions() 112 | { 113 | final FlexibleSearchQuery fQuery = 114 | selectFrom(OrderModel.class) 115 | .where( 116 | braces( 117 | condition(OrderModel.SUBTOTAL, IS_NULL) 118 | .or() 119 | .condition(OrderModel.TOTALPRICE, IS_NULL) 120 | ) 121 | .and() 122 | .condition(OrderModel.CALCULATED, IS_EQUAL_TO, false) 123 | ) 124 | .build(); 125 | assertEquals("Query does not match", "SELECT {pk} FROM {Order} WHERE ({subtotal} IS NULL OR {totalPrice} IS NULL) AND {calculated}=?calculated1", fQuery.getQuery()); 126 | assertEquals("Wrong number of query parameters", 1, fQuery.getQueryParameters().size()); 127 | assertEquals("Query parameter doesn't match", false, fQuery.getQueryParameters().get("calculated1")); 128 | } 129 | 130 | @Test 131 | public void testSelectWithJoin() 132 | { 133 | final Alias o = new Alias("o"); 134 | final Alias e = new Alias("e"); 135 | final FlexibleSearchQuery fQuery = 136 | select(o) 137 | .from( 138 | table(OrderModel.class).as(o) 139 | .join(OrderEntryModel.class).as(e) 140 | .on(o.pk(), IS_EQUAL_TO, e.field(OrderEntryModel.ORDER)) 141 | ) 142 | .build(); 143 | 144 | assertEquals("Query does not match", "SELECT {o.pk} FROM {Order AS o JOIN OrderEntry AS e ON {o.pk}={e.order}}", fQuery.getQuery()); 145 | assertTrue("Non-expected parameter(-s) found", CollectionUtils.isEmpty(fQuery.getQueryParameters())); 146 | } 147 | 148 | @Test 149 | public void testSelectFields() 150 | { 151 | final FlexibleSearchQuery fQuery = 152 | select( 153 | FieldWithType.of(ProductModel.NAME, String.class), 154 | FieldWithType.of(ProductModel.DESCRIPTION, String.class), 155 | FieldWithType.of(ProductModel.PK, Long.class) 156 | ) 157 | .from( 158 | table(ProductModel.class) 159 | ) 160 | .where( 161 | condition(ProductModel.SUMMARY, IS_NULL) 162 | .and() 163 | .condition(ProductModel.NAME, IS_NOT_NULL) 164 | .and() 165 | .condition(ProductModel.DESCRIPTION, IS_NOT_NULL) 166 | ) 167 | .build(); 168 | 169 | assertEquals("Query does not match", "SELECT {name},{description},{pk} FROM {Product} WHERE {summary} IS NULL" + 170 | " AND {name} IS NOT NULL AND {description} IS NOT NULL", fQuery.getQuery()); 171 | assertEquals("Result classes don't match", Arrays.asList(String.class, String.class, Long.class), fQuery.getResultClassList()); 172 | } 173 | 174 | @Test 175 | public void testSelectAliasedFields() 176 | { 177 | final Alias p = new Alias("p"); 178 | final FlexibleSearchQuery fQuery = 179 | select( 180 | FieldWithType.of(p.field(ProductModel.NAME), String.class), 181 | FieldWithType.of(p.pk(), Long.class) 182 | ) 183 | .from( 184 | table(ProductModel.class).as(p) 185 | ) 186 | .where( 187 | condition(p.field(ProductModel.SUMMARY), IS_NULL) 188 | .and() 189 | .condition(p.field(ProductModel.NAME), IS_NOT_NULL) 190 | ) 191 | .build(); 192 | 193 | assertEquals("Query does not match", "SELECT {p.name},{p.pk} FROM {Product AS p} WHERE {p.summary} IS NULL" + 194 | " AND {p.name} IS NOT NULL", fQuery.getQuery()); 195 | assertEquals("Result classes don't match", Arrays.asList(String.class, Long.class), fQuery.getResultClassList()); 196 | } 197 | 198 | @Test 199 | public void testSelectWithCustomStatement() 200 | { 201 | final FlexibleSearchQuery fQuery = 202 | selectCustom( 203 | "DISTINCT {code}" 204 | ) 205 | .from( 206 | table(ProductModel.class) 207 | ) 208 | .build(); 209 | assertEquals("Query does not match", "SELECT DISTINCT {code} FROM {Product}", fQuery.getQuery()); 210 | assertEquals("Wrong number of query parameters", 0, fQuery.getQueryParameters().size()); 211 | } 212 | 213 | @Test 214 | public void testSelectWithCustomStatementAndResultTypes() 215 | { 216 | final FlexibleSearchQuery fQuery = 217 | selectCustom( 218 | "COUNT({pk}),{name}", Long.class, String.class 219 | ) 220 | .from( 221 | table(ProductModel.class) 222 | ) 223 | .groupBy(ProductModel.NAME) 224 | .build(); 225 | assertEquals("Query does not match", "SELECT COUNT({pk}),{name} FROM {Product} GROUP BY {name}", fQuery.getQuery()); 226 | assertEquals("Wrong number of query parameters", 0, fQuery.getQueryParameters().size()); 227 | assertEquals("Result classes don't match", Arrays.asList(Long.class, String.class), fQuery.getResultClassList()); 228 | } 229 | 230 | @Test 231 | public void testSelectWithInnerQueryFieldCondition() 232 | { 233 | final TerminateQueryChainElement innerQuery = 234 | selectFrom(MediaModel.class) 235 | .where( 236 | condition(MediaModel.REMOVABLE, IS_EQUAL_TO, true) 237 | ); 238 | final FlexibleSearchQuery fQuery = 239 | selectFrom(ProductModel.class) 240 | .where( 241 | condition(ProductModel.LOGO, IN, innerQuery) 242 | .and() 243 | .condition(ProductModel.SUMMARY, IS_NULL) 244 | ) 245 | .build(); 246 | assertEquals("Query does not match", "SELECT {pk} FROM {Product} WHERE {logo} IN " + 247 | "({{SELECT {pk} FROM {Media} WHERE {removable}=?removable1}}) AND {summary} IS NULL", fQuery.getQuery()); 248 | assertEquals("Wrong number of query parameters", 1, fQuery.getQueryParameters().size()); 249 | assertEquals("Query parameter doesn't match", true, fQuery.getQueryParameters().get("removable1")); 250 | } 251 | 252 | @Test 253 | public void testSelectWithInnerQueryUnaryCondition() 254 | { 255 | final Alias m = new Alias("m"); 256 | final Alias p = new Alias("p"); 257 | final TerminateQueryChainElement innerQuery = 258 | select(m) 259 | .from( 260 | table(MediaModel.class).as(m) 261 | ) 262 | .where( 263 | condition(m.field(MediaModel.REMOVABLE), IS_EQUAL_TO, false) 264 | .and() 265 | .condition(m.field(MediaModel.MODIFIEDTIME), IS_GREATER_THAN, p.field(ProductModel.MODIFIEDTIME)) 266 | ); 267 | final FlexibleSearchQuery fQuery = 268 | select(p) 269 | .from( 270 | table(ProductModel.class).as(p) 271 | ) 272 | .where( 273 | condition(EXISTS, innerQuery) 274 | ) 275 | .build(); 276 | assertEquals("Query does not match", "SELECT {p.pk} FROM {Product AS p} WHERE EXISTS " + 277 | "({{SELECT {m.pk} FROM {Media AS m} WHERE {m.removable}=?removable1 AND {m.modifiedtime}>{p.modifiedtime}}})", fQuery.getQuery()); 278 | assertEquals("Wrong number of query parameters", 1, fQuery.getQueryParameters().size()); 279 | assertEquals("Query parameter doesn't match", false, fQuery.getQueryParameters().get("removable1")); 280 | } 281 | 282 | @Test 283 | public void testSelectWithOrderBy() 284 | { 285 | final FlexibleSearchQuery fQuery = 286 | selectFrom(ProductModel.class) 287 | .orderByAsc(ProductModel.CODE, ProductModel.CATALOGVERSION) 288 | .orderByDesc(ProductModel.NAME) 289 | .build(); 290 | assertEquals("Query does not match", "SELECT {pk} FROM {Product} ORDER BY {code} ASC,{catalogVersion} ASC,{name} DESC", fQuery.getQuery()); 291 | assertEquals("Wrong number of query parameters", 0, fQuery.getQueryParameters().size()); 292 | } 293 | 294 | @Test 295 | public void testSelectWithGroupByAndOrderBy() 296 | { 297 | final FlexibleSearchQuery fQuery = 298 | selectFrom(ProductModel.class) 299 | .groupBy(ProductModel.PK, ProductModel.CODE, ProductModel.CATALOGVERSION) 300 | .orderByDesc(ProductModel.CODE) 301 | .build(); 302 | assertEquals("Query does not match", "SELECT {pk} FROM {Product} GROUP BY {pk},{code},{catalogVersion} ORDER BY {code} DESC", fQuery.getQuery()); 303 | assertEquals("Wrong number of query parameters", 0, fQuery.getQueryParameters().size()); 304 | } 305 | 306 | @Test 307 | public void testSelectWithGroupByAndFunctions() 308 | { 309 | final Alias p = new Alias("p"); 310 | final Alias o = new Alias("o"); 311 | final Alias oe = new Alias("oe"); 312 | 313 | final FlexibleSearchQuery fQuery = 314 | select( 315 | FunctionWithType.of(count(o.field(OrderModel.CODE)), Long.class), 316 | FieldWithType.of(p.field(ProductModel.CODE), String.class) 317 | ) 318 | .from( 319 | table(ProductModel.class).as(p) 320 | .join(OrderEntryModel.class).as(oe) 321 | .on(p.pk(), oe.field(OrderEntryModel.PRODUCT)) 322 | .join(OrderModel.class).as(o) 323 | .on(o.pk(), oe.field(OrderEntryModel.ORDER)) 324 | ) 325 | .groupBy(p.field(ProductModel.CODE)) 326 | .orderByDesc(count(o.field(OrderModel.CODE))) 327 | .build(); 328 | assertEquals("Query does not match", "SELECT COUNT({o.code}),{p.code} FROM {Product AS p JOIN OrderEntry " + 329 | "AS oe ON {p.pk}={oe.product} JOIN Order AS o ON {o.pk}={oe.order}} GROUP BY {p.code} ORDER BY COUNT({o.code}) DESC", fQuery.getQuery()); 330 | assertEquals("Wrong number of query parameters", 0, fQuery.getQueryParameters().size()); 331 | } 332 | 333 | @Test 334 | public void testSelectWithJoinAndDifferentConditions() 335 | { 336 | final Alias productAlias = new Alias("p"); 337 | final Alias entryAlias = new Alias("e"); 338 | final Alias orderAlias = new Alias("o"); 339 | final String productCode = "23191";//product code from electronics store sample data 340 | final Double priceBarrier = 50d; 341 | final FlexibleSearchQuery fQuery = 342 | select(orderAlias) 343 | .from( 344 | table(OrderModel.class).as(orderAlias) 345 | .leftJoin(OrderEntryModel.class).as(entryAlias) 346 | .on(orderAlias.pk(), IS_EQUAL_TO, entryAlias.field(OrderEntryModel.ORDER)) 347 | .join(ProductModel.class).as(productAlias) 348 | .on(productAlias.pk(), IS_EQUAL_TO, entryAlias.field(OrderEntryModel.PRODUCT)) 349 | ) 350 | .where( 351 | condition(productAlias.field(ProductModel.CODE), IS_EQUAL_TO, productCode) 352 | .and() 353 | .condition(orderAlias.field(OrderModel.TOTALPRICE), IS_GREATER_THAN, priceBarrier) 354 | ) 355 | .groupBy(orderAlias.pk(), orderAlias.field(OrderModel.CODE), entryAlias.field(OrderEntryModel.ENTRYNUMBER)) 356 | .orderByDesc(orderAlias.field(OrderModel.CODE), entryAlias.field(OrderEntryModel.ENTRYNUMBER)) 357 | .build(); 358 | 359 | assertEquals("Query does not match", "SELECT {o.pk} FROM {Order AS o LEFT JOIN OrderEntry AS e ON " + 360 | "{o.pk}={e.order} JOIN Product AS p ON {p.pk}={e.product}} WHERE {p.code}=?code1 AND {o.totalPrice}>?totalPrice1" + 361 | " GROUP BY {o.pk},{o.code},{e.entryNumber} ORDER BY {o.code} DESC,{e.entryNumber} DESC", 362 | fQuery.getQuery()); 363 | assertEquals("Wrong number of query parameters", 2, fQuery.getQueryParameters().size()); 364 | assertEquals("Query parameter doesn't match", productCode, fQuery.getQueryParameters().get("code1")); 365 | assertEquals("Query parameter doesn't match", priceBarrier, fQuery.getQueryParameters().get("totalPrice1")); 366 | } 367 | } 368 | --------------------------------------------------------------------------------