Skip to content

Commit

Permalink
Merge branch 'or-filters'
Browse files Browse the repository at this point in the history
  • Loading branch information
stickfigure committed Sep 27, 2022
2 parents bbfc82c + d6d8d6d commit b8bf8b4
Show file tree
Hide file tree
Showing 9 changed files with 460 additions and 6 deletions.
267 changes: 267 additions & 0 deletions src/main/java/com/googlecode/objectify/cmd/Filter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
package com.googlecode.objectify.cmd;

import com.google.cloud.datastore.StructuredQuery;
import com.google.cloud.datastore.Value;
import com.google.common.base.Preconditions;
import com.googlecode.objectify.impl.FilterOperator;
import com.googlecode.objectify.impl.ObjectifyImpl;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.NonFinal;

/**
* Gives us the ability to compose arbitrarily complex filters with OR and AND sections.
*/
abstract public class Filter {
/**
* Create a filter condition that requires the property to equal the scalar value.
*/
public static Filter equalTo(final String property, final Object value) {
return new EqualToFilter(property, value);
}

/**
* Create a filter condition that requires the property to not be equal to the scalar value.
*/
public static Filter notEqualTo(final String property, final Object value) {
return new NotEqualToFilter(property, value);
}

/**
* Create a filter condition that requires the property to be greater than the scalar value
*/
public static Filter greaterThan(final String property, final Object value) {
return new GreaterThanFilter(property, value);
}

/**
* Create a filter condition that requires the property to be greater than or equal to scalar value
*/
public static Filter greaterThanOrEqualTo(final String property, final Object value) {
return new GreaterThanOrEqualToFilter(property, value);
}

/**
* Create a filter condition that requires the property to be less than the scalar value
*/
public static Filter lessThan(final String property, final Object value) {
return new LessThanFilter(property, value);
}

/**
* Create a filter condition that requires the property to be less than or equal to the scalar value
*/
public static Filter lessThanOrEqualTo(final String property, final Object value) {
return new LessThanOrEqualToFilter(property, value);
}

/**
* Create a filter condition that requires the property to be equal to at least one of a list of scalar
* values.
*
* @param value must be an array or collection
*/
public static Filter in(final String property, final Object value) {
return new InFilter(property, value);
}

/**
* Create a filter condition that requires the property to NOT be equal to any of a list of scalar
* values.
*
* @param value must be an array or collection
*/
public static Filter notIn(final String property, final Object value) {
return new NotInFilter(property, value);
}

/**
* Combine an arbitrary list of filter conditions. They can be nested.
*/
public static Filter or(final Filter... filters) {
return new OrFilter(filters);
}

/**
* Combine an arbitrary list of filter conditions. They can be nested.
*/
public static Filter and(final Filter... filters) {
return new AndFilter(filters);
}

/**
* Convert to a low level API filter.
*/
abstract public StructuredQuery.Filter convert(final ObjectifyImpl ofyImpl);

@lombok.Value @NonFinal
@EqualsAndHashCode(callSuper = false)
abstract private static class PropertyFilter extends Filter {
String property;
Object value;

@Override
public final StructuredQuery.Filter convert(final ObjectifyImpl ofyImpl) {
final Value<?> raw = ofyImpl.makeFilterable(getValue());
return convert(raw);
}

abstract protected StructuredQuery.Filter convert(final Value<?> rawValue);
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class EqualToFilter extends PropertyFilter {
public EqualToFilter(final String property, final Object value) {
super(property, value);
}

@Override
protected StructuredQuery.Filter convert(final Value<?> rawValue) {
return FilterOperator.EQUAL.of(getProperty(), rawValue);
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class NotEqualToFilter extends PropertyFilter {
public NotEqualToFilter(final String property, final Object value) {
super(property, value);
}

@Override
protected StructuredQuery.Filter convert(final Value<?> rawValue) {
return FilterOperator.NOT_EQUAL.of(getProperty(), rawValue);
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class GreaterThanFilter extends PropertyFilter {
public GreaterThanFilter(final String property, final Object value) {
super(property, value);
}

@Override
protected StructuredQuery.Filter convert(final Value<?> rawValue) {
return FilterOperator.GREATER_THAN.of(getProperty(), rawValue);
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class GreaterThanOrEqualToFilter extends PropertyFilter {
public GreaterThanOrEqualToFilter(final String property, final Object value) {
super(property, value);
}

@Override
protected StructuredQuery.Filter convert(final Value<?> rawValue) {
return FilterOperator.GREATER_THAN_OR_EQUAL.of(getProperty(), rawValue);
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class LessThanFilter extends PropertyFilter {
public LessThanFilter(final String property, final Object value) {
super(property, value);
}

@Override
protected StructuredQuery.Filter convert(final Value<?> rawValue) {
return FilterOperator.LESS_THAN.of(getProperty(), rawValue);
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class LessThanOrEqualToFilter extends PropertyFilter {
public LessThanOrEqualToFilter(final String property, final Object value) {
super(property, value);
}

@Override
protected StructuredQuery.Filter convert(final Value<?> rawValue) {
return FilterOperator.LESS_THAN_OR_EQUAL.of(getProperty(), rawValue);
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class InFilter extends PropertyFilter {
public InFilter(final String property, final Object value) {
super(property, value);
}

@Override
protected StructuredQuery.Filter convert(final Value<?> rawValue) {
return FilterOperator.IN.of(getProperty(), rawValue);
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class NotInFilter extends PropertyFilter {
public NotInFilter(final String property, final Object value) {
super(property, value);
}

@Override
protected StructuredQuery.Filter convert(final Value<?> rawValue) {
return FilterOperator.NOT_IN.of(getProperty(), rawValue);
}
}

@lombok.Value @NonFinal
@EqualsAndHashCode(callSuper = false)
abstract private static class CompositeFilter extends Filter {
Filter[] filters;

public CompositeFilter(final Filter[] filters) {
Preconditions.checkArgument(filters.length >= 1, "You must include at least one condition");
this.filters = filters;
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class AndFilter extends CompositeFilter {
public AndFilter(final Filter[] filters) {
super(filters);
}

@Override
public StructuredQuery.Filter convert(final ObjectifyImpl ofyImpl) {
final StructuredQuery.Filter first = getFilters()[0].convert(ofyImpl);

final StructuredQuery.Filter[] rest = new StructuredQuery.Filter[getFilters().length - 1];
for (int i = 1; i < getFilters().length; i++) {
rest[i - 1] = getFilters()[i].convert(ofyImpl);
}

return StructuredQuery.CompositeFilter.and(first, rest);
}
}

@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
private static class OrFilter extends CompositeFilter {
public OrFilter(final Filter[] filters) {
super(filters);
}

@Override
public StructuredQuery.Filter convert(final ObjectifyImpl ofyImpl) {
final StructuredQuery.Filter first = getFilters()[0].convert(ofyImpl);

final StructuredQuery.Filter[] rest = new StructuredQuery.Filter[getFilters().length - 1];
for (int i = 1; i < getFilters().length; i++) {
rest[i - 1] = getFilters()[i].convert(ofyImpl);
}

//return StructuredQuery.CompositeFilter.or(first, rest);
throw new UnsupportedOperationException("OR is not yet available in the low-level API. Coming soon.");
}
}
}
14 changes: 13 additions & 1 deletion src/main/java/com/googlecode/objectify/cmd/Query.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.googlecode.objectify.cmd;

import com.google.cloud.datastore.Cursor;
import com.google.cloud.datastore.StructuredQuery.Filter;
import com.google.cloud.datastore.StructuredQuery;


/**
Expand Down Expand Up @@ -55,6 +55,18 @@ public interface Query<T> extends SimpleQuery<T>
* <p>You can <strong>not</strong> filter on @Id or @Parent properties. Use
* {@code filterKey()} or {@code ancestor()} instead.</p>
*/
public Query<T> filter(StructuredQuery.Filter filter);

/**
* <p>Create an arbitrarily complex filter. This method is preferred to the low-level Filter method
* because it has better ergonomics and automatically handles objects like Objectify {@code Key<?>}
* and {@code Ref<?>}.</p>
*
* <p>Construct Filter objects using static methods on the Filter class.</p>
*
* <p>Note that like the other filter methods, you can <strong>not</strong> filter on @Id or @Parent properties.
* You can filter by {@code __key__} however.</p>
*/
public Query<T> filter(Filter filter);

/* (non-Javadoc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

/** The cloud sdk filtering API is somewhat hostile to programmatic query generation, so we need this adaptor */
@RequiredArgsConstructor
enum FilterOperator {
public enum FilterOperator {
LESS_THAN(PropertyFilter::lt),
LESS_THAN_OR_EQUAL(PropertyFilter::le),
GREATER_THAN(PropertyFilter::gt),
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/com/googlecode/objectify/impl/LoadTypeImpl.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.googlecode.objectify.impl;

import com.google.cloud.datastore.StructuredQuery.Filter;
import com.google.cloud.datastore.StructuredQuery;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.LoadResult;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.cmd.Filter;
import com.googlecode.objectify.cmd.LoadIds;
import com.googlecode.objectify.cmd.LoadType;
import com.googlecode.objectify.cmd.Query;
Expand Down Expand Up @@ -64,6 +65,13 @@ public Query<T> filter(String condition, Object value) {
}

/* */
@Override
public Query<T> filter(final StructuredQuery.Filter filter) {
final QueryImpl<T> q = createQuery();
q.addFilter(filter);
return q;
}

@Override
public Query<T> filter(final Filter filter) {
final QueryImpl<T> q = createQuery();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ protected WriteEngine createWriteEngine() {
*
* @return whatever can be put into a filter clause.
*/
protected Value<?> makeFilterable(Object value) {
public Value<?> makeFilterable(Object value) {
if (value == null)
return NullValue.of();

Expand Down
20 changes: 18 additions & 2 deletions src/main/java/com/googlecode/objectify/impl/QueryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.google.cloud.datastore.KeyQuery;
import com.google.cloud.datastore.QueryResults;
import com.google.cloud.datastore.StringValue;
import com.google.cloud.datastore.StructuredQuery.Filter;
import com.google.cloud.datastore.StructuredQuery;
import com.google.cloud.datastore.StructuredQuery.OrderBy;
import com.google.cloud.datastore.StructuredQuery.PropertyFilter;
import com.google.cloud.datastore.Value;
Expand All @@ -13,6 +13,7 @@
import com.googlecode.objectify.LoadResult;
import com.googlecode.objectify.ObjectifyFactory;
import com.googlecode.objectify.annotation.Subclass;
import com.googlecode.objectify.cmd.Filter;
import com.googlecode.objectify.cmd.Query;
import com.googlecode.objectify.cmd.QueryResultIterable;
import com.googlecode.objectify.impl.translate.ClassTranslator;
Expand Down Expand Up @@ -94,6 +95,13 @@ public QueryImpl<T> filter(final String condition, final Object value) {
}

/* */
@Override
public QueryImpl<T> filter(final StructuredQuery.Filter filter) {
final QueryImpl<T> q = createQuery();
q.addFilter(filter);
return q;
}

@Override
public QueryImpl<T> filter(final Filter filter) {
final QueryImpl<T> q = createQuery();
Expand Down Expand Up @@ -144,10 +152,18 @@ else if (prop.equals(meta.getIdFieldName())) {
/**
* Add the filter as an AND to whatever is currently set as the actual filter.
*/
void addFilter(final Filter filter) {
void addFilter(final StructuredQuery.Filter filter) {
actual = actual.andFilter(filter);
}

/**
* Add the filter as an AND to whatever is currently set as the actual filter.
*/
void addFilter(final Filter filter) {
final StructuredQuery.Filter munged = filter.convert(this.loader.getObjectifyImpl());
actual = actual.andFilter(munged);
}

/**
* Converts the textual operator (">", "<=", etc) into a FilterOperator.
* Forgiving about the syntax; != and <> are NOT_EQUAL, = and == are EQUAL.
Expand Down

0 comments on commit b8bf8b4

Please sign in to comment.