use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property in project appengine-java-standard by GoogleCloudPlatform.
the class DataTypeTranslator method addPropertyToPb.
/**
* Adds a property to {@code entity}.
*
* @param name the property name
* @param value the property value
* @param indexed whether this property should be indexed. This may be overridden by property
* types like Blob and Text that are never indexed.
* @param forceIndexedEmbeddedEntity whether indexed embedded entities should actually be indexed,
* as opposed to silently moved to unindexed properties (legacy behavior)
* @param multiple whether this property has multiple values
* @param entity the entity to populate
*/
private static void addPropertyToPb(String name, @Nullable Object value, boolean indexed, boolean forceIndexedEmbeddedEntity, boolean multiple, EntityProto entity) {
Property property = new Property();
property.setName(name);
property.setMultiple(multiple);
PropertyValue newValue = property.getMutableValue();
if (value != null) {
Type<?> type = getType(value.getClass());
Meaning meaning = type.getV3Meaning();
if (meaning != property.getMeaningEnum()) {
property.setMeaning(meaning);
}
type.toV3Value(value, newValue);
if (indexed && forceIndexedEmbeddedEntity && DataTypeUtils.isUnindexableType(value.getClass())) {
// with collections whose contents have been changed in the meantime.
throw new UnsupportedOperationException("Value must be indexable.");
}
if (!forceIndexedEmbeddedEntity || !(value instanceof EmbeddedEntity)) {
// If client was trying to index a type that they shouldn't then clear the index flag for
// them.
indexed &= type.canBeIndexed();
}
}
if (indexed) {
entity.addProperty(property);
} else {
entity.addRawProperty(property);
}
}
use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property 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));
}
}
use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property 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);
}
}
}
}
use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property 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;
}
}
}
use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property in project appengine-java-standard by GoogleCloudPlatform.
the class LocalDatastoreService method validateLengthLimit.
private void validateLengthLimit(Property property) {
String name = property.getName();
PropertyValue value = property.getValue();
if (value.hasStringValue()) {
if (property.hasMeaning() && property.getMeaningEnum() == Property.Meaning.ATOM_LINK) {
if (value.getStringValueAsBytes().length > MAX_LINK_LENGTH) {
throw newError(ErrorCode.BAD_REQUEST, "Link property " + name + " is too long. Use TEXT for links over " + MAX_LINK_LENGTH + " bytes.");
}
} else if (property.hasMeaning() && property.getMeaningEnum() == Property.Meaning.ENTITY_PROTO) {
if (value.getStringValueAsBytes().length > MAX_BLOB_LENGTH) {
throw newError(ErrorCode.BAD_REQUEST, "embedded entity property " + name + " is too big. It cannot exceed " + MAX_BLOB_LENGTH + " bytes.");
}
} else {
if (value.getStringValueAsBytes().length > MAX_STRING_LENGTH) {
throw newError(ErrorCode.BAD_REQUEST, "string property " + name + " is too long. It cannot exceed " + MAX_STRING_LENGTH + " bytes.");
}
}
}
}
Aggregations