use of siena.core.options.QueryOptionOffset in project siena by mandubian.
the class SdbMappingUtils method buildFilterOrder.
public static <T> StringBuilder buildFilterOrder(Query<T> query, StringBuilder q) {
List<QueryFilter> filters = query.getFilters();
Set<Field> filteredFields = new HashSet<Field>();
boolean first = true;
if (!filters.isEmpty()) {
q.append(WHERE);
for (QueryFilter filter : filters) {
if (QueryFilterSimple.class.isAssignableFrom(filter.getClass())) {
QueryFilterSimple qf = (QueryFilterSimple) filter;
Field f = qf.field;
Object value = qf.value;
String op = qf.operator;
// for order verification in case the order is not on a filtered field
filteredFields.add(f);
if (!first) {
q.append(AND);
}
first = false;
String[] columns = ClassInfo.getColumnNames(f);
if ("IN".equals(op)) {
if (!Collection.class.isAssignableFrom(value.getClass()))
throw new SienaException("Collection needed when using IN operator in filter() query");
StringBuilder s = new StringBuilder();
Collection<?> col = (Collection<?>) value;
for (Object object : col) {
// TO BE VERIFIED: SHOULD BE MANAGED by toString!!!
if (object != null) {
s.append("," + SimpleDB.quote(toString(f, object)));
} else {
throw new SienaException("Can't use NULL in collection for IN operator");
}
}
String column = null;
if (ClassInfo.isId(f)) {
column = ITEM_NAME;
} else {
column = ClassInfo.getColumnNames(f)[0];
}
q.append(column + " in(" + s.toString().substring(1) + ")");
} else if (ClassInfo.isModel(f.getType())) {
// TODO could manage other ops here
if (!op.equals("=")) {
throw new SienaException("Unsupported operator for relationship: " + op);
}
ClassInfo relInfo = ClassInfo.getClassInfo(f.getType());
int i = 0;
for (Field key : relInfo.keys) {
if (value == null) {
q.append(columns[i++] + IS_NULL);
} else {
q.append(columns[i++] + op + SimpleDB.quote(objectFieldToString(value, key)));
}
}
} else {
String column = null;
if (ClassInfo.isId(f)) {
column = "itemName()";
if (value == null && op.equals("=")) {
throw new SienaException("SDB filter on @Id field with 'IS NULL' is not possible");
}
} else {
column = ClassInfo.getColumnNames(f)[0];
}
if (value == null && op.equals("=")) {
q.append(column + IS_NULL);
} else if (value == null && op.equals("!=")) {
q.append(column + IS_NOT_NULL);
} else {
q.append(column + op + SimpleDB.quote(toString(f, value)));
}
}
} else if (QueryFilterSearch.class.isAssignableFrom(filter.getClass())) {
Class<T> clazz = query.getQueriedClass();
QueryFilterSearch qf = (QueryFilterSearch) filter;
// throw new SienaException("Search not possible for several fields in SDB: only one field");
try {
//Field field = Util.getField(clazz, qf.fields[0]);
//if(field.isAnnotationPresent(Unindexed.class)){
// throw new SienaException("Cannot search the @Unindexed field "+field.getName());
//}
// cuts match into words
String[] words = qf.match.split("\\s");
// if several words, then only OR operator represented by IN GAE
Pattern pNormal = Pattern.compile("[\\%]*(\\w+)[\\%]*");
if (!first) {
q.append(AND);
}
// forces true
first = true;
q.append(" ( ");
for (String f : qf.fields) {
Field field = Util.getField(clazz, f);
if (!first) {
q.append(OR);
}
first = false;
q.append(" ( ");
String column = null;
if (ClassInfo.isId(field)) {
column = "itemName()";
} else {
column = ClassInfo.getColumnNames(field)[0];
}
first = true;
for (String word : words) {
if (!first) {
q.append(OR);
}
first = false;
if (!pNormal.matcher(word).matches()) {
throw new SienaException("'" + word + "' doesn't match pattern [\\%]*(\\w+)[\\%]*");
}
if (word.contains("%")) {
q.append(column + LIKE + SimpleDB.quote(word));
} else {
q.append(column + EQ + SimpleDB.quote(word));
}
}
q.append(" ) ");
}
q.append(" ) ");
} catch (Exception e) {
throw new SienaException(e);
}
}
}
}
List<QueryOrder> orders = query.getOrders();
if (!orders.isEmpty()) {
QueryOrder last = orders.get(orders.size() - 1);
Field field = last.field;
if (ClassInfo.isId(field)) {
if (!filteredFields.contains(field)) {
if (filters.isEmpty()) {
q.append(WHERE);
} else {
q.append(AND);
}
q.append(ITEM_NAME + IS_NOT_NULL);
}
q.append(ORDER_BY + ITEM_NAME);
} else {
String column = ClassInfo.getColumnNames(field)[0];
if (!filteredFields.contains(field)) {
if (filters.isEmpty()) {
q.append(WHERE);
} else {
q.append(AND);
}
q.append(column + IS_NOT_NULL);
}
q.append(ORDER_BY + column);
}
if (!last.ascending)
q.append(DESC);
}
QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext) query.option(QueryOptionSdbContext.ID);
QueryOptionOffset off = (QueryOptionOffset) query.option(QueryOptionOffset.ID);
if (sdbCtx != null && sdbCtx.realPageSize != 0) {
if (off != null && off.isActive()) {
// if offset is active, adds it to the page size to be sure to retrieve enough elements
q.append(LIMIT + (sdbCtx.realPageSize + off.offset));
} else {
q.append(LIMIT + sdbCtx.realPageSize);
}
}
return q;
}
use of siena.core.options.QueryOptionOffset in project siena by mandubian.
the class SdbPersistenceManager method doFetchList.
protected <T> void doFetchList(Query<T> query, int limit, int offset, List<T> resList, int depth) {
if (depth >= MAX_DEPTH) {
throw new SienaException("Reached maximum depth of recursion when retrieving more data (" + MAX_DEPTH + ")");
}
preFetch(query, limit, offset, !resList.isEmpty());
QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext) query.option(QueryOptionSdbContext.ID);
QueryOptionFetchType fetchType = (QueryOptionFetchType) query.option(QueryOptionFetchType.ID);
QueryOptionOffset off = (QueryOptionOffset) query.option(QueryOptionOffset.ID);
// if previousPage has detected there is no more data, simply returns an empty list
if (sdbCtx.noMoreDataBefore || sdbCtx.noMoreDataAfter) {
return;
}
// manages cursor limitations for IN and != operators with offsets
if (!sdbCtx.isActive()) {
StringBuffer domainBuf = new StringBuffer();
SelectRequest req = SdbMappingUtils.buildQuery(query, prefix, domainBuf);
req.setConsistentRead(isReadConsistent());
checkDomain(domainBuf.toString());
SelectResult res = sdb.select(req);
// activates the SdbCtx now that it is really initialised
sdbCtx.activate();
postFetch(query, res);
// cursor not yet created
switch(fetchType.fetchType) {
case KEYS_ONLY:
if (off.isActive()) {
SdbMappingUtils.mapSelectResultToListKeysOnly(res, resList, query.getQueriedClass(), off.offset);
} else {
SdbMappingUtils.mapSelectResultToListKeysOnly(res, resList, query.getQueriedClass());
}
break;
case NORMAL:
default:
if (off.isActive()) {
SdbMappingUtils.mapSelectResultToList(res, resList, query.getQueriedClass(), off.offset);
} else {
SdbMappingUtils.mapSelectResultToList(res, resList, query.getQueriedClass());
}
// join management
if (!query.getJoins().isEmpty() || !ClassInfo.getClassInfo(query.getQueriedClass()).joinFields.isEmpty())
mapJoins(query, resList);
}
continueFetchNextToken(query, resList, depth);
postMapping(query);
} else {
// we prepare the query each time
StringBuffer domainBuf = new StringBuffer();
SelectRequest req = SdbMappingUtils.buildQuery(query, prefix, domainBuf);
req.setConsistentRead(isReadConsistent());
checkDomain(domainBuf.toString());
// we can't use real asynchronous function with cursors
// so the page is extracted at once and wrapped into a SienaFuture
String token = sdbCtx.currentToken();
if (token != null) {
req.setNextToken(token);
}
SelectResult res = sdb.select(req);
postFetch(query, res);
switch(fetchType.fetchType) {
case KEYS_ONLY:
if (off.isActive()) {
SdbMappingUtils.mapSelectResultToListKeysOnly(res, resList, query.getQueriedClass(), off.offset);
} else {
SdbMappingUtils.mapSelectResultToListKeysOnly(res, resList, query.getQueriedClass());
}
break;
case NORMAL:
default:
if (off.isActive()) {
SdbMappingUtils.mapSelectResultToList(res, resList, query.getQueriedClass(), off.offset);
} else {
SdbMappingUtils.mapSelectResultToList(res, resList, query.getQueriedClass());
}
// join management
if (!query.getJoins().isEmpty() || !ClassInfo.getClassInfo(query.getQueriedClass()).joinFields.isEmpty())
mapJoins(query, resList);
}
continueFetchNextToken(query, resList, depth);
postMapping(query);
}
}
use of siena.core.options.QueryOptionOffset in project siena by mandubian.
the class SdbMappingUtils method previousPage.
public static <T> void previousPage(QueryData<T> query) {
QueryOptionPage pag = (QueryOptionPage) query.option(QueryOptionPage.ID);
QueryOptionState state = (QueryOptionState) query.option(QueryOptionState.ID);
QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext) query.option(QueryOptionSdbContext.ID);
if (sdbCtx == null) {
sdbCtx = new QueryOptionSdbContext();
query.options().put(sdbCtx.type, sdbCtx);
}
// if no more data before, doesn't try to go before
if (sdbCtx.noMoreDataBefore) {
return;
}
// if no more data after, removes flag to be able to go before
if (sdbCtx.noMoreDataAfter) {
// here the realoffset is not at the end of current pages
// but at the beginning of the last page
// so need to fake that we are at the end of the last page
sdbCtx.realOffset += pag.pageSize;
sdbCtx.noMoreDataAfter = false;
}
if (pag.isPaginating()) {
if (sdbCtx.hasToken()) {
// if tokenIdx is 0, it means at first page after beginning
if (sdbCtx.tokenIdx == 0) {
sdbCtx.previousToken();
// follows the real offset
sdbCtx.realOffset -= pag.pageSize;
// uses offset
if (sdbCtx.currentTokenOffset() <= sdbCtx.realOffset) {
QueryOptionOffset offset = (QueryOptionOffset) query.option(QueryOptionOffset.ID);
offset.activate();
offset.offset = sdbCtx.realOffset - sdbCtx.currentTokenOffset();
} else // if currentokenoffset is greater than previous page realoffset
// go to previous page again
{
previousPage(query);
}
} else {
if (sdbCtx.previousToken() == null) {
sdbCtx.realOffset -= pag.pageSize;
// so now uses realOffset
if (sdbCtx.realOffset >= 0) {
QueryOptionOffset offset = (QueryOptionOffset) query.option(QueryOptionOffset.ID);
offset.activate();
offset.offset = sdbCtx.realOffset;
} else {
// resets realOffset to 0 because it was negative
sdbCtx.realOffset = 0;
sdbCtx.noMoreDataBefore = true;
}
} else {
// follows the real offset
sdbCtx.realOffset -= pag.pageSize;
// uses offset
if (sdbCtx.currentTokenOffset() <= sdbCtx.realOffset) {
QueryOptionOffset offset = (QueryOptionOffset) query.option(QueryOptionOffset.ID);
offset.activate();
offset.offset = sdbCtx.realOffset - sdbCtx.currentTokenOffset();
} else // if currentokenoffset is greater than previous page realoffset
// go to previous page again
{
previousPage(query);
}
}
}
} else {
QueryOptionOffset offset = (QueryOptionOffset) query.option(QueryOptionOffset.ID);
// to simulate the nextPage as there was no token yet
if (sdbCtx.realOffset != 0) {
// follows the real offset
sdbCtx.realOffset -= pag.pageSize;
offset.activate();
offset.offset = sdbCtx.realOffset;
} else {
sdbCtx.noMoreDataBefore = true;
}
/*if(offset.offset != 0){
offset.offset -= pag.pageSize;
offset.activate();
// follows the real offset
sdbCtx.realOffset -= pag.pageSize;
}else {
// if the realOffset is not null, it means we are not at the index 0 of the table
// so now uses realOffset
if(sdbCtx.realOffset != 0){
offset.activate();
offset.offset = sdbCtx.realOffset;
}
sdbCtx.noMoreDataBefore = true;
}*/
}
} else {
// throws exception because it's impossible to reuse nextPage when paginating has been interrupted, the cases are too many
throw new SienaException("Can't use nextPage after pagination has been interrupted...");
}
}
use of siena.core.options.QueryOptionOffset in project siena by mandubian.
the class SdbPersistenceManager method continueFetchNextToken.
protected <T> void continueFetchNextToken(Query<T> query, List<T> results, int depth) {
QueryOptionPage pag = (QueryOptionPage) query.option(QueryOptionPage.ID);
QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext) query.option(QueryOptionSdbContext.ID);
QueryOptionState state = (QueryOptionState) query.option(QueryOptionState.ID);
QueryOptionOffset off = (QueryOptionOffset) query.option(QueryOptionOffset.ID);
// desactivates offset not to use if fetching more items from next token
if (state.isStateless()) {
off.passivate();
}
if (!pag.isActive()) {
if (state.isStateless()) {
// retrieves next token
if (sdbCtx.nextToken() != null) {
doFetchList(query, Integer.MAX_VALUE, 0, results, depth + 1);
}
} else {
if (sdbCtx.currentToken() != null) {
// desactivates offset because we don't to go on using offset while going to next tokens
boolean b = off.isActive();
off.passivate();
doFetchList(query, Integer.MAX_VALUE, 0, results, depth + 1);
// reactivate it if it was activated
if (b)
off.activate();
}
}
}
}
use of siena.core.options.QueryOptionOffset in project siena by mandubian.
the class SdbPersistenceManager method preFetch.
protected <T> void preFetch(Query<T> query, int limit, int offset, boolean recursing) {
QueryOptionSdbContext sdbCtx = (QueryOptionSdbContext) query.option(QueryOptionSdbContext.ID);
QueryOptionState state = (QueryOptionState) query.option(QueryOptionState.ID);
QueryOptionPage pag = (QueryOptionPage) query.option(QueryOptionPage.ID);
QueryOptionOffset off = (QueryOptionOffset) query.option(QueryOptionOffset.ID);
if (sdbCtx == null) {
sdbCtx = new QueryOptionSdbContext();
query.customize(sdbCtx);
}
if (!pag.isPaginating()) {
if (state.isStateless()) {
// if not empty, it means we are recursing on tokens
sdbCtx.reset(recursing);
}
// no pagination but pageOption active
if (pag.isActive()) {
// if local limit is set, it overrides the pageOption.pageSize
if (limit != Integer.MAX_VALUE) {
sdbCtx.realPageSize = limit;
// DONT DO THAT BECAUSE IT PREVENTS GOING TO NEXT TOKENS USING PAGE SIZE
// pageOption is passivated to be sure it is not reused
//pag.passivate();
} else // using pageOption.pageSize
{
sdbCtx.realPageSize = pag.pageSize;
// DONT DO THAT BECAUSE IT PREVENTS GOING TO NEXT TOKENS USING PAGE SIZE
// passivates the pageOption in stateless mode not to keep anything between 2 requests
//if(state.isStateless()){
// pag.passivate();
//}
}
} else {
if (limit != Integer.MAX_VALUE) {
sdbCtx.realPageSize = limit;
// activates paging (but not pagination)
pag.activate();
} else {
sdbCtx.realPageSize = 0;
}
}
} else {
// paginating so use the pagesize and don't passivate pageOption
// local limit is not taken into account
sdbCtx.realPageSize = pag.pageSize;
}
// if local offset has been set, uses it
if (offset != 0) {
off.activate();
off.offset = offset;
}
}
Aggregations