#606 - Add not() as a junction expression (like and() and or()) to the query criteria API

This commit is contained in:
Robin Bygrave
2016-03-18 14:11:51 +13:00
parent 3ddd7099b5
commit 5de67e1939
8 changed files with 291 additions and 204 deletions
@@ -357,6 +357,6 @@ public interface ExpressionFactory {
* This is doc store Elastic only.
* </p>
*/
<T> Junction<T> textJunction(Query<T> query, ExpressionList<T> parent, Junction.Type type);
<T> Junction<T> junction(Junction.Type type, Query<T> query, ExpressionList<T> parent);
}
@@ -900,6 +900,11 @@ public interface ExpressionList<T> {
*/
Junction<T> or();
/**
* Return a list of expressions that will be wrapped by NOT.
*/
Junction<T> not();
/**
* Return a list of expressions that will be joined by AND's.
*/
@@ -910,15 +915,6 @@ public interface ExpressionList<T> {
*/
Junction<T> disjunction();
/**
* End a Conjunction or Disjunction returning the parent expression list.
* <p>
* Alternatively you can always use where() to return the top level expression
* list.
* </p>
*/
ExpressionList<T> endJunction();
/**
* Start a list of expressions that will be joined by MUST.
*/
@@ -934,6 +930,30 @@ public interface ExpressionList<T> {
*/
Junction<T> mustNot();
/**
* End a Conjunction or Disjunction returning the parent expression list.
* <p>
* Alternatively you can always use where() to return the top level expression
* list.
* </p>
*/
ExpressionList<T> endJunction();
/**
* End the list of AND expressions.
*/
ExpressionList<T> endAnd();
/**
* End the list of OR expressions.
*/
ExpressionList<T> endOr();
/**
* End the list of NOT expressions.
*/
ExpressionList<T> endNot();
/**
* End the list of MUST expressions.
*/
+42 -28
View File
@@ -10,68 +10,68 @@ package com.avaje.ebean;
* Note: where() always takes you to the top level WHERE expression list.
* </p>
*
* <pre class="code">
* <pre>{@code
* Query q =
* Ebean.find(Person.class)
* .where().disjunction()
* .like(&quot;name&quot;, &quot;Rob%&quot;)
* .eq(&quot;status&quot;, Status.NEW)
* .like("name", "Rob%")
* .eq("status", Status.NEW)
*
* // where() returns us to the top level expression list
* .where().gt(&quot;id&quot;, 10);
* .where().gt("id", 10);
*
* // read as...
* // where ( ((name like Rob%) or (status = NEW)) AND (id &gt; 10) )
* </pre>
* }</pre>
*
* <p>
* Note: endJunction() takes you to the parent expression list
* </p>
*
* <pre class="code">
* <pre>{@code
* Query q =
* Ebean.find(Person.class)
* .where().disjunction()
* .like(&quot;name&quot;, &quot;Rob%&quot;)
* .eq(&quot;status&quot;, Status.NEW)
* .like("name", "Rob%")
* .eq("status", Status.NEW)
* .endJunction()
*
* // endJunction().. takes us to the 'parent' expression list
* // which in this case is the top level (same as where())
*
* .gt(&quot;id&quot;, 10);
* .gt("id", 10);
*
* // read as...
* // where ( ((name like Rob%) or (status = NEW)) AND (id &gt; 10) )
* </pre>
* }</pre>
*
* <p>
* Example of a nested disjunction.
* </p>
*
* <pre class="code">
* Query&lt;Customer&gt; q =
* <pre>{@code
* Query<Customer> q =
* Ebean.find(Customer.class)
* .where()
* .disjunction()
* .conjunction()
* .startsWith(&quot;name&quot;, &quot;r&quot;)
* .eq(&quot;anniversary&quot;, onAfter)
* .or()
* .and()
* .startsWith("name", "r")
* .eq("anniversary", onAfter)
* .endJunction()
* .conjunction()
* .eq(&quot;status&quot;, Customer.Status.ACTIVE)
* .gt(&quot;id&quot;, 0)
* .and()
* .eq("status", Customer.Status.ACTIVE)
* .gt("id", 0)
* .endJunction()
* .order().asc(&quot;name&quot;);
* .order().asc("name");
*
* q.findList();
* String s = q.getGeneratedSql();
*
* // this produces an expression like:
*
* ( name like ? and c.anniversary = ? ) or (c.status = ? and c.id &gt; ? )
* ( name like ? and c.anniversary = ? ) or (c.status = ? and c.id > ? )
*
* </pre>
* }</pre>
*/
public interface Junction<T> extends Expression, ExpressionList<T> {
@@ -83,32 +83,39 @@ public interface Junction<T> extends Expression, ExpressionList<T> {
/**
* AND group.
*/
AND(" and "),
AND(" and ", ""),
/**
* OR group.
*/
OR(" or "),
OR(" or ", ""),
/**
* NOT group.
*/
NOT(" and ", "not "),
/**
* Text search AND group.
*/
MUST("must"),
MUST("must", ""),
/**
* Text search NOT group.
*/
MUST_NOT("must_not"),
MUST_NOT("must_not", ""),
/**
* Text search OR group.
*/
SHOULD("should");
SHOULD("should", "");
String prefix;
String literal;
Type(String literal) {
Type(String literal, String prefix) {
this.literal = literal;
this.prefix = prefix;
}
/**
@@ -117,6 +124,13 @@ public interface Junction<T> extends Expression, ExpressionList<T> {
public String literal() {
return literal;
}
/**
* Return the prefix value for this type.
*/
public String prefix() {
return prefix;
}
}
}
@@ -436,35 +436,42 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
* Return a list of expressions that will be joined by AND's.
*/
public <T> Junction<T> conjunction(Query<T> query) {
return new JunctionExpression.Conjunction<T>(query, query.where());
return new JunctionExpression<T>(Junction.Type.AND, query, query.where());
}
/**
* Return a list of expressions that will be joined by OR's.
*/
public <T> Junction<T> disjunction(Query<T> query) {
return new JunctionExpression.Disjunction<T>(query, query.where());
return new JunctionExpression<T>(Junction.Type.OR, query, query.where());
}
/**
* Return a list of expressions that will be joined by AND's.
*/
public <T> Junction<T> conjunction(Query<T> query, ExpressionList<T> parent) {
return new JunctionExpression.Conjunction<T>(query, parent);
return new JunctionExpression<T>(Junction.Type.AND, query, parent);
}
/**
* Return a list of expressions that will be joined by OR's.
*/
public <T> Junction<T> disjunction(Query<T> query, ExpressionList<T> parent) {
return new JunctionExpression.Disjunction<T>(query, parent);
return new JunctionExpression<T>(Junction.Type.OR, query, parent);
}
/**
* Return a list of expressions that are wrapped by NOT.
*/
public <T> Junction<T> junction(Junction.Type type, Query<T> query) {
return new JunctionExpression<T>(type, query, query.where());
}
/**
* Create and return a Full text junction (Must, Must Not or Should).
*/
@Override
public <T> Junction<T> textJunction(Query<T> query, ExpressionList<T> parent, Junction.Type type) {
return new JunctionExpression.TextJunction<T>(query, parent, type);
public <T> Junction<T> junction(Junction.Type type, Query<T> query, ExpressionList<T> parent) {
return new JunctionExpression<T>(type, query, parent);
}
}
@@ -202,15 +202,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
}
@Override
public ExpressionList<T> endJunction() {
return parentExprList == null ? this : parentExprList;
}
protected ExpressionList<T> endTextJunction() {
return parentExprList == null ? this : parentExprList;
}
@Override
public Query<T> query() {
return query;
@@ -646,42 +637,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return this;
}
public Junction<T> textJunction(Junction.Type type) {
Junction<T> junction = expr.textJunction(query, this, type);
add(junction);
return junction;
}
@Override
public Junction<T> and() {
return conjunction();
}
@Override
public Junction<T> or() {
return disjunction();
}
@Override
public Junction<T> conjunction() {
Junction<T> conjunction = expr.conjunction(query, this);
add(conjunction);
return conjunction;
}
@Override
public ExpressionList<T> contains(String propertyName, String value) {
add(expr.contains(propertyName, value));
return this;
}
@Override
public Junction<T> disjunction() {
Junction<T> disjunction = expr.disjunction(query, this);
add(disjunction);
return disjunction;
}
@Override
public ExpressionList<T> endsWith(String propertyName, String value) {
add(expr.endsWith(propertyName, value));
@@ -911,33 +872,84 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return this;
}
private Junction<T> junction(Junction.Type type) {
Junction<T> junction = expr.junction(type, query, this);
add(junction);
return junction;
}
@Override
public ExpressionList<T> endJunction() {
return parentExprList == null ? this : parentExprList;
}
@Override
public Junction<T> and() {
return conjunction();
}
@Override
public Junction<T> or() {
return disjunction();
}
@Override
public Junction<T> not() {
return junction(Junction.Type.NOT);
}
@Override
public Junction<T> conjunction() {
return junction(Junction.Type.AND);
}
@Override
public Junction<T> disjunction() {
return junction(Junction.Type.OR);
}
@Override
public Junction<T> must() {
return textJunction(Junction.Type.MUST);
return junction(Junction.Type.MUST);
}
@Override
public Junction<T> should() {
return textJunction(Junction.Type.SHOULD);
return junction(Junction.Type.SHOULD);
}
@Override
public Junction<T> mustNot() {
return textJunction(Junction.Type.MUST_NOT);
return junction(Junction.Type.MUST_NOT);
}
@Override
public ExpressionList<T> endAnd() {
return endJunction();
}
@Override
public ExpressionList<T> endOr() {
return endJunction();
}
@Override
public ExpressionList<T> endNot() {
return endJunction();
}
@Override
public ExpressionList<T> endMust() {
return endTextJunction();
return endJunction();
}
@Override
public ExpressionList<T> endShould() {
return endTextJunction();
return endJunction();
}
@Override
public ExpressionList<T> endMustNot() {
return endTextJunction();
return endJunction();
}
}
@@ -1,6 +1,19 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.*;
import com.avaje.ebean.Expression;
import com.avaje.ebean.ExpressionList;
import com.avaje.ebean.FetchPath;
import com.avaje.ebean.FutureIds;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.Junction;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PagedList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryEachConsumer;
import com.avaje.ebean.QueryEachWhileConsumer;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.Version;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebean.search.Match;
import com.avaje.ebean.search.MultiMatch;
@@ -25,52 +38,7 @@ import java.util.Set;
/**
* Junction implementation.
*/
abstract class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, ExpressionList<T> {
static class TextJunction<T> extends JunctionExpression<T> {
TextJunction(Query<T> query, ExpressionList<T> parent, TextJunction.Type type) {
super(type, query, parent);
}
TextJunction(TextJunction.Type type, DefaultExpressionList<T> expressionList) {
super(type, expressionList);
}
@Override
public SpiExpression copyForPlanKey() {
return new TextJunction<T>(type, exprList.copyForPlanKey());
}
}
static class Conjunction<T> extends JunctionExpression<T> {
Conjunction(Query<T> query, ExpressionList<T> parent) {
super(Type.AND, query, parent);
}
Conjunction(DefaultExpressionList<T> expressionList) {
super(Type.AND, expressionList);
}
@Override
public SpiExpression copyForPlanKey() {
return new Conjunction<T>(exprList.copyForPlanKey());
}
}
static class Disjunction<T> extends JunctionExpression<T> {
Disjunction(Query<T> query, ExpressionList<T> parent) {
super(Type.OR, query, parent);
}
Disjunction(DefaultExpressionList<T> expressionList) {
super(Type.OR, expressionList);
}
@Override
public SpiExpression copyForPlanKey() {
return new Disjunction<T>(exprList.copyForPlanKey());
}
}
class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, ExpressionList<T> {
protected final DefaultExpressionList<T> exprList;
@@ -89,6 +57,10 @@ abstract class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, E
this.exprList = exprList;
}
public SpiExpression copyForPlanKey() {
return new JunctionExpression<T>(type, exprList.copyForPlanKey());
}
@Override
public void writeDocQuery(DocQueryContext context) throws IOException {
context.startBool(type == Type.AND);
@@ -151,7 +123,6 @@ abstract class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, E
public void addBindValues(SpiExpressionRequest request) {
List<SpiExpression> list = exprList.internalList();
for (int i = 0; i < list.size(); i++) {
list.get(i).addBindValues(request);
}
@@ -163,8 +134,8 @@ abstract class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, E
List<SpiExpression> list = exprList.internalList();
if (!list.isEmpty()) {
request.append(type.prefix());
request.append("(");
for (int i = 0; i < list.size(); i++) {
SpiExpression item = list.get(i);
if (i > 0) {
@@ -172,7 +143,6 @@ abstract class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, E
}
item.addSql(request);
}
request.append(") ");
}
}
@@ -200,12 +170,10 @@ abstract class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, E
@Override
public int queryBindHash() {
int hc = JunctionExpression.class.getName().hashCode();
List<SpiExpression> list = exprList.internalList();
for (int i = 0; i < list.size(); i++) {
hc = hc * 31 + list.get(i).queryBindHash();
}
return hc;
}
@@ -261,44 +229,6 @@ abstract class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, E
return exprList.textCommonTerms(search, options);
}
@Override
public Junction<T> must() {
return exprList.must();
}
@Override
public Junction<T> should() {
return exprList.should();
}
@Override
public Junction<T> mustNot() {
return exprList.mustNot();
}
@Override
public ExpressionList<T> endMust() {
return endTextJunction();
}
@Override
public ExpressionList<T> endShould() {
return endTextJunction();
}
@Override
public ExpressionList<T> endMustNot() {
return endTextJunction();
}
private ExpressionList<T> endTextJunction() {
return exprList.endTextJunction();
}
@Override
public ExpressionList<T> endJunction() {
return exprList.endJunction();
}
@Override
public ExpressionList<T> allEq(Map<String, Object> propertyMap) {
@@ -320,31 +250,11 @@ abstract class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, E
return exprList.betweenProperties(lowProperty, highProperty, value);
}
@Override
public Junction<T> and() {
return conjunction();
}
@Override
public Junction<T> or() {
return disjunction();
}
@Override
public Junction<T> conjunction() {
return exprList.conjunction();
}
@Override
public ExpressionList<T> contains(String propertyName, String value) {
return exprList.contains(propertyName, value);
}
@Override
public Junction<T> disjunction() {
return exprList.disjunction();
}
@Override
public ExpressionList<T> endsWith(String propertyName, String value) {
return exprList.endsWith(propertyName, value);
@@ -787,4 +697,79 @@ abstract class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, E
return exprList.where();
}
@Override
public Junction<T> and() {
return conjunction();
}
@Override
public Junction<T> or() {
return disjunction();
}
@Override
public Junction<T> not() {
return exprList.not();
}
@Override
public Junction<T> conjunction() {
return exprList.conjunction();
}
@Override
public Junction<T> disjunction() {
return exprList.disjunction();
}
@Override
public Junction<T> must() {
return exprList.must();
}
@Override
public Junction<T> should() {
return exprList.should();
}
@Override
public Junction<T> mustNot() {
return exprList.mustNot();
}
@Override
public ExpressionList<T> endJunction() {
return exprList.endJunction();
}
@Override
public ExpressionList<T> endAnd() {
return endJunction();
}
@Override
public ExpressionList<T> endOr() {
return endJunction();
}
@Override
public ExpressionList<T> endNot() {
return endJunction();
}
@Override
public ExpressionList<T> endMust() {
return endJunction();
}
@Override
public ExpressionList<T> endShould() {
return endJunction();
}
@Override
public ExpressionList<T> endMustNot() {
return endJunction();
}
}