All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.appengine.api.datastore.MultiQueryBuilder Maven / Gradle / Ivy

Go to download

API for Google App Engine standard environment with some of the dependencies shaded (repackaged)

There is a newer version: 2.0.27
Show newest version
/*
 * 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 static com.google.common.base.Preconditions.checkNotNull;

import com.google.appengine.api.datastore.MultiQueryComponent.Order;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * A class that generates multiple lists of query filters to execute to satisfy the {@link
 * MultiQueryComponent}s given to it.
 *
 * 

Each component contains multiple {@link List}s of {@link FilterPredicate}s and an order in * which MultiQuery should produce queries that contain these filters. * *

If the order of a {@link MultiQueryComponent} is set to {@link Order#SERIAL} the given filters * are applied to sequentially generated sets of queries. This denotes that the result of the * sequentially generated queries can be concatenated to produce a valid result set. However this * class makes no guarantees to this effect and this assurance must come from the code that * constructs this class. * *

If the order of a {@link MultiQueryComponent} is set to {@link Order#PARALLEL} the given * filters are used to generate multiple queries (one for each list of filters) that are returned * together. This is used to denote that results must be merged in memory to create a valid result * set. However this class makes no guarantees to this effect and this assurance must come from the * code that constructs this class. * *

{@link MultiQueryBuilder#iterator()} will provide an iterator that generates the filters for * these queries in the given order. The iterator generates each list of filters as they are needed. * In this way, if the user request is satisfied the first few sets of queries, the remaining * queries need never be created. This is important because the total number of queries generated by * this class is mult_i(|component_i.filters|). * *

This class preserves the order in which {@link MultiQueryComponent} are given to it when * applying the filters. However filters are generated most optimally when {@link Order#PARALLEL} * are the last to be applied. Thus to provide a convenient way to push components with {@link * Order#PARALLEL} to the back, {@link MultiQueryComponent} are sortable. * *

{@link MultiQueryComponent}s with different {@link Order}s can be added to the same {@link * MultiQueryBuilder} in any configuration. */ // TODO: consider allowing the order in which components are // process be separate from the order of the filters appear in the // resulting query. It is unclear to me whether or not this would help // users optimize for specific queries. class MultiQueryBuilder implements Iterable>> { final List baseFilters; final List components; final int parallelQuerySize; /* @VisibleForTesting */ MultiQueryBuilder( List baseFilters, List components, int parallelQuerySize) { this.baseFilters = checkNotNull(baseFilters); this.components = components; this.parallelQuerySize = parallelQuerySize; } public MultiQueryBuilder( List baseFilters, List splitComponents, boolean hasSort) { this.baseFilters = checkNotNull(baseFilters); if (splitComponents.isEmpty()) { components = Collections.emptyList(); parallelQuerySize = 1; } else { components = Lists.newArrayListWithCapacity(splitComponents.size()); // First we need to sort the components. This orders sequential components // by sortIndex and pushes all arbitrary components to end // // TODO: is this a stable sort? a stable sort would probably be // better as it would give the user some control of the ordering in which // these queries are produced. Collections.sort(splitComponents); // If there are no sorts we can do everything serially MultiQueryComponent.Order applyToRemaining = hasSort ? null : MultiQueryComponent.Order.SERIAL; int currentSortIndex = 0; int totalParallelQueries = 1; for (QuerySplitComponent component : splitComponents) { if ((applyToRemaining == null) && (component.getSortIndex() != currentSortIndex)) { if (component.getSortIndex() == currentSortIndex + 1) { // We can safely serialize these components as there can only be one // inequality filter and that filter must match the first sort order // so this component must only contain equality filters. In this way // we are assured that we are maintaining the correct sort order // of the resultSet when running the queries in serial ++currentSortIndex; } else { // If we have jumped over a sort order or hit an arbitrary sorting // component we must run all the remaining components in parallel // NOTE: a component with an arbitrary ordering will // always land here as they have a sortIndex of -1. applyToRemaining = MultiQueryComponent.Order.PARALLEL; // TODO: if there are no inequality filters and we have no // more sorts we can technically run the rest in serial. } } // Converting the current component and adding it to the result components.add( new MultiQueryComponent( applyToRemaining != null ? applyToRemaining : MultiQueryComponent.Order.SERIAL, component.getFilters())); if (applyToRemaining == MultiQueryComponent.Order.PARALLEL) { totalParallelQueries *= component.getFilters().size(); } } this.parallelQuerySize = totalParallelQueries; } } @Override public Iterator>> iterator() { if (components.isEmpty()) { return Iterators.singletonIterator(Collections.singletonList(baseFilters)); } return new MultiQueryIterator(baseFilters, components); } public List getBaseFilters() { return baseFilters; } public boolean isSingleton() { return components.isEmpty(); } /** Returns the parallelQuerySize. */ public int getParallelQuerySize() { return parallelQuerySize; } @Override public String toString() { return "MultiQueryBuilder [baseFilters=" + baseFilters + ", components=" + components + "]"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy