Add _ifPresent() expressions for GT, GE, LT, LE (follow up from #2768)

This commit is contained in:
rob
2023-02-22 14:04:26 +13:00
parent 6e98486a03
commit 0b615a79cc
6 changed files with 208 additions and 6 deletions
@@ -951,9 +951,12 @@ public interface ExpressionList<T> {
ExpressionList<T> gtOrNull(String propertyName, Object value);
/**
* Greater Than or Equal to OR Null - ({@code >= or null }).
* Is GREATER THAN if value is non-null and otherwise no expression is added to the query.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>gtIfPresent()</code> rather than having a separate if block.
*/
ExpressionList<T> geOrNull(String propertyName, Object value);
ExpressionList<T> gtIfPresent(String propertyName, @Nullable Object value);
/**
* Greater Than or Equal to the result of a sub-query.
@@ -966,6 +969,20 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> ge(String propertyName, Object value);
/**
* Greater Than or Equal to OR Null - ({@code >= or null }).
*/
ExpressionList<T> geOrNull(String propertyName, Object value);
/**
* Is GREATER THAN OR EQUAL TO if value is non-null and otherwise no expression is added to the query.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>geIfPresent()</code> rather than having a separate if block.
*/
ExpressionList<T> geIfPresent(String propertyName, @Nullable Object value);
/**
* Less Than the result of a sub-query.
*/
@@ -982,9 +999,12 @@ public interface ExpressionList<T> {
ExpressionList<T> ltOrNull(String propertyName, Object value);
/**
* Less Than or Equal to OR Null - ({@code <= or null }).
* Is LESS THAN if value is non-null and otherwise no expression is added to the query.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>ltIfPresent()</code> rather than having a separate if block.
*/
ExpressionList<T> leOrNull(String propertyName, Object value);
ExpressionList<T> ltIfPresent(String propertyName, @Nullable Object value);
/**
* Less Than or Equal to the result of a sub-query.
@@ -996,6 +1016,19 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> le(String propertyName, Object value);
/**
* Less Than or Equal to OR Null - ({@code <= or null }).
*/
ExpressionList<T> leOrNull(String propertyName, Object value);
/**
* Is LESS THAN OR EQUAL TO if value is non-null and otherwise no expression is added to the query.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>leIfPresent()</code> rather than having a separate if block.
*/
ExpressionList<T> leIfPresent(String propertyName, @Nullable Object value);
/**
* Is Null - property is null.
*/
@@ -903,6 +903,16 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return add(expr.geOrNull(propertyName, value));
}
@Override
public ExpressionList<T> gtIfPresent(String propertyName, @Nullable Object value) {
return value == null ? this : add(expr.gt(propertyName, value));
}
@Override
public ExpressionList<T> geIfPresent(String propertyName, @Nullable Object value) {
return value == null ? this : add(expr.ge(propertyName, value));
}
@Override
public ExpressionList<T> icontains(String propertyName, String value) {
return add(expr.icontains(propertyName, value));
@@ -1061,6 +1071,16 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return add(expr.leOrNull(propertyName, value));
}
@Override
public ExpressionList<T> ltIfPresent(String propertyName, @Nullable Object value) {
return value == null ? this : add(expr.lt(propertyName, value));
}
@Override
public ExpressionList<T> leIfPresent(String propertyName, @Nullable Object value) {
return value == null ? this : add(expr.le(propertyName, value));
}
@Override
public ExpressionList<T> not(Expression exp) {
return add(expr.not(exp));
@@ -677,6 +677,16 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
return exprList.geOrNull(propertyName, value);
}
@Override
public ExpressionList<T> gtIfPresent(String propertyName, @Nullable Object value) {
return value == null ? this : exprList.gt(propertyName, value);
}
@Override
public ExpressionList<T> geIfPresent(String propertyName, @Nullable Object value) {
return value == null ? this : exprList.ge(propertyName, value);
}
@Override
public ExpressionList<T> having() {
throw new IllegalStateException("having() not allowed on Junction expression list");
@@ -837,6 +847,16 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
return exprList.leOrNull(propertyName, value);
}
@Override
public ExpressionList<T> ltIfPresent(String propertyName, @Nullable Object value) {
return value == null ? this : exprList.lt(propertyName, value);
}
@Override
public ExpressionList<T> leIfPresent(String propertyName, @Nullable Object value) {
return value == null ? this : exprList.le(propertyName, value);
}
@Override
public ExpressionList<T> ne(String propertyName, Query<?> subQuery) {
return exprList.ne(propertyName, subQuery);
@@ -1,6 +1,7 @@
package io.ebean.typequery;
import io.avaje.lang.Nullable;
import io.ebean.Query;
/**
@@ -65,6 +66,23 @@ public abstract class PBaseComparable<R, T> extends PBaseValueEqual<R, T> {
return _root;
}
/**
* Is greater than if value is non-null and otherwise no expression is added to the query.
* <p>
* That is, only add the GREATER THAN predicate if the value is not null.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>gtIfPresent()</code> rather than having a separate if block.
*
* @param value the value which can be null
* @return the root query bean instance
*/
public final R gtIfPresent(@Nullable T value) {
expr().gtIfPresent(_name, value);
return _root;
}
/**
* Greater than or Equal to.
*
@@ -98,6 +116,22 @@ public abstract class PBaseComparable<R, T> extends PBaseValueEqual<R, T> {
return _root;
}
/**
* Is greater than or equal to if value is non-null and otherwise no expression is added to the query.
* <p>
* That is, only add the GREATER THAN OR EQUAL TO predicate if the value is not null.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>geIfPresent()</code> rather than having a separate if block.
*
* @param value the value which can be null
* @return the root query bean instance
*/
public final R geIfPresent(@Nullable T value) {
expr().geIfPresent(_name, value);
return _root;
}
/**
* Less than.
*
@@ -131,6 +165,22 @@ public abstract class PBaseComparable<R, T> extends PBaseValueEqual<R, T> {
return _root;
}
/**
* Is less than if value is non-null and otherwise no expression is added to the query.
* <p>
* That is, only add the LESS THAN predicate if the value is not null.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>ltIfPresent()</code> rather than having a separate if block.
*
* @param value the value which can be null
* @return the root query bean instance
*/
public final R ltIfPresent(@Nullable T value) {
expr().ltIfPresent(_name, value);
return _root;
}
/**
* Less than or Equal to.
*
@@ -164,6 +214,22 @@ public abstract class PBaseComparable<R, T> extends PBaseValueEqual<R, T> {
return _root;
}
/**
* Is less than or equal to if value is non-null and otherwise no expression is added to the query.
* <p>
* That is, only add the LESS THAN OR EQUAL TO predicate if the value is not null.
* <p>
* This is effectively a helper method that allows a query to be built in fluid style where some predicates are
* effectively optional. We can use <code>leIfPresent()</code> rather than having a separate if block.
*
* @param value the value which can be null
* @return the root query bean instance
*/
public final R leIfPresent(@Nullable T value) {
expr().leIfPresent(_name, value);
return _root;
}
/**
* Greater or equal to lower value and strictly less than upper value.
* <p>
@@ -7,7 +7,6 @@ import org.example.domain.query.QMyInner;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
@@ -45,9 +44,33 @@ class MyInnerTest {
assertThat(found4).isNotNull();
MyInner found5 = new QMyInner()
.id.lt(99)
.one.gtIfPresent(null)
.one.geIfPresent(null)
.one.ltIfPresent(null)
.one.leIfPresent(null)
.description.eqIfPresent("foo")
.findOne();
assertThat(found5).isNotNull();
MyInner found6 = new QMyInner()
.id.lt(99)
.one.gtIfPresent("o")
.one.geIfPresent("o")
.one.ltIfPresent("oz")
.one.leIfPresent("oz")
.id.gt(1)
.findOne();
assertThat(found6).isNotNull();
List<String> sql = LoggedSql.stop();
assertThat(sql).hasSize(2);
assertThat(sql).hasSize(4);
assertThat(sql.get(0)).contains("select t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.one = ?;");
assertThat(sql.get(1)).contains("select t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.description = ?;");
assertThat(sql.get(2)).contains("select t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.id < ? and t0.description = ?;");
assertThat(sql.get(3)).contains("select t0.id, t0.one, t0.id, t0.one, t0.description from my_inner t0 where t0.id < ? and t0.one > ? and t0.one >= ? and t0.one < ? and t0.one <= ? and t0.id > ?;");
}
}
@@ -69,6 +69,46 @@ public class TestQueryIsNull extends BaseTestCase {
assertThat(query4.getGeneratedSql()).isEqualTo("select t0.id, t0.status from o_order t0 where t0.order_date is not null and t0.status = ? and t0.ship_date is null");
}
@Test
void ifPresent() {
ResetBasicData.reset();
Query<Order> query = DB.find(Order.class).select("id, status")
.where().eqIfPresent("status", null)
.gtIfPresent("id", null)
.geIfPresent("id", null)
.ltIfPresent("id", null)
.leIfPresent("id", null)
.query();
query.findList();
assertThat(query.getGeneratedSql()).isEqualTo("select t0.id, t0.status from o_order t0");
Query<Order> query1 = DB.find(Order.class).select("id, status")
.where().eqIfPresent("status", null)
.gtIfPresent("id", 90)
.geIfPresent("id", 91)
.ltIfPresent("id", 92)
.leIfPresent("id", 93)
.isNull("shipDate").query();
query1.findList();
assertThat(query1.getGeneratedSql()).isEqualTo("select t0.id, t0.status from o_order t0 where t0.id > ? and t0.id >= ? and t0.id < ? and t0.id <= ? and t0.ship_date is null");
Query<Order> query2 = DB.find(Order.class).select("id, status")
.where()
.leIfPresent("id", 93)
.isNull("orderDate")
.ltIfPresent("id", 92)
.geIfPresent("id", 91)
.isNull("shipDate")
.gtIfPresent("id", 90)
.query();
query2.findList();
assertThat(query2.getGeneratedSql()).isEqualTo("select t0.id, t0.status from o_order t0 where t0.id <= ? and t0.order_date is null and t0.id < ? and t0.id >= ? and t0.ship_date is null and t0.id > ?");
}
@Test
public void isNotNull_when_OneToMany_expect_existsSubquery() {
ResetBasicData.reset();