adelapena commented on code in PR #2556: URL: https://github.com/apache/cassandra/pull/2556#discussion_r1385108639
########## src/java/org/apache/cassandra/db/MultiClusteringBuilder.java: ########## @@ -0,0 +1,437 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.cassandra.db; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.NavigableSet; + +import org.apache.cassandra.cql3.statements.Bound; +import org.apache.cassandra.db.marshal.AbstractType; +import org.apache.cassandra.schema.ColumnMetadata; +import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.utils.UniqueComparator; +import org.apache.cassandra.utils.btree.BTreeSet; + +/** + * Builder that allows to build multiple {@link Clustering}/{@link ClusteringBound} at the same time. + * Builds a set of clusterings incrementally, by computing cartesian products of + * sets of values present in each statement restriction. The typical use of this builder is as follows: + * <ol> + * <li>Call {@link MultiClusteringBuilder#extend(ClusteringElements, List)} or {@link MultiClusteringBuilder#extend(List, List)} + * method once per each restriction. Slice restrictons, if they exist, must be added last. + * <li>Finally, call {@link MultiClusteringBuilder#build()} or {@link MultiClusteringBuilder#buildBound(boolean)} method + * to obtain the set of clusterings / clustering bounds.</li> + * </ol> + * <p> + * Important: When dealing with slices, you likely want the number of start and end bounds to match. + * If some columns are restricted from one side only, you can use the special {@link ClusteringElements#BOTTOM} or + * {@link ClusteringElements#TOP} values to generate a proper clustering bound for the "unbounded" + * side of the restriction. + * </p> + * <h1>Example</h1> + * <p> + * + * Imagine we have a CQL query with multiple restrictions joined by AND: + * <pre> + * SELECT * FROM tab + * WHERE a IN (a1, a2) + * AND b IN (b1, b2, b3) + * AND c > c1 + * </pre> + * <p> + * We need to generate a list of clustering bounds that will be used to fetch proper contiguous chunks of the partition. + * + * <p> + * The builder initial state is a single empty clustering, denoted by the {@code ROOT} constant, + * which is a natural zero element of cartesian set multiplication. This significantly simplifies the logic. + * <pre> + * point: () + * </pre> + * + * After adding the IN restriction on column {@code a} we get 2 points: + * <pre> + * point: (a1) + * point: (a2) + * </pre> + * + * Next when we add the IN restrction on column {@code b}, we get a cartesian product of all values + * of {@code a} with all values of {@code b}: + * <pre> + * point: (a1, b1) + * point: (a1, b2) + * point: (a1, b3) + * point: (a2, b1) + * point: (a2, b2) + * point: (a2, b3) + * </pre> + * + * Finally, we add the slice of column {@code c} by specifying the lower and upper bound + * (we use {@code TOP} for the upper bound), and we get the final set of clustering bounds: + * <pre> + * excl start: (a1, b1, c1) + * incl end: (a1, b1) + * excl start: (a1, b2, c1) + * incl end: (a1, b2) + * excl start: (a1, b3, c1) + * incl end: (a1, b3) + * excl start: (a2, b1, c1) + * incl end: (a2, b1) + * excl start: (a2, b2, c1) + * incl end: (a2, b2) + * excl start: (a2, b3, c1) + * incl end: (a2, b3) + * </pre> + */ +public class MultiClusteringBuilder +{ + /** + * Represents a building block of a clustering. + * Either a point or a bound. + * Can consist of multiple column values. + * + * <p> + * For bounds, it additionally stores the inclusiveness of a bound and whether it is start or end, so that + * it is possible to mix bounds of different inclusiveness. + */ + public static class ClusteringElements + { + public enum Kind + { + POINT, INCL_START, EXCL_START, INCL_END, EXCL_END + } + + public static final ClusteringElements BOTTOM = new ClusteringElements(Collections.emptyList(), Kind.INCL_START); + public static final ClusteringElements TOP = new ClusteringElements(Collections.emptyList(), Kind.INCL_END); + public static final ClusteringElements ROOT = new ClusteringElements(Collections.emptyList(), Kind.POINT); + + final List<ByteBuffer> values; + final Kind kind; + + + private ClusteringElements(List<ByteBuffer> values, Kind kind) + { + this.values = values; + this.kind = kind; + } + + public static ClusteringElements point(ByteBuffer value) + { + return point(Collections.singletonList(value)); + } + + public static ClusteringElements point(List<ByteBuffer> values) + { + return new ClusteringElements(values, Kind.POINT); + } + + public static ClusteringElements bound(ByteBuffer value, Bound bound, boolean inclusive) + { + return bound(Collections.singletonList(value), bound, inclusive); + } + + public static ClusteringElements bound(List<ByteBuffer> values, Bound bound, boolean inclusive) + { + Kind kind = bound.isStart() + ? (inclusive ? Kind.INCL_START : Kind.EXCL_START) + : (inclusive ? Kind.INCL_END : Kind.EXCL_END); + return new ClusteringElements(values, kind); + } + + public boolean isBound() + { + return kind != Kind.POINT; + } + + public boolean isStart() + { + return kind == ClusteringElements.Kind.EXCL_START || + kind == ClusteringElements.Kind.INCL_START; + } + + public boolean isInclusive() + { + return kind == Kind.INCL_START || + kind == Kind.INCL_END || + kind == Kind.POINT; + } + + public String toString() + { + return "Element{" + + "kind=" + kind + + ", value=" + values + + '}'; + } + } + + /** + * The table comparator. + */ + private final ClusteringComparator comparator; + + /** + * Columns corresponding to the already added elements. + */ + private final List<ColumnMetadata> columns = new ArrayList<>(); + + /** + * The elements of the clusterings. + */ + private List<ClusteringElements> clusterings = Collections.singletonList(ClusteringElements.ROOT); + + + /** + * <code>true</code> if the clusterings have been build, <code>false</code> otherwise. + */ + private boolean built; + + /** + * <code>true</code> if the clusterings contains some <code>null</code> elements. + */ + private boolean containsNull; + + /** + * <code>true</code> if the composites contains some <code>unset</code> elements. + */ + private boolean containsUnset; + + /** + * <code>true</code> if the composites contains some slice bound elements. + */ + private boolean containsSliceBound; + + + private MultiClusteringBuilder(ClusteringComparator comparator) + { + this.comparator = comparator; + } + + /** + * Creates a new empty {@code MultiCBuilder}. Review Comment: I think this nit hasn't actually been resolved, it still says `{@code MultiCBuilder}`. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]

