Skip to content
This repository has been archived by the owner on Oct 13, 2020. It is now read-only.

Commit

Permalink
#57 - ENH: Support inOrEmpty() and rawOrEmpty() expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
rbygrave committed Mar 5, 2019
1 parent 6cf2b83 commit e38b707
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 2 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>io.ebean</groupId>
<artifactId>ebean-querybean</artifactId>
<version>11.35.2-SNAPSHOT</version>
<version>11.35.3-SNAPSHOT</version>

<parent>
<groupId>org.avaje</groupId>
Expand All @@ -23,7 +23,7 @@
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>11.35.1</version>
<version>11.35.3</version>
<scope>provided</scope>
</dependency>

Expand Down
43 changes: 43 additions & 0 deletions src/main/java/io/ebean/typequery/PBaseValueEqual.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,49 @@ public final R in(T... values) {
return _root;
}

/**
* In where null or empty values means that no predicate is added to the query.
* <p>
* That is, only add the IN predicate if the values are not null or empty.
* <p>
* Without this we typically need to code an <code>if</code> block to only add
* the IN predicate if the collection is not empty like:
* </p>
*
* <h3>Without inOrEmpty()</h3>
* <pre>{@code
*
* List<String> names = Arrays.asList("foo", "bar");
*
* QCustomer query = new QCustomer()
* .registered.before(LocalDate.now())
*
* // conditionally add the IN expression to the query
* if (names != null && !names.isEmpty()) {
* query.name.in(names)
* }
*
* query.findList();
*
* }</pre>
*
* <h3>Using inOrEmpty()</h3>
* <pre>{@code
*
* List<String> names = Arrays.asList("foo", "bar");
*
* new QCustomer()
* .registered.before(LocalDate.now())
* .name.inOrEmpty(names)
* .findList();
*
* }</pre>
*/
public final R inOrEmpty(Collection<T> values) {
expr().inOrEmpty(_name, values);
return _root;
}

/**
* Is NOT in a list of values.
*
Expand Down
66 changes: 66 additions & 0 deletions src/main/java/io/ebean/typequery/TQRootBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -949,6 +950,71 @@ public R raw(String rawExpression, Object... bindValues) {
return root;
}

/**
* Only add the raw expression if the values is not null or empty.
* <p>
* This is a pure convenience expression to make it nicer to deal with the pattern where we use
* raw() expression with a subquery and only want to add the subquery predicate when the collection
* of values is not empty.
* </p>
* <h3>Without inOrEmpty()</h3>
* <pre>{@code
*
* QCustomer query = new QCustomer() // add some predicates
* .status.equalTo(Status.NEW);
*
* // common pattern - we can use rawOrEmpty() instead
* if (orderIds != null && !orderIds.isEmpty()) {
* query.raw("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
* }
*
* query.findList();
*
* }</pre>
*
* <h3>Using rawOrEmpty()</h3>
* Note that in the example below we use the <code>?1</code> bind parameter to get "parameter expansion"
* for each element in the collection.
*
* <pre>{@code
*
* new QCustomer()
* .status.equalTo(Status.NEW)
* // only add the expression if orderIds is not empty
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id in (?1))", orderIds);
* .findList();
*
* }</pre>
*
* <h3>Postgres ANY</h3>
* With Postgres we would often use the SQL <code>ANY</code> expression and array parameter binding
* rather than <code>IN</code>.
*
* <pre>{@code
*
* new QCustomer()
* .status.equalTo(Status.NEW)
* .rawOrEmpty("t0.customer_id in (select o.customer_id from orders o where o.id = any(?))", orderIds);
* .findList();
*
* }</pre>
* <p>
* Note that we need to cast the Postgres array for UUID types like:
* </p>
* <pre>{@code
*
* " ... = any(?::uuid[])"
*
* }</pre>
*
* @param raw The raw expression that is typically a subquery
* @param values The values which is typically a list or set of id values.
*/
public R rawOrEmpty(String raw, Collection<?> values) {
peekExprList().rawOrEmpty(raw, values);
return root;
}

/**
* Add raw expression with a single parameter.
* <p>
Expand Down
44 changes: 44 additions & 0 deletions src/test/java/org/querytest/QCustomerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
import org.junit.Ignore;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -157,6 +160,29 @@ public void testIn() {
.findList();
}

@Test
public void testInOrEmpty() {

List<String> names = Arrays.asList("asd", "foo", "bar");

new QCustomer()
.registered.before(new Date())
.name.inOrEmpty(names)
.findList();

new QCustomer()
.registered.before(new Date())
.name.inOrEmpty(null)
.findList();

names = Collections.emptyList();

new QCustomer()
.registered.before(new Date())
.name.inOrEmpty(names)
.findList();
}

@Test
public void testNotIn() {
new QCustomer()
Expand Down Expand Up @@ -353,6 +379,24 @@ public void query_setBaseTable() {
.findList();
}

@Test
public void query_rawOrEmpty() {

List<String> names = Arrays.asList("A", "B");

new QCustomer()
.rawOrEmpty("name in (?1)", names)
.findList();

new QCustomer()
.rawOrEmpty("name in (?1)", null)
.findList();

new QCustomer()
.rawOrEmpty("name in (?1)", new ArrayList<Long>())
.findList();
}

@Test
public void query_orNull() {

Expand Down

0 comments on commit e38b707

Please sign in to comment.