Search in sources :

Example 11 with Filter

use of com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter in project appengine-java-standard by GoogleCloudPlatform.

the class QueryTranslator method convertFilterPredicateToPb.

private static Filter convertFilterPredicateToPb(Query.FilterPredicate predicate) {
    Filter filterPb = new Filter();
    filterPb.setOp(getFilterOp(predicate.getOperator()));
    if (predicate.getValue() instanceof Iterable<?>) {
        if (predicate.getOperator() != Query.FilterOperator.IN) {
            throw new IllegalArgumentException("Only the IN operator supports multiple values");
        }
        for (Object value : (Iterable<?>) predicate.getValue()) {
            filterPb.addProperty().setName(predicate.getPropertyName()).setValue(DataTypeTranslator.toV3Value(value));
        }
    } else {
        filterPb.addProperty().setName(predicate.getPropertyName()).setValue(DataTypeTranslator.toV3Value(predicate.getValue()));
    }
    return filterPb;
}
Also used : Filter(com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter)

Example 12 with Filter

use of com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter in project appengine-java-standard by GoogleCloudPlatform.

the class QueryTranslator method copyGeoFilterToPb.

/**
 * Converts the filter from a geo-spatial query into proto-buf form. Should only be called when
 * the filter indeed has a geo-spatial term; but the filter as a whole has not yet been entirely
 * validated, so we complete the validation here.
 */
private static void copyGeoFilterToPb(Query.Filter filter, DatastoreV3Pb.Query proto) {
    if (filter instanceof Query.CompositeFilter) {
        Query.CompositeFilter conjunction = (Query.CompositeFilter) filter;
        checkArgument(conjunction.getOperator() == Query.CompositeFilterOperator.AND, "Geo-spatial filters may only be composed with CompositeFilterOperator.AND");
        for (Query.Filter f : conjunction.getSubFilters()) {
            copyGeoFilterToPb(f, proto);
        }
    } else if (filter instanceof Query.StContainsFilter) {
        Query.StContainsFilter containmentFilter = (Query.StContainsFilter) filter;
        Filter f = proto.addFilter();
        f.setOp(Operator.CONTAINED_IN_REGION);
        f.setGeoRegion(convertGeoRegionToPb(containmentFilter.getRegion()));
        // It's a bit weird to add a Value with nothing in it; but we
        // need Property in order to convey the property name, and Value
        // is required in Property.  But in our case there is no value:
        // the geo region acts as the thing to which we "compare" the property.
        f.addProperty().setName(containmentFilter.getPropertyName()).setMultiple(false).setValue(new PropertyValue());
    } else {
        checkArgument(filter instanceof Query.FilterPredicate);
        Query.FilterPredicate predicate = (Query.FilterPredicate) filter;
        checkArgument(predicate.getOperator() == Query.FilterOperator.EQUAL, "Geo-spatial filters may only be combined with equality comparisons");
        Filter f = proto.addFilter();
        f.copyFrom(convertFilterPredicateToPb(predicate));
    }
}
Also used : Filter(com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter) Filter(com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter) PropertyValue(com.google.storage.onestore.v3.OnestoreEntity.PropertyValue)

Example 13 with Filter

use of com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter in project appengine-java-standard by GoogleCloudPlatform.

the class QueryTranslator method convertToPb.

@SuppressWarnings("deprecation")
public static DatastoreV3Pb.Query convertToPb(Query query, FetchOptions fetchOptions) {
    Key ancestor = query.getAncestor();
    List<Query.SortPredicate> sortPredicates = query.getSortPredicates();
    DatastoreV3Pb.Query proto = new DatastoreV3Pb.Query();
    if (query.getKind() != null) {
        proto.setKind(query.getKind());
    }
    proto.setApp(query.getAppIdNamespace().getAppId());
    String nameSpace = query.getAppIdNamespace().getNamespace();
    if (nameSpace.length() != 0) {
        proto.setNameSpace(nameSpace);
    }
    if (fetchOptions.getOffset() != null) {
        proto.setOffset(fetchOptions.getOffset());
    }
    if (fetchOptions.getLimit() != null) {
        proto.setLimit(fetchOptions.getLimit());
    }
    if (fetchOptions.getPrefetchSize() != null) {
        proto.setCount(fetchOptions.getPrefetchSize());
    } else if (fetchOptions.getChunkSize() != null) {
        proto.setCount(fetchOptions.getChunkSize());
    }
    if (fetchOptions.getStartCursor() != null) {
        if (!proto.getMutableCompiledCursor().parseFrom(fetchOptions.getStartCursor().toByteString())) {
            throw new IllegalArgumentException("Invalid cursor");
        }
    }
    if (fetchOptions.getEndCursor() != null) {
        if (!proto.getMutableEndCompiledCursor().parseFrom(fetchOptions.getEndCursor().toByteString())) {
            throw new IllegalArgumentException("Invalid cursor");
        }
    }
    if (fetchOptions.getCompile() != null) {
        proto.setCompile(fetchOptions.getCompile());
    }
    if (ancestor != null) {
        Reference ref = KeyTranslator.convertToPb(ancestor);
        if (!ref.getApp().equals(proto.getApp())) {
            throw new IllegalArgumentException("Query and ancestor appid/namespace mismatch");
        }
        proto.setAncestor(ref);
    }
    if (query.getDistinct()) {
        if (query.getProjections().isEmpty()) {
            throw new IllegalArgumentException("Projected properties must be set to " + "allow for distinct projections");
        }
        for (Projection projection : query.getProjections()) {
            proto.addGroupByPropertyName(projection.getPropertyName());
        }
    }
    proto.setKeysOnly(query.isKeysOnly());
    Query.Filter filter = query.getFilter();
    if (filter != null) {
        // At this point, all non-geo queries have had their filters
        // converted to sets of FilterPredicate objects; so this must be
        // a geo query.
        copyGeoFilterToPb(filter, proto);
    } else {
        for (Query.FilterPredicate filterPredicate : query.getFilterPredicates()) {
            Filter filterPb = proto.addFilter();
            filterPb.copyFrom(convertFilterPredicateToPb(filterPredicate));
        }
    }
    for (Query.SortPredicate sortPredicate : sortPredicates) {
        Order order = proto.addOrder();
        order.copyFrom(convertSortPredicateToPb(sortPredicate));
    }
    for (Projection projection : query.getProjections()) {
        proto.addPropertyName(projection.getPropertyName());
    }
    return proto;
}
Also used : Order(com.google.apphosting.datastore.DatastoreV3Pb.Query.Order) Reference(com.google.storage.onestore.v3.OnestoreEntity.Reference) DatastoreV3Pb(com.google.apphosting.datastore.DatastoreV3Pb) Filter(com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter) Filter(com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter)

Example 14 with Filter

use of com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter in project appengine-java-standard by GoogleCloudPlatform.

the class ValidatedQuery method validateQuery.

/**
 * Determines if a given query is supported by the datastore.
 *
 * @throws IllegalQueryException If the provided query fails validation.
 */
private void validateQuery() {
    if (query.propertyNameSize() > 0 && query.isKeysOnly()) {
        throw new IllegalQueryException("projection and keys_only cannot both be set", IllegalQueryType.ILLEGAL_PROJECTION);
    }
    Set<String> projectionProperties = new HashSet<String>(query.propertyNameSize());
    for (String property : query.propertyNames()) {
        if (!projectionProperties.add(property)) {
            throw new IllegalQueryException("cannot project a property multiple times", IllegalQueryType.ILLEGAL_PROJECTION);
        }
    }
    Set<String> groupBySet = Sets.newHashSetWithExpectedSize(query.groupByPropertyNameSize());
    for (String name : query.groupByPropertyNames()) {
        if (!groupBySet.add(name)) {
            throw new IllegalQueryException("cannot group by a property multiple times", IllegalQueryType.ILLEGAL_GROUPBY);
        }
        // filters.
        if (Entity.RESERVED_NAME.matcher(name).matches()) {
            throw new IllegalQueryException("group by is not supported for the property: " + name, IllegalQueryType.ILLEGAL_GROUPBY);
        }
    }
    /* Validate group by properties in orderings. */
    Set<String> groupByInOrderSet = Sets.newHashSetWithExpectedSize(query.groupByPropertyNameSize());
    for (Order order : query.orders()) {
        if (groupBySet.contains(order.getProperty())) {
            groupByInOrderSet.add(order.getProperty());
        } else if (groupByInOrderSet.size() != groupBySet.size()) {
            throw new IllegalQueryException("must specify all group by orderings before any non group by orderings", IllegalQueryType.ILLEGAL_GROUPBY);
        }
    }
    // Transaction requires ancestor
    if (query.hasTransaction() && !query.hasAncestor()) {
        throw new IllegalQueryException("Only ancestor queries are allowed inside transactions.", IllegalQueryType.TRANSACTION_REQUIRES_ANCESTOR);
    }
    // Filters and sort orders require kind.
    if (!query.hasKind()) {
        for (Filter filter : query.filters()) {
            if (!filter.getProperty(0).getName().equals(Entity.KEY_RESERVED_PROPERTY)) {
                throw new IllegalQueryException("kind is required for non-__key__ filters", IllegalQueryType.KIND_REQUIRED);
            }
        }
        for (Order order : query.orders()) {
            if (!(order.getProperty().equals(Entity.KEY_RESERVED_PROPERTY) && order.getDirection() == Order.Direction.ASCENDING.getValue())) {
                throw new IllegalQueryException("kind is required for all orders except __key__ ascending", IllegalQueryType.KIND_REQUIRED);
            }
        }
    }
    /* Validate ancestor, if it exists. */
    if (query.hasAncestor()) {
        Reference ancestor = query.getAncestor();
        if (!ancestor.getApp().equals(query.getApp())) {
            throw new IllegalQueryException("The query app is " + query.getApp() + " but ancestor app is " + ancestor.getApp(), IllegalQueryType.ILLEGAL_VALUE);
        }
        if (!ancestor.getNameSpace().equals(query.getNameSpace())) {
            throw new IllegalQueryException("The query namespace is " + query.getNameSpace() + " but ancestor namespace is " + ancestor.getNameSpace(), IllegalQueryType.ILLEGAL_VALUE);
        }
    }
    // Check for unsupported filter values.  We only support one property
    // per filter and one property with an inequality filter.
    String ineqProp = null;
    this.isGeo = false;
    for (Filter filter : query.filters()) {
        int numProps = filter.propertySize();
        if (numProps != 1) {
            throw new IllegalQueryException(String.format("Filter has %s properties, expected 1", numProps), IllegalQueryType.FILTER_WITH_MULTIPLE_PROPS);
        }
        Property prop = filter.getProperty(0);
        String propName = prop.getName();
        /* Extra validation for __key__. The filter value must be a key,
       * if the query has a kind, the key's kind must match, and the
       * app and namespace must match the query. */
        if (Entity.KEY_RESERVED_PROPERTY.equals(propName)) {
            PropertyValue value = prop.getValue();
            if (!value.hasReferenceValue()) {
                throw new IllegalQueryException(Entity.KEY_RESERVED_PROPERTY + " filter value must be a Key", IllegalQueryType.ILLEGAL_VALUE);
            }
            ReferenceValue refVal = value.getReferenceValue();
            if (!refVal.getApp().equals(query.getApp())) {
                throw new IllegalQueryException(Entity.KEY_RESERVED_PROPERTY + " filter app is " + refVal.getApp() + " but query app is " + query.getApp(), IllegalQueryType.ILLEGAL_VALUE);
            }
            if (!refVal.getNameSpace().equals(query.getNameSpace())) {
                throw new IllegalQueryException(Entity.KEY_RESERVED_PROPERTY + " filter namespace is " + refVal.getNameSpace() + " but query namespace is " + query.getNameSpace(), IllegalQueryType.ILLEGAL_VALUE);
            }
        }
        if (INEQUALITY_OPERATORS.contains(filter.getOpEnum())) {
            if (ineqProp == null) {
                ineqProp = propName;
            } else if (!ineqProp.equals(propName)) {
                throw new IllegalQueryException(String.format("Only one inequality filter per query is supported.  " + "Encountered both %s and %s", ineqProp, propName), IllegalQueryType.MULTIPLE_INEQ_FILTERS);
            }
        } else if (filter.getOpEnum() == Operator.EQUAL) {
            if (projectionProperties.contains(propName)) {
                throw new IllegalQueryException("cannot use projection on a property with an equality filter", IllegalQueryType.ILLEGAL_PROJECTION);
            } else if (groupBySet.contains(propName)) {
                throw new IllegalQueryException("cannot use group by on a property with an equality filter", IllegalQueryType.ILLEGAL_GROUPBY);
            }
        } else if (filter.getOpEnum() == Operator.CONTAINED_IN_REGION) {
            isGeo = true;
            if (!filter.hasGeoRegion() || prop.getValue().hasPointValue()) {
                throw new IllegalQueryException(String.format("Geo-spatial filter on %s should specify GeoRegion rather than Property Value", propName), IllegalQueryType.UNSUPPORTED_FILTER);
            }
            GeoRegion region = filter.getGeoRegion();
            if ((region.hasCircle() && region.hasRectangle()) || (!region.hasCircle() && !region.hasRectangle())) {
                throw new IllegalQueryException(String.format("Geo-spatial filter on %s should specify Circle or Rectangle, but not both", propName), IllegalQueryType.UNSUPPORTED_FILTER);
            }
        } else if (UNSUPPORTED_OPERATORS.contains(filter.getOpEnum())) {
            throw new IllegalQueryException(String.format("Unsupported filter operator: %s", filter.getOp()), IllegalQueryType.UNSUPPORTED_FILTER);
        }
    }
    if (isGeo) {
        if (ineqProp != null) {
            throw new IllegalQueryException("Inequality filter with geo-spatial query is not supported.", IllegalQueryType.UNSUPPORTED_FILTER);
        }
        if (query.hasAncestor()) {
            throw new IllegalQueryException("Geo-spatial filter on ancestor query is not supported.", IllegalQueryType.UNSUPPORTED_FILTER);
        }
        if (query.hasCompiledCursor() || query.hasEndCompiledCursor()) {
            throw new IllegalQueryException("Start and end cursors are not supported on geo-spatial queries.", IllegalQueryType.CURSOR_NOT_SUPPORTED);
        }
    }
    if (ineqProp != null && query.groupByPropertyNameSize() > 0) {
        if (!groupBySet.contains(ineqProp)) {
            throw new IllegalQueryException(String.format("Inequality filter on %s must also be a group by property when " + "group by properties are set.", ineqProp), IllegalQueryType.ILLEGAL_GROUPBY);
        }
    }
    if (ineqProp != null) {
        if (query.orderSize() > 0) {
            if (!ineqProp.equals(query.getOrder(0).getProperty())) {
                // First order must match the inequality filter.
                throw new IllegalQueryException(String.format("The first sort property must be the same as the property to which " + "the inequality filter is applied.  In your query the first sort property " + "is %s but the inequality filter is on %s", query.getOrder(0).getProperty(), ineqProp), IllegalQueryType.FIRST_SORT_NEQ_INEQ_PROP);
            }
        }
    }
}
Also used : Order(com.google.apphosting.datastore.DatastoreV3Pb.Query.Order) Filter(com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter) Reference(com.google.storage.onestore.v3.OnestoreEntity.Reference) ReferenceValue(com.google.storage.onestore.v3.OnestoreEntity.PropertyValue.ReferenceValue) GeoRegion(com.google.apphosting.datastore.DatastoreV3Pb.GeoRegion) PropertyValue(com.google.storage.onestore.v3.OnestoreEntity.PropertyValue) Property(com.google.storage.onestore.v3.OnestoreEntity.Property) HashSet(java.util.HashSet)

Example 15 with Filter

use of com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter in project appengine-java-standard by GoogleCloudPlatform.

the class NormalizedQuery method normalizeQuery.

private void normalizeQuery() {
    // TODO: consider sharing this code with MegastoreQueryPlanner
    // NOTE: Keep in sync with MegastoreQueryPlanner#normalize()
    Set<Property> equalityFilterProperties = new HashSet<Property>();
    Set<String> equalityProperties = new HashSet<String>();
    Set<String> inequalityProperties = new HashSet<String>();
    for (Iterator<Filter> itr = query.mutableFilters().iterator(); itr.hasNext(); ) {
        Filter f = itr.next();
        /* Normalize IN filters to EQUAL. */
        if (f.propertySize() == 1 && f.getOpEnum() == Operator.IN) {
            f.setOp(Operator.EQUAL);
        }
        if (f.propertySize() >= 1) {
            String name = f.getProperty(0).getName();
            if (f.getOpEnum() == Operator.EQUAL) {
                if (!equalityFilterProperties.add(f.getProperty(0))) {
                    // The filter is an exact duplicate, remove it.
                    itr.remove();
                } else {
                    equalityProperties.add(name);
                }
            } else if (INEQUALITY_OPERATORS.contains(f.getOpEnum())) {
                inequalityProperties.add(name);
            }
        }
    }
    equalityProperties.removeAll(inequalityProperties);
    // Strip repeated orders and orders coinciding with EQUAL filters.
    for (Iterator<Order> itr = query.mutableOrders().iterator(); itr.hasNext(); ) {
        if (!equalityProperties.add(itr.next().getProperty())) {
            itr.remove();
        }
    }
    Set<String> allProperties = equalityProperties;
    allProperties.addAll(inequalityProperties);
    // Removing redundant exists filters.
    for (Iterator<Filter> itr = query.mutableFilters().iterator(); itr.hasNext(); ) {
        Filter f = itr.next();
        if (f.getOpEnum() == Operator.EXISTS && f.propertySize() >= 1 && !allProperties.add(f.getProperty(0).getName())) {
            itr.remove();
        }
    }
    // Adding exist filters for any requested properties or group by properties that need them.
    for (String propName : Iterables.concat(query.propertyNames(), query.groupByPropertyNames())) {
        if (allProperties.add(propName)) {
            query.addFilter().setOp(Operator.EXISTS).addProperty().setName(propName).setMultiple(false).getMutableValue();
        }
    }
    /* Strip all orders if filtering on __key__ with equals. */
    for (Filter f : query.filters()) {
        if (f.getOpEnum() == Operator.EQUAL && f.propertySize() >= 1 && f.getProperty(0).getName().equals(Entity.KEY_RESERVED_PROPERTY)) {
            query.clearOrder();
            break;
        }
    }
    /* Strip all orders that follow a ordering on __key__ as keys are unique
     * thus additional ordering has no effect. */
    boolean foundKeyOrder = false;
    for (Iterator<Order> i = query.mutableOrders().iterator(); i.hasNext(); ) {
        String property = i.next().getProperty();
        if (foundKeyOrder) {
            i.remove();
        } else if (property.equals(Entity.KEY_RESERVED_PROPERTY)) {
            foundKeyOrder = true;
        }
    }
}
Also used : Order(com.google.apphosting.datastore.DatastoreV3Pb.Query.Order) Filter(com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter) Property(com.google.storage.onestore.v3.OnestoreEntity.Property) HashSet(java.util.HashSet)

Aggregations

Filter (com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter)20 Order (com.google.apphosting.datastore.DatastoreV3Pb.Query.Order)10 Test (org.junit.Test)8 DatastoreV3Pb (com.google.apphosting.datastore.DatastoreV3Pb)7 PropertyValue (com.google.storage.onestore.v3.OnestoreEntity.PropertyValue)4 EntityProtoComparator (com.google.appengine.api.datastore.EntityProtoComparators.EntityProtoComparator)2 Property (com.google.storage.onestore.v3.OnestoreEntity.Index.Property)2 Property (com.google.storage.onestore.v3.OnestoreEntity.Property)2 Reference (com.google.storage.onestore.v3.OnestoreEntity.Reference)2 HashSet (java.util.HashSet)2 Key (com.google.appengine.api.datastore.Key)1 GeoRegion (com.google.apphosting.datastore.DatastoreV3Pb.GeoRegion)1 Operator (com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter.Operator)1 OnestoreEntity (com.google.storage.onestore.v3.OnestoreEntity)1 Index (com.google.storage.onestore.v3.OnestoreEntity.Index)1 ReferenceValue (com.google.storage.onestore.v3.OnestoreEntity.PropertyValue.ReferenceValue)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 Nullable (org.checkerframework.checker.nullness.qual.Nullable)1