Skip to content

Commit

Permalink
add support for CTX parameter in custom queries, refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
agrgr committed May 5, 2024
1 parent 895db95 commit 91ac05f
Show file tree
Hide file tree
Showing 45 changed files with 905 additions and 591 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,16 @@ static void verifyUnsortedWithOffset(Sort sort, long offset) {
static Predicate<KeyRecord> getDistinctPredicate(Query query) {
Predicate<KeyRecord> distinctPredicate;
if (query != null && query.isDistinct()) {
List<String> dotPathList = query.getCriteriaObject().getDotPath();
if (dotPathList != null && dotPathList.size() > 0 && dotPathList.get(0) != null) {
String dotPathString = String.join(",", query.getCriteriaObject().getDotPath());
List<String> ctxPathList = query.getCriteriaObject().getCtxPath();
if (ctxPathList != null && ctxPathList.size() > 0 && ctxPathList.get(0) != null) {
String dotPathString = String.join(",", query.getCriteriaObject().getCtxPath());
throw new UnsupportedOperationException("DISTINCT queries are currently supported only for the first " +
"level objects, got a query for " + dotPathString);
}

final Set<Object> distinctValues = ConcurrentHashMap.newKeySet();
distinctPredicate = kr -> {
final String distinctField = query.getCriteriaObject().getField();
final String distinctField = query.getCriteriaObject().getBinName();
if (kr.record == null || kr.record.bins == null) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package org.springframework.data.aerospike.index;

import com.aerospike.client.Value;
import com.aerospike.client.cdt.CTX;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
Expand Down Expand Up @@ -92,8 +91,11 @@ private CTX[] toCtxArray(String ctxString) {
if (!StringUtils.hasLength(ctxString)) return null;

String[] ctxTokens = ctxString.split("\\.");
CTX[] ctxArr = Arrays.stream(ctxTokens).filter(not(String::isEmpty))
.map(this::toCtx).filter(Objects::nonNull).toArray(CTX[]::new);
CTX[] ctxArr = Arrays.stream(ctxTokens)
.filter(not(String::isEmpty))
.map(AerospikeIndexResolverUtils::toCtx)
.filter(Objects::nonNull)
.toArray(CTX[]::new);

if (ctxTokens.length != ctxArr.length) {
throw new IllegalArgumentException("@Indexed annotation '" + ctxString + "' contains empty context");
Expand All @@ -102,10 +104,10 @@ private CTX[] toCtxArray(String ctxString) {
return ctxArr;
}

private enum CtxType {
protected enum CtxType {
MAP('}'), LIST(']');

private final char closingChar;
final char closingChar;

CtxType(char closingChar) {
this.closingChar = closingChar;
Expand All @@ -116,95 +118,4 @@ public String toString() {
return name().toLowerCase(); // when mentioned in exceptions
}
}

private CTX toCtx(String singleCtx) {
switch (singleCtx.charAt(0)) {
case '{' -> {
return processSingleCtx(singleCtx, CtxType.MAP);
}
case '[' -> {
return processSingleCtx(singleCtx, CtxType.LIST);
}
default -> {
Object res = isInDoubleOrSingleQuotes(singleCtx) ? singleCtx.substring(1, singleCtx.length() - 1) :
parseIntOrReturnStr(singleCtx);
return CTX.mapKey(Value.get(res));
}
}
}

private CTX processSingleCtx(String singleCtx, CtxType ctxType) {
int length = singleCtx.length();
if (length < 3) {
throw new IllegalArgumentException("@Indexed annotation: context string '" + singleCtx +
"' has no content");
}
if (singleCtx.charAt(length - 1) != ctxType.closingChar) {
throw new IllegalArgumentException("@Indexed annotation: brackets mismatch, " +
"expecting '" + ctxType.closingChar + "', got '" + singleCtx.charAt(length - 1) + "' instead");
}

CTX result;
String substring = singleCtx.substring(2, length - 1);
if (singleCtx.charAt(1) == '=' && length > 3) {
result = processCtxValue(substring, ctxType);
} else if (singleCtx.charAt(1) == '#' && length > 3) {
result = processCtxRank(substring, ctxType);
} else {
result = processCtxIndex(singleCtx, length, ctxType);
}

return result;
}

private CTX processCtxValue(String substring, CtxType ctxType) {
Object result = isInDoubleOrSingleQuotes(substring) ? substring.substring(1, substring.length() - 1) :
parseIntOrReturnStr(substring);
return switch (ctxType) {
case MAP -> CTX.mapValue(Value.get(result));
case LIST -> CTX.listValue(Value.get(result));
};
}

private CTX processCtxRank(String substring, CtxType ctxType) {
int rank = parseIntOrFail(substring, ctxType, "rank");
return switch (ctxType) {
case MAP -> CTX.mapRank(rank);
case LIST -> CTX.listRank(rank);
};
}

private CTX processCtxIndex(String singleCtx, int length, CtxType ctxType) {
String substring = singleCtx.substring(1, length - 1);
int idx = parseIntOrFail(substring, ctxType, "index");
return switch (ctxType) {
case MAP -> CTX.mapIndex(idx);
case LIST -> CTX.listIndex(idx);
};
}

private int parseIntOrFail(String substring, CtxType ctxType, String parameterName) {
try {
return Integer.parseInt(substring);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("@Indexed annotation " + ctxType + " " + parameterName + ": " +
"expecting only integer values, got '" + substring + "' instead");
}
}

private static Object parseIntOrReturnStr(String str) {
Object res;
try {
res = Integer.parseInt(str);
} catch (NumberFormatException e) {
res = str;
}

return res;
}

private static boolean isInDoubleOrSingleQuotes(String str) {
return str.length() > 2 && (str.charAt(0) == '"' || str.charAt(0) == '\'')
&& (str.charAt(str.length() - 1) == '"' || str.charAt(str.length() - 1) == '\'');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package org.springframework.data.aerospike.index;

import com.aerospike.client.Value;
import com.aerospike.client.cdt.CTX;

public class AerospikeIndexResolverUtils {

public static CTX toCtx(String singleCtx) {
switch (singleCtx.charAt(0)) {
case '{' -> {
return processSingleCtx(singleCtx, AerospikeIndexResolver.CtxType.MAP);
}
case '[' -> {
return processSingleCtx(singleCtx, AerospikeIndexResolver.CtxType.LIST);
}
default -> {
Object res = isInDoubleOrSingleQuotes(singleCtx) ? singleCtx.substring(1, singleCtx.length() - 1) :
parseIntOrReturnStr(singleCtx);
return CTX.mapKey(Value.get(res));
}
}
}

private static CTX processSingleCtx(String singleCtx, AerospikeIndexResolver.CtxType ctxType) {
int length = singleCtx.length();
if (length < 3) {
throw new IllegalArgumentException("@Indexed annotation: context string '" + singleCtx +
"' has no content");
}
if (singleCtx.charAt(length - 1) != ctxType.closingChar) {
throw new IllegalArgumentException("@Indexed annotation: brackets mismatch, " +
"expecting '" + ctxType.closingChar + "', got '" + singleCtx.charAt(length - 1) + "' instead");
}

CTX result;
String substring = singleCtx.substring(2, length - 1);
if (singleCtx.charAt(1) == '=' && length > 3) {
result = processCtxValue(substring, ctxType);
} else if (singleCtx.charAt(1) == '#' && length > 3) {
result = processCtxRank(substring, ctxType);
} else {
result = processCtxIndex(singleCtx, length, ctxType);
}

return result;
}

private static CTX processCtxValue(String substring, AerospikeIndexResolver.CtxType ctxType) {
Object result = isInDoubleOrSingleQuotes(substring) ? substring.substring(1, substring.length() - 1) :
parseIntOrReturnStr(substring);
return switch (ctxType) {
case MAP -> CTX.mapValue(Value.get(result));
case LIST -> CTX.listValue(Value.get(result));
};
}

private static CTX processCtxRank(String substring, AerospikeIndexResolver.CtxType ctxType) {
int rank = parseIntOrFail(substring, ctxType, "rank");
return switch (ctxType) {
case MAP -> CTX.mapRank(rank);
case LIST -> CTX.listRank(rank);
};
}

private static CTX processCtxIndex(String singleCtx, int length, AerospikeIndexResolver.CtxType ctxType) {
String substring = singleCtx.substring(1, length - 1);
int idx = parseIntOrFail(substring, ctxType, "index");
return switch (ctxType) {
case MAP -> CTX.mapIndex(idx);
case LIST -> CTX.listIndex(idx);
};
}

private static int parseIntOrFail(String substring, AerospikeIndexResolver.CtxType ctxType, String parameterName) {
try {
return Integer.parseInt(substring);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("@Indexed annotation " + ctxType + " " + parameterName + ": " +
"expecting only integer values, got '" + substring + "' instead");
}
}

private static Object parseIntOrReturnStr(String str) {
Object res;
try {
res = Integer.parseInt(str);
} catch (NumberFormatException e) {
res = str;
}

return res;
}

private static boolean isInDoubleOrSingleQuotes(String str) {
return str.length() > 2 && (str.charAt(0) == '"' || str.charAt(0) == '\'')
&& (str.charAt(str.length() - 1) == '"' || str.charAt(str.length() - 1) == '\'');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class ExpiryQualifier extends Qualifier {

public ExpiryQualifier(FilterOperation op, Value value) {
super(Qualifier.builder()
.setField(QueryEngine.Meta.EXPIRATION.toString())
.setBinName(QueryEngine.Meta.EXPIRATION.toString())
.setFilterOperation(op)
.setValue(value)
);
Expand Down
Loading

0 comments on commit 91ac05f

Please sign in to comment.