Skip to content

Commit

Permalink
add support for findBy...IsNull/isNotNull/Exists, tests coverage, ent…
Browse files Browse the repository at this point in the history
…ries in query-methods documentation
  • Loading branch information
agrgr committed Jul 30, 2023
1 parent e589cc2 commit 569b064
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 35 deletions.
32 changes: 30 additions & 2 deletions src/main/asciidoc/reference/query-methods.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,39 @@ include::{spring-data-commons-docs}/query-by-example.adoc[leveloffset=+1]
|Not In |findByLastNameNotIn(Collection<String>) |...where x.lastName not in ?
|IgnoreCase |findByLastNameIgnoreCase |...where UPPER(x.lastName) = UPPER(?)
|Exists
IsNotNull
|findByAddressExists()
findByParameterIsNotNull()
findByParameterFieldExists()
|...where address exists and != null
...where parameter exists and != null
...where parameter.field exists and != null
("Exists" and "IsNotNull" represent the same functionality and can be used interchangeably as bins/fields exist only when not set to null)
|IsNull |findByParameterIsNull()
findByParameterFieldIsNull()
|...where parameter = null
...where parameter.field = null
("IsNull" in fact means that the bin/field does not exist)
|True |findByEnabledTrue() |...where x.enabled = true
|False |findByOptOutFalse() |...where x.optOut = false
|IgnoreCase |findByLastNameIgnoreCase |...where UPPER(x.lastName) = UPPER(?)
|===
An example of an interface with several query methods is:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,10 @@ protected Map<String, Object> convertMap(final Map<Object, Object> source, final
return source.entrySet().stream().collect(TreeMap::new, (m, e) -> {
Object key = e.getKey();
Object value = e.getValue();
if (key == null) {
throw new UnsupportedOperationException("Key of a map cannot be null");
}

if (!conversions.isSimpleType(key.getClass())) {
throw new MappingException("Cannot use a complex object as a key value.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.aerospike.client.cdt.CTX;
import com.aerospike.client.cdt.ListReturnType;
import com.aerospike.client.cdt.MapReturnType;
import com.aerospike.client.command.ParticleType;
import com.aerospike.client.exp.Exp;
import com.aerospike.client.exp.ListExp;
import com.aerospike.client.exp.MapExp;
Expand All @@ -29,6 +30,7 @@
import static com.aerospike.client.command.ParticleType.JBLOB;
import static com.aerospike.client.command.ParticleType.LIST;
import static com.aerospike.client.command.ParticleType.MAP;
import static com.aerospike.client.command.ParticleType.NULL;
import static com.aerospike.client.command.ParticleType.STRING;
import static org.springframework.data.aerospike.query.Qualifier.CONVERTER;
import static org.springframework.data.aerospike.query.Qualifier.DOT_PATH;
Expand Down Expand Up @@ -157,9 +159,8 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
}
case BOOL -> Exp.eq(Exp.boolBin(getField(qualifierMap)), Exp.val((Boolean) value.getObject()));
case JBLOB -> getFilterExp(getConverter(qualifierMap), value, getField(qualifierMap), Exp::eq);
case MAP ->
getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::eq,
Exp::mapBin);
case MAP -> getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::eq,
Exp::mapBin);
case LIST ->
getFilterExp(Exp.val((List<?>) value.getObject()), getField(qualifierMap), Exp::eq, Exp::listBin);
default -> throw new IllegalArgumentException("EQ FilterExpression unsupported particle type: " +
Expand Down Expand Up @@ -207,9 +208,8 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
yield Exp.or(Exp.not(Exp.binExists(getField(qualifierMap))), ne);
}
case JBLOB -> getFilterExp(getConverter(qualifierMap), value, getField(qualifierMap), Exp::ne);
case MAP ->
getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::ne,
Exp::mapBin);
case MAP -> getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::ne,
Exp::mapBin);
case LIST ->
getFilterExp(Exp.val((List<?>) value.getObject()), getField(qualifierMap), Exp::ne, Exp::listBin);
default -> throw new IllegalArgumentException("NOTEQ FilterExpression unsupported particle type: " +
Expand All @@ -231,9 +231,8 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
case STRING ->
Exp.gt(Exp.stringBin(getField(qualifierMap)), Exp.val(getValue1(qualifierMap).toString()));
case JBLOB -> getFilterExp(getConverter(qualifierMap), value, getField(qualifierMap), Exp::gt);
case MAP ->
getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::gt,
Exp::mapBin);
case MAP -> getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::gt,
Exp::mapBin);
case LIST ->
getFilterExp(Exp.val((List<?>) value.getObject()), getField(qualifierMap), Exp::gt, Exp::listBin);
default -> throw new IllegalArgumentException("GT FilterExpression unsupported particle type: " +
Expand All @@ -260,9 +259,8 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
case STRING ->
Exp.ge(Exp.stringBin(getField(qualifierMap)), Exp.val(getValue1(qualifierMap).toString()));
case JBLOB -> getFilterExp(getConverter(qualifierMap), value, getField(qualifierMap), Exp::ge);
case MAP ->
getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::ge,
Exp::mapBin);
case MAP -> getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::ge,
Exp::mapBin);
case LIST ->
getFilterExp(Exp.val((List<?>) value.getObject()), getField(qualifierMap), Exp::ge, Exp::listBin);
default -> throw new IllegalArgumentException("GTEQ FilterExpression unsupported particle type: " +
Expand All @@ -287,9 +285,8 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
case STRING ->
Exp.lt(Exp.stringBin(getField(qualifierMap)), Exp.val(getValue1(qualifierMap).toString()));
case JBLOB -> getFilterExp(getConverter(qualifierMap), value, getField(qualifierMap), Exp::lt);
case MAP ->
getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::lt,
Exp::mapBin);
case MAP -> getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::lt,
Exp::mapBin);
case LIST ->
getFilterExp(Exp.val((List<?>) value.getObject()), getField(qualifierMap), Exp::lt, Exp::listBin);
default -> throw new IllegalArgumentException("LT FilterExpression unsupported particle type: " +
Expand All @@ -315,9 +312,8 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
case STRING ->
Exp.le(Exp.stringBin(getField(qualifierMap)), Exp.val(getValue1(qualifierMap).toString()));
case JBLOB -> getFilterExp(getConverter(qualifierMap), value, getField(qualifierMap), Exp::le);
case MAP ->
getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::le,
Exp::mapBin);
case MAP -> getFilterExp(Exp.val(getConvertedMap(qualifierMap, value)), getField(qualifierMap), Exp::le,
Exp::mapBin);
case LIST ->
getFilterExp(Exp.val((List<?>) value.getObject()), getField(qualifierMap), Exp::le, Exp::listBin);
default -> throw new IllegalArgumentException("LTEQ FilterExpression unsupported particle type: " +
Expand Down Expand Up @@ -487,7 +483,11 @@ public Filter sIndexFilter(Map<String, Object> qualifierMap) {
MAP_VAL_NOTEQ_BY_KEY {
@Override
public Exp filterExp(Map<String, Object> qualifierMap) {
return getFilterExpMapValNotEqOrFail(qualifierMap, Exp::ne);
if (getValue1(qualifierMap) instanceof Value.NullValue || getValue1(qualifierMap).getObject() != null) {
return findNotNullByMapKey(qualifierMap);
} else {
return getFilterExpMapValNotEqOrFail(qualifierMap, Exp::ne);
}
}

@Override
Expand Down Expand Up @@ -778,6 +778,28 @@ public Filter sIndexFilter(Map<String, Object> qualifierMap) {
return null; // String secondary index does not support "contains" queries
}
},
MAP_VAL_NOT_NULL_BY_KEY {
@Override
public Exp filterExp(Map<String, Object> qualifierMap) {
return findNotNullByMapKey(qualifierMap);
}

@Override
public Filter sIndexFilter(Map<String, Object> qualifierMap) {
return null;
}
},
MAP_VAL_IS_NULL_BY_KEY {
@Override
public Exp filterExp(Map<String, Object> qualifierMap) {
return getMapValEqOrFail(qualifierMap, Exp::eq, "MAP_VAL_IS_NULL_BY_KEY");
}

@Override
public Filter sIndexFilter(Map<String, Object> qualifierMap) {
return null;
}
},
MAP_KEYS_CONTAIN {
@Override
public Exp filterExp(Map<String, Object> qualifierMap) {
Expand Down Expand Up @@ -839,6 +861,7 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
case JBLOB -> getConvertedValue1Exp(qualifierMap);
case LIST -> Exp.val((List<?>) getValue1(qualifierMap).getObject());
case MAP -> Exp.val(getConvertedMap(qualifierMap, FilterOperation::getValue1));
case NULL -> Exp.nil();
default -> throw new IllegalArgumentException(
"MAP_VALUES_CONTAIN FilterExpression unsupported type: got " +
getValue1(qualifierMap).getClass().getSimpleName());
Expand All @@ -863,6 +886,7 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
case JBLOB -> getConvertedValue1Exp(qualifierMap);
case LIST -> Exp.val((List<?>) getValue1(qualifierMap).getObject());
case MAP -> Exp.val(getConvertedMap(qualifierMap, FilterOperation::getValue1));
case NULL -> Exp.nil();
default -> throw new IllegalArgumentException(
"MAP_VALUES_CONTAIN FilterExpression unsupported type: got " +
getValue1(qualifierMap).getClass().getSimpleName());
Expand Down Expand Up @@ -976,6 +1000,7 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
case JBLOB -> getConvertedValue1Exp(qualifierMap);
case LIST -> Exp.val((List<?>) getValue1(qualifierMap).getObject());
case MAP -> Exp.val(getConvertedMap(qualifierMap, FilterOperation::getValue1));
case ParticleType.NULL -> Exp.nil();
default -> throw new IllegalArgumentException(
"LIST_VAL_CONTAINING FilterExpression unsupported type: got " +
getValue1(qualifierMap).getClass().getSimpleName());
Expand Down Expand Up @@ -1011,6 +1036,7 @@ public Exp filterExp(Map<String, Object> qualifierMap) {
case JBLOB -> getConvertedValue1Exp(qualifierMap);
case LIST -> Exp.val((List<?>) getValue1(qualifierMap).getObject());
case MAP -> Exp.val(getConvertedMap(qualifierMap, FilterOperation::getValue1));
case ParticleType.NULL -> Exp.nil();
default -> throw new IllegalArgumentException(
"LIST_VAL_CONTAINING FilterExpression unsupported type: got " +
getValue1(qualifierMap).getClass().getSimpleName());
Expand Down Expand Up @@ -1218,18 +1244,46 @@ public Filter sIndexFilter(Map<String, Object> qualifierMap) {
return Filter.range(getField(qualifierMap), IndexCollectionType.LIST, Long.MIN_VALUE,
getValue1(qualifierMap).toLong());
}
}, EXISTS {
}, IS_NOT_NULL {
@Override
public Exp filterExp(Map<String, Object> qualifierMap) {
return Exp.binExists(getField(qualifierMap));
}

@Override
public Filter sIndexFilter(Map<String, Object> qualifierMap) {
return null;
}
}, IS_NULL {
@Override
public Exp filterExp(Map<String, Object> qualifierMap) {
return Exp.not(Exp.binExists(getField(qualifierMap))); // with value set to null a bin becomes non-existing
}

@Override
public Filter sIndexFilter(Map<String, Object> qualifierMap) {
return null;
}
}, NOT_NULL {
@Override
public Exp filterExp(Map<String, Object> qualifierMap) {
return Exp.binExists(getField(qualifierMap)); // if a bin exists its value is not null
}

@Override
public Filter sIndexFilter(Map<String, Object> qualifierMap) {
return null;
}
};

private static Exp findNotNullByMapKey(Map<String, Object> qualifierMap) {
String key = getValue2(qualifierMap).toString();
return Exp.gt(
MapExp.getByKey(MapReturnType.COUNT, Exp.Type.INT, Exp.val(key),
Exp.mapBin(getField(qualifierMap))),
Exp.val(0));
}

private static Exp getConvertedValue1Exp(Map<String, Object> qualifierMap) {
Object convertedValue = getConvertedValue(qualifierMap, FilterOperation::getValue1);
Exp exp;
Expand Down Expand Up @@ -1364,7 +1418,11 @@ yield getMapValEqExp(qualifierMap, expType, convertedValue, dotPathArr, operator
}
case LIST -> getMapValEqExp(qualifierMap, Exp.Type.LIST, value1.getObject(), dotPathArr, operator,
useCtx);
case MAP -> getMapValEqExp(qualifierMap, Exp.Type.MAP, Exp.val(getConvertedMap(qualifierMap, value1)), dotPathArr, operator,
case MAP ->
getMapValEqExp(qualifierMap, Exp.Type.MAP, Exp.val(getConvertedMap(qualifierMap, value1)), dotPathArr
, operator,
useCtx);
case ParticleType.NULL -> getMapValEqExp(qualifierMap, Exp.Type.NIL, value1, dotPathArr, operator,
useCtx);
default -> throw new IllegalArgumentException(
opName + " FilterExpression unsupported type: " + value1.getClass().getSimpleName());
Expand Down Expand Up @@ -1487,6 +1545,8 @@ private static Exp toExp(Object value) {
res = Exp.val((byte[]) value);
} else if (value instanceof Calendar) {
res = Exp.val((Calendar) value);
} else if (value instanceof Value.NullValue) {
res = Exp.nil();
} else {
throw new IllegalArgumentException("Unsupported type for converting: " + value.getClass()
.getCanonicalName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,10 @@ private AerospikeCriteria create(Part part, AerospikePersistentProperty property
case NEGATING_SIMPLE_PROPERTY -> getCriteria(part, property, v1, null, parameters, FilterOperation.NOTEQ);
case IN -> getCriteria(part, property, v1, null, parameters, FilterOperation.IN);
case NOT_IN -> getCriteria(part, property, v1, null, parameters, FilterOperation.NOT_IN);
case TRUE -> getCriteria(part, property, true, null, parameters, FilterOperation.EQ);
case FALSE -> getCriteria(part, property, false, null, parameters, FilterOperation.EQ);
case EXISTS -> getCriteria(part, property, false, null, parameters, FilterOperation.EXISTS);
case IS_NOT_NULL -> getCriteria(part, property, false, null, parameters, FilterOperation.EXISTS);
case TRUE -> getCriteria(part, property, true, null, parameters, FilterOperation.EQ);
case FALSE -> getCriteria(part, property, false, null, parameters, FilterOperation.EQ);
case EXISTS, IS_NOT_NULL -> getCriteria(part, property, null, null, parameters, FilterOperation.IS_NOT_NULL);
case IS_NULL -> getCriteria(part, property, null, null, parameters, FilterOperation.IS_NULL);
default -> throw new IllegalArgumentException("Unsupported keyword '" + part.getType() + "'");
};
}
Expand Down
Loading

0 comments on commit 569b064

Please sign in to comment.