Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FMWK-470 Find by string not in doesn't return entities with null in the relevant field #757

Merged
merged 2 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
import static org.springframework.data.aerospike.util.FilterOperationRegexpBuilder.getStringEquals;
import static org.springframework.data.aerospike.util.Utils.ctxArrToString;
import static org.springframework.data.aerospike.util.Utils.getExpType;
import static org.springframework.data.aerospike.util.Utils.getValueExpOrFail;
import static org.springframework.data.aerospike.util.Utils.getExpValOrFail;

public enum FilterOperation {
/**
Expand Down Expand Up @@ -544,7 +544,10 @@ public Filter sIndexFilter(Map<QualifierKey, Object> qualifierMap) {
@Override
public Exp filterExp(Map<QualifierKey, Object> qualifierMap) {
Exp binIsNull = Exp.not(Exp.binExists(getBinName(qualifierMap)));
return Exp.or(binIsNull, getFilterExpMapValNotEqOrFail(qualifierMap, Exp::ne));
String errMsg = "MAP_VAL_NOTEQ_BY_KEY FilterExpression unsupported type: got " +
getKey(qualifierMap).getClass().getSimpleName();
Exp keyIsNull = mapKeysCountComparedToZero(qualifierMap, Exp::eq, getKey(qualifierMap), errMsg);
return Exp.or(binIsNull, keyIsNull, getFilterExpMapValNotEqOrFail(qualifierMap, Exp::ne));
}

@Override
Expand Down Expand Up @@ -1232,7 +1235,7 @@ public Exp filterExp(Map<QualifierKey, Object> qualifierMap) {
}
String errMsg = "COLLECTION_VAL_CONTAINING FilterExpression unsupported type: got " +
val.getClass().getSimpleName();
Exp value = getValueExpOrFail(val, errMsg);
Exp value = getExpValOrFail(val, errMsg);
return Exp.gt(
ListExp.getByValue(ListReturnType.COUNT, value, Exp.listBin(getBinName(qualifierMap))),
Exp.val(0));
Expand Down Expand Up @@ -1262,7 +1265,7 @@ public Exp filterExp(Map<QualifierKey, Object> qualifierMap) {
}
String errMsg = "COLLECTION_VAL_NOT_CONTAINING FilterExpression unsupported type: got " +
val.getClass().getSimpleName();
Exp value = getValueExpOrFail(val, errMsg);
Exp value = getExpValOrFail(val, errMsg);

Exp binIsNull = Exp.not(Exp.binExists(getBinName(qualifierMap)));
Exp listNotContaining = Exp.eq(
Expand Down Expand Up @@ -1575,7 +1578,7 @@ private static Exp processMetadataFieldNotIn(Map<QualifierKey, Object> qualifier
private static Exp getExpOrFail(Value value, String filterOpName) {
String errMsg = String.format("%s FilterExpression unsupported value type: got %s", filterOpName,
value.getClass().getSimpleName());
return getValueExpOrFail(value, errMsg);
return getExpValOrFail(value, errMsg);
}

private static Collection<?> getValueAsCollectionOrFail(Map<QualifierKey, Object> qualifierMap) {
Expand Down Expand Up @@ -1676,15 +1679,15 @@ private static Exp mapValBetweenByKey(Map<QualifierKey, Object> qualifierMap, CT
private static Exp mapKeysNotContain(Map<QualifierKey, Object> qualifierMap) {
String errMsg = "MAP_KEYS_NOT_CONTAIN FilterExpression unsupported type: got " +
getKey(qualifierMap).getClass().getSimpleName();
Exp mapKeysNotContain = mapKeysCount(qualifierMap, Exp::eq, errMsg);
Exp mapKeysNotContain = mapKeysCountComparedToZero(qualifierMap, Exp::eq, getValue(qualifierMap), errMsg);
Exp binDoesNotExist = Exp.not(Exp.binExists(getBinName(qualifierMap)));
return Exp.or(binDoesNotExist, mapKeysNotContain);
}

private static Exp mapKeysContain(Map<QualifierKey, Object> qualifierMap) {
String errMsg = "MAP_KEYS_CONTAIN FilterExpression unsupported type: got " +
getValue(qualifierMap).getClass().getSimpleName();
return mapKeysCount(qualifierMap, Exp::gt, errMsg);
return mapKeysCountComparedToZero(qualifierMap, Exp::gt, getValue(qualifierMap), errMsg);
}

private static Exp mapValuesNotContain(Map<QualifierKey, Object> qualifierMap) {
Expand All @@ -1702,41 +1705,30 @@ private static Exp mapValuesContain(Map<QualifierKey, Object> qualifierMap) {
}

// operator is Exp::gt to query for mapKeysContain or Exp::eq to query for mapKeysNotContain
private static Exp mapKeysCount(Map<QualifierKey, Object> qualifierMap, BinaryOperator<Exp> operator,
String errMsg) {
Exp key = getValueExpOrFail(getValue(qualifierMap), errMsg);
private static Exp mapKeysCountComparedToZero(Map<QualifierKey, Object> qualifierMap, BinaryOperator<Exp> operator,
Object mapKey, String errMsg) {
Exp key = getExpValOrFail(Value.get(mapKey), errMsg);
Exp map = Exp.mapBin(getBinName(qualifierMap));
Value mapBinKey = getKey(qualifierMap);

if (!mapBinKey.equals(Value.NullValue.INSTANCE)) {
// If map key != null it is a nested query (one level)
String err = "MAP_VAL_NOT_CONTAINING_BY_KEY FilterExpression unsupported type: got " +
mapBinKey.getClass().getSimpleName();
Exp nestedMapKey = getValueExpOrFail(mapBinKey, err);

// locate a Map within its parent bin
map = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.MAP, nestedMapKey,
Exp.mapBin(getBinName(qualifierMap)));
}
CTX[] ctxArr = getCtxArr(qualifierMap);

return operator.apply(
MapExp.getByKey(MapReturnType.COUNT, Exp.Type.INT, key, map),
MapExp.getByKey(MapReturnType.COUNT, Exp.Type.INT, key, map, ctxArr),
Exp.val(0));
}

// operator is Exp::gt to query for mapValuesContain or Exp::eq to query for mapValuesNotContain
private static Exp mapValuesCountComparedToZero(Map<QualifierKey, Object> qualifierMap,
BinaryOperator<Exp> operator,
String errMsg) {
Exp value = getValueExpOrFail(getValue(qualifierMap), errMsg);
Exp value = getExpValOrFail(getValue(qualifierMap), errMsg);
Exp map = Exp.mapBin(getBinName(qualifierMap));
Value mapBinKey = getKey(qualifierMap);

if (!mapBinKey.equals(Value.NullValue.INSTANCE)) {
// If map key != null it means a nested query (one level)
String err = "MAP_VAL_NOT_CONTAINING_BY_KEY FilterExpression unsupported type: got " +
mapBinKey.getClass().getSimpleName();
Exp nestedMapKey = getValueExpOrFail(mapBinKey, err);
Exp nestedMapKey = getExpValOrFail(mapBinKey, err);

// if it is a nested query we need to locate a Map within its parent bin
map = MapExp.getByKey(MapReturnType.VALUE, Exp.Type.MAP, nestedMapKey,
Expand Down Expand Up @@ -1853,11 +1845,11 @@ private static Exp getMapValEq(Map<QualifierKey, Object> qualifierMap, Exp.Type
Exp bin = getBin(getBinName(qualifierMap), getBinType(qualifierMap), "MAP_VAL_EQ");
if (ctxArr != null && ctxArr.length > 0) {
return MapExp.getByKey(MapReturnType.VALUE, expType,
getValueExpOrFail(getKey(qualifierMap), "MAP_VAL_EQ: unsupported type"),
getExpValOrFail(getKey(qualifierMap), "MAP_VAL_EQ: unsupported type"),
bin, ctxArr);
} else {
return MapExp.getByKey(MapReturnType.VALUE, expType,
getValueExpOrFail(getKey(qualifierMap), "MAP_VAL_EQ: unsupported type"),
getExpValOrFail(getKey(qualifierMap), "MAP_VAL_EQ: unsupported type"),
bin);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.util.TypeInformation;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -262,11 +263,18 @@ private Qualifier processMapTwoParams(QueryQualifierBuilder qb, Part part, List<
private Qualifier processMapContaining(QueryQualifierBuilder qb, Part part, String fieldName, FilterOperation keysOp,
FilterOperation valuesOp, FilterOperation byKeyOp) {
FilterOperation op = byKeyOp;
List<String> dotPath = new ArrayList<>();
dotPath.add(part.getProperty().toDotPath());
if (queryParameters.get(0) instanceof AerospikeQueryCriterion queryCriterion) {
switch (queryCriterion) {
case KEY -> {
op = keysOp;
setQualifierBuilderValue(qb, queryParameters.get(1));
// the actual value is irrelevant here,
// the last dotPath element is discarded within AerospikeQueryCreatorUtils.getCtxFromDotPathArray(),
// the elements except the first and the last are converted to CTX,
// the key object (can contain '.') is transferred separately via qualifier builder value
dotPath.add("mapKeyPlaceholder");
}
case VALUE -> {
op = valuesOp;
Expand All @@ -275,7 +283,7 @@ private Qualifier processMapContaining(QueryQualifierBuilder qb, Part part, Stri
default -> throw new UnsupportedOperationException("Unsupported parameter: " + queryCriterion);
}
}
return setQualifier(qb, fieldName, op, part, null, versionSupport);
return setQualifier(qb, fieldName, op, part, dotPath, versionSupport);
}

private Qualifier processMapOtherThanContaining(QueryQualifierBuilder qb, Part part, List<Object> queryParameters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public static long getObjectsCount(Node node, String namespace, String setName)
* @param errMsg Error message to use
* @return Exp instance
*/
public static Exp getValueExpOrFail(Value value, String errMsg) {
public static Exp getExpValOrFail(Value value, String errMsg) {
return switch (value.getType()) {
case INTEGER -> Exp.val(value.toLong());
case BOOL -> Exp.val((Boolean) value.getObject());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ void findByNestedSimplePropertyNotIn_String() {
assertThat(carter.getAddress().getZipCode()).isEqualTo("C0124");
assertThat(dave.getAddress().getZipCode()).isEqualTo("C0123");
// find all records where address' zipCode is not C0123 or C0125, and all without address.zipCode
assertThat(repository.findByAddressZipCodeNotIn(List.of("C0123", "C0125"))).doesNotContain(dave);
assertThat(repository.findByAddressZipCodeNotIn(List.of("C0123", "C0125")))
.containsOnly(donny, oliver, alicia, boyd, stefan, leroi, leroi2, matias, douglas, carter);
}

@Test
Expand Down
Loading