Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / api / datastore / MultiQueryBuilder.java
blob6afd7f2a524f7bda99dd667b1b1c73e71ed182e5
1 // Copyright 2009 Google Inc. All Rights Reserved.
2 package com.google.appengine.api.datastore;
4 import static com.google.common.base.Preconditions.checkNotNull;
6 import com.google.appengine.api.datastore.MultiQueryComponent.Order;
7 import com.google.appengine.api.datastore.Query.FilterPredicate;
8 import com.google.common.collect.Iterators;
9 import com.google.common.collect.Lists;
11 import java.util.Collections;
12 import java.util.Iterator;
13 import java.util.List;
15 /**
16 * A class that generates multiple lists of query filters to execute to satisfy
17 * the {@link MultiQueryComponent}s given to it.
19 * Each component contains multiple {@link List}s of {@link FilterPredicate}s
20 * and an order in which MultiQuery should produce queries that contain these
21 * filters.
23 * If the order of a {@link MultiQueryComponent} is set to {@link Order#SERIAL}
24 * the given filters are applied to sequentially generated sets of queries.
25 * This denotes that the result of the sequentially generated queries can be
26 * concatenated to produce a valid result set. However this class makes no
27 * guarantees to this effect and this assurance must come from the code that
28 * constructs this class.
30 * If the order of a {@link MultiQueryComponent} is set to {@link
31 * Order#PARALLEL} the given filters are used to generate multiple queries (one
32 * for each list of filters) that are returned together. This is used to denote
33 * that results must be merged in memory to create a valid result set. However
34 * this class makes no guarantees to this effect and this assurance must come from
35 * the code that constructs this class.
37 * {@link MultiQueryBuilder#iterator()} will provide an iterator that generates
38 * the filters for these queries in the given order. The iterator generates each
39 * list of filters as they are needed. In this way, if the user request is
40 * satisfied the first few sets of queries, the remaining queries need never be
41 * created. This is important because the total number of queries generated by
42 * this class is mult_i(|component_i.filters|).
44 * This class preserves the order in which {@link MultiQueryComponent} are
45 * given to it when applying the filters. However filters are generated most
46 * optimally when {@link Order#PARALLEL} are the last to be applied. Thus to
47 * provide a convenient way to push components with {@link Order#PARALLEL} to
48 * the back, {@link MultiQueryComponent} are sortable.
50 * {@link MultiQueryComponent}s with different {@link Order}s can be added to
51 * the same {@link MultiQueryBuilder} in any configuration.
54 class MultiQueryBuilder implements Iterable<List<List<FilterPredicate>>> {
55 final List<FilterPredicate> baseFilters;
56 final List<MultiQueryComponent> components;
57 final int parallelQuerySize;
59 MultiQueryBuilder(List<FilterPredicate> baseFilters,
60 List<MultiQueryComponent> components, int parallelQuerySize) {
61 this.baseFilters = checkNotNull(baseFilters);
62 this.components = components;
63 this.parallelQuerySize = parallelQuerySize;
66 public MultiQueryBuilder(List<FilterPredicate> baseFilters,
67 List<QuerySplitComponent> splitComponents, boolean hasSort) {
68 this.baseFilters = checkNotNull(baseFilters);
70 if (splitComponents.isEmpty()) {
71 components = Collections.emptyList();
72 parallelQuerySize = 1;
73 } else {
74 components = Lists.newArrayListWithCapacity(splitComponents.size());
76 Collections.sort(splitComponents);
78 MultiQueryComponent.Order applyToRemaining =
79 hasSort ? null : MultiQueryComponent.Order.SERIAL;
81 int currentSortIndex = 0;
82 int totalParallelQueries = 1;
83 for (QuerySplitComponent component : splitComponents) {
84 if ((applyToRemaining == null) && (component.getSortIndex() != currentSortIndex)) {
85 if (component.getSortIndex() == currentSortIndex + 1) {
86 ++currentSortIndex;
87 } else {
88 applyToRemaining = MultiQueryComponent.Order.PARALLEL;
92 components.add(new MultiQueryComponent(
93 applyToRemaining != null ? applyToRemaining : MultiQueryComponent.Order.SERIAL,
94 component.getFilters()));
96 if (applyToRemaining == MultiQueryComponent.Order.PARALLEL) {
97 totalParallelQueries *= component.getFilters().size();
100 this.parallelQuerySize = totalParallelQueries;
104 @Override
105 public Iterator<List<List<FilterPredicate>>> iterator() {
106 if (components.isEmpty()) {
107 return Iterators.singletonIterator(Collections.singletonList(baseFilters));
109 return new MultiQueryIterator(baseFilters, components);
112 public List<FilterPredicate> getBaseFilters() {
113 return baseFilters;
116 public boolean isSingleton() {
117 return components.isEmpty();
121 * @return the parallelQuerySize
123 public int getParallelQuerySize() {
124 return parallelQuerySize;
127 @Override
128 public String toString() {
129 return "MultiQueryBuilder [baseFilters=" + baseFilters + ", components=" + components + "]";