Search in sources :

Example 1 with Property

use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property in project appengine-java-standard by GoogleCloudPlatform.

the class CompositeIndexManager method compositeIndexForQuery.

/**
 * Given a {@link IndexComponentsOnlyQuery}, return the {@link Index} needed to fulfill the query,
 * or {@code null} if no index is needed.
 *
 * <p>This code needs to remain in sync with its counterparts in other languages. If you modify
 * this code please make sure you make the same update in the local datastore for other languages.
 *
 * @param indexOnlyQuery The query.
 * @return The index that must be present in order to fulfill the query, or {@code null} if no
 *     index is needed.
 */
@Nullable
protected Index compositeIndexForQuery(final IndexComponentsOnlyQuery indexOnlyQuery) {
    DatastoreV3Pb.Query query = indexOnlyQuery.getQuery();
    boolean hasKind = query.hasKind();
    boolean isAncestor = query.hasAncestor();
    List<Filter> filters = query.filters();
    List<Order> orders = query.orders();
    if (filters.isEmpty() && orders.isEmpty()) {
        // built-in primary key or kind index can handle this.
        return null;
    }
    // Group the filters by operator.
    List<String> eqProps = indexOnlyQuery.getPrefix();
    List<Property> indexProperties = indexOnlyQuery.isGeo() ? getNeededSearchProps(eqProps, indexOnlyQuery.getGeoProperties()) : getRecommendedIndexProps(indexOnlyQuery);
    if (hasKind && !eqProps.isEmpty() && eqProps.size() == filters.size() && !indexOnlyQuery.hasKeyProperty() && orders.isEmpty()) {
        // specified, and those queries can _not_ be satisfied by merge-join.
        return null;
    }
    if (hasKind && !isAncestor && indexProperties.size() <= 1 && !indexOnlyQuery.isGeo() && (!indexOnlyQuery.hasKeyProperty() || indexProperties.get(0).getDirectionEnum() == Property.Direction.ASCENDING)) {
        // (a.k.a. Search) indexes, we might.)
        return null;
    }
    Index index = new Index();
    index.setEntityType(query.getKind());
    index.setAncestor(isAncestor);
    index.mutablePropertys().addAll(indexProperties);
    return index;
}
Also used : Order(com.google.apphosting.datastore.DatastoreV3Pb.Query.Order) Filter(com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter) Index(com.google.storage.onestore.v3.OnestoreEntity.Index) DatastoreV3Pb(com.google.apphosting.datastore.DatastoreV3Pb) Property(com.google.storage.onestore.v3.OnestoreEntity.Index.Property) Nullable(org.checkerframework.checker.nullness.qual.Nullable)

Example 2 with Property

use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property in project appengine-java-standard by GoogleCloudPlatform.

the class CompositeIndexManager method minimumCompositeIndexForQuery.

/**
 * Given a {@link IndexComponentsOnlyQuery} and a collection of existing {@link Index}s, return
 * the minimum {@link Index} needed to fulfill the query, or {@code null} if no index is needed.
 *
 * <p>This code needs to remain in sync with its counterparts in other languages. If you modify
 * this code please make sure you make the same update in the local datastore for other languages.
 *
 * @param indexOnlyQuery The query.
 * @param indexes The existing indexes.
 * @return The minimum index that must be present in order to fulfill the query, or {@code null}
 *     if no index is needed.
 */
@Nullable
protected Index minimumCompositeIndexForQuery(IndexComponentsOnlyQuery indexOnlyQuery, Collection<Index> indexes) {
    Index suggestedIndex = compositeIndexForQuery(indexOnlyQuery);
    if (suggestedIndex == null) {
        return null;
    }
    if (indexOnlyQuery.isGeo()) {
        // None of the shortcuts/optimizations below are applicable for Search indexes.
        return suggestedIndex;
    }
    class EqPropsAndAncestorConstraint {

        final Set<String> equalityProperties;

        final boolean ancestorConstraint;

        EqPropsAndAncestorConstraint(Set<String> equalityProperties, boolean ancestorConstraint) {
            this.equalityProperties = equalityProperties;
            this.ancestorConstraint = ancestorConstraint;
        }
    }
    // Map from postfix to the remaining equality properties and ancestor constraints.
    Map<List<Property>, EqPropsAndAncestorConstraint> remainingMap = new HashMap<List<Property>, EqPropsAndAncestorConstraint>();
    index_for: for (Index index : indexes) {
        if (// Kind must match.
        !indexOnlyQuery.getQuery().getKind().equals(index.getEntityType()) || // Ancestor indexes can only be used on ancestor queries.
        (!indexOnlyQuery.getQuery().hasAncestor() && index.isAncestor())) {
            continue;
        }
        // Matching the postfix.
        int postfixSplit = index.propertySize();
        for (IndexComponent component : Lists.reverse(indexOnlyQuery.getPostfix())) {
            if (!component.matches(index.propertys().subList(Math.max(postfixSplit - component.size(), 0), postfixSplit))) {
                continue index_for;
            }
            postfixSplit -= component.size();
        }
        // Postfix matches! Now checking the prefix.
        Set<String> indexEqProps = Sets.newHashSetWithExpectedSize(postfixSplit);
        for (Property prop : index.propertys().subList(0, postfixSplit)) {
            // Index must not contain extra properties in the prefix.
            if (!indexOnlyQuery.getPrefix().contains(prop.getName())) {
                continue index_for;
            }
            indexEqProps.add(prop.getName());
        }
        // Index matches!
        // Find the matching remaining requirements.
        List<Property> indexPostfix = index.propertys().subList(postfixSplit, index.propertySize());
        Set<String> remainingEqProps;
        boolean remainingAncestor;
        {
            EqPropsAndAncestorConstraint remaining = remainingMap.get(indexPostfix);
            if (remaining == null) {
                remainingEqProps = Sets.newHashSet(indexOnlyQuery.getPrefix());
                remainingAncestor = indexOnlyQuery.getQuery().hasAncestor();
            } else {
                remainingEqProps = remaining.equalityProperties;
                remainingAncestor = remaining.ancestorConstraint;
            }
        }
        // Remove any remaining requirements handled by this index.
        boolean modified = remainingEqProps.removeAll(indexEqProps);
        if (remainingAncestor && index.isAncestor()) {
            modified = true;
            remainingAncestor = false;
        }
        if (remainingEqProps.isEmpty() && !remainingAncestor) {
            // No new index needed!
            return null;
        }
        if (!modified) {
            // Index made no contribution, don't update the map.
            continue;
        }
        // Save indexes contribution
        remainingMap.put(indexPostfix, new EqPropsAndAncestorConstraint(remainingEqProps, remainingAncestor));
    }
    if (remainingMap.isEmpty()) {
        // suggested index is the minimum index
        return suggestedIndex;
    }
    int minimumCost = Integer.MAX_VALUE;
    List<Property> minimumPostfix = null;
    EqPropsAndAncestorConstraint minimumRemaining = null;
    for (Map.Entry<List<Property>, EqPropsAndAncestorConstraint> entry : remainingMap.entrySet()) {
        int cost = entry.getValue().equalityProperties.size();
        if (entry.getValue().ancestorConstraint) {
            // Arbitrary value picked because ancestor are multi-valued.
            cost += 2;
        }
        if (cost < minimumCost) {
            minimumCost = cost;
            minimumPostfix = entry.getKey();
            minimumRemaining = entry.getValue();
        }
    }
    // map not empty so we should have found cost < MAX_VALUE.
    requireNonNull(minimumRemaining);
    requireNonNull(minimumPostfix);
    // Populating suggesting the minimal index instead.
    suggestedIndex.clearProperty();
    suggestedIndex.setAncestor(minimumRemaining.ancestorConstraint);
    for (String name : minimumRemaining.equalityProperties) {
        suggestedIndex.addProperty().setName(name).setDirection(Direction.ASCENDING);
    }
    Collections.sort(suggestedIndex.mutablePropertys(), PROPERTY_NAME_COMPARATOR);
    suggestedIndex.mutablePropertys().addAll(minimumPostfix);
    return suggestedIndex;
}
Also used : Set(java.util.Set) HashMap(java.util.HashMap) Index(com.google.storage.onestore.v3.OnestoreEntity.Index) ArrayList(java.util.ArrayList) List(java.util.List) Property(com.google.storage.onestore.v3.OnestoreEntity.Index.Property) HashMap(java.util.HashMap) Map(java.util.Map) Nullable(org.checkerframework.checker.nullness.qual.Nullable)

Example 3 with Property

use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property in project appengine-java-standard by GoogleCloudPlatform.

the class CompositeIndexUtils method generateXmlForIndex.

/**
 * Generate an xml representation of the provided {@link Index}.
 *
 * <pre>{@code
 * <datastore-indexes autoGenerate="true">
 *     <datastore-index kind="a" ancestor="false">
 *         <property name="yam" direction="asc"/>
 *         <property name="not yam" direction="desc"/>
 *     </datastore-index>
 * </datastore-indexes>
 * }</pre>
 *
 * @param index The index for which we want an xml representation.
 * @param source The source of the provided index.
 * @return The xml representation of the provided index.
 */
public static String generateXmlForIndex(Index index, IndexSource source) {
    // Careful with pb method names - index.hasAncestor just returns whether or not
    // the ancestor was set, not the value of the field itself.  We always provide
    // a value for this field, so we just want the value.
    boolean isAncestor = index.isAncestor();
    if (index.propertySize() == 0) {
        return String.format(DATASTORE_INDEX_NO_PROPERTIES_XML_FORMAT, index.getEntityType(), isAncestor, source);
    }
    boolean isSearchIndex = false;
    StringBuilder sb = new StringBuilder();
    for (Property prop : index.propertys()) {
        String extraAttribute;
        if (prop.getDirectionEnum() == Direction.ASCENDING) {
            extraAttribute = ASC_ATTRIBUTE;
        } else if (prop.getDirectionEnum() == Direction.DESCENDING) {
            extraAttribute = DESC_ATTRIBUTE;
        } else if (prop.getModeEnum() == Mode.GEOSPATIAL) {
            isSearchIndex = true;
            extraAttribute = GEOSPATIAL_ATTRIBUTE;
        } else {
            extraAttribute = "";
        }
        sb.append(String.format(PROPERTY_XML_FORMAT, prop.getName(), extraAttribute));
    }
    String ancestorAttribute = isSearchIndex ? "" : String.format(ANCESTOR_ATTRIBUTE_FORMAT, isAncestor);
    return String.format(DATASTORE_INDEX_WITH_PROPERTIES_XML_FORMAT, index.getEntityType(), ancestorAttribute, source, sb.toString());
}
Also used : Property(com.google.storage.onestore.v3.OnestoreEntity.Index.Property)

Example 4 with Property

use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property in project appengine-java-standard by GoogleCloudPlatform.

the class DataTypeTranslator method buildImplicitKeyProperty.

private static Property buildImplicitKeyProperty(EntityProto proto) {
    Property keyProp = new Property();
    keyProp.setName(Entity.KEY_RESERVED_PROPERTY);
    PropertyValue propVal = new PropertyValue();
    propVal.setReferenceValue(KeyType.toReferenceValue(proto.getKey()));
    keyProp.setValue(propVal);
    return keyProp;
}
Also used : PropertyValue(com.google.storage.onestore.v3.OnestoreEntity.PropertyValue) Property(com.google.storage.onestore.v3.OnestoreEntity.Property)

Example 5 with Property

use of com.google.storage.onestore.v3.OnestoreEntity.Index.Property in project appengine-java-standard by GoogleCloudPlatform.

the class DataTypeTranslator method addListPropertyToPb.

private static void addListPropertyToPb(EntityProto proto, String name, boolean indexed, Collection<?> values, boolean forceIndexedEmbeddedEntity) {
    if (values.isEmpty()) {
        Property property = new Property();
        property.setName(name);
        property.setMultiple(false);
        if (DatastoreServiceConfig.getEmptyListSupport()) {
            // DS now supports empty lists, so we write a real empty list
            property.setMeaning(Meaning.EMPTY_LIST);
        } else {
        // Backward compatible behavior: Write an empty collection as null.
        // If the value is indexed it appears in queries, but distinction between
        // null and empty list is lost.
        }
        // Indicate to the proto that we have set this field
        property.getMutableValue();
        if (indexed) {
            proto.addProperty(property);
        } else {
            proto.addRawProperty(property);
        }
    } else {
        // Write every element to the PB
        for (Object listValue : values) {
            addPropertyToPb(name, listValue, indexed, forceIndexedEmbeddedEntity, true, /* multiple */
            proto);
        }
    }
}
Also used : Property(com.google.storage.onestore.v3.OnestoreEntity.Property)

Aggregations

Property (com.google.storage.onestore.v3.OnestoreEntity.Property)15 PropertyValue (com.google.storage.onestore.v3.OnestoreEntity.PropertyValue)9 EntityProto (com.google.storage.onestore.v3.OnestoreEntity.EntityProto)8 Property (com.google.storage.onestore.v3.OnestoreEntity.Index.Property)8 Filter (com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter)5 Order (com.google.apphosting.datastore.DatastoreV3Pb.Query.Order)4 ByteString (com.google.protobuf.ByteString)4 Index (com.google.storage.onestore.v3.OnestoreEntity.Index)4 OnestoreEntity (com.google.storage.onestore.v3.OnestoreEntity)3 Map (java.util.Map)3 Nullable (org.checkerframework.checker.nullness.qual.Nullable)3 Test (org.junit.Test)3 Entity (com.google.appengine.api.datastore.Entity)2 ImmutableList (com.google.common.collect.ImmutableList)2 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 Test (org.junit.jupiter.api.Test)2 Property (com.google.appengine.api.datastore.Index.Property)1 Extent (com.google.appengine.api.datastore.dev.LocalDatastoreService.Extent)1 Profile (com.google.appengine.api.datastore.dev.LocalDatastoreService.Profile)1