use of com.blazebit.persistence.KeysetPage in project blaze-persistence by Blazebit.
the class PaginatedTypedQueryImpl method getResultList.
private PagedList<X> getResultList(int queryFirstResult, int firstRow, long totalSize) {
if (idQuery != null) {
idQuery.setMaxResults(pageSize);
if (forceFirstResult || keysetMode == KeysetMode.NONE) {
idQuery.setFirstResult(firstRow);
} else {
idQuery.setFirstResult(0);
}
List<?> ids = idQuery.getResultList();
if (ids.isEmpty()) {
KeysetPage newKeysetPage = null;
if (keysetMode == KeysetMode.NEXT) {
// When we scroll over the last page to a non existing one, we reuse the current keyset
newKeysetPage = keysetPage;
}
long size;
if (withCount && totalSize == -1) {
size = getTotalCount();
} else {
size = totalSize;
}
if (boundedCount) {
if (keysetMode == KeysetMode.NEXT) {
size = Math.max(size, keysetPage.getFirstResult() + keysetPage.getMaxResults());
} else if (forceFirstResult || keysetMode == KeysetMode.NONE) {
size = Math.max(size, firstRow);
}
}
return new PagedArrayList<X>(newKeysetPage, size, queryFirstResult, pageSize);
}
Serializable[] lowest = null;
Serializable[] highest = null;
Serializable[][] keysets = null;
if (needsNewIdList) {
if (keysetToSelectIndexMapping != null) {
int keysetPageSize = pageSize - highestOffset;
int size = Math.min(ids.size(), keysetPageSize);
int lowestIndex;
int highestIndex;
// Swap keysets as we have inverse ordering when going to the previous page
if (keysetMode == KeysetMode.PREVIOUS) {
lowestIndex = size - 1;
highestIndex = 0;
} else {
lowestIndex = 0;
highestIndex = size - 1;
}
if (ids.get(0) instanceof Object[]) {
if (withExtractAllKeysets) {
keysets = new Serializable[size][];
for (int i = 0; i < size; i++) {
keysets[i] = KeysetPaginationHelper.extractKey((Object[]) ids.get(i), keysetToSelectIndexMapping, keysetSuffix);
}
lowest = keysets[lowestIndex];
highest = keysets[highestIndex];
} else {
lowest = KeysetPaginationHelper.extractKey((Object[]) ids.get(lowestIndex), keysetToSelectIndexMapping, keysetSuffix);
highest = KeysetPaginationHelper.extractKey((Object[]) ids.get(highestIndex), keysetToSelectIndexMapping, keysetSuffix);
}
} else {
if (withExtractAllKeysets) {
keysets = new Serializable[size][];
for (int i = 0; i < size; i++) {
keysets[i] = new Serializable[] { (Serializable) (ids.get(i)) };
}
lowest = keysets[lowestIndex];
highest = keysets[highestIndex];
} else {
lowest = new Serializable[] { (Serializable) ids.get(lowestIndex) };
highest = new Serializable[] { (Serializable) ids.get(highestIndex) };
}
}
// Swap keysets as we have inverse ordering when going to the previous page
if (keysetMode == KeysetMode.PREVIOUS) {
if (withExtractAllKeysets) {
// Reverse the keysets
Serializable[] tmp;
for (int i = 0, mid = size >> 1, j = size - 1; i < mid; i++, j--) {
tmp = keysets[i];
keysets[i] = keysets[j];
keysets[j] = tmp;
}
}
}
}
// extract count
if (inlinedCountQuery) {
Object[] first = (Object[]) ids.get(0);
totalSize = (long) first[first.length - 1];
}
List<Object> newIds = new ArrayList<Object>(ids.size());
if (identifierCount > 1) {
for (int i = 0; i < ids.size(); i++) {
Object[] tuple = (Object[]) ids.get(i);
Object newId = new Object[identifierCount];
System.arraycopy(tuple, 0, newId, 0, identifierCount);
newIds.add(newId);
}
} else {
for (int i = 0; i < ids.size(); i++) {
Object o = ids.get(i);
if (o instanceof Object[]) {
newIds.add(((Object[]) o)[0]);
} else {
newIds.add(o);
}
}
}
ids = newIds;
} else if (inlinedCountQuery) {
Object[] first = (Object[]) ids.get(0);
int newSize = first.length - 1;
totalSize = (long) first[first.length - 1];
// If this would have been a non-object array type without the count query, we must unwrap the result
List<Object> newIds = new ArrayList<>(ids.size());
if (newSize == 1) {
for (int i = 0; i < ids.size(); i++) {
newIds.add(((Object[]) ids.get(i))[0]);
}
} else {
for (int i = 0; i < ids.size(); i++) {
Object[] tuple = (Object[]) ids.get(i);
Object newId = new Object[newSize];
System.arraycopy(tuple, 0, newId, 0, newSize);
newIds.add(newId);
}
}
ids = newIds;
}
if (identifierCount > 1) {
StringBuilder parameterNameBuilder = new StringBuilder(AbstractCommonQueryBuilder.ID_PARAM_NAME.length() + 10);
parameterNameBuilder.append(AbstractCommonQueryBuilder.ID_PARAM_NAME).append('_');
int start = parameterNameBuilder.length();
Object[] empty = ids.size() < pageSize ? new Object[identifierCount] : null;
for (int i = 0; i < pageSize; i++) {
Object[] tuple;
if (ids.size() > i) {
tuple = (Object[]) ids.get(i);
} else {
tuple = empty;
}
for (int j = 0; j < identifierCount; j++) {
parameterNameBuilder.setLength(start);
parameterNameBuilder.append(j).append('_').append(i);
objectQuery.setParameter(parameterNameBuilder.toString(), tuple[j]);
}
}
} else {
objectQuery.setParameter(AbstractCommonQueryBuilder.ID_PARAM_NAME, ids);
}
KeysetPage newKeyset = null;
if (keysetToSelectIndexMapping != null) {
newKeyset = new DefaultKeysetPage(firstRow, pageSize, lowest, highest, keysets);
}
totalSize = Math.max(totalSize, firstRow + ids.size());
List<X> queryResultList = objectQuery.getResultList();
PagedList<X> pagedResultList = new PagedArrayList<X>(queryResultList, newKeyset, totalSize, queryFirstResult, pageSize);
return pagedResultList;
} else {
if (!inlinedIdQuery) {
objectQuery.setMaxResults(pageSize);
if (forceFirstResult || keysetMode == KeysetMode.NONE) {
objectQuery.setFirstResult(firstRow);
} else {
objectQuery.setFirstResult(0);
}
}
List<X> result = objectQuery.getResultList();
if (result.isEmpty()) {
KeysetPage newKeysetPage = null;
if (keysetMode == KeysetMode.NEXT) {
// When we scroll over the last page to a non existing one, we reuse the current keyset
newKeysetPage = keysetPage;
}
if (totalSize == -1) {
if (inlinedCountQuery && firstRow == 0) {
totalSize = 0L;
} else if (withCount) {
totalSize = getTotalCount();
}
}
if (boundedCount) {
if (keysetMode == KeysetMode.NEXT) {
totalSize = Math.max(totalSize, keysetPage.getFirstResult() + keysetPage.getMaxResults());
} else if (forceFirstResult || keysetMode == KeysetMode.NONE) {
totalSize = Math.max(totalSize, firstRow);
}
}
return new PagedArrayList<X>(newKeysetPage, totalSize, queryFirstResult, pageSize);
}
if (keysetMode == KeysetMode.PREVIOUS) {
Collections.reverse(result);
}
KeysetPage newKeyset = null;
if (keysetToSelectIndexMapping != null) {
if (objectBuilder == null) {
// extract count
if (inlinedCountQuery) {
Object[] first = (Object[]) result.get(0);
totalSize = (long) first[first.length - 1];
// If this would have been a non-object array type without the count query, we must unwrap the result
if (first.length == 2) {
List<X> newResult = new ArrayList<>(result.size());
for (int i = 0; i < result.size(); i++) {
newResult.add((X) ((Object[]) result.get(i))[0]);
}
result = newResult;
}
}
} else if (objectBuilder instanceof KeysetExtractionObjectBuilder<?>) {
KeysetExtractionObjectBuilder<?> keysetExtractionObjectBuilder = (KeysetExtractionObjectBuilder<?>) objectBuilder;
Serializable[] lowest = keysetExtractionObjectBuilder.getLowest();
Serializable[] highest = keysetExtractionObjectBuilder.getHighest();
Serializable[][] keysets = keysetExtractionObjectBuilder.getKeysets();
// extract count
if (inlinedCountQuery) {
totalSize = keysetExtractionObjectBuilder.getCount();
}
newKeyset = new DefaultKeysetPage(firstRow, pageSize, lowest, highest, keysets);
} else if (objectBuilder instanceof CountExtractionObjectBuilder<?>) {
totalSize = ((CountExtractionObjectBuilder<X>) objectBuilder).getCount();
}
}
totalSize = Math.max(totalSize, firstRow + result.size());
PagedList<X> pagedResultList = new PagedArrayList<X>(result, newKeyset, totalSize, queryFirstResult, pageSize);
return pagedResultList;
}
}
use of com.blazebit.persistence.KeysetPage in project blaze-persistence by Blazebit.
the class KeysetAwarePagedResourcesAssembler method createLink.
private Link createLink(UriTemplate base, Page<?> page, Pageable pageable, LinkRelation relation) {
UriComponentsBuilder builder;
if (base.getVariables().isEmpty()) {
builder = fromUriString(base.toString());
} else {
builder = fromUri(base.expand());
}
pageableResolver.enhance(builder, getMethodParameter(), pageable);
String previousPagePropertyName = pageableResolver.getParameterNameToUse(pageableResolver.getPreviousPageParameterName(), getMethodParameter());
String lowestPropertyName = pageableResolver.getParameterNameToUse(pageableResolver.getLowestParameterName(), getMethodParameter());
String highestPropertyName = pageableResolver.getParameterNameToUse(pageableResolver.getHighestParameterName(), getMethodParameter());
if ((relation == IanaLinkRelations.NEXT || relation == IanaLinkRelations.PREV) && page instanceof KeysetAwarePage<?>) {
int pageNumber = page.getNumber();
builder.replaceQueryParam(previousPagePropertyName, pageableResolver.isOneIndexedParameters() ? pageNumber + 1 : pageNumber);
Sort sort = page.getSort();
KeysetPage keysetPage = ((KeysetAwarePage<?>) page).getKeysetPage();
if (relation == IanaLinkRelations.NEXT) {
builder.replaceQueryParam(highestPropertyName, serialize(sort, keysetPage.getHighest()));
builder.replaceQueryParam(lowestPropertyName);
} else if (pageable.getOffset() != 0) {
builder.replaceQueryParam(lowestPropertyName, serialize(sort, keysetPage.getLowest()));
builder.replaceQueryParam(highestPropertyName);
} else {
builder.replaceQueryParam(previousPagePropertyName);
builder.replaceQueryParam(lowestPropertyName);
builder.replaceQueryParam(highestPropertyName);
}
} else {
builder.replaceQueryParam(previousPagePropertyName);
builder.replaceQueryParam(lowestPropertyName);
builder.replaceQueryParam(highestPropertyName);
}
return link(UriTemplate.of(builder.build().toString()), relation);
}
use of com.blazebit.persistence.KeysetPage in project blaze-persistence by Blazebit.
the class KeysetPageableHandlerMethodArgumentResolver method resolveArgument.
@Override
public Mono<Object> resolveArgument(MethodParameter methodParameter, BindingContext bindingContext, ServerWebExchange serverWebExchange) {
assertPageableUniqueness(methodParameter);
Pageable defaultOrFallback = getDefaultFromAnnotationOrFallback(methodParameter);
String pageString = serverWebExchange.getRequest().getQueryParams().getFirst(getParameterNameToUse(getPageParameterName(), methodParameter));
String offsetString = serverWebExchange.getRequest().getQueryParams().getFirst(getParameterNameToUse(getOffsetParameterName(), methodParameter));
String pageSizeString = serverWebExchange.getRequest().getQueryParams().getFirst(getParameterNameToUse(getSizeParameterName(), methodParameter));
boolean pageAndSizeGiven = (StringUtils.hasText(pageString) || StringUtils.hasText(offsetString)) && StringUtils.hasText(pageSizeString);
if (!pageAndSizeGiven && defaultOrFallback == null) {
return null;
}
int pageSize = StringUtils.hasText(pageSizeString) ? parseAndApplyBoundaries(pageSizeString, getMaxPageSize(), false) : defaultOrFallback.getPageSize();
// Limit lower bound
pageSize = pageSize < 1 ? defaultOrFallback.getPageSize() : pageSize;
// Limit upper bound
pageSize = pageSize > getMaxPageSize() ? getMaxPageSize() : pageSize;
int offset;
if (StringUtils.hasText(offsetString)) {
offset = parseAndApplyBoundaries(offsetString, Integer.MAX_VALUE, false);
} else if (StringUtils.hasText(pageString)) {
offset = pageSize * parseAndApplyBoundaries(pageString, Integer.MAX_VALUE, true);
} else {
offset = pageSize * defaultOrFallback.getPageNumber();
}
org.springframework.data.domain.Sort sort = (org.springframework.data.domain.Sort) sortResolver.resolveArgument(methodParameter, bindingContext, serverWebExchange).block();
// Default if necessary and default configured
sort = sort == UNSORTED && defaultOrFallback != null ? defaultOrFallback.getSort() : sort;
KeysetPage keysetPage = null;
Iterator<org.springframework.data.domain.Sort.Order> iterator;
if (sort != null && (iterator = sort.iterator()).hasNext()) {
KeysetConfig keysetConfig = methodParameter.getParameterAnnotation(KeysetConfig.class);
Class<?> domainClass = keysetConfig.keysetClass();
if (domainClass == void.class) {
domainClass = keysetConfig.value();
}
if (domainClass == void.class) {
Method annotatedMethod = methodParameter.getMethod();
throw new IllegalStateException(String.format(INVALID_KEYSET_DOMAIN_CLASS, annotatedMethod));
}
String previousOffsetName = getParameterName(keysetConfig.previousOffsetName(), getParameterNameToUse(getPreviousOffsetParameterName(), methodParameter));
String previousOffsetString = serverWebExchange.getRequest().getQueryParams().getFirst(previousOffsetName);
String previousPageName = getParameterName(keysetConfig.previousPageName(), getParameterNameToUse(getPreviousPageParameterName(), methodParameter));
String previousPageString = serverWebExchange.getRequest().getQueryParams().getFirst(previousPageName);
if (StringUtils.hasText(previousOffsetString) || StringUtils.hasText(previousPageString)) {
String previousPageSizeName = getParameterName(keysetConfig.previousPageSizeName(), getParameterNameToUse(getPreviousSizeParameterName(), methodParameter));
String previousPageSizeString = serverWebExchange.getRequest().getQueryParams().getFirst(previousPageSizeName);
int previousPageSize = StringUtils.hasText(previousPageSizeString) ? parseAndApplyBoundaries(previousPageSizeString, getMaxPageSize(), false) : pageSize;
int previousOffset;
if (StringUtils.hasText(previousOffsetString)) {
previousOffset = parseAndApplyBoundaries(previousOffsetString, Integer.MAX_VALUE, false);
} else {
int previousPage = parseAndApplyBoundaries(previousPageString, Integer.MAX_VALUE, true);
previousOffset = previousPage * previousPageSize;
}
String lowestName = getParameterName(keysetConfig.lowestName(), getParameterNameToUse(getLowestParameterName(), methodParameter));
String lowestString = serverWebExchange.getRequest().getQueryParams().getFirst(lowestName);
String highestName = getParameterName(keysetConfig.highestName(), getParameterNameToUse(getHighestParameterName(), methodParameter));
String highestString = serverWebExchange.getRequest().getQueryParams().getFirst(highestName);
if (StringUtils.hasText(lowestString) && StringUtils.hasText(highestString)) {
List<Serializable> lowest = new ArrayList<>();
List<Serializable> highest = new ArrayList<>();
JsonNode lowestObject;
JsonNode highestObject;
try {
lowestObject = mapper.readTree(lowestString);
} catch (IOException ex) {
throw new IllegalArgumentException("Invalid lowest object!", ex);
}
try {
highestObject = mapper.readTree(highestString);
} catch (IOException ex) {
throw new IllegalArgumentException("Invalid highest object!", ex);
}
while (iterator.hasNext()) {
org.springframework.data.domain.Sort.Order o = iterator.next();
JsonNode low = lowestObject;
JsonNode high = highestObject;
String[] propertyParts = o.getProperty().split("\\.");
Class<? extends Serializable> propertyType = getPropertyType(domainClass, o.getProperty());
for (int i = 0; i < propertyParts.length; i++) {
low = low == null ? null : low.get(propertyParts[i]);
high = high == null ? null : high.get(propertyParts[i]);
}
lowest.add(low == null ? null : convert(low, propertyType));
highest.add(high == null ? null : convert(high, propertyType));
}
keysetPage = new DefaultKeysetPage(previousOffset, previousPageSize, new DefaultKeyset(lowest.toArray(new Serializable[lowest.size()])), new DefaultKeyset(highest.toArray(new Serializable[highest.size()])));
}
}
}
return Mono.just(new KeysetPageRequest(keysetPage, sort, offset, pageSize));
}
use of com.blazebit.persistence.KeysetPage in project blaze-persistence by Blazebit.
the class GraphQLEntityViewSupport method createPaginatedSetting.
/**
* Like calling {@link #createSetting(Class, DataFetchingEnvironment, String)} with the configured page elements name.
*
* @param entityViewClass The entity view class
* @param dataFetchingEnvironment The GraphQL data fetching environment
* @param elementRoot The field at which to find the elements for fetch extraction
* @param <T> The entity view type
* @return the entity view setting
*/
public <T> EntityViewSetting<T, PaginatedCriteriaBuilder<T>> createPaginatedSetting(Class<T> entityViewClass, DataFetchingEnvironment dataFetchingEnvironment, String elementRoot) {
KeysetPage keysetPage = extractKeysetPage(dataFetchingEnvironment);
Integer pageSize = null;
Integer offset = null;
Integer last = null;
if (keysetPage != null) {
pageSize = dataFetchingEnvironment.getArgument(pageSizeName);
offset = dataFetchingEnvironment.getArgument(offsetName);
last = dataFetchingEnvironment.getArgument(RELAY_LAST_NAME);
}
return createPaginatedSetting(entityViewClass, dataFetchingEnvironment, elementRoot, keysetPage, pageSize, last, offset);
}
use of com.blazebit.persistence.KeysetPage in project blaze-persistence by Blazebit.
the class GraphQLEntityViewSupport method createSetting.
/**
* Returns a new entity view setting for the given data fetching environment.
*
* @param entityViewClass The entity view class
* @param dataFetchingEnvironment The GraphQL data fetching environment
* @param elementRoot The field at which to find the elements for fetch extraction
* @param <T> The entity view type
* @return the entity view setting
*/
public <T> EntityViewSetting<T, CriteriaBuilder<T>> createSetting(Class<T> entityViewClass, DataFetchingEnvironment dataFetchingEnvironment, String elementRoot) {
KeysetPage keysetPage = extractKeysetPage(dataFetchingEnvironment);
Integer pageSize = null;
Integer offset = null;
Integer last = null;
if (keysetPage != null) {
pageSize = dataFetchingEnvironment.getArgument(pageSizeName);
offset = dataFetchingEnvironment.getArgument(offsetName);
last = dataFetchingEnvironment.getArgument(RELAY_LAST_NAME);
}
return createSetting(entityViewClass, dataFetchingEnvironment, elementRoot, keysetPage, pageSize, last, offset);
}
Aggregations