
com.google.appengine.api.datastore.BaseEntityComparator Maven / Gradle / Ivy
/*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.appengine.api.datastore;
import com.google.apphosting.datastore.DatastoreV3Pb;
import com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter;
import com.google.apphosting.datastore.DatastoreV3Pb.Query.Order;
import com.google.apphosting.datastore.DatastoreV3Pb.Query.Order.Direction;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
/** Base class for Entity comparators. */
abstract class BaseEntityComparator implements Comparator {
static final Comparator> MULTI_TYPE_COMPARATOR = new MultiTypeComparator();
static final Comparator ORDER_PROPERTY_COMPARATOR =
new Comparator() {
@Override
public int compare(Order o1, Order o2) {
return o1.getProperty().compareTo(o2.getProperty());
}
};
// default sort order is by Key in ascending order
static final Order KEY_ASC_ORDER =
new Order().setProperty(Entity.KEY_RESERVED_PROPERTY).setDirection(Order.Direction.ASCENDING);
final List orders;
final Map filters;
BaseEntityComparator(List orders, List filters) {
this.orders = makeAdjustedOrders(orders, filters);
this.filters = makeFilterMatchers(orders, filters);
}
/**
* Get a {@link List} with comparable representations of the Entity's values with the given
* property name.
*
* @return A {@link List} of comparable property values, or {@code null} if the entity has no
* property with the given name.
*/
abstract @Nullable List> getComparablePropertyValues(
EntityT entity, String property);
private static List makeAdjustedOrders(List orders, List filters) {
// Making arbitrary guess about order implied by exists filters.
List existsOrders = Lists.newArrayList();
for (Filter filter : filters) {
if (filter.getOpEnum() == Filter.Operator.EXISTS) {
existsOrders.add(
new Order()
.setProperty(filter.getProperty(0).getName())
.setDirection(Direction.ASCENDING));
}
}
Collections.sort(existsOrders, ORDER_PROPERTY_COMPARATOR);
List adjusted = new ArrayList(orders.size() + existsOrders.size() + 1);
adjusted.addAll(orders);
if (adjusted.isEmpty()) {
// Check for a inequality filter to order by
for (Filter filter : filters) {
if (ValidatedQuery.INEQUALITY_OPERATORS.contains(filter.getOpEnum())) {
// Only the first inequality is applied natively
adjusted.add(
new Order()
.setProperty(filter.getProperty(0).getName())
.setDirection(Direction.ASCENDING));
break;
}
}
}
adjusted.addAll(existsOrders);
if (adjusted.isEmpty() || !adjusted.get(adjusted.size() - 1).equals(KEY_ASC_ORDER)) {
// make sure we're always sorted by the key as the final sort order.
adjusted.add(KEY_ASC_ORDER);
}
return adjusted;
}
private static Map makeFilterMatchers(
List orders, List filters) {
Map filterMatchers = new HashMap();
for (Filter filter : filters) {
String name = filter.getProperty(0).getName();
FilterMatcher filterMatcher = filterMatchers.get(name);
if (filterMatcher == null) {
filterMatcher = new FilterMatcher();
filterMatchers.put(name, filterMatcher);
}
filterMatcher.addFilter(filter);
}
// order implies existence filter
for (Order order : orders) {
if (!filterMatchers.containsKey(order.getProperty())) {
filterMatchers.put(order.getProperty(), FilterMatcher.MATCH_ALL);
}
if (order.getProperty().equals(KEY_ASC_ORDER.getProperty())) {
// sort orders after a key sort are ignored
break;
}
}
return filterMatchers;
}
@Override
public int compare(EntityT entityA, EntityT entityB) {
int result;
// Walk the list of sort properties
// The first one that doesn't match determines our result
for (Order order : orders) {
String property = order.getProperty();
Collection> aValues = getComparablePropertyValues(entityA, property);
Collection> bValues = getComparablePropertyValues(entityB, property);
// If one of the values is null, then this is a cursor that has been truncated for group by
// properties.
if (aValues == null || bValues == null) {
return 0;
}
// No guarantee that these collections all contain values of the same
// type so we have to use our own method to find the extreme. If we're
// sorting in ascending order we want to compare the minimum values.
// If we're sorting in descending order we want to compare the maximum
// values.
boolean findMin = order.getDirectionEnum() == DatastoreV3Pb.Query.Order.Direction.ASCENDING;
FilterMatcher matcher = filters.get(property);
if (matcher == null) {
matcher = FilterMatcher.MATCH_ALL;
}
Comparable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy