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

org.apache.cassandra.db.ClusteringComparator Maven / Gradle / Ivy

There is a newer version: 4.3.1.0
Show newest version
/*
 * 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.Comparator;
import java.util.List;
import java.util.Objects;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;

import org.apache.cassandra.db.rows.Row;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.serializers.MarshalException;

import org.apache.cassandra.io.sstable.IndexInfo;

/**
 * A comparator of clustering prefixes (or more generally of {@link Clusterable}}.
 * 

* This is essentially just a composite comparator that the clustering values of the provided * clustering prefixes in lexicographical order, with each component being compared based on * the type of the clustering column this is a value of. */ public class ClusteringComparator implements Comparator { private final List> clusteringTypes; private final Comparator indexComparator; private final Comparator indexReverseComparator; private final Comparator reverseComparator; private final Comparator rowComparator = (r1, r2) -> compare(r1.clustering(), r2.clustering()); public ClusteringComparator(AbstractType... clusteringTypes) { this(ImmutableList.copyOf(clusteringTypes)); } public ClusteringComparator(List> clusteringTypes) { // copy the list to ensure despatch is monomorphic this.clusteringTypes = ImmutableList.copyOf(clusteringTypes); this.indexComparator = (o1, o2) -> ClusteringComparator.this.compare(o1.lastName, o2.lastName); this.indexReverseComparator = (o1, o2) -> ClusteringComparator.this.compare(o1.firstName, o2.firstName); this.reverseComparator = (c1, c2) -> ClusteringComparator.this.compare(c2, c1); for (AbstractType type : clusteringTypes) type.checkComparable(); // this should already be enforced by CFMetaData.rebuild, but we check again for other constructors } /** * The number of clustering columns for the table this is the comparator of. */ public int size() { return clusteringTypes.size(); } /** * The "subtypes" of this clustering comparator, that is the types of the clustering * columns for the table this is a comparator of. */ public List> subtypes() { return clusteringTypes; } /** * Returns the type of the ith clustering column of the table. */ public AbstractType subtype(int i) { return clusteringTypes.get(i); } /** * Creates a row clustering based on the clustering values. *

* Every argument can either be a {@code ByteBuffer}, in which case it is used as-is, or a object * corresponding to the type of the corresponding clustering column, in which case it will be * converted to a byte buffer using the column type. * * @param values the values to use for the created clustering. There should be exactly {@code size()} * values which must be either byte buffers or of the type the column expect. * * @return the newly created clustering. */ public Clustering make(Object... values) { if (values.length != size()) throw new IllegalArgumentException(String.format("Invalid number of components, expecting %d but got %d", size(), values.length)); CBuilder builder = CBuilder.create(this); for (Object val : values) { if (val instanceof ByteBuffer) builder.add((ByteBuffer) val); else builder.add(val); } return builder.build(); } public int compare(Clusterable c1, Clusterable c2) { return compare(c1.clustering(), c2.clustering()); } public int compare(ClusteringPrefix c1, ClusteringPrefix c2) { int s1 = c1.size(); int s2 = c2.size(); int minSize = Math.min(s1, s2); for (int i = 0; i < minSize; i++) { int cmp = compareComponent(i, c1.get(i), c2.get(i)); if (cmp != 0) return cmp; } if (s1 == s2) return ClusteringPrefix.Kind.compare(c1.kind(), c2.kind()); return s1 < s2 ? c1.kind().comparedToClustering : -c2.kind().comparedToClustering; } public int compare(Clustering c1, Clustering c2) { return compare(c1, c2, size()); } /** * Compares the specified part of the specified clusterings. * * @param c1 the first clustering * @param c2 the second clustering * @param size the number of components to compare * @return a negative integer, zero, or a positive integer as the first argument is less than, * equal to, or greater than the second. */ public int compare(Clustering c1, Clustering c2, int size) { for (int i = 0; i < size; i++) { int cmp = compareComponent(i, c1.get(i), c2.get(i)); if (cmp != 0) return cmp; } return 0; } public int compareComponent(int i, ByteBuffer v1, ByteBuffer v2) { if (v1 == null) return v2 == null ? 0 : -1; if (v2 == null) return 1; return clusteringTypes.get(i).compare(v1, v2); } /** * Returns whether this clustering comparator is compatible with the provided one, * that is if the provided one can be safely replaced by this new one. * * @param previous the previous comparator that we want to replace and test * compatibility with. * * @return whether {@code previous} can be safely replaced by this comparator. */ public boolean isCompatibleWith(ClusteringComparator previous) { if (this == previous) return true; // Extending with new components is fine, shrinking is not if (size() < previous.size()) return false; for (int i = 0; i < previous.size(); i++) { AbstractType tprev = previous.subtype(i); AbstractType tnew = subtype(i); if (!tnew.isCompatibleWith(tprev)) return false; } return true; } /** * Validates the provided prefix for corrupted data. * * @param clustering the clustering prefix to validate. * * @throws MarshalException if {@code clustering} contains some invalid data. */ public void validate(ClusteringPrefix clustering) { for (int i = 0; i < clustering.size(); i++) { ByteBuffer value = clustering.get(i); if (value != null) subtype(i).validate(value); } } /** * A comparator for rows. * * A {@code Row} is a {@code Clusterable} so {@code ClusteringComparator} can be used * to compare rows directly, but when we know we deal with rows (and not {@code Clusterable} in * general), this is a little faster because by knowing we compare {@code Clustering} objects, * we know that 1) they all have the same size and 2) they all have the same kind. */ public Comparator rowComparator() { return rowComparator; } public Comparator indexComparator(boolean reversed) { return reversed ? indexReverseComparator : indexComparator; } public Comparator reversed() { return reverseComparator; } @Override public String toString() { return String.format("comparator(%s)", Joiner.on(", ").join(clusteringTypes)); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ClusteringComparator)) return false; ClusteringComparator that = (ClusteringComparator)o; return this.clusteringTypes.equals(that.clusteringTypes); } @Override public int hashCode() { return Objects.hashCode(clusteringTypes); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy