net.e6tech.elements.cassandra.query.RangeQuery Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2015-2020 Futeh Kao
*
* 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
*
* 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 net.e6tech.elements.cassandra.query;
import net.e6tech.elements.cassandra.Sibyl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
/**
* Range query is used to query a range, e.g. creationTime >= x and createTime < y order by creationTime asc. Keep in
* mind the inclusion of end points depends on the order by. Using the previous example, if we were to change it to descending
* order, the query would look like creationTime > x and createTime <= y order by creationTime desc.
*
* The query does its best to limit the result set to the limit size. In some cases, it may return a list that is slightly larger
* than the limit size. This could happen for example when multiple records of the same creationTime span the page boundary.
*
*
*
* RangeQuery<X> query = new RangeQuery<>(sibyl, X.class);
* query.partition(X::setPartitionKey, 1L)
* .descending(X::setCreationTime, 0L, 9999999999999L)
* .limit(50);
*
*
*
* @param entity table type
*/
public class RangeQuery extends BaseQuery> {
private boolean subQuery;
public RangeQuery(Sibyl sibyl, Class entityClass) {
super(sibyl, entityClass);
}
public List query() {
validate();
List list = select();
return patchBoundary(list);
}
private List patchBoundary(List list) {
if (!subQuery && !orderBy.isEmpty() && list.size() == limit && limit > 0) {
T last = list.get(list.size() - 1);
RangeQuery sub = subQuery(last);
return merge(list, sub.query(), last);
}
return list;
}
private RangeQuery subQuery(T last) {
RangeQuery sub = new RangeQuery<>(sibyl, entityClass);
sub.subQuery = true;
sub.partitionRelations = new ArrayList<>(partitionRelations);
// get a list of order by columns. use them to filter remove last few potentially same entries
Set set = new HashSet<>();
Consumer consumer = r -> {
if (set.contains(r.keyColumn.getName()))
return;
Relation subR = new Relation(r.keyColumn, Comparison.EQUAL, r.value);
sub.clusteringRelations.add(subR);
set.add(r.keyColumn.getName());
};
clusteringRelations.forEach(consumer);
orderBy.forEach(consumer);
for (Relation r : sub.clusteringRelations) {
r.value = r.accessor.get(last);
}
return sub;
}
private List merge(List list, List list2, T last) {
int trimLast = list.size();
for (int i = list.size() - 1; i >= 0; i --) {
T t = list.get(i);
boolean same = true;
for (Relation relation : orderBy) {
if (!relation.isRelated(last, t)) {
same = false;
break;
}
}
if (!same)
break;
trimLast = i;
}
list = list.subList(0, trimLast);
list.addAll(list2);
return list;
}
}