in harry-core/src/harry/operations/Query.java [404:481]
public static Query clusteringRangeQuery(SchemaSpec schema, long pd, long cd1, long cd2, long queryDescriptor, boolean isMinEq, boolean isMaxEq, boolean reverse)
{
List<Relation> relations = new ArrayList<>();
long[] minBound = schema.ckGenerator.slice(cd1);
long[] maxBound = schema.ckGenerator.slice(cd2);
int nonEqFrom = RngUtils.asInt(queryDescriptor, 0, schema.clusteringKeys.size() - 1);
// Logic here is similar to how clustering slices are implemented, except for both lower and upper bound
// get their values from sliced value in (1) and (2) cases:
//
// 1. Every part that is restricted with an EQ relation, takes its value from the min bound.
// TODO: this can actually be improved, since in case of hierarchical clustering generation we can
// pick out of the keys that are already locked. That said, we'll exercise more cases the way
// it is implemented right now.
// 2. Every part that is restricted with a non-EQ relation is taken into the bound, if it is used in
// the query. For example in, `ck1 = 0 AND ck2 > 2 AND ck2 < 5`, ck2 values 2 and 5 will be placed,
// correspondingly, to the min and max bound.
// 3. Every other part has to be restricted according to equality. Similar to clustering slice, we have
// to decide whether we use a min or the max value for the bound. Foe example `ck1 = 0 AND ck2 > 2 AND ck2 <= 5`,
// assuming we have ck3 that is present in schema but not mentioned in the query, we'll have bounds
// created as follows: [0, 2, max_value] and [0, 5, max_value]. Idea here is that since ck2 = 2 is excluded,
// we also disallow all ck3 values for [0, 2] prefix. Similarly, since ck2 = 5 is included, we allow every
// ck3 value with a prefix of [0, 5].
for (int i = 0; i < schema.clusteringKeys.size(); i++)
{
ColumnSpec<?> col = schema.clusteringKeys.get(i);
if (i < nonEqFrom)
{
relations.add(Relation.eqRelation(col, minBound[i]));
maxBound[i] = minBound[i];
}
else if (i == nonEqFrom)
{
long minLocked = Math.min(minBound[nonEqFrom], maxBound[nonEqFrom]);
long maxLocked = Math.max(minBound[nonEqFrom], maxBound[nonEqFrom]);
relations.add(Relation.relation(relationKind(true, col.isReversed() ? isMaxEq : isMinEq), col,
col.isReversed() ? maxLocked : minLocked));
relations.add(Relation.relation(relationKind(false, col.isReversed() ? isMinEq : isMaxEq), col,
col.isReversed() ? minLocked : maxLocked));
minBound[i] = minLocked;
maxBound[i] = maxLocked;
// Impossible query
if (i == 0 && minLocked == maxLocked)
throw new IllegalArgumentException("impossible query");
}
else
{
minBound[i] = isMinEq ? schema.ckGenerator.minValue(i) : schema.ckGenerator.maxValue(i);
maxBound[i] = isMaxEq ? schema.ckGenerator.maxValue(i) : schema.ckGenerator.minValue(i);
}
}
long stitchedMin = schema.ckGenerator.stitch(minBound);
long stitchedMax = schema.ckGenerator.stitch(maxBound);
// if we're about to create an "impossible" query, just bump the modifier and re-generate
// TODO: this isn't considered "normal" that we do it this way, but I'd rather fix it with
// a refactoring that's mentioned below
if (stitchedMin == stitchedMax)
throw new IllegalArgumentException("impossible query");
// TODO: one of the ways to get rid of garbage here, and potentially even simplify the code is to
// simply return bounds here. After bounds are created, we slice them and generate query right
// from the bounds. In this case, we can even say that things like -inf/+inf are special values,
// and use them as placeholders. Also, it'll be easier to manipulate relations.
return new Query.ClusteringRangeQuery(Query.QueryKind.CLUSTERING_RANGE,
pd,
stitchedMin,
stitchedMax,
relationKind(true, isMinEq),
relationKind(false, isMaxEq),
reverse,
relations,
schema);
}