diff --git a/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java b/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java index 4ec19c688..a341d9c5d 100644 --- a/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java +++ b/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java @@ -26,7 +26,6 @@ import com.aerospike.client.query.ResultSet; import org.springframework.data.aerospike.core.model.GroupedEntities; import org.springframework.data.aerospike.core.model.GroupedKeys; -import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.domain.Sort; import org.springframework.data.mapping.context.MappingContext; @@ -712,11 +711,10 @@ public interface AerospikeOperations { * @param id The id of the document to find. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. * @param targetClass The class to map the document to. - * @param qualifiers {@link Qualifier}s provided to build a filter Expression for the query. Optional argument. + * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). * @return The document from Aerospike, returned document will be mapped to targetClass's type. */ - Object findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, - Qualifier... qualifiers); + Object findByIdUsingQuery(Object id, Class entityClass, Class targetClass, @Nullable Query query); /** * Find document by providing id with a given set name. @@ -728,11 +726,11 @@ Object findByIdUsingQualifiers(Object id, Class entityClass, Class * {@literal null}. * @param targetClass The class to map the document to. * @param setName Set name to find the document from. - * @param qualifiers {@link Qualifier}s provided to build a filter Expression for the query. Optional argument. + * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). * @return The document from Aerospike, returned document will be mapped to targetClass's type. */ - Object findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, String setName, - Qualifier... qualifiers); + Object findByIdUsingQuery(Object id, Class entityClass, Class targetClass, String setName, + @Nullable Query query); /** * Find documents by providing multiple ids, set name will be determined by the given entityClass. @@ -742,12 +740,12 @@ Object findByIdUsingQualifiers(Object id, Class entityClass, Class * @param ids The ids of the documents to find. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. * @param targetClass The class to map the document to. - * @param qualifiers {@link Qualifier}s provided to build a filter Expression for the query. Optional argument. + * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document * exists, an empty list is returned. */ - List findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, - Qualifier... qualifiers); + List findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, + @Nullable Query query); /** * Find documents by providing multiple ids with a given set name. @@ -759,17 +757,17 @@ List findByIdsUsingQualifiers(Collection ids, Class entityClass, * {@literal null}. * @param targetClass The class to map the document to. * @param setName Set name to find the document from. - * @param qualifiers {@link Qualifier}s provided to build a filter Expression for the query. Optional argument. + * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document * exists, an empty list is returned. */ - List findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, - String setName, Qualifier... qualifiers); + List findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, String setName, + @Nullable Query query); /** * Find documents in the given entityClass's set using a query and map them to the given class type. * - * @param query The query to filter results. Must not be {@literal null}. + * @param query The {@link Query} to filter results. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from and to map the documents to. Must not be * {@literal null}. * @return A Stream of matching documents, returned documents will be mapped to entityClass's type. @@ -779,7 +777,7 @@ List findByIdsUsingQualifiers(Collection ids, Class entityClass, /** * Find documents in the given entityClass's set using a query and map them to the given target class type. * - * @param query The query to filter results. Must not be {@literal null}. + * @param query The {@link Query} to filter results. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. * @param targetClass The class to map the document to. Must not be {@literal null}. * @return A Stream of matching documents, returned documents will be mapped to targetClass's type. @@ -789,58 +787,13 @@ List findByIdsUsingQualifiers(Collection ids, Class entityClass, /** * Find documents in the given set using a query and map them to the given target class type. * - * @param query The query to filter results. Must not be {@literal null}. + * @param query The {@link Query} to filter results. Must not be {@literal null}. * @param setName Set name to find the documents in. * @param targetClass The class to map the document to. Must not be {@literal null}. * @return A Stream of matching documents, returned documents will be mapped to targetClass's type. */ Stream find(Query query, Class targetClass, String setName); - /** - * Find all documents in the given entityClass's set using provided {@link Qualifier}. - * - * @param entityClass The class to extract the Aerospike set from and to map the entity to. Must not be - * {@literal null}. - * @param filter Secondary index filter. - * @param qualifier Qualifier to build filter expressions from. Can contain other qualifiers. Must not be - * {@literal null}. If filter param is null and qualifier has - * {@link Qualifier#getExcludeFilter()} == false, secondary index filter is built based on the - * first processed qualifier. - * @return Stream of entities. - */ - Stream findUsingQualifier(Class entityClass, @Nullable Filter filter, Qualifier qualifier); - - /** - * Find all documents in the given entityClass's set using provided {@link Qualifier}. - * - * @param entityClass The class to extract the Aerospike set from and to map the entity to. Must not be - * {@literal null}. - * @param targetClass The class to map the entity to. Must not be {@literal null}. - * @param filter Secondary index filter. - * @param qualifier Qualifier to build filter expressions from. Can contain other qualifiers. Must not be - * {@literal null}. If filter param is null and qualifier has - * {@link Qualifier#getExcludeFilter()} == false, secondary index filter is built based on the - * first processed qualifier. - * @return Stream of entities. - */ - Stream findUsingQualifier(Class entityClass, Class targetClass, @Nullable Filter filter, - Qualifier qualifier); - - /** - * Find all documents in the given set using provided {@link Qualifier}. - * - * @param targetClass The class to map the entity to. Must not be {@literal null}. - * @param setName Set name to find the documents in. - * @param filter Secondary index filter. - * @param qualifier Qualifier to build filter expressions from. Can contain other qualifiers. Must not be - * {@literal null}. If filter param is null and qualifier has - * {@link Qualifier#getExcludeFilter()} == false, secondary index filter is built based on the - * first processed qualifier. - * @return Stream of entities. - */ - Stream findUsingQualifier(Class targetClass, String setName, @Nullable Filter filter, - Qualifier qualifier); - /** * Find all documents in the given entityClass's set and map them to the given class type. * diff --git a/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java b/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java index eb110fbe5..6374eee22 100644 --- a/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java +++ b/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java @@ -72,8 +72,7 @@ import static org.springframework.data.aerospike.core.TemplateUtils.excludeIdQualifier; import static org.springframework.data.aerospike.core.TemplateUtils.getIdValue; import static org.springframework.data.aerospike.query.QualifierUtils.getOneIdQualifier; -import static org.springframework.data.aerospike.query.QualifierUtils.validateQualifiers; -import static org.springframework.data.aerospike.utility.Utils.allArrayElementsAreNull; +import static org.springframework.data.aerospike.query.QualifierUtils.queryCriteriaIsNotNull; /** * Primary implementation of {@link AerospikeOperations}. @@ -664,25 +663,25 @@ public S findById(Object id, Class entityClass, Class targetClass) public S findById(Object id, Class entityClass, Class targetClass, String setName) { Assert.notNull(id, "Id must not be null!"); Assert.notNull(entityClass, "Class must not be null!"); - return (S) findByIdUsingQualifiers(id, entityClass, targetClass, setName); + return (S) findByIdUsingQuery(id, entityClass, targetClass, setName, null); } - private Record getRecord(AerospikePersistentEntity entity, Key key, Qualifier... qualifiers) { + private Record getRecord(AerospikePersistentEntity entity, Key key, Query query) { Record aeroRecord; if (entity.isTouchOnRead()) { Assert.state(!entity.hasExpirationProperty(), "Touch on read is not supported for expiration property"); - aeroRecord = getAndTouch(key, entity.getExpiration(), null); + aeroRecord = getAndTouch(key, entity.getExpiration(), null, null); } else { - Policy policy = getPolicyFilterExp(qualifiers); + Policy policy = getPolicyFilterExp(query); aeroRecord = getAerospikeClient().get(policy, key); } return aeroRecord; } - private BatchPolicy getBatchPolicyFilterExp(Qualifier[] qualifiers) { - if (qualifiers != null && qualifiers.length > 0) { + private BatchPolicy getBatchPolicyFilterExp(Query query) { + if (queryCriteriaIsNotNull(query)) { BatchPolicy policy = new BatchPolicy(getAerospikeClient().getBatchPolicyDefault()); - policy.filterExp = queryEngine.getFilterExpressionsBuilder().build(qualifiers); + policy.filterExp = queryEngine.getFilterExpressionsBuilder().build(query); return policy; } return null; @@ -695,14 +694,14 @@ private Key[] getKeys(Collection ids, String setName) { } private Object getRecordMapToTargetClass(AerospikePersistentEntity entity, Key key, Class targetClass, - Qualifier... qualifiers) { + Query query) { Record aeroRecord; String[] binNames = getBinNamesFromTargetClass(targetClass); if (entity.isTouchOnRead()) { Assert.state(!entity.hasExpirationProperty(), "Touch on read is not supported for expiration property"); - aeroRecord = getAndTouch(key, entity.getExpiration(), binNames, qualifiers); + aeroRecord = getAndTouch(key, entity.getExpiration(), binNames, query); } else { - Policy policy = getPolicyFilterExp(qualifiers); + Policy policy = getPolicyFilterExp(query); aeroRecord = getAerospikeClient().get(policy, key, binNames); } return mapToEntity(key, targetClass, aeroRecord); @@ -719,21 +718,21 @@ private String[] getBinNamesFromTargetClass(Class targetClass) { return binNamesList.toArray(new String[0]); } - private Policy getPolicyFilterExp(Qualifier[] qualifiers) { - if (qualifiers != null && qualifiers.length > 0) { + private Policy getPolicyFilterExp(Query query) { + if (queryCriteriaIsNotNull(query)) { Policy policy = new Policy(getAerospikeClient().getReadPolicyDefault()); - policy.filterExp = queryEngine.getFilterExpressionsBuilder().build(qualifiers); + policy.filterExp = queryEngine.getFilterExpressionsBuilder().build(query); return policy; } return null; } - private Record getAndTouch(Key key, int expiration, String[] binNames, Qualifier... qualifiers) { + private Record getAndTouch(Key key, int expiration, String[] binNames, Query query) { WritePolicyBuilder writePolicyBuilder = WritePolicyBuilder.builder(client.getWritePolicyDefault()) .expiration(expiration); - if (qualifiers != null && qualifiers.length > 0) { - writePolicyBuilder.filterExp(queryEngine.getFilterExpressionsBuilder().build(qualifiers)); + if (queryCriteriaIsNotNull(query)) { + writePolicyBuilder.filterExp(queryEngine.getFilterExpressionsBuilder().build(query)); } WritePolicy writePolicy = writePolicyBuilder.build(); @@ -779,7 +778,7 @@ public List findByIds(Iterable ids, Class entityClass, Class Assert.notNull(entityClass, "Entity class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return (List) findByIdsUsingQualifiers(IterableConverter.toList(ids), entityClass, targetClass, setName); + return (List) findByIdsUsingQuery(IterableConverter.toList(ids), entityClass, targetClass, setName, null); } @Override @@ -801,44 +800,44 @@ private GroupedEntities findGroupedEntitiesByGroupedKeys(GroupedKeys groupedKeys } @Override - public Object findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, - Qualifier... qualifiers) { + public Object findByIdUsingQuery(Object id, Class entityClass, Class targetClass, + Query query) { Assert.notNull(id, "Id must not be null!"); Assert.notNull(entityClass, "Class must not be null!"); - return findByIdUsingQualifiers(id, entityClass, targetClass, getSetName(entityClass), qualifiers); + return findByIdUsingQuery(id, entityClass, targetClass, getSetName(entityClass), query); } @Override - public Object findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, String setName, - Qualifier... qualifiers) { + public Object findByIdUsingQuery(Object id, Class entityClass, Class targetClass, String setName, + Query query) { Assert.notNull(id, "Id must not be null!"); Assert.notNull(entityClass, "Entity class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + Qualifier qualifier = queryCriteriaIsNotNull(query) ? query.getCriteria().getCriteriaObject() : null; try { AerospikePersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); Key key = getKey(id, setName); if (targetClass != null && targetClass != entityClass) { - return getRecordMapToTargetClass(entity, key, targetClass, qualifiers); + return getRecordMapToTargetClass(entity, key, targetClass, query); } - return mapToEntity(key, entityClass, getRecord(entity, key, qualifiers)); + return mapToEntity(key, entityClass, getRecord(entity, key, query)); } catch (AerospikeException e) { throw translateError(e); } } @Override - public List findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, - Qualifier... qualifiers) { - Assert.notNull(entityClass, "Class must not be null!"); - return findByIdsUsingQualifiers(ids, entityClass, targetClass, getSetName(entityClass), qualifiers); + public List findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, + Query query) { + return findByIdsUsingQuery(ids, entityClass, targetClass, getSetName(entityClass), query); } @Override - public List findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, - String setName, Qualifier... qualifiers) { - Assert.notNull(ids, "List of ids must not be null!"); + public List findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, + String setName, Query query) { + Assert.notNull(ids, "Ids must not be null!"); Assert.notNull(entityClass, "Entity class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); @@ -851,7 +850,7 @@ public List findByIdsUsingQualifiers(Collection ids, Class entit .map(id -> getKey(id, setName)) .toArray(Key[]::new); - BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + BatchPolicy policy = getBatchPolicyFilterExp(query); Class target; Record[] aeroRecords; @@ -892,23 +891,8 @@ public Stream find(Query query, Class targetClass, String setName) { return findUsingQueryWithPostProcessing(setName, targetClass, query); } - @Override - public Stream findUsingQualifier(Class entityClass, Filter filter, - Qualifier qualifier) { - return findUsingQualifier(entityClass, getSetName(entityClass), filter, qualifier); - } - - @Override - public Stream findUsingQualifier(Class entityClass, Class targetClass, Filter filter, - Qualifier qualifier) { - return findRecordsUsingQualifiers(getSetName(entityClass), targetClass, filter, qualifier) - .map(keyRecord -> mapToEntity(keyRecord, targetClass)); - } - - @Override - public Stream findUsingQualifier(Class targetClass, String setName, Filter filter, - Qualifier qualifier) { - return findRecordsUsingQualifiers(setName, targetClass, filter, qualifier) + private Stream find(Class targetClass, String setName) { + return findRecordsUsingQuery(setName, targetClass, null) .map(keyRecord -> mapToEntity(keyRecord, targetClass)); } @@ -932,7 +916,7 @@ public Stream findAll(Class targetClass, String setName) { Assert.notNull(targetClass, "Target class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return findUsingQualifier(targetClass, setName, null, null); + return find(targetClass, setName); } @Override @@ -950,22 +934,20 @@ public Stream findAll(Sort sort, long offset, long limit, Class target Assert.notNull(setName, "Set name must not be null!"); Assert.notNull(targetClass, "Target class must not be null!"); - return findUsingQualifierWithPostProcessing(setName, targetClass, sort, offset, limit, - null, null); + return findUsingQualifierWithPostProcessing(setName, targetClass, sort, offset, limit, null); } private Stream findUsingQueryWithPostProcessing(String setName, Class targetClass, Query query) { verifyUnsortedWithOffset(query.getSort(), query.getOffset()); - Qualifier qualifier = query.getCriteria().getCriteriaObject(); - Stream results = findUsingQualifiersWithDistinctPredicate(setName, targetClass, - getDistinctPredicate(query), qualifier); + Stream results = findUsingQueryWithDistinctPredicate(setName, targetClass, + getDistinctPredicate(query), query); return applyPostProcessingOnResults(results, query); } - private Stream findUsingQualifiersWithDistinctPredicate(String setName, Class targetClass, - Predicate distinctPredicate, - Qualifier... qualifiers) { - return findRecordsUsingQualifiers(setName, targetClass, null, qualifiers) + private Stream findUsingQueryWithDistinctPredicate(String setName, Class targetClass, + Predicate distinctPredicate, + Query query) { + return findRecordsUsingQuery(setName, targetClass, query) .filter(distinctPredicate) .map(keyRecord -> mapToEntity(keyRecord, targetClass)); } @@ -988,8 +970,7 @@ public Stream findInRange(long offset, long limit, Sort sort, Assert.notNull(targetClass, "Target class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return findUsingQualifierWithPostProcessing(setName, targetClass, sort, offset, limit, - null, null); + return findUsingQualifierWithPostProcessing(setName, targetClass, sort, offset, limit, null); } @Override @@ -1071,8 +1052,7 @@ private Stream findRecordsUsingQuery(String setName, Query query) { Assert.notNull(query, "Query must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - Qualifier qualifier = query.getCriteria().getCriteriaObject(); - return findRecordsUsingQualifiers(setName, null, null, qualifier); + return findRecordsUsingQuery(setName, null, query); } @Override @@ -1255,10 +1235,9 @@ private Record putAndGetHeader(AerospikeWriteData data, WritePolicy policy, bool @SuppressWarnings("SameParameterValue") private Stream findUsingQualifierWithPostProcessing(String setName, Class targetClass, Sort sort, - long offset, long limit, Filter filter, - Qualifier qualifier) { + long offset, long limit, Qualifier qualifier) { verifyUnsortedWithOffset(sort, offset); - Stream results = findUsingQualifier(targetClass, setName, filter, qualifier); + Stream results = find(targetClass, setName); return applyPostProcessingOnResults(results, sort, offset, limit); } @@ -1293,16 +1272,14 @@ private Stream applyPostProcessingOnResults(Stream results, Sort sort, return results; } - private Stream findRecordsUsingQualifiers(String setName, Class targetClass, Filter filter, - Qualifier... qualifiers) { - if (qualifiers != null && qualifiers.length > 0 && !allArrayElementsAreNull(qualifiers)) { - validateQualifiers(qualifiers); - - Qualifier idQualifier = getOneIdQualifier(qualifiers); + private Stream findRecordsUsingQuery(String setName, Class targetClass, Query query) { + Qualifier qualifier = queryCriteriaIsNotNull(query) ? query.getCriteria().getCriteriaObject() : null; + if (qualifier != null) { + Qualifier idQualifier = getOneIdQualifier(qualifier); if (idQualifier != null) { // a special flow if there is id given return findByIdsWithoutMapping(getIdValue(idQualifier), setName, targetClass, - excludeIdQualifier(qualifiers)).stream(); + new Query(excludeIdQualifier(qualifier))).stream(); } } @@ -1310,9 +1287,9 @@ private Stream findRecordsUsingQualifiers(String setName, Class Stream findRecordsUsingQualifiers(String setName, Class findByIdsWithoutMapping(Collection ids, String setName, - Class targetClass, - Qualifier... qualifiers) { + Class targetClass, Query query) { Assert.notNull(ids, "Ids must not be null"); if (ids.isEmpty()) { return Collections.emptyList(); @@ -1336,7 +1312,7 @@ private List findByIdsWithoutMapping(Collection ids, String setNam try { Key[] keys = getKeys(ids, setName); - BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + BatchPolicy policy = getBatchPolicyFilterExp(query); Record[] aeroRecords; if (targetClass != null) { diff --git a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java index 21c39a1d3..6b959e59c 100644 --- a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java +++ b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java @@ -18,19 +18,17 @@ import com.aerospike.client.AerospikeException; import com.aerospike.client.cdt.CTX; import com.aerospike.client.policy.WritePolicy; -import com.aerospike.client.query.Filter; import com.aerospike.client.query.IndexCollectionType; import com.aerospike.client.query.IndexType; import com.aerospike.client.reactor.IAerospikeReactorClient; import org.springframework.data.aerospike.core.model.GroupedEntities; import org.springframework.data.aerospike.core.model.GroupedKeys; -import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.domain.Sort; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.lang.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.util.annotation.Nullable; import java.util.Collection; import java.util.Map; @@ -681,11 +679,11 @@ public interface ReactiveAerospikeOperations { * @param id The id of the document to find. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. * @param targetClass The class to map the document to. - * @param qualifiers {@link Qualifier}s provided to build a filter Expression for the query. Optional argument. + * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). * @return The document from Aerospike, returned document will be mapped to targetClass's type. */ - Mono findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, - Qualifier... qualifiers); + Mono findByIdUsingQuery(Object id, Class entityClass, Class targetClass, + @Nullable Query query); /** * Find document by providing id with a given set name. @@ -697,11 +695,11 @@ Mono findByIdUsingQualifiers(Object id, Class entityClass, Class * {@literal null}. * @param targetClass The class to map the document to. * @param setName Set name to find the document from. - * @param qualifiers {@link Qualifier}s provided to build a filter Expression for the query. Optional argument. + * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). * @return The document from Aerospike, returned document will be mapped to targetClass's type. */ - Mono findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, String setName, - Qualifier... qualifiers); + Mono findByIdUsingQuery(Object id, Class entityClass, Class targetClass, String setName, + @Nullable Query query); /** * Find documents by providing multiple ids, set name will be determined by the given entityClass. @@ -711,12 +709,12 @@ Mono findByIdUsingQualifiers(Object id, Class entityClass, Class * @param ids The ids of the documents to find. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. * @param targetClass The class to map the document to. - * @param qualifiers {@link Qualifier}s provided to build a filter Expression for the query. Optional argument. + * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document * exists, an empty list is returned. */ - Flux findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, - Qualifier... qualifiers); + Flux findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, + @Nullable Query query); /** * Find documents by providing multiple ids with a given set name. @@ -728,18 +726,17 @@ Flux findByIdsUsingQualifiers(Collection ids, Class entityClass, * {@literal null}. * @param targetClass The class to map the document to. * @param setName Set name to find the document from. - * @param qualifiers {@link Qualifier}s provided to build a filter Expression for the query. Optional argument. + * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document * exists, an empty list is returned. */ - Flux findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, - String setName, - Qualifier... qualifiers); + Flux findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, String setName, + @Nullable Query query); /** * Reactively find documents in the given entityClass's set using a query and map them to the given class type. * - * @param query The query to filter results. Must not be {@literal null}. + * @param query The {@link Query} to filter results. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from and to map the documents to. Must not be * {@literal null}. * @return A Flux of matching documents, returned documents will be mapped to entityClass's type. @@ -750,7 +747,7 @@ Flux findByIdsUsingQualifiers(Collection ids, Class entityClass, * Reactively find documents in the given entityClass's set using a query and map them to the given target class * type. * - * @param query The query to filter results. Must not be {@literal null}. + * @param query The {@link Query} to filter results. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. * @param targetClass The class to map the document to. Must not be {@literal null}. * @return A Flux of matching documents, returned documents will be mapped to targetClass's type. @@ -760,53 +757,13 @@ Flux findByIdsUsingQualifiers(Collection ids, Class entityClass, /** * Reactively find documents in the given set using a query and map them to the given target class type. * - * @param query The query to filter results. Must not be {@literal null}. + * @param query The {@link Query} to filter results. Must not be {@literal null}. * @param targetClass The class to map the document to. Must not be {@literal null}. * @param setName Set name to find the documents from. * @return A Flux of matching documents, returned documents will be mapped to targetClass's type. */ Flux find(Query query, Class targetClass, String setName); - /** - * Find all documents in the given entityClass's set using provided {@link Qualifier}. - * - * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. - * @param filter Secondary index filter. - * @param qualifier Qualifier to build filter expressions from. Must not be {@literal null}. If filter param is - * null and qualifier has {@link Qualifier#getExcludeFilter()} == false, secondary index filter - * is built based on the first processed qualifier. - * @return Flux of entities. - */ - Flux findUsingQualifier(Class entityClass, @Nullable Filter filter, Qualifier qualifier); - - /** - * Find all documents in the given entityClass's set using provided {@link Qualifier} and map them to the given - * target class type. - * - * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. - * @param targetClass The class to map the document to. Must not be {@literal null}. - * @param filter Secondary index filter. - * @param qualifier Qualifier to build filter expressions from. Must not be {@literal null}. If filter param is - * null and qualifier has {@link Qualifier#getExcludeFilter()} == false, secondary index filter - * is built based on the first processed qualifier. - * @return Flux of entities. - */ - Flux findUsingQualifier(Class entityClass, Class targetClass, Filter filter, Qualifier qualifier); - - /** - * Find all documents in the given set using provided {@link Qualifier} and map them to the given target class - * type. - * - * @param targetClass The class to map the document to. Must not be {@literal null}. - * @param filter Secondary index filter. - * @param qualifier Qualifier to build filter expressions from. Must not be {@literal null}. If filter param is - * null and qualifier has {@link Qualifier#getExcludeFilter()} == false, secondary index filter - * is built based on the first processed qualifier. - * @return Flux of entities. - */ - Flux findUsingQualifier(Class targetClass, String setName, Filter filter, - Qualifier qualifier); - /** * Reactively find all documents in the given entityClass's set and map them to the given class type. * diff --git a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java index 19aa06892..cec4bd2a2 100644 --- a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java +++ b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java @@ -23,7 +23,6 @@ import com.aerospike.client.policy.Policy; import com.aerospike.client.policy.RecordExistsAction; import com.aerospike.client.policy.WritePolicy; -import com.aerospike.client.query.Filter; import com.aerospike.client.query.IndexCollectionType; import com.aerospike.client.query.IndexType; import com.aerospike.client.query.KeyRecord; @@ -69,8 +68,7 @@ import static org.springframework.data.aerospike.core.TemplateUtils.excludeIdQualifier; import static org.springframework.data.aerospike.core.TemplateUtils.getIdValue; import static org.springframework.data.aerospike.query.QualifierUtils.getOneIdQualifier; -import static org.springframework.data.aerospike.query.QualifierUtils.validateQualifiers; -import static org.springframework.data.aerospike.utility.Utils.allArrayElementsAreNull; +import static org.springframework.data.aerospike.query.QualifierUtils.queryCriteriaIsNotNull; /** * Primary implementation of {@link ReactiveAerospikeOperations}. @@ -622,7 +620,7 @@ public Mono findById(Object id, Class entityClass, String setName) { if (entity.isTouchOnRead()) { Assert.state(!entity.hasExpirationProperty(), "Touch on read is not supported for entity without expiration property"); - return getAndTouch(key, entity.getExpiration(), null) + return getAndTouch(key, entity.getExpiration(), null, null) .filter(keyRecord -> Objects.nonNull(keyRecord.record)) .map(keyRecord -> mapToEntity(keyRecord.key, entityClass, keyRecord.record)) .onErrorResume( @@ -653,7 +651,7 @@ public Mono findById(Object id, Class entityClass, Class targetC if (entity.isTouchOnRead()) { Assert.state(!entity.hasExpirationProperty(), "Touch on read is not supported for entity without expiration property"); - return getAndTouch(key, entity.getExpiration(), binNames) + return getAndTouch(key, entity.getExpiration(), binNames, null) .filter(keyRecord -> Objects.nonNull(keyRecord.record)) .map(keyRecord -> mapToEntity(keyRecord.key, targetClass, keyRecord.record)) .onErrorResume( @@ -714,14 +712,14 @@ private Mono findGroupedEntitiesByGroupedKeys(GroupedKeys group } @Override - public Mono findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, - Qualifier... qualifiers) { - return findByIdUsingQualifiers(id, entityClass, targetClass, getSetName(entityClass), qualifiers); + public Mono findByIdUsingQuery(Object id, Class entityClass, Class targetClass, + Query query) { + return findByIdUsingQuery(id, entityClass, targetClass, getSetName(entityClass), query); } @Override - public Mono findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, String setName, - Qualifier... qualifiers) { + public Mono findByIdUsingQuery(Object id, Class entityClass, Class targetClass, String setName, + Query query) { AerospikePersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); Key key = getKey(id, setName); @@ -737,7 +735,7 @@ public Mono findByIdUsingQualifiers(Object id, Class entityClass, C if (entity.isTouchOnRead()) { Assert.state(!entity.hasExpirationProperty(), "Touch on read is not supported for entity without expiration property"); - return getAndTouch(key, entity.getExpiration(), binNames, qualifiers) + return getAndTouch(key, entity.getExpiration(), binNames, query) .filter(keyRecord -> Objects.nonNull(keyRecord.record)) .map(keyRecord -> mapToEntity(keyRecord.key, target, keyRecord.record)) .onErrorResume( @@ -747,9 +745,9 @@ public Mono findByIdUsingQualifiers(Object id, Class entityClass, C .onErrorMap(this::translateError); } else { Policy policy = null; - if (qualifiers != null && qualifiers.length > 0) { + if (queryCriteriaIsNotNull(query)) { policy = new Policy(reactorClient.getReadPolicyDefault()); - policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(qualifiers); + policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(query); } return reactorClient.get(policy, key, binNames) .filter(keyRecord -> Objects.nonNull(keyRecord.record)) @@ -759,23 +757,23 @@ public Mono findByIdUsingQualifiers(Object id, Class entityClass, C } @Override - public Flux findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, - Qualifier... qualifiers) { - return findByIdsUsingQualifiers(ids, entityClass, targetClass, getSetName(entityClass), qualifiers); + public Flux findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, + Query query) { + return findByIdsUsingQuery(ids, entityClass, targetClass, getSetName(entityClass), query); } @Override - public Flux findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, - String setName, - Qualifier... qualifiers) { - Assert.notNull(ids, "List of ids must not be null!"); - Assert.notNull(entityClass, "Class must not be null!"); + public Flux findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, + String setName, Query query) { + Assert.notNull(ids, "Ids must not be null!"); + Assert.notNull(entityClass, "Entity class must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); if (ids.isEmpty()) { return Flux.empty(); } - BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + BatchPolicy policy = getBatchPolicyFilterExp(query); Class target; if (targetClass != null && targetClass != entityClass) { @@ -812,26 +810,6 @@ public Flux find(Query query, Class targetClass, String setName) { return findUsingQueryWithPostProcessing(setName, targetClass, query); } - @Override - public Flux findUsingQualifier(Class entityClass, Filter filter, - Qualifier qualifier) { - return findUsingQualifier(entityClass, getSetName(entityClass), filter, qualifier); - } - - @Override - public Flux findUsingQualifier(Class entityClass, Class targetClass, Filter filter, - Qualifier qualifier) { - return findRecordsUsingQualifiers(getSetName(entityClass), targetClass, filter, qualifier) - .map(keyRecord -> mapToEntity(keyRecord.key, targetClass, keyRecord.record)); - } - - @Override - public Flux findUsingQualifier(Class targetClass, String setName, Filter filter, - Qualifier qualifier) { - return findRecordsUsingQualifiers(setName, targetClass, filter, qualifier) - .map(keyRecord -> mapToEntity(keyRecord.key, targetClass, keyRecord.record)); - } - @Override public Flux findAll(Class entityClass) { Assert.notNull(entityClass, "Entity class must not be null!"); @@ -852,7 +830,7 @@ public Flux findAll(Class targetClass, String setName) { Assert.notNull(targetClass, "Target class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return findUsingQualifiers(setName, targetClass, null, (Qualifier[]) null); + return findUsingQuery(setName, targetClass, null); } @Override @@ -875,8 +853,7 @@ public Flux findAll(Sort sort, long offset, long limit, Class targetCl Assert.notNull(targetClass, "Target class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return findUsingQualifiersWithPostProcessing(setName, targetClass, sort, offset, limit, - null, (Qualifier[]) null); + return findUsingQueryWithPostProcessing(setName, targetClass, sort, offset, limit, null); } @Override @@ -899,14 +876,13 @@ public Flux findInRange(long offset, long limit, Sort sort, Class targ Assert.notNull(targetClass, "Target Class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return findUsingQualifiersWithPostProcessing(setName, targetClass, sort, offset, limit, - null, (Qualifier[]) null); + return findUsingQueryWithPostProcessing(setName, targetClass, sort, offset, limit, null); } - private BatchPolicy getBatchPolicyFilterExp(Qualifier[] qualifiers) { - if (qualifiers != null && qualifiers.length > 0) { + private BatchPolicy getBatchPolicyFilterExp(Query query) { + if (queryCriteriaIsNotNull(query)) { BatchPolicy policy = new BatchPolicy(reactorClient.getAerospikeClient().getBatchPolicyDefault()); - policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(qualifiers); + policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(query); return policy; } return null; @@ -1005,8 +981,7 @@ private Flux findRecordsUsingQuery(String setName, Query query) { Assert.notNull(query, "Query must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - Qualifier qualifier = query.getCriteria().getCriteriaObject(); - return findRecordsUsingQualifiers(setName, null, null, qualifier); + return findRecordsUsingQuery(setName, null, query); } @Override @@ -1146,12 +1121,12 @@ private Mono putAndGetHeader(AerospikeWriteData data, WritePolicy policy .map(keyRecord -> keyRecord.record); } - private Mono getAndTouch(Key key, int expiration, String[] binNames, Qualifier... qualifiers) { + private Mono getAndTouch(Key key, int expiration, String[] binNames, Query query) { WritePolicyBuilder writePolicyBuilder = WritePolicyBuilder.builder(this.writePolicyDefault) .expiration(expiration); - if (qualifiers != null && qualifiers.length > 0) { - writePolicyBuilder.filterExp(reactorQueryEngine.getFilterExpressionsBuilder().build(qualifiers)); + if (queryCriteriaIsNotNull(query)) { + writePolicyBuilder.filterExp(reactorQueryEngine.getFilterExpressionsBuilder().build(query)); } WritePolicy writePolicy = writePolicyBuilder.build(); @@ -1187,19 +1162,17 @@ private Throwable translateError(Throwable e) { private Flux findUsingQueryWithPostProcessing(String setName, Class targetClass, Query query) { verifyUnsortedWithOffset(query.getSort(), query.getOffset()); - Qualifier qualifier = query.getCriteria().getCriteriaObject(); - Flux results = findUsingQualifiersWithDistinctPredicate(setName, targetClass, - getDistinctPredicate(query), qualifier); + Flux results = findUsingQueryWithDistinctPredicate(setName, targetClass, getDistinctPredicate(query), + query); results = applyPostProcessingOnResults(results, query); return results; } @SuppressWarnings("SameParameterValue") - private Flux findUsingQualifiersWithPostProcessing(String setName, Class targetClass, Sort sort, - long offset, long limit, Filter filter, - Qualifier... qualifiers) { + private Flux findUsingQueryWithPostProcessing(String setName, Class targetClass, Sort sort, + long offset, long limit, Query query) { verifyUnsortedWithOffset(sort, offset); - Flux results = findUsingQualifiers(setName, targetClass, filter, qualifiers); + Flux results = findUsingQuery(setName, targetClass, query); results = applyPostProcessingOnResults(results, sort, offset, limit); return results; } @@ -1243,44 +1216,39 @@ private Flux applyPostProcessingOnResults(Flux results, Sort sort, lon return results; } - private Flux findUsingQualifiers(String setName, Class targetClass, Filter filter, - Qualifier... qualifiers) { - return findRecordsUsingQualifiers(setName, targetClass, filter, qualifiers) + private Flux findUsingQuery(String setName, Class targetClass, Query query) { + return findRecordsUsingQuery(setName, targetClass, query) .map(keyRecord -> mapToEntity(keyRecord, targetClass)); } - private Flux findUsingQualifiersWithDistinctPredicate(String setName, Class targetClass, - Predicate distinctPredicate, - Qualifier... qualifiers) { - return findRecordsUsingQualifiers(setName, targetClass, null, qualifiers) + private Flux findUsingQueryWithDistinctPredicate(String setName, Class targetClass, + Predicate distinctPredicate, Query query) { + return findRecordsUsingQuery(setName, targetClass, query) .filter(distinctPredicate) .map(keyRecord -> mapToEntity(keyRecord, targetClass)); } - private Flux findRecordsUsingQualifiers(String setName, Class targetClass, Filter filter, - Qualifier... qualifiers) { - if (qualifiers != null && qualifiers.length > 0 && !allArrayElementsAreNull(qualifiers)) { - validateQualifiers(qualifiers); - - Qualifier idQualifier = getOneIdQualifier(qualifiers); + private Flux findRecordsUsingQuery(String setName, Class targetClass, Query query) { + Qualifier qualifier = queryCriteriaIsNotNull(query) ? query.getCriteria().getCriteriaObject() : null; + if (qualifier != null) { + Qualifier idQualifier = getOneIdQualifier(qualifier); if (idQualifier != null) { // a special flow if there is id given return findByIdsWithoutMapping(getIdValue(idQualifier), setName, targetClass, - excludeIdQualifier(qualifiers)); + new Query(excludeIdQualifier(qualifier))); } } if (targetClass != null) { String[] binNames = getBinNamesFromTargetClass(targetClass); - return this.reactorQueryEngine.select(this.namespace, setName, binNames, filter, qualifiers); + return this.reactorQueryEngine.select(this.namespace, setName, binNames, query); } else { - return this.reactorQueryEngine.select(this.namespace, setName, filter, qualifiers); + return this.reactorQueryEngine.select(this.namespace, setName, query); } } private Flux findByIdsWithoutMapping(Collection ids, String setName, - Class targetClass, - Qualifier... qualifiers) { + Class targetClass, Query query) { Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(setName, "Set name must not be null!"); @@ -1288,7 +1256,7 @@ private Flux findByIdsWithoutMapping(Collection ids, String se return Flux.empty(); } - BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + BatchPolicy policy = getBatchPolicyFilterExp(query); return Flux.fromIterable(ids) .map(id -> getKey(id, setName)) diff --git a/src/main/java/org/springframework/data/aerospike/core/TemplateUtils.java b/src/main/java/org/springframework/data/aerospike/core/TemplateUtils.java index c9111d150..e4bf0089f 100644 --- a/src/main/java/org/springframework/data/aerospike/core/TemplateUtils.java +++ b/src/main/java/org/springframework/data/aerospike/core/TemplateUtils.java @@ -58,6 +58,25 @@ public static Qualifier[] excludeIdQualifier(Qualifier[] qualifiers) { return null; } + public static Qualifier excludeIdQualifier(Qualifier qualifier) { + List qualifiersWithoutId = new ArrayList<>(); + if (qualifier != null && qualifier.hasQualifiers()) { + for (Qualifier innerQual : qualifier.getQualifiers()) { + if (innerQual.hasQualifiers()) { + Qualifier[] internalQuals = excludeIdQualifier(innerQual.getQualifiers()); + qualifiersWithoutId.add(combineMultipleQualifiers(innerQual.getOperation(), internalQuals)); + } else if (!innerQual.hasId()) { + qualifiersWithoutId.add(innerQual); + } + } + return combineMultipleQualifiers(qualifier.getOperation() != null ? qualifier.getOperation() : + FilterOperation.AND, qualifiersWithoutId.toArray(Qualifier[]::new)); + } else if (qualifier.hasId()) { + return null; + } + return qualifier; + } + private static Qualifier combineMultipleQualifiers(FilterOperation operation, Qualifier[] qualifiers) { if (operation == FilterOperation.OR) { return or(qualifiers); diff --git a/src/main/java/org/springframework/data/aerospike/query/FilterExpressionsBuilder.java b/src/main/java/org/springframework/data/aerospike/query/FilterExpressionsBuilder.java index 1a3d6d1a2..ecfb4f62e 100644 --- a/src/main/java/org/springframework/data/aerospike/query/FilterExpressionsBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/FilterExpressionsBuilder.java @@ -17,29 +17,16 @@ import com.aerospike.client.exp.Exp; import com.aerospike.client.exp.Expression; +import org.springframework.data.aerospike.repository.query.Query; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; +import static org.springframework.data.aerospike.query.QualifierUtils.queryCriteriaIsNotNull; public class FilterExpressionsBuilder { - public Expression build(Qualifier[] qualifiers) { - if (qualifiers != null && qualifiers.length != 0) { - List relevantQualifiers = Arrays.stream(qualifiers) - .filter(Objects::nonNull) - .filter(this::excludeIrrelevantFilters).toList(); - - // in case there is more than 1 relevant qualifier -> the default behaviour is AND - if (relevantQualifiers.size() > 1) { - Exp[] exps = relevantQualifiers.stream() - .map(Qualifier::toFilterExp) - .toArray(Exp[]::new); - Exp finalExp = Exp.and(exps); - return Exp.build(finalExp); - } else if (relevantQualifiers.size() == 1) { - return Exp.build(relevantQualifiers.get(0).toFilterExp()); - } + public Expression build(Query query) { + Qualifier qualifier = queryCriteriaIsNotNull(query) ? query.getCriteria().getCriteriaObject() : null; + if (qualifier != null && excludeIrrelevantFilters(qualifier)) { + return Exp.build(qualifier.toFilterExp()); } return null; } diff --git a/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java b/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java index 66fafda54..1f779681a 100644 --- a/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java +++ b/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java @@ -18,6 +18,7 @@ import com.aerospike.client.Key; import com.aerospike.client.Value; +import org.springframework.data.aerospike.repository.AerospikeRepository; import org.springframework.data.aerospike.repository.support.SimpleAerospikeRepository; import java.io.Serial; @@ -27,7 +28,8 @@ * * @author peter * @deprecated Since 4.6.0. Use {@link SimpleAerospikeRepository#findById(Object)} or - * {@link SimpleAerospikeRepository#findByQualifier(Qualifier)} with {@link Qualifier#idEquals(String)} + * {@link AerospikeRepository#findUsingQuery(org.springframework.data.aerospike.repository.query.Query)} with + * {@link Qualifier#idEquals(String)} */ @Deprecated(since = "4.6.0", forRemoval = true) public class KeyQualifier extends Qualifier { diff --git a/src/main/java/org/springframework/data/aerospike/query/Qualifier.java b/src/main/java/org/springframework/data/aerospike/query/Qualifier.java index 9c45dd3ac..4dbbfb8d4 100644 --- a/src/main/java/org/springframework/data/aerospike/query/Qualifier.java +++ b/src/main/java/org/springframework/data/aerospike/query/Qualifier.java @@ -40,7 +40,7 @@ * * @author Peter Milne */ -public class Qualifier implements Map, Serializable { +public class Qualifier implements CriteriaDefinition, Map, Serializable { protected static final String FIELD = "field"; protected static final String METADATA_FIELD = "metadata_field"; @@ -55,7 +55,6 @@ public class Qualifier implements Map, Serializable { protected static final String QUALIFIERS = "qualifiers"; protected static final String OPERATION = "operation"; protected static final String AS_FILTER = "queryAsFilter"; - protected static final String EXCLUDE_FILTER = "excludeFilter"; @Serial private static final long serialVersionUID = -2689196529952712849L; protected final Map internalMap = new HashMap<>(); @@ -72,6 +71,16 @@ protected Qualifier(Qualifier qualifier) { } } + @Override + public Qualifier getCriteriaObject() { + return this; + } + + @Override + public String getKey() { + return this.getField(); + } + private Map getMap() { return Collections.unmodifiableMap(this.internalMap); } @@ -104,14 +113,6 @@ public Boolean queryAsFilter() { return internalMap.containsKey(AS_FILTER) && (Boolean) internalMap.get(AS_FILTER); } - public boolean getExcludeFilter() { - return internalMap.containsKey(EXCLUDE_FILTER) && (Boolean) internalMap.get(EXCLUDE_FILTER); - } - - public void setExcludeFilter(boolean excludeFilter) { - internalMap.put(EXCLUDE_FILTER, excludeFilter); - } - public boolean hasQualifiers() { return internalMap.get(QUALIFIERS) != null; } @@ -431,7 +432,7 @@ protected void validate() { } /** - * Create a qualifier for the condition when the primary key is equal to the given string. + * Create a qualifier for the condition when the primary key equals the given string * * @param id String value * @return Single id qualifier @@ -443,7 +444,7 @@ public static Qualifier idEquals(String id) { } /** - * Create a qualifier for the condition when the primary key is equal to one of the given strings (logical OR). + * Create a qualifier for the condition when the primary key equals one of the given strings (logical OR) * * @param ids String values * @return Multiple ids qualifier with OR condition diff --git a/src/main/java/org/springframework/data/aerospike/query/QualifierUtils.java b/src/main/java/org/springframework/data/aerospike/query/QualifierUtils.java index 5fa7b40db..5fc22c71c 100644 --- a/src/main/java/org/springframework/data/aerospike/query/QualifierUtils.java +++ b/src/main/java/org/springframework/data/aerospike/query/QualifierUtils.java @@ -1,7 +1,7 @@ package org.springframework.data.aerospike.query; import lombok.experimental.UtilityClass; -import org.springframework.data.aerospike.repository.query.AerospikeCriteria; +import org.springframework.data.aerospike.repository.query.Query; import java.util.ArrayList; import java.util.List; @@ -9,42 +9,20 @@ @UtilityClass public class QualifierUtils { - public static Qualifier getIdQualifier(AerospikeCriteria criteria) { - Object qualifiers = getQualifiers(criteria); - return getOneIdQualifier((Qualifier[]) qualifiers); - } - - public static Qualifier[] getQualifiers(AerospikeCriteria criteria) { - if (criteria == null) { - return null; - } else if (criteria.getQualifiers() == null) { - return new Qualifier[]{(criteria)}; - } - return criteria.getQualifiers(); - } - - public static void validateQualifiers(Qualifier... qualifiers) { - boolean haveInternalQualifiers = qualifiers.length > 1; - for (Qualifier qualifier : qualifiers) { - haveInternalQualifiers = haveInternalQualifiers || qualifier.hasQualifiers(); - // excludeFilter in the upmost parent qualifier is set to true - // if there are multiple qualifiers - // must not build secondary index filter based on any of them - // as it might conflict with the combination of qualifiers - qualifier.setExcludeFilter(haveInternalQualifiers); - } + public static Qualifier getIdQualifier(Qualifier qualifier) { + return getOneIdQualifier(qualifier); } /** * Find id qualifier. * - * @param qualifiers Qualifiers to search through - * @return The only id qualifier or null. + * @param qualifier {@link Qualifier} to search through + * @return The only id qualifier or null * @throws IllegalArgumentException if more than one id qualifier given */ - public static Qualifier getOneIdQualifier(Qualifier... qualifiers) { - if (qualifiers != null && qualifiers.length > 0) { - List idQualifiers = getIdQualifiers(qualifiers); + public static Qualifier getOneIdQualifier(Qualifier qualifier) { + if (qualifier != null) { + List idQualifiers = getIdQualifiers(qualifier); if (idQualifiers.size() > 1) { throw new IllegalArgumentException("Expecting not more than one id qualifier in qualifiers array," + " got " + idQualifiers.size()); @@ -55,7 +33,7 @@ public static Qualifier getOneIdQualifier(Qualifier... qualifiers) { return null; } - private static List getIdQualifiers(Qualifier[] qualifiers) { + private static List getIdQualifiers(Qualifier... qualifiers) { List idQualifiers = new ArrayList<>(); for (Qualifier qualifier : qualifiers) { if (qualifier.hasId()) { @@ -68,4 +46,8 @@ private static List getIdQualifiers(Qualifier[] qualifiers) { } return idQualifiers; } + + public static boolean queryCriteriaIsNotNull(Query query) { + return query != null && query.getCriteria() != null; + } } diff --git a/src/main/java/org/springframework/data/aerospike/query/QueryEngine.java b/src/main/java/org/springframework/data/aerospike/query/QueryEngine.java index 536403dcc..e926c6cab 100644 --- a/src/main/java/org/springframework/data/aerospike/query/QueryEngine.java +++ b/src/main/java/org/springframework/data/aerospike/query/QueryEngine.java @@ -21,11 +21,14 @@ import com.aerospike.client.Record; import com.aerospike.client.policy.Policy; import com.aerospike.client.policy.QueryPolicy; -import com.aerospike.client.query.Filter; import com.aerospike.client.query.KeyRecord; import com.aerospike.client.query.RecordSet; import com.aerospike.client.query.Statement; import lombok.Getter; +import org.springframework.data.aerospike.repository.query.Query; +import org.springframework.lang.Nullable; + +import static org.springframework.data.aerospike.query.QualifierUtils.queryCriteriaIsNotNull; /** * This class provides a multi-filter query engine that augments the query capability in Aerospike. @@ -61,35 +64,33 @@ public QueryEngine(IAerospikeClient client, StatementBuilder statementBuilder, } /** - * Select records filtered by a Filter and Qualifiers + * Select records filtered by a query * - * @param namespace Namespace to storing the data - * @param set Set storing the data - * @param filter Aerospike Filter to be used - * @param qualifiers Zero or more Qualifiers for the update query + * @param namespace Namespace to storing the data + * @param set Set storing the data + * @param query {@link Query} for filtering results * @return A KeyRecordIterator to iterate over the results */ - public KeyRecordIterator select(String namespace, String set, Filter filter, Qualifier... qualifiers) { - return select(namespace, set, null, filter, qualifiers); + public KeyRecordIterator select(String namespace, String set, @Nullable Query query) { + return select(namespace, set, null, query); } /** - * Select records filtered by a Filter and Qualifiers + * Select records filtered by a query * - * @param namespace Namespace to storing the data - * @param set Set storing the data - * @param binNames Bin names to return from the query - * @param filter Aerospike Filter to be used - * @param qualifiers Zero or more Qualifiers for the update query + * @param namespace Namespace to storing the data + * @param set Set storing the data + * @param binNames Bin names to return from the query + * @param query {@link Query} for filtering results * @return A KeyRecordIterator to iterate over the results */ - public KeyRecordIterator select(String namespace, String set, String[] binNames, Filter filter, - Qualifier... qualifiers) { + public KeyRecordIterator select(String namespace, String set, String[] binNames, @Nullable Query query) { + Qualifier qualifier = queryCriteriaIsNotNull(query) ? query.getCriteria().getCriteriaObject() : null; /* * singleton using primary key */ - // TODO: if filter is provided together with KeyQualifier it is completely ignored (Anastasiia Smirnova) - if (qualifiers != null && qualifiers.length == 1 && qualifiers[0] instanceof KeyQualifier kq) { + // KeyQualifier is deprecated and marked for removal + if (qualifier instanceof KeyQualifier kq) { Key key = kq.makeKey(namespace, set); Record record = getRecord(null, key, binNames); if (record == null) { @@ -103,9 +104,9 @@ public KeyRecordIterator select(String namespace, String set, String[] binNames, /* * query with filters */ - Statement statement = statementBuilder.build(namespace, set, filter, qualifiers, binNames); + Statement statement = statementBuilder.build(namespace, set, query, binNames); QueryPolicy localQueryPolicy = new QueryPolicy(queryPolicy); - localQueryPolicy.filterExp = filterExpressionsBuilder.build(qualifiers); + localQueryPolicy.filterExp = filterExpressionsBuilder.build(query); if (!scansEnabled && statement.getFilter() == null) { throw new IllegalStateException(SCANS_DISABLED_MESSAGE); diff --git a/src/main/java/org/springframework/data/aerospike/query/ReactorQueryEngine.java b/src/main/java/org/springframework/data/aerospike/query/ReactorQueryEngine.java index 34d6645d0..bb849c98c 100644 --- a/src/main/java/org/springframework/data/aerospike/query/ReactorQueryEngine.java +++ b/src/main/java/org/springframework/data/aerospike/query/ReactorQueryEngine.java @@ -19,16 +19,19 @@ import com.aerospike.client.Key; import com.aerospike.client.policy.Policy; import com.aerospike.client.policy.QueryPolicy; -import com.aerospike.client.query.Filter; import com.aerospike.client.query.KeyRecord; import com.aerospike.client.query.Statement; import com.aerospike.client.reactor.IAerospikeReactorClient; import lombok.Getter; +import org.springframework.data.aerospike.repository.query.Query; +import org.springframework.lang.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Objects; +import static org.springframework.data.aerospike.query.QualifierUtils.queryCriteriaIsNotNull; + /** * This class provides a multi-filter reactive query engine that augments the query capability in Aerospike. * @@ -59,33 +62,31 @@ public ReactorQueryEngine(IAerospikeReactorClient client, StatementBuilder state /** * Select records filtered by a Filter and Qualifiers * - * @param namespace Namespace to storing the data - * @param set Set storing the data - * @param filter Aerospike Filter to be used - * @param qualifiers Zero or more Qualifiers for the update query + * @param namespace Namespace to storing the data + * @param set Set storing the data + * @param query {@link Query} for filtering results * @return A Flux to iterate over the results */ - public Flux select(String namespace, String set, Filter filter, Qualifier... qualifiers) { - return select(namespace, set, null, filter, qualifiers); + public Flux select(String namespace, String set, @Nullable Query query) { + return select(namespace, set, null, query); } /** * Select records filtered by a Filter and Qualifiers * - * @param namespace Namespace to storing the data - * @param set Set storing the data - * @param binNames Bin names to return from the query - * @param filter Aerospike Filter to be used - * @param qualifiers Zero or more Qualifiers for the update query + * @param namespace Namespace to storing the data + * @param set Set storing the data + * @param binNames Bin names to return from the query + * @param query {@link Query} for filtering results * @return A Flux to iterate over the results */ - public Flux select(String namespace, String set, String[] binNames, Filter filter, - Qualifier... qualifiers) { + public Flux select(String namespace, String set, String[] binNames, @Nullable Query query) { + Qualifier qualifier = queryCriteriaIsNotNull(query) ? query.getCriteria().getCriteriaObject() : null; /* * singleton using primary key */ - // TODO: if filter is provided together with KeyQualifier it is completely ignored (Anastasiia Smirnova) - if (qualifiers != null && qualifiers.length == 1 && qualifiers[0] instanceof KeyQualifier kq) { + // KeyQualifier is deprecated and marked for removal + if (qualifier instanceof KeyQualifier kq) { Key key = kq.makeKey(namespace, set); return Flux.from(getRecord(null, key, binNames)) .filter(keyRecord -> Objects.nonNull(keyRecord.record)); @@ -94,9 +95,9 @@ public Flux select(String namespace, String set, String[] binNames, F /* * query with filters */ - Statement statement = statementBuilder.build(namespace, set, filter, qualifiers, binNames); + Statement statement = statementBuilder.build(namespace, set, query, binNames); QueryPolicy localQueryPolicy = new QueryPolicy(queryPolicy); - localQueryPolicy.filterExp = filterExpressionsBuilder.build(qualifiers); + localQueryPolicy.filterExp = filterExpressionsBuilder.build(query); if (!scansEnabled && statement.getFilter() == null) { return Flux.error(new IllegalStateException(QueryEngine.SCANS_DISABLED_MESSAGE)); } diff --git a/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java b/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java index e88c54d08..d6dca3447 100644 --- a/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java +++ b/src/main/java/org/springframework/data/aerospike/query/StatementBuilder.java @@ -21,8 +21,12 @@ import org.slf4j.LoggerFactory; import org.springframework.data.aerospike.query.cache.IndexesCache; import org.springframework.data.aerospike.query.model.IndexedField; +import org.springframework.data.aerospike.repository.query.Query; +import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; +import static org.springframework.data.aerospike.query.QualifierUtils.queryCriteriaIsNotNull; + /** * @author peter * @author Anastasiia Smirnova @@ -36,53 +40,48 @@ public StatementBuilder(IndexesCache indexesCache) { this.indexesCache = indexesCache; } - public Statement build(String namespace, String set, Filter filter, Qualifier[] qualifiers) { - return build(namespace, set, filter, qualifiers, null); + public Statement build(String namespace, String set, Query query) { + return build(namespace, set, query, null); } - public Statement build(String namespace, String set, Filter filter, Qualifier[] qualifiers, String[] binNames) { + public Statement build(String namespace, String set, @Nullable Query query, String[] binNames) { Statement stmt = new Statement(); stmt.setNamespace(namespace); stmt.setSetName(set); if (binNames != null && binNames.length != 0) { stmt.setBinNames(binNames); } - if (filter != null) { - stmt.setFilter(filter); - } else if (qualifiers != null && qualifiers.length != 0) { + if (queryCriteriaIsNotNull(query)) { // statement's filter is set based on the first processed qualifier's filter - // if the qualifier doesn't have EXCLUDE_FILTER set to true - setStatementFilterFromQualifiers(stmt, qualifiers); + setStatementFilterFromQualifiers(stmt, query.getCriteria().getCriteriaObject()); } return stmt; } - private void setStatementFilterFromQualifiers(Statement stmt, Qualifier[] qualifiers) { + private void setStatementFilterFromQualifiers(Statement stmt, Qualifier qualifier) { /* - * query with filters + * query with qualifier */ - for (Qualifier qualifier : qualifiers) { - if (qualifier == null || qualifier.getExcludeFilter()) continue; - if (qualifier.getOperation() == FilterOperation.AND) { - // no sense to use secondary index in case of OR - // as it requires to enlarge selection to more than 1 field - for (Qualifier innerQualifier : qualifier.getQualifiers()) { - if (innerQualifier != null && isIndexedBin(stmt, innerQualifier)) { - Filter filter = innerQualifier.setQueryAsFilter(); - if (filter != null) { - stmt.setFilter(filter); - innerQualifier.setQueryAsFilter(true); - break; // the filter from the first processed qualifier becomes statement's sIndex filter - } + if (qualifier == null) return; + if (qualifier.getOperation() == FilterOperation.AND) { + // no sense to use secondary index in case of OR + // as it requires to enlarge selection to more than 1 field + for (Qualifier innerQualifier : qualifier.getQualifiers()) { + if (innerQualifier != null && isIndexedBin(stmt, innerQualifier)) { + Filter filter = innerQualifier.setQueryAsFilter(); + if (filter != null) { + stmt.setFilter(filter); + innerQualifier.setQueryAsFilter(true); + break; // the filter from the first processed qualifier becomes statement's sIndex filter } } - } else if (isIndexedBin(stmt, qualifier)) { - Filter filter = qualifier.setQueryAsFilter(); - if (filter != null) { - stmt.setFilter(filter); - qualifier.setQueryAsFilter(true); - break; // the filter from the first processed qualifier becomes statement's sIndex filter - } + } + } else if (isIndexedBin(stmt, qualifier)) { + Filter filter = qualifier.setQueryAsFilter(); + if (filter != null) { + stmt.setFilter(filter); + // the filter from the first processed qualifier becomes statement's sIndex filter + qualifier.setQueryAsFilter(true); } } } diff --git a/src/main/java/org/springframework/data/aerospike/repository/AerospikeRepository.java b/src/main/java/org/springframework/data/aerospike/repository/AerospikeRepository.java index 41aeab539..98caa34a2 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/AerospikeRepository.java +++ b/src/main/java/org/springframework/data/aerospike/repository/AerospikeRepository.java @@ -18,6 +18,7 @@ import com.aerospike.client.query.IndexType; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.Repository; @@ -58,13 +59,13 @@ public interface AerospikeRepository extends PagingAndSortingRepository - * A qualifier may contain other qualifiers and combine them using either {@link FilterOperation#AND} or - * {@link FilterOperation#OR}. + * A {@link Query} can be created using a qualifier. A {@link Qualifier} may contain other qualifiers and combine + * them using either {@link FilterOperation#AND} or {@link FilterOperation#OR}. * - * @param qualifier A qualifier representing expressions to be performed. Must not be {@literal null}. + * @param query A query to be performed. Must not be {@literal null}. * @return Iterable of entities. */ - Iterable findByQualifier(Qualifier qualifier); + Iterable findUsingQuery(Query query); } diff --git a/src/main/java/org/springframework/data/aerospike/repository/ReactiveAerospikeRepository.java b/src/main/java/org/springframework/data/aerospike/repository/ReactiveAerospikeRepository.java index bb88bc927..199a3d252 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/ReactiveAerospikeRepository.java +++ b/src/main/java/org/springframework/data/aerospike/repository/ReactiveAerospikeRepository.java @@ -17,6 +17,7 @@ import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.repository.Repository; import org.springframework.data.repository.reactive.ReactiveCrudRepository; import reactor.core.publisher.Flux; @@ -29,13 +30,13 @@ public interface ReactiveAerospikeRepository extends ReactiveCrudRepository { /** - * Run a query to find entities by providing {@link Qualifier}. + * Run a query to find entities. *

- * A qualifier may contain other qualifiers and combine them using either {@link FilterOperation#AND} or - * {@link FilterOperation#OR}. + * A {@link Query} can be created using a qualifier. A {@link Qualifier} may contain other qualifiers and combine + * them using either {@link FilterOperation#AND} or {@link FilterOperation#OR}. * - * @param qualifier A qualifiers representing expressions. Must not be {@literal null}. + * @param query A qualifiers representing expressions. Must not be {@literal null}. * @return Flux of entities. */ - Flux findByQualifier(Qualifier qualifier); + Flux findUsingQuery(Query query); } diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeCriteria.java b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeCriteria.java index ae43d746e..0635a6b2d 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeCriteria.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeCriteria.java @@ -21,7 +21,9 @@ /** * @author Michael Zhang * @author Jeff Boone + * @deprecated Since 4.6.0. Use {@link Qualifier} */ +@Deprecated(since = "4.6.0", forRemoval = true) public class AerospikeCriteria extends Qualifier implements CriteriaDefinition { public AerospikeCriteria(Qualifier.Builder builder) { diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikePartTreeQuery.java b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikePartTreeQuery.java index 3af0b8d3d..6d21068c2 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikePartTreeQuery.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikePartTreeQuery.java @@ -26,7 +26,6 @@ import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.parser.AbstractQueryCreator; -import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -34,8 +33,6 @@ import static org.springframework.data.aerospike.core.TemplateUtils.excludeIdQualifier; import static org.springframework.data.aerospike.core.TemplateUtils.getIdValue; import static org.springframework.data.aerospike.query.QualifierUtils.getIdQualifier; -import static org.springframework.data.aerospike.query.QualifierUtils.getQualifiers; -import static org.springframework.data.aerospike.repository.query.AerospikeCriteria.isSingleIdQuery; /** * @author Peter Milne @@ -63,15 +60,17 @@ public Object execute(Object[] parameters) { // queries that include id have their own processing flow if (parameters != null && parameters.length > 0) { - AerospikeCriteria criteria = query.getAerospikeCriteria(); - Qualifier[] qualifiers = getQualifiers(criteria); - Qualifier idQualifier; - if (isSingleIdQuery(criteria)) { - return runIdQuery(entityClass, targetClass, getIdValue(qualifiers[0])); + Qualifier criteria = query.getCriteria().getCriteriaObject(); + List ids; + if (criteria.hasSingleId()) { + ids = getIdValue(criteria); + return operations.findByIdsUsingQuery(ids, entityClass, targetClass, null); } else { + Qualifier idQualifier; if ((idQualifier = getIdQualifier(criteria)) != null) { - return runIdQuery(entityClass, targetClass, getIdValue(idQualifier), - excludeIdQualifier(qualifiers)); + ids = getIdValue(idQualifier); + return operations.findByIdsUsingQuery(ids, entityClass, targetClass, + new Query(excludeIdQualifier(criteria))); } } } @@ -100,11 +99,6 @@ public Object execute(Object[] parameters) { "supported"); } - protected Object findByIds(Collection ids, Class entityClass, Class targetClass, - Qualifier... qualifiers) { - return operations.findByIdsUsingQualifiers(ids, entityClass, targetClass, qualifiers); - } - private Stream findByQuery(Query query, Class targetClass) { // Run query and map to different target class. if (targetClass != null && targetClass != entityClass) { diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/BaseAerospikePartTreeQuery.java b/src/main/java/org/springframework/data/aerospike/repository/query/BaseAerospikePartTreeQuery.java index 4e7f3278d..ae8418545 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/BaseAerospikePartTreeQuery.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/BaseAerospikePartTreeQuery.java @@ -26,11 +26,9 @@ import org.springframework.data.repository.query.parser.PartTree; import org.springframework.expression.EvaluationContext; import org.springframework.expression.spel.standard.SpelExpression; -import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import java.lang.reflect.Constructor; -import java.util.Collection; /** * @author Peter Milne @@ -62,7 +60,7 @@ protected Query prepareQuery(Object[] parameters, ParametersParameterAccessor ac PartTree tree = new PartTree(queryMethod.getName(), entityClass); Query baseQuery = createQuery(accessor, tree); - AerospikeCriteria criteria = baseQuery.getAerospikeCriteria(); + Qualifier criteria = baseQuery.getCriteria().getCriteriaObject(); Query query = new Query(criteria); if (accessor.getPageable().isPaged()) { @@ -112,13 +110,4 @@ public Query createQuery(ParametersParameterAccessor accessor, PartTree tree) { .getConstructorIfAvailable(queryCreator, PartTree.class, ParameterAccessor.class); return (Query) BeanUtils.instantiateClass(constructor, tree, accessor).createQuery(); } - - protected Object runIdQuery(Class sourceClass, Class targetClass, Collection ids, - Qualifier... qualifiers) { - Assert.notNull(ids, "Ids must not be null"); - return findByIds(ids, sourceClass, targetClass, qualifiers); - } - - abstract Object findByIds(Collection ids, Class sourceClass, Class targetClass, - Qualifier... qualifiers); } diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/Query.java b/src/main/java/org/springframework/data/aerospike/repository/query/Query.java index 38b3af1d9..caca336d3 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/Query.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/Query.java @@ -33,7 +33,8 @@ public class Query { private long offset = NOT_SPECIFIED; private int rows = NOT_SPECIFIED; private CriteriaDefinition criteria; - @Setter @Getter + @Setter + @Getter private boolean isDistinct; /** @@ -49,7 +50,9 @@ public Query(CriteriaDefinition criteria) { * Creates new instance of {@link Query} with given {@link Sort}. * * @param sort can be {@literal null}. + * @deprecated Since 4.6.0. Use {@link Query#Query(CriteriaDefinition)}} instead */ + @Deprecated(since = "4.6.0", forRemoval = true) public Query(Sort sort) { this.sort = sort; } diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/ReactiveAerospikePartTreeQuery.java b/src/main/java/org/springframework/data/aerospike/repository/query/ReactiveAerospikePartTreeQuery.java index 54ad01dc0..4b7b9ef5b 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/ReactiveAerospikePartTreeQuery.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/ReactiveAerospikePartTreeQuery.java @@ -24,12 +24,11 @@ import org.springframework.data.repository.query.parser.AbstractQueryCreator; import reactor.core.publisher.Flux; -import java.util.Collection; +import java.util.List; import static org.springframework.data.aerospike.core.TemplateUtils.excludeIdQualifier; import static org.springframework.data.aerospike.core.TemplateUtils.getIdValue; import static org.springframework.data.aerospike.query.QualifierUtils.getIdQualifier; -import static org.springframework.data.aerospike.query.QualifierUtils.getQualifiers; /** * @author Igor Ermolenko @@ -54,14 +53,18 @@ public Object execute(Object[] parameters) { // queries that include id have their own processing flow if (parameters != null && parameters.length > 0) { - AerospikeCriteria criteria = query.getAerospikeCriteria(); - Qualifier[] qualifiers = getQualifiers(criteria); - Qualifier idQualifier; - if (AerospikeCriteria.isSingleIdQuery(criteria)) { - return runIdQuery(entityClass, targetClass, getIdValue(qualifiers[0])); - } else if ((idQualifier = getIdQualifier(criteria)) != null) { - return runIdQuery(entityClass, targetClass, getIdValue(idQualifier), - excludeIdQualifier(qualifiers)); + Qualifier criteria = query.getCriteria().getCriteriaObject(); + List ids; + if (criteria.hasSingleId()) { + ids = getIdValue(criteria); + return operations.findByIdsUsingQuery(ids, entityClass, targetClass, null); + } else { + Qualifier idQualifier; + if ((idQualifier = getIdQualifier(criteria)) != null) { + ids = getIdValue(idQualifier); + return operations.findByIdsUsingQuery(ids, entityClass, targetClass, + new Query(excludeIdQualifier(criteria))); + } } } return findByQuery(query, targetClass); @@ -75,9 +78,4 @@ private Flux findByQuery(Query query, Class targetClass) { // Run query and map to entity class type. return operations.find(query, entityClass); } - - protected Object findByIds(Collection ids, Class sourceClass, Class targetClass, - Qualifier... qualifiers) { - return operations.findByIdsUsingQualifiers(ids, sourceClass, targetClass, qualifiers); - } } diff --git a/src/main/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepository.java b/src/main/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepository.java index 9657a0b3f..0c57e4fe1 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepository.java +++ b/src/main/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepository.java @@ -17,8 +17,8 @@ import com.aerospike.client.query.IndexType; import org.springframework.data.aerospike.core.AerospikeOperations; -import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.AerospikeRepository; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; @@ -160,8 +160,7 @@ public boolean indexExists(String indexName) { return operations.indexExists(indexName); } - public Iterable findByQualifier(Qualifier qualifier) { - Assert.notNull(qualifier, "Qualifier must not be null"); - return operations.findUsingQualifier(entityInformation.getJavaType(), null, qualifier).toList(); + public Iterable findUsingQuery(Query query) { + return operations.find(query, entityInformation.getJavaType()).toList(); } } diff --git a/src/main/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepository.java b/src/main/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepository.java index 3845bdace..82e9d30c3 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepository.java +++ b/src/main/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepository.java @@ -19,8 +19,8 @@ import lombok.RequiredArgsConstructor; import org.reactivestreams.Publisher; import org.springframework.data.aerospike.core.ReactiveAerospikeOperations; -import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.ReactiveAerospikeRepository; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.repository.core.EntityInformation; import org.springframework.util.Assert; import reactor.core.publisher.Flux; @@ -154,8 +154,7 @@ public void deleteIndex(Class domainType, String indexName) { } @Override - public Flux findByQualifier(Qualifier qualifier) { - Assert.notNull(qualifier, "Qualifiers must not be null"); - return operations.findUsingQualifier(entityInformation.getJavaType(), null, qualifier); + public Flux findUsingQuery(Query query) { + return operations.find(query, entityInformation.getJavaType()); } } diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java index d65f585f6..6bfc70925 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java @@ -536,23 +536,17 @@ public void findAllUsingQuery_shouldRunWithDifferentArgumentsCombinations() { additionalAerospikeTestOperations.createIndex(SampleClasses.CustomCollectionClass.class, "CustomCollectionClass_field", fieldName, IndexType.STRING); - // find by qualifiers, no predefined secondary index filter + // find by query Qualifier qualifier = Qualifier.builder() .setField(fieldName) .setFilterOperation(FilterOperation.EQ) .setValue1(Value.get(fieldValue1)) .build(); Stream result1 = - template.findUsingQualifier(SampleClasses.CustomCollectionClass.class, null, qualifier); + template.find(new Query(qualifier), SampleClasses.CustomCollectionClass.class); assertThat(result1).containsOnly(doc1); - // find by a predefined secondary index filter, no qualifiers - Filter filter = Filter.equal(fieldName, fieldValue1); - Stream result2 = - template.findUsingQualifier(SampleClasses.CustomCollectionClass.class, filter, null); - assertThat(result2).containsOnly(doc1); - - // find by a complex qualifier + // find by query with a complex qualifier Qualifier dataEqFieldValue1 = Qualifier.builder() .setFilterOperation(FilterOperation.EQ) .setField(fieldName) @@ -565,14 +559,9 @@ public void findAllUsingQuery_shouldRunWithDifferentArgumentsCombinations() { .build(); Qualifier qualifierOr = Qualifier.or(dataEqFieldValue1, dataEqFieldValue2); Stream result3 = - template.findUsingQualifier(SampleClasses.CustomCollectionClass.class, null, qualifierOr); + template.find(new Query(qualifierOr), SampleClasses.CustomCollectionClass.class); assertThat(result3).containsOnly(doc1, doc2); - // no secondary index filter and no qualifiers - Stream result4 = - template.findUsingQualifier(SampleClasses.CustomCollectionClass.class, null, null); - assertThat(result4).contains(doc1, doc2); - additionalAerospikeTestOperations.dropIndex(SampleClasses.CustomCollectionClass.class, "CustomCollectionClass_field"); // cleanup template.delete(doc1); // cleanup diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryProjectionTest.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryProjectionTest.java index c71d6b8a9..c4c6ffebe 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryProjectionTest.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryProjectionTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.springframework.data.aerospike.BaseReactiveIntegrationTests; +import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.sample.PersonSomeFields; @@ -106,7 +107,7 @@ public void findInRange_shouldFindLimitedNumberOfDocumentsProjection() { @Test public void find_throwsExceptionForUnsortedQueryWithSpecifiedOffsetValueProjection() { - Query query = new Query((Sort) null); + Query query = new Query((Qualifier) null); query.setOffset(1); assertThatThrownBy(() -> reactiveTemplate.find(query, Person.class, PersonSomeFields.class) diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryTests.java index 864daf4d7..2040c04b5 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryTests.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.springframework.data.aerospike.BaseReactiveIntegrationTests; +import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.query.CriteriaDefinition; import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.sample.Address; @@ -193,7 +194,7 @@ public void findAll_OrderByFirstName() { @Test public void find_throwsExceptionForUnsortedQueryWithSpecifiedOffsetValue() { - Query query = new Query((Sort) null); + Query query = new Query((Qualifier) null); query.setOffset(1); assertThatThrownBy(() -> reactiveTemplate.find(query, Person.class) diff --git a/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java b/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java index 5366dce55..1a8ebd0e7 100644 --- a/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java +++ b/src/test/java/org/springframework/data/aerospike/logging/LoggingTests.java @@ -14,6 +14,7 @@ import org.springframework.data.aerospike.query.StatementBuilder; import org.springframework.data.aerospike.query.cache.IndexesCache; import org.springframework.data.aerospike.repository.query.AerospikeQueryCreator; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.repository.query.StubParameterAccessor; import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.utility.MemoryAppender; @@ -46,7 +47,7 @@ void binIsIndexed() { .build(); StatementBuilder statementBuilder = new StatementBuilder(indexesCacheMock); - statementBuilder.build("TEST", "testSet", null, new Qualifier[]{qualifier}); + statementBuilder.build("TEST", "testSet", new Query(qualifier)); assertThat(memoryAppender.countEventsForLogger(LOGGER_NAME)).isEqualTo(1); String msg = "Bin TEST.testSet.testField has secondary index: false"; diff --git a/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java index e2cc344bb..09e4e041f 100644 --- a/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java @@ -17,10 +17,10 @@ package org.springframework.data.aerospike.query; import com.aerospike.client.Value; -import com.aerospike.client.query.Filter; import com.aerospike.client.query.IndexType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.utility.CollectionUtils; import org.springframework.data.aerospike.utility.ServerVersionUtils; @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.AGES; import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.BLUE; import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.GEO_BIN_NAME; import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.GREEN; @@ -56,7 +57,8 @@ void selectOnIndexedLTQualifier() { .setValue1(Value.get(26)) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, + new Query(qualifier)); assertThat(iterator) .toIterable() @@ -76,7 +78,8 @@ void selectOnIndexedLTEQQualifier() { .setValue1(Value.get(26)) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, + new Query(qualifier)); Map ageCount = CollectionUtils.toStream(iterator) .map(rec -> rec.record.getInt("age")) @@ -99,7 +102,8 @@ void selectOnIndexedNumericEQQualifier() { .setValue1(Value.get(26)) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, + new Query(qualifier)); assertThat(iterator) .toIterable() @@ -110,16 +114,15 @@ void selectOnIndexedNumericEQQualifier() { } @Test - void selectOnIndexWithQualifiers() { + void selectWithBlueColorQuery() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { - Filter filter = Filter.range("age", 25, 29); Qualifier qual1 = Qualifier.builder() .setField("color") .setFilterOperation(FilterOperation.EQ) .setValue1(Value.get(BLUE)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, INDEXED_SET_NAME, filter, qual1); + KeyRecordIterator it = queryEngine.select(namespace, INDEXED_SET_NAME, new Query(qual1)); assertThat(it) .toIterable() @@ -139,7 +142,8 @@ void selectOnIndexedGTEQQualifier() { .setValue1(Value.get(28)) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, + new Query(qualifier)); Map ageCount = CollectionUtils.toStream(iterator) .map(rec -> rec.record.getInt("age")) @@ -161,7 +165,8 @@ void selectOnIndexedGTQualifier() { .setValue1(Value.get(28)) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, + new Query(qualifier)); assertThat(iterator) .toIterable() @@ -180,7 +185,8 @@ void selectOnIndexedStringEQQualifier() { .setValue1(Value.get(ORANGE)) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_SET_NAME, null, + new Query(qualifier)); assertThat(iterator) .toIterable() @@ -207,7 +213,8 @@ void selectWithGeoWithin() { .setValue1(Value.getAsGeoJSON(rgnstr)) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_GEO_SET, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, INDEXED_GEO_SET, null, + new Query(qualifier)); assertThat(iterator).toIterable() .isNotEmpty() @@ -218,32 +225,21 @@ void selectWithGeoWithin() { } @Test - void selectOnIndexFilter() { + void selectWithoutQuery() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { - Filter filter = Filter.range("age", 28, 29); - KeyRecordIterator it = queryEngine.select(namespace, INDEXED_SET_NAME, filter); + KeyRecordIterator it = queryEngine.select(namespace, INDEXED_SET_NAME, null); Map ageCount = CollectionUtils.toStream(it) .map(rec -> rec.record.getInt("age")) .collect(Collectors.groupingBy(k -> k, countingInt())); assertThat(ageCount.keySet()) .isNotEmpty() - .allSatisfy(age -> assertThat(age).isBetween(28, 29)); + .allSatisfy(age -> assertThat(age).isIn((Object[]) AGES)); assertThat(ageCount.get(28)).isEqualTo(queryEngineTestDataPopulator.ageCount.get(28)); assertThat(ageCount.get(29)).isEqualTo(queryEngineTestDataPopulator.ageCount.get(29)); }); } - @Test - void selectOnIndexFilterNonExistingKeys() { - withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { - Filter filter = Filter.range("age", 30, 35); - KeyRecordIterator it = queryEngine.select(namespace, INDEXED_SET_NAME, filter); - - assertThat(it).toIterable().isEmpty(); - }); - } - @Test void selectWithQualifiersOnly() { Qualifier qual1 = Qualifier.builder() @@ -259,7 +255,8 @@ void selectWithQualifiersOnly() { .build(); withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { - KeyRecordIterator it = queryEngine.select(namespace, INDEXED_SET_NAME, null, qual1, qual2); + KeyRecordIterator it = queryEngine.select(namespace, INDEXED_SET_NAME, null, + new Query(Qualifier.and(qual1, qual2))); assertThat(it).toIterable() .isNotEmpty() @@ -287,7 +284,7 @@ void selectWithAndQualifier() { try { Qualifier qualifier = Qualifier.and(colorIsGreen, ageBetween28And29); - KeyRecordIterator it = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, INDEXED_SET_NAME, null, new Query(qualifier)); assertThat(it).toIterable().isNotEmpty() .allSatisfy(rec -> { diff --git a/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java index 2b4ed8128..02aeeab0c 100644 --- a/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/QualifierTests.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.utility.CollectionUtils; import java.util.Arrays; @@ -62,7 +63,7 @@ void throwsExceptionWhenScansDisabled() { .setValue1(Value.get(26)).build(); //noinspection resource - assertThatThrownBy(() -> queryEngine.select(namespace, SET_NAME, null, qualifier)) + assertThatThrownBy(() -> queryEngine.select(namespace, SET_NAME, null, new Query(qualifier))) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("disabled by default"); } finally { @@ -75,7 +76,7 @@ void throwsExceptionWhenScansDisabled() { void selectOneWitKey() { KeyQualifier kq = new KeyQualifier(Value.get("selector-test:3")); - KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, kq); + KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, new Query(kq)); assertThat(iterator).toIterable().hasSize(1); } @@ -85,14 +86,14 @@ void selectOneWitKey() { void selectOneWitKeyNonExisting() { KeyQualifier kq = new KeyQualifier(Value.get("selector-test:unknown")); - KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, kq); + KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, new Query(kq)); assertThat(iterator).toIterable().isEmpty(); } @Test void selectAll() { - KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null); + KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, null); assertThat(iterator).toIterable().hasSize(RECORD_COUNT); } @@ -106,7 +107,7 @@ void lTQualifier() { .setValue1(Value.get(26)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -124,7 +125,7 @@ void numericLTEQQualifier() { .setValue1(Value.get(26)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); Map ageCount = CollectionUtils.toStream(it) .map(rec -> rec.record.getInt("age")) @@ -145,7 +146,7 @@ void numericEQQualifier() { .setValue1(Value.get(26)) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(iterator) .toIterable() @@ -163,7 +164,7 @@ void numericGTEQQualifier() { .setValue1(Value.get(28)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); Map ageCount = CollectionUtils.toStream(it) .map(rec -> rec.record.getInt("age")) @@ -184,7 +185,7 @@ void numericGTQualifier() { .setValue1(Value.get(28)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -201,7 +202,7 @@ void metadataSinceUpdateEQQualifier() { .setValue1AsObj(1L) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(iterator) .toIterable() @@ -218,7 +219,7 @@ void stringEQQualifier() { .setValue1(Value.get(ORANGE)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -236,7 +237,7 @@ void stringEQIgnoreCaseQualifier() { .setValue1(Value.get(ORANGE.toUpperCase())) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -255,7 +256,7 @@ void stringEqualIgnoreCaseWorksOnUnindexedBin() { .setValue1(Value.get("BlUe")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -275,7 +276,7 @@ void stringEqualIgnoreCaseWorksOnIndexedBin() { .setValue1(Value.get("BlUe")) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(iterator) .toIterable() @@ -298,7 +299,7 @@ void stringEqualIgnoreCaseWorksRequiresFullMatch() { .setValue1(Value.get("lue")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it).toIterable().isEmpty(); } @@ -311,7 +312,7 @@ void stringStartWithQualifier() { .setValue1(Value.get(BLUE.substring(0, 2))) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -328,7 +329,7 @@ void stringStartWithEntireWordQualifier() { .setValue1(Value.get(BLUE)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -346,7 +347,7 @@ void stringStartWithICASEQualifier() { .setValue1(Value.get("BLU")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -363,7 +364,7 @@ void stringEndsWithQualifier() { .setValue1(Value.get(GREEN.substring(2))) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -380,7 +381,7 @@ void selectEndsWith() { .setValue1(Value.get("e")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -397,7 +398,7 @@ void stringEndsWithEntireWordQualifier() { .setValue1(Value.get(GREEN)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it) .toIterable() @@ -416,7 +417,7 @@ void betweenQualifier() { .setValue2(Value.get(29)) // + 1 as upper limit is exclusive .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); Map ageCount = CollectionUtils.toStream(it) .map(rec -> rec.record.getInt("age")) @@ -441,7 +442,7 @@ void containingQualifier() { .setValue1(Value.get("l")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); Map colorCount = CollectionUtils.toStream(it) .map(rec -> rec.record.getString("color")) @@ -461,7 +462,7 @@ void inQualifier() { .setValue1(Value.get(inColors)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); Map colorCount = CollectionUtils.toStream(it) .map(rec -> rec.record.getString("color")) @@ -480,7 +481,7 @@ void listContainsQualifier() { .setValue1(Value.get(searchColor)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it).toIterable() .isNotEmpty() @@ -506,7 +507,7 @@ void listBetweenQualifier() { .setValue2(Value.get(ageEnd + 1L)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); Map ageCount = CollectionUtils.toStream(it) .map(rec -> { @@ -533,7 +534,7 @@ void mapKeysContainsQualifier() { .setValue1(Value.get(searchColor)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it).toIterable() .isNotEmpty() @@ -556,7 +557,7 @@ void mapValuesContainsQualifier() { .setValue1(Value.get(searchColor)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it).toIterable() .isNotEmpty() @@ -581,7 +582,7 @@ void mapKeysBetweenQualifier() { .setValue2(Value.get(ageEnd + 1L)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); Map ageCount = CollectionUtils.toStream(it) .map(rec -> { @@ -613,7 +614,7 @@ void mapValuesBetweenQualifier() { .setValue2(Value.get(ageEnd + 1L)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); Map ageCount = CollectionUtils.toStream(it) .map(rec -> { @@ -640,7 +641,7 @@ void containingDoesNotUseSpecialCharacterQualifier() { .setValue1(Value.get(".*")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(qualifier)); assertThat(it).toIterable() .isNotEmpty() @@ -656,7 +657,7 @@ void startWithDoesNotUseSpecialCharacterQualifier() { .setValue1(Value.get(".*")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(qualifier)); assertThat(it).toIterable() .isNotEmpty() @@ -672,7 +673,7 @@ void endWithDoesNotUseSpecialCharacterQualifier() { .setValue1(Value.get(".*")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(qualifier)); assertThat(it).toIterable() .isNotEmpty() @@ -689,7 +690,7 @@ void eQIcaseDoesNotUseSpecialCharacter() { .setValue1(Value.get(".*")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(qualifier)); assertThat(it).toIterable().isEmpty(); } @@ -704,7 +705,7 @@ void containingFindsSquareBracket(String specialString) { .setValue1(Value.get(specialString)) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(qualifier)); assertThat(it).toIterable() .isNotEmpty() @@ -726,7 +727,7 @@ void selectWithGeoWithin() { .setValue1(Value.getAsGeoJSON(rgnstr)) .build(); - KeyRecordIterator iterator = queryEngine.select(namespace, GEO_SET, null, qualifier); + KeyRecordIterator iterator = queryEngine.select(namespace, GEO_SET, null, new Query(qualifier)); assertThat(iterator).toIterable() .isNotEmpty() @@ -750,7 +751,7 @@ void startWithAndEqualIgnoreCaseReturnsAllItems() { .setValue1(Value.get("NA")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qual1, qual2); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(Qualifier.and(qual1, qual2))); assertThat(it).toIterable() .isNotEmpty() @@ -768,7 +769,7 @@ void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { .setValue1(Value.get(BLUE.toUpperCase())) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qual1); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qual1)); assertThat(it).toIterable().isEmpty(); } @@ -783,7 +784,7 @@ void startWithIgnoreCaseReturnsNoItemsIfNoneMatched() { .setValue1(Value.get("NA")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qual1); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qual1)); assertThat(it).toIterable().isEmpty(); } @@ -816,7 +817,7 @@ void selectWithBetweenAndOrQualifiers() { Qualifier or2 = Qualifier.or(colorIsGreen, nameIs696); Qualifier qualifier = Qualifier.and(or, or2); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); assertThat(it).toIterable().isNotEmpty() .allSatisfy(rec -> { @@ -853,7 +854,7 @@ void selectWithOrQualifiers() { Qualifier or = Qualifier.or(colorIsBlue, ageBetween28And29); - KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, or); + KeyRecordIterator it = queryEngine.select(namespace, SET_NAME, null, new Query(or)); List result = CollectionUtils.toStream(it).collect(Collectors.toList()); assertThat(result) diff --git a/src/test/java/org/springframework/data/aerospike/query/UsersTests.java b/src/test/java/org/springframework/data/aerospike/query/UsersTests.java index a358c5a55..9b002f07c 100644 --- a/src/test/java/org/springframework/data/aerospike/query/UsersTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/UsersTests.java @@ -2,6 +2,7 @@ import com.aerospike.client.Value; import org.junit.jupiter.api.Test; +import org.springframework.data.aerospike.repository.query.Query; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.RECORD_COUNT; @@ -11,14 +12,14 @@ class UsersTests extends BaseQueryEngineTests { @Test void allUsers() { - KeyRecordIterator it = queryEngine.select(namespace, USERS_SET, null); + KeyRecordIterator it = queryEngine.select(namespace, USERS_SET, null, null); assertThat(it).toIterable().hasSize(RECORD_COUNT); } @Test void usersInterrupted() { - try (KeyRecordIterator it = queryEngine.select(namespace, USERS_SET, null)) { + try (KeyRecordIterator it = queryEngine.select(namespace, USERS_SET, null, null)) { int counter = 0; while (it.hasNext()) { it.next(); @@ -37,7 +38,7 @@ void usersInNorthRegion() { .setValue1(Value.get("n")) .build(); - KeyRecordIterator it = queryEngine.select(namespace, USERS_SET, null, qualifier); + KeyRecordIterator it = queryEngine.select(namespace, USERS_SET, null, new Query(qualifier)); assertThat(it).toIterable() .isNotEmpty() diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedPersonRepositoryQueryTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedPersonRepositoryQueryTests.java index ce779f3b9..a98a18ab7 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedPersonRepositoryQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedPersonRepositoryQueryTests.java @@ -13,6 +13,7 @@ import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.query.CriteriaDefinition; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.sample.Address; import org.springframework.data.aerospike.sample.IndexedPerson; import org.springframework.data.aerospike.sample.ReactiveIndexedPersonRepository; @@ -300,7 +301,7 @@ public void findPersonsByMetadata() { .setFilterOperation(FilterOperation.LT) .setValue1AsObj(50000L) .build(); - assertThat(reactiveRepository.findByQualifier(sinceUpdateTimeLt10Seconds).collectList().block()) + assertThat(reactiveRepository.findUsingQuery(new Query(sinceUpdateTimeLt10Seconds)).collectList().block()) .containsAll(allIndexedPersons); // creating a condition "since_update_time metadata value is between 1 millisecond and 50 seconds" @@ -310,8 +311,9 @@ public void findPersonsByMetadata() { .setValue1AsObj(1L) .setValue2AsObj(50000L) .build(); - assertThat(reactiveRepository.findByQualifier(sinceUpdateTimeBetween1And50000).collectList().block()) - .containsAll(reactiveRepository.findByQualifier(sinceUpdateTimeLt10Seconds).collectList().block()); + assertThat(reactiveRepository.findUsingQuery(new Query(sinceUpdateTimeBetween1And50000)).collectList().block()) + .containsAll(reactiveRepository.findUsingQuery(new Query(sinceUpdateTimeLt10Seconds)).collectList() + .block()); } @Test @@ -331,7 +333,7 @@ public void findPersonsByQualifiers() { .setFilterOperation(FilterOperation.LT) .setValue1AsObj(50000L) .build(); - assertThat(reactiveRepository.findByQualifier(sinceUpdateTimeLt50Seconds).collectList().block()) + assertThat(reactiveRepository.findUsingQuery(new Query(sinceUpdateTimeLt50Seconds)).collectList().block()) .containsAll(allIndexedPersons); // creating a condition "since_update_time metadata value is between 1 and 50 seconds" @@ -355,7 +357,7 @@ public void findPersonsByQualifiers() { .setFilterOperation(FilterOperation.EQ) .setValue1(Value.get(34)) .build(); - result = reactiveRepository.findByQualifier(ageEq34).collectList().block(); + result = reactiveRepository.findUsingQuery(new Query(ageEq34)).collectList().block(); assertThat(result).containsOnly(petra); // creating a condition "age is greater than 34" @@ -364,48 +366,49 @@ public void findPersonsByQualifiers() { .setField("age") .setValue1(Value.get(34)) .build(); - result = reactiveRepository.findByQualifier(ageGt34).collectList().block(); + result = reactiveRepository.findUsingQuery(new Query(ageGt34)).collectList().block(); assertThat(result).doesNotContain(petra); - result = reactiveRepository.findByQualifier(Qualifier.and(sinceUpdateTimeGt1, sinceUpdateTimeLt50Seconds, + result = reactiveRepository.findUsingQuery(new Query(Qualifier.and(sinceUpdateTimeGt1, + sinceUpdateTimeLt50Seconds, ageEq34, - firstNameEqPetra, sinceUpdateTimeBetween1And50000)).collectList().block(); + firstNameEqPetra, sinceUpdateTimeBetween1And50000))).collectList().block(); assertThat(result).containsOnly(petra); // conditions "age == 34", "firstName is Petra" and "since_update_time metadata value is less than 50 seconds" // are combined with OR Qualifier orWide = Qualifier.or(ageEq34, firstNameEqPetra, sinceUpdateTimeLt50Seconds); - result = reactiveRepository.findByQualifier(orWide).collectList().block(); + result = reactiveRepository.findUsingQuery(new Query(orWide)).collectList().block(); assertThat(result).containsAll(allIndexedPersons); // conditions "age == 34" and "firstName is Petra" are combined with OR Qualifier orNarrow = Qualifier.or(ageEq34, firstNameEqPetra); - result = reactiveRepository.findByQualifier(orNarrow).collectList().block(); + result = reactiveRepository.findUsingQuery(new Query(orNarrow)).collectList().block(); assertThat(result).containsOnly(petra); - result = reactiveRepository.findByQualifier(Qualifier.and(ageEq34, ageGt34)).collectList().block(); + result = reactiveRepository.findUsingQuery(new Query(Qualifier.and(ageEq34, ageGt34))).collectList().block(); assertThat(result).isEmpty(); // conditions "age == 34" and "age > 34" are not overlapping - result = reactiveRepository.findByQualifier(Qualifier.and(ageEq34, ageGt34)).collectList().block(); + result = reactiveRepository.findUsingQuery(new Query(Qualifier.and(ageEq34, ageGt34))).collectList().block(); assertThat(result).isEmpty(); // conditions "age == 34" and "age > 34" are combined with OR Qualifier ageEqOrGt34 = Qualifier.or(ageEq34, ageGt34); - result = reactiveRepository.findByQualifier(ageEqOrGt34).collectList().block(); + result = reactiveRepository.findUsingQuery(new Query(ageEqOrGt34)).collectList().block(); List personsWithAgeEqOrGt34 = allIndexedPersons.stream().filter(person -> person.getAge() >= 34) .toList(); assertThat(result).containsAll(personsWithAgeEqOrGt34); // a condition that returns all entities and a condition that returns one entity are combined using AND - result = reactiveRepository.findByQualifier(Qualifier.and(orWide, orNarrow)).collectList().block(); + result = reactiveRepository.findUsingQuery(new Query(Qualifier.and(orWide, orNarrow))).collectList().block(); assertThat(result).containsOnly(petra); // a condition that returns all entities and a condition that returns one entity are combined using AND // another way of running the same query Qualifier orCombinedWithAnd = Qualifier.and(orWide, orNarrow); - result = reactiveRepository.findByQualifier(orCombinedWithAnd).collectList().block(); + result = reactiveRepository.findUsingQuery(new Query(orCombinedWithAnd)).collectList().block(); assertThat(result).containsOnly(petra); } } diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedQualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedQualifierTests.java index 28b9f6ca1..c9007a0f4 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedQualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedQualifierTests.java @@ -17,19 +17,17 @@ package org.springframework.data.aerospike.query.reactive; import com.aerospike.client.Value; -import com.aerospike.client.query.Filter; import com.aerospike.client.query.IndexType; import com.aerospike.client.query.KeyRecord; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; -import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.data.aerospike.query.FilterOperation.EQ; @@ -44,7 +42,6 @@ import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.INDEXED_GEO_SET; import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.INDEXED_SET_NAME; import static org.springframework.data.aerospike.query.QueryEngineTestDataPopulator.ORANGE; -import static org.springframework.data.aerospike.utility.CollectionUtils.countingInt; /* * These tests generate qualifiers on indexed bins. @@ -66,7 +63,7 @@ public void selectOnIndexedLTQualifier() { .setValue1(Value.get(26)) .build(); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, new Query(qualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { @@ -98,7 +95,7 @@ public void selectOnIndexedLTEQQualifier() { .setValue1(Value.get(26)) .build(); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, new Query(qualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { @@ -133,7 +130,7 @@ public void selectOnIndexedNumericEQQualifier() { .setValue1(Value.get(26)) .build(); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, new Query(qualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -157,7 +154,7 @@ public void selectOnIndexedGTEQQualifier() { .setValue1(Value.get(28)) .build(); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, new Query(qualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { @@ -192,7 +189,7 @@ public void selectOnIndexedGTQualifier() { .setValue1(Value.get(28)) .build(); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, new Query(qualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { @@ -215,7 +212,7 @@ public void selectOnIndexedStringEQQualifier() { .setValue1(Value.get(ORANGE)) .build(); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, qualifier); + Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, new Query(qualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { @@ -230,48 +227,15 @@ public void selectOnIndexedStringEQQualifier() { } @Test - public void selectOnIndexFilter() { + public void selectWithBlueColorQuery() { withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { - Filter filter = Filter.range("age", 28, 29); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, filter); - StepVerifier.create(flux.collectList()) - .expectNextMatches(results -> { - Map ageCount = results.stream() - .map(rec -> rec.record.getInt("age")) - .collect(Collectors.groupingBy(k -> k, countingInt())); - assertThat(ageCount.keySet()) - .isNotEmpty() - .allSatisfy(age -> assertThat(age).isBetween(28, 29)); - assertThat(ageCount.get(28)).isEqualTo(queryEngineTestDataPopulator.ageCount.get(28)); - assertThat(ageCount.get(29)).isEqualTo(queryEngineTestDataPopulator.ageCount.get(29)); - return true; - }) - .verifyComplete(); - }); - } - - @Test - void selectOnIndexFilterNonExistingKeys() { - withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { - Filter filter = Filter.range("age", 30, 35); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, filter); - StepVerifier.create(flux) - .expectNextCount(0) - .verifyComplete(); - }); - } - - @Test - public void selectOnIndexWithQualifiers() { - withIndex(namespace, INDEXED_SET_NAME, "age_index", "age", IndexType.NUMERIC, () -> { - Filter filter = Filter.range("age", 25, 29); Qualifier qual1 = Qualifier.builder() .setField("color") .setFilterOperation(FilterOperation.EQ) .setValue1(Value.get(BLUE)) .build(); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, filter, qual1); + Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, new Query(qual1)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -301,7 +265,8 @@ public void selectWithQualifiersOnly() { .setValue2(Value.get(29)) .build(); - Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, qual1, qual2); + Flux flux = queryEngine.select(namespace, INDEXED_SET_NAME, null, + new Query(Qualifier.and(qual1, qual2))); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { @@ -331,7 +296,7 @@ public void selectWithGeoWithin() { .setValue1(Value.getAsGeoJSON(rgnstr)) .build(); - Flux flux = queryEngine.select(namespace, INDEXED_GEO_SET, null, qualifier); + Flux flux = queryEngine.select(namespace, INDEXED_GEO_SET, null, new Query(qualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveQualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveQualifierTests.java index c770e305d..12d70cbd2 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveQualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveQualifierTests.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.TestInstance; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; @@ -72,7 +73,7 @@ void throwsExceptionWhenScansDisabled() { .setValue1(Value.get(26)) .build(); - StepVerifier.create(queryEngine.select(namespace, SET_NAME, null, qualifier)) + StepVerifier.create(queryEngine.select(namespace, SET_NAME, null, new Query(qualifier))) .expectErrorSatisfies(e -> assertThat(e) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("disabled by default")) @@ -85,13 +86,13 @@ void throwsExceptionWhenScansDisabled() { @Test public void lTQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age < 26 - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField("age") .setFilterOperation(FilterOperation.LT) .setValue1(Value.get(26)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -109,13 +110,13 @@ public void lTQualifier() { @Test public void numericLTEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age <= 26 - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField("age") .setFilterOperation(FilterOperation.LTEQ) .setValue1(Value.get(26)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { AtomicInteger age25Count = new AtomicInteger(); @@ -140,13 +141,13 @@ public void numericLTEQQualifier() { @Test public void numericEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age == 26 - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField("age") .setFilterOperation(FilterOperation.EQ) .setValue1(Value.get(26)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { @@ -161,13 +162,13 @@ public void numericEQQualifier() { @Test public void numericGTEQQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age >= 28 - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField("age") .setFilterOperation(FilterOperation.GTEQ) .setValue1(Value.get(28)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { AtomicInteger age28Count = new AtomicInteger(); @@ -192,13 +193,13 @@ public void numericGTEQQualifier() { @Test public void numericGTQualifier() { // Ages range from 25 -> 29. We expected to only get back values with age > 28 or equivalently == 29 - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField("age") .setFilterOperation(FilterOperation.GT) .setValue1(Value.get(28)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -217,7 +218,7 @@ public void stringEQQualifier() { .setValue1(Value.get(ORANGE)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, stringEqQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(stringEqQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -237,7 +238,7 @@ public void stringEQQualifierCaseSensitive() { .setValue1(Value.get(ORANGE.toUpperCase())) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, stringEqQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(stringEqQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -258,7 +259,7 @@ public void stringStartWithQualifier() { .setValue1(Value.get("blu")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, stringEqQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(stringEqQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -277,7 +278,7 @@ public void stringStartWithEntireWordQualifier() { .setValue1(Value.get(BLUE)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, stringEqQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(stringEqQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -299,7 +300,7 @@ public void stringStartWithICASEQualifier() { .setValue1(Value.get("BLU")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, stringEqQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(stringEqQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -320,7 +321,7 @@ public void stringEndsWithQualifier() { .setValue1(Value.get(greenEnding)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, stringEqQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(stringEqQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -339,7 +340,7 @@ public void stringEndsWithEntireWordQualifier() { .setValue1(Value.get(GREEN)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, stringEqQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(stringEqQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -353,14 +354,14 @@ public void stringEndsWithEntireWordQualifier() { @Test public void betweenQualifier() { // Ages range from 25 -> 29. Get back age between 26 and 28 inclusive - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField("age") .setFilterOperation(FilterOperation.BETWEEN) .setValue1(Value.get(26)) .setValue2(Value.get(29)) // + 1 as upper limit is exclusive .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { AtomicInteger age26Count = new AtomicInteger(); @@ -391,13 +392,13 @@ public void containingQualifier() { .filter(c -> c.contains("l")) .collect(Collectors.toMap(c -> c, color -> queryEngineTestDataPopulator.colourCounts.get(color))); - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField("color") .setFilterOperation(FilterOperation.CONTAINING) .setValue1(Value.get("l")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { Map actualColors = results.stream() @@ -421,7 +422,7 @@ public void inQualifier() { .setValue1(Value.get(inColours)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, qualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(qualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { Map actualColors = results.stream() @@ -439,13 +440,13 @@ public void listContainsQualifier() { String binName = "colorList"; - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField(binName) .setFilterOperation(FilterOperation.LIST_VAL_CONTAINING) .setValue1(Value.get(searchColor)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { // Every Record with a color == "color" has a one element list ["color"] @@ -470,14 +471,14 @@ public void listBetweenQualifier() { String binName = "longList"; - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField(binName) .setFilterOperation(FilterOperation.LIST_VAL_BETWEEN) .setValue1(Value.get(ageStart)) .setValue2(Value.get(ageEnd)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { AtomicInteger age25Count = new AtomicInteger(); @@ -508,13 +509,13 @@ public void mapKeysContainsQualifier() { String binName = "colorAgeMap"; - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField(binName) .setFilterOperation(FilterOperation.MAP_KEYS_CONTAIN) .setValue1(Value.get(searchColor)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { // Every Record with a color == "color" has a one element map {"color" => #} @@ -537,13 +538,13 @@ public void testMapValuesContainsQualifier() { String binName = "ageColorMap"; - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField(binName) .setFilterOperation(FilterOperation.MAP_VALUES_CONTAIN) .setValue1(Value.get(searchColor)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { // Every Record with a color == "color" has a one element map {"color" => #} @@ -574,7 +575,7 @@ public void testMapKeysBetweenQualifier() { .setValue2(Value.get(ageEnd)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, ageRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { AtomicInteger age25Count = new AtomicInteger(); @@ -620,7 +621,7 @@ public void testMapValuesBetweenQualifier() { .setValue2(Value.get(ageEnd)) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, ageRangeQualifier); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { AtomicInteger age25Count = new AtomicInteger(); @@ -654,13 +655,13 @@ public void testMapValuesBetweenQualifier() { @Test public void testContainingDoesNotUseSpecialCharacterQualifier() { - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.CONTAINING) .setValue1(Value.get(".*")) .build(); - Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -673,13 +674,13 @@ public void testContainingDoesNotUseSpecialCharacterQualifier() { @Test public void testStartWithDoesNotUseSpecialCharacterQualifier() { - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.STARTS_WITH) .setValue1(Value.get(".*")) .build(); - Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -695,13 +696,13 @@ public void testStartWithDoesNotUseSpecialCharacterQualifier() { @Test public void testEndWithDoesNotUseSpecialCharacterQualifier() { - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.ENDS_WITH) .setValue1(Value.get(".*")) .build(); - Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -717,14 +718,14 @@ public void testEndWithDoesNotUseSpecialCharacterQualifier() { @Test public void testEQIcaseDoesNotUseSpecialCharacter() { - Qualifier AgeRangeQualifier = Qualifier.builder() + Qualifier ageRangeQualifier = Qualifier.builder() .setField(SPECIAL_CHAR_BIN) .setFilterOperation(FilterOperation.EQ) .setIgnoreCase(true) .setValue1(Value.get(".*")) .build(); - Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, AgeRangeQualifier); + Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(ageRangeQualifier)); StepVerifier.create(flux) .verifyComplete(); } @@ -740,7 +741,7 @@ public void testContainingFindsSquareBracket() { .setValue1(Value.get(specialString)) .build(); - Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, ageRangeQualifier); + Flux flux = queryEngine.select(namespace, SPECIAL_CHAR_SET, null, new Query(ageRangeQualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -769,7 +770,7 @@ public void stringEqualIgnoreCaseWorksOnIndexedBin() { .setValue1(Value.get("BlUe")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, caseInsensitiveQual); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(caseInsensitiveQual)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -802,7 +803,7 @@ public void selectWithOrQualifiers() { Qualifier or = Qualifier.or(qual1, qual2); - Flux flux = queryEngine.select(namespace, SET_NAME, null, or); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(or)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { AtomicInteger colorMatched = new AtomicInteger(); @@ -857,7 +858,7 @@ public void selectWithBetweenAndOrQualifiers() { Qualifier or2 = Qualifier.or(qualColorIsGreen, qualNameIs696); Qualifier and = Qualifier.and(or, or2); - Flux flux = queryEngine.select(namespace, SET_NAME, null, and); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(and)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { AtomicBoolean has25 = new AtomicBoolean(false); diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveSelectorTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveSelectorTests.java index 21b14608d..030c381bd 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveSelectorTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveSelectorTests.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.query.KeyQualifier; import org.springframework.data.aerospike.query.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; @@ -25,7 +26,7 @@ public class ReactiveSelectorTests extends BaseReactiveQueryEngineTests { @Test public void selectOneWithKey() { KeyQualifier kq = new KeyQualifier(Value.get("selector-test:3")); - Flux flux = queryEngine.select(namespace, SET_NAME, null, kq); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(kq)); StepVerifier.create(flux) .expectNextCount(1) @@ -35,7 +36,7 @@ public void selectOneWithKey() { @Test public void selectOneWithNonExistingKey() { KeyQualifier kq = new KeyQualifier(Value.get("selector-test:no-such-record")); - Flux flux = queryEngine.select(namespace, SET_NAME, null, kq); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(kq)); StepVerifier.create(flux) .expectNextCount(0) @@ -44,7 +45,7 @@ public void selectOneWithNonExistingKey() { @Test public void selectAll() { - Flux flux = queryEngine.select(namespace, SET_NAME, null); + Flux flux = queryEngine.select(namespace, SET_NAME, null, null); StepVerifier.create(flux) .expectNextCount(RECORD_COUNT) @@ -59,7 +60,7 @@ public void selectEndssWith() { .setValue1(Value.get("e")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, qual1); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(qual1)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -78,7 +79,7 @@ public void selectStartsWith() { .setValue1(Value.get("bl")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, startsWithQual); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(startsWithQual)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -106,7 +107,7 @@ public void startWithAndEqualIgnoreCaseReturnsAllItems() { .setValue1(Value.get("NA")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, qual1, qual2); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(Qualifier.and(qual1, qual2))); StepVerifier.create(flux) .expectNextCount(queryEngineTestDataPopulator.colourCounts.get("blue")) .verifyComplete(); @@ -122,7 +123,7 @@ public void equalIgnoreCaseReturnsNoItemsIfNoneMatched() { .setValue1(Value.get("BLUE")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, qual1); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(qual1)); StepVerifier.create(flux) .expectNextCount(0) .verifyComplete(); @@ -138,7 +139,7 @@ public void startWithIgnoreCaseReturnsNoItemsIfNoneMatched() { .setValue1(Value.get("NA")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, qual1); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(qual1)); StepVerifier.create(flux) .expectNextCount(0) .verifyComplete(); @@ -156,7 +157,7 @@ public void stringEqualIgnoreCaseWorksOnUnindexedBin() { .setValue1(Value.get("BlUe")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, caseInsensitiveQual); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(caseInsensitiveQual)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) @@ -177,7 +178,7 @@ public void stringEqualIgnoreCaseWorksRequiresFullMatch() { .setValue1(Value.get("lue")) .build(); - Flux flux = queryEngine.select(namespace, SET_NAME, null, caseInsensitiveQual); + Flux flux = queryEngine.select(namespace, SET_NAME, null, new Query(caseInsensitiveQual)); StepVerifier.create(flux) .expectNextCount(0) @@ -198,7 +199,7 @@ public void selectWithGeoWithin() { .setValue1(Value.getAsGeoJSON(rgnstr)) .build(); - Flux flux = queryEngine.select(namespace, GEO_SET, null, qual1); + Flux flux = queryEngine.select(namespace, GEO_SET, null, new Query(qual1)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { assertThat(results) diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveUsersTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveUsersTests.java index 99e0d6aa0..7ea82b220 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveUsersTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveUsersTests.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; @@ -21,7 +22,7 @@ public void usersInNorthRegion() { .setValue1(Value.get("n")) .build(); - Flux flux = queryEngine.select(namespace, USERS_SET, null, qualifier); + Flux flux = queryEngine.select(namespace, USERS_SET, null, new Query(qualifier)); StepVerifier.create(flux.collectList()) .expectNextMatches(results -> { diff --git a/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java b/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java index a7c082d05..605c0f93a 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java @@ -9,6 +9,7 @@ import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.Qualifier; +import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.sample.Address; import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.sample.PersonRepository; @@ -1201,21 +1202,35 @@ public void deleteAllPersonsFromList() { } @Test - public void findPersonByIdAndFirstName() { - List persons = repository.findByIdAndFirstName(dave.getId(), dave.getFirstName()); + public void findPersonByIdsAndFields() { + List persons = repository.findByIdAndFirstName(List.of(boyd.getId(), dave.getId(), carter.getId()), + dave.getFirstName()); assertThat(persons).containsOnly(dave); - } - @Test - public void findPersonByIdAndFirstNameEmptyResult() { - List persons = repository.findByIdAndFirstName(dave.getId(), carter.getFirstName()); - assertThat(persons).isEmpty(); + List persons2 = repository.findByIdAndFirstNameAndAge(List.of(leroi.getId(), leroi2.getId(), + carter.getId()), + leroi.getFirstName(), leroi2.getAge()); + assertThat(persons2).containsOnly(leroi2); + + // "findByIdOr..." will return empty list because primary keys are used to firstly narrow down the results.parts + // In a combined id query "OR" can be put only between the non-id fields like shown below, + // and the results are limited by the given ids + List persons3 = repository.findByIdAndFirstNameOrAge(List.of(leroi.getId(), leroi2.getId(), + carter.getId()), + leroi.getFirstName(), leroi2.getAge()); + assertThat(persons3).containsOnly(leroi, leroi2); + + List persons4 = repository.findByIdAndFirstNameOrAge(List.of(leroi.getId(), leroi2.getId(), + carter.getId()), + leroi.getFirstName(), stefan.getAge()); + assertThat(persons4).containsOnly(leroi, leroi2); } @Test - public void findPersonByFirstNameAndId() { - List persons = repository.findByFirstNameAndId(dave.getFirstName(), dave.getId()); - assertThat(persons).containsOnly(dave); + public void findPersonByIdsAndFirstNameEmptyResult() { + List persons = repository.findByIdAndFirstName(List.of(dave.getId(), boyd.getId()), + carter.getFirstName()); + assertThat(persons).isEmpty(); } @Test @@ -1254,7 +1269,7 @@ public void findPersonsByMetadata() { .setFilterOperation(FilterOperation.LT) .setValue1AsObj(50000L) .build(); - assertThat(repository.findByQualifier(sinceUpdateTimeLt10Seconds)).containsAll(allPersons); + assertThat(repository.findUsingQuery(new Query(sinceUpdateTimeLt10Seconds))).containsAll(allPersons); // creating a condition "since_update_time metadata value is between 1 millisecond and 50 seconds" Qualifier sinceUpdateTimeBetween1And50000 = Qualifier.metadataBuilder() @@ -1263,12 +1278,12 @@ public void findPersonsByMetadata() { .setValue1AsObj(1L) .setValue2AsObj(50000L) .build(); - assertThat(repository.findByQualifier(sinceUpdateTimeBetween1And50000)) - .containsAll(repository.findByQualifier(sinceUpdateTimeLt10Seconds)); + assertThat(repository.findUsingQuery(new Query(sinceUpdateTimeBetween1And50000))) + .containsAll(repository.findUsingQuery(new Query(sinceUpdateTimeLt10Seconds))); } @Test - public void findPersonsByQualifiers() { + public void findPersonsByQuery() { Iterable result; // creating a condition "since_update_time metadata value is greater than 1 millisecond" @@ -1284,7 +1299,7 @@ public void findPersonsByQualifiers() { .setFilterOperation(FilterOperation.LT) .setValue1AsObj(50000L) .build(); - assertThat(repository.findByQualifier(sinceUpdateTimeLt50Seconds)).containsAll(allPersons); + assertThat(repository.findUsingQuery(new Query(sinceUpdateTimeLt50Seconds))).containsAll(allPersons); // creating a condition "since_update_time metadata value is between 1 millisecond and 50 seconds" Qualifier sinceUpdateTimeBetween1And50000 = Qualifier.metadataBuilder() @@ -1294,7 +1309,7 @@ public void findPersonsByQualifiers() { .setValue2AsObj(50000L) .build(); - // creating a condition "firsName is equal to Carter" + // creating a condition "firstName is equal to Carter" Qualifier firstNameEqCarter = Qualifier.builder() .setField("firstName") .setFilterOperation(FilterOperation.EQ) @@ -1307,128 +1322,147 @@ public void findPersonsByQualifiers() { .setFilterOperation(FilterOperation.EQ) .setValue1(Value.get(49)) .build(); - result = repository.findByQualifier(ageEq49); + result = repository.findUsingQuery(new Query(ageEq49)); assertThat(result).containsOnly(carter); + // creating a condition "firstName is equal to Leroi" with sorting by age and limiting by 1 row + Qualifier firstNameEqLeroi = Qualifier.builder() + .setField("firstName") + .setFilterOperation(FilterOperation.EQ) + .setValue1(Value.get("Leroi")) + .build(); + Query query = new Query(firstNameEqLeroi); + query.setSort(Sort.by("age")); + query.setRows(1); + result = repository.findUsingQuery(query); + assertThat(result).containsOnly(leroi2); + // creating a condition "age is greater than 49" Qualifier ageGt49 = Qualifier.builder() .setFilterOperation(FilterOperation.GT) .setField("age") .setValue1(Value.get(49)) .build(); - result = repository.findByQualifier(ageGt49); + result = repository.findUsingQuery(new Query(ageGt49)); assertThat(result).doesNotContain(carter); // creating a condition "id equals Carter's id" Qualifier keyEqCartersId = Qualifier.idEquals(carter.getId()); - result = repository.findByQualifier(keyEqCartersId); + result = repository.findUsingQuery(new Query(keyEqCartersId)); assertThat(result).containsOnly(carter); // creating a condition "id equals Boyd's id" Qualifier keyEqBoydsId = Qualifier.idEquals(boyd.getId()); - result = repository.findByQualifier(keyEqBoydsId); + result = repository.findUsingQuery(new Query(keyEqBoydsId)); assertThat(result).containsOnly(boyd); // analogous to {@link SimpleAerospikeRepository#findAllById(Iterable)} // creating a condition "id equals Carter's id OR Boyd's id" Qualifier keyEqMultipleIds = Qualifier.idIn(carter.getId(), boyd.getId()); - result = repository.findByQualifier(keyEqMultipleIds); + result = repository.findUsingQuery(new Query(keyEqMultipleIds)); assertThat(result).containsOnly(carter, boyd); // metadata and id qualifiers combined with AND - // not more than one id qualifier is allowed, otherwise the conditions will not overlap - result = repository.findByQualifier(Qualifier.and(sinceUpdateTimeGt1, keyEqCartersId)); + // not more than one id qualifier is allowed, otherwise the conditions will not overlap because of uniqueness + result = repository.findUsingQuery(new Query(Qualifier.and(sinceUpdateTimeGt1, keyEqCartersId))); + // if a query contains id qualifier the results are firstly narrowed down to satisfy the given id(s) + // that's why queries with qualifier like Qualifier.or(Qualifier.idEquals(...), ageGt49)) return empty result assertThat(result).containsOnly(carter); + // if a query contains id qualifier the results are firstly narrowed down to satisfy the given id(s) + result = repository.findUsingQuery(new Query(Qualifier.and(sinceUpdateTimeGt1, Qualifier.idIn(carter.getId(), + dave.getId(), boyd.getId())))); + assertThat(result).containsOnly(carter, dave, boyd); + // the same qualifiers in different order - result = repository.findByQualifier(Qualifier.and(keyEqCartersId, sinceUpdateTimeGt1)); + result = repository.findUsingQuery(new Query(Qualifier.and(keyEqCartersId, sinceUpdateTimeGt1))); assertThat(result).containsOnly(carter); - result = repository.findByQualifier(Qualifier.and(sinceUpdateTimeGt1, + result = repository.findUsingQuery(new Query(Qualifier.and(sinceUpdateTimeGt1, sinceUpdateTimeLt50Seconds, ageEq49, firstNameEqCarter, - sinceUpdateTimeBetween1And50000, keyEqCartersId)); + sinceUpdateTimeBetween1And50000, keyEqCartersId))); assertThat(result).containsOnly(carter); // conditions "age == 49", "firstName is Carter" and "since_update_time metadata value is less than 50 seconds" // are combined with OR Qualifier orWide = Qualifier.or(ageEq49, firstNameEqCarter, sinceUpdateTimeLt50Seconds); - result = repository.findByQualifier(orWide); + result = repository.findUsingQuery(new Query(orWide)); assertThat(result).containsAll(allPersons); // conditions "age == 49" and "firstName is Carter" are combined with OR Qualifier orNarrow = Qualifier.or(ageEq49, firstNameEqCarter); - result = repository.findByQualifier(orNarrow); + result = repository.findUsingQuery(new Query(orNarrow)); assertThat(result).containsOnly(carter); // conditions "age == 49" and "age > 49" are not overlapping - result = repository.findByQualifier(Qualifier.and(ageEq49, ageGt49)); + result = repository.findUsingQuery(new Query(Qualifier.and(ageEq49, ageGt49))); assertThat(result).isEmpty(); // conditions "age == 49" and "age > 49" are combined with OR Qualifier ageEqOrGt49 = Qualifier.or(ageEq49, ageGt49); - result = repository.findByQualifier(ageEqOrGt49); + result = repository.findUsingQuery(new Query(ageEqOrGt49)); List personsWithAgeEqOrGt49 = allPersons.stream().filter(person -> person.getAge() >= 49).toList(); assertThat(result).containsAll(personsWithAgeEqOrGt49); // a condition that returns all entities and a condition that returns one entity are combined using AND - result = repository.findByQualifier(Qualifier.and(orWide, orNarrow)); + result = repository.findUsingQuery(new Query(Qualifier.and(orWide, orNarrow))); assertThat(result).containsOnly(carter); // a condition that returns all entities and a condition that returns one entity are combined using AND // another way of running the same query Qualifier orCombinedWithAnd = Qualifier.and(orWide, orNarrow); - result = repository.findByQualifier(orCombinedWithAnd); + result = repository.findUsingQuery(new Query(orCombinedWithAnd)); assertThat(result).containsOnly(carter); } @Test public void findPersonsByQualifiersMustBeValid() { - assertThatThrownBy(() -> repository.findByQualifier(Qualifier.metadataBuilder() + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.BETWEEN) .setValue1AsObj(1L) - .build())) + .build()))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("BETWEEN: value2 is expected to be set as Long"); - assertThatThrownBy(() -> repository.findByQualifier(Qualifier.metadataBuilder() + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.BETWEEN) .setValue2AsObj(1L) - .build())) + .build()))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("BETWEEN: value1 is expected to be set as Long"); - assertThatThrownBy(() -> repository.findByQualifier(Qualifier.metadataBuilder() + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.GT) .setValue1AsObj(1) - .build())) + .build()))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("GT: value1 is expected to be set as Long"); - assertThatThrownBy(() -> repository.findByQualifier(Qualifier.metadataBuilder() + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.LT) .setValue1AsObj(1) - .build())) + .build()))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("LT: value1 is expected to be set as Long"); - assertThatThrownBy(() -> repository.findByQualifier(Qualifier.metadataBuilder() + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.LTEQ) .setValue1AsObj(Value.get(1)) - .build())) + .build()))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("LTEQ: value1 is expected to be set as Long"); - assertThatThrownBy(() -> repository.findByQualifier(Qualifier.metadataBuilder() + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.metadataBuilder() .setMetadataField(SINCE_UPDATE_TIME) .setFilterOperation(FilterOperation.STARTS_WITH) .setValue1AsObj(1L) - .build())) + .build()))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Operation STARTS_WITH cannot be applied to metadataField"); @@ -1436,19 +1470,19 @@ public void findPersonsByQualifiersMustBeValid() { Qualifier keyEqBoydsId = Qualifier.idEquals(boyd.getId()); // not more than one id qualifier is allowed - assertThatThrownBy(() -> repository.findByQualifier(Qualifier.and(keyEqCartersId, - keyEqBoydsId))) + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.and(keyEqCartersId, + keyEqBoydsId)))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Expecting not more than one id qualifier in qualifiers array, got 2"); // not more than one id qualifier is allowed - assertThatThrownBy(() -> repository.findByQualifier(Qualifier.and(keyEqCartersId, - keyEqBoydsId))) + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.and(keyEqCartersId, + keyEqBoydsId)))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Expecting not more than one id qualifier in qualifiers array, got 2"); // not more than one id qualifier is allowed - assertThatThrownBy(() -> repository.findByQualifier(Qualifier.or(keyEqCartersId, keyEqBoydsId))) + assertThatThrownBy(() -> repository.findUsingQuery(new Query(Qualifier.or(keyEqCartersId, keyEqBoydsId)))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Expecting not more than one id qualifier in qualifiers array, got 2"); } @@ -1479,14 +1513,16 @@ public void findByIdDynamicProjection() { @Test public void findByIdAndLastNameDynamicProjection() { - List result = repository.findByIdAndLastName(carter.getId(), carter.getLastName(), + List result = repository.findByIdAndLastName(List.of(carter.getId(), leroi.getId(), + leroi2.getId()), carter.getLastName(), PersonSomeFields.class); assertThat(result).containsOnly(carter.toPersonSomeFields()); } @Test public void findByIdAndLastNameDynamicProjectionNullResult() { - List result = repository.findByIdAndLastName(carter.getId(), dave.getLastName(), + List result = repository.findByIdAndLastName(List.of(carter.getId(), boyd.getId()), + dave.getLastName(), PersonSomeFields.class); assertThat(result).isEmpty(); } diff --git a/src/test/java/org/springframework/data/aerospike/sample/PersonRepository.java b/src/test/java/org/springframework/data/aerospike/sample/PersonRepository.java index d385f3995..9af28fda7 100644 --- a/src/test/java/org/springframework/data/aerospike/sample/PersonRepository.java +++ b/src/test/java/org/springframework/data/aerospike/sample/PersonRepository.java @@ -56,7 +56,7 @@ public interface PersonRepository

extends AerospikeRepository< List findById(String id, Class type); // Dynamic Projection - List findByIdAndLastName(String id, String lastName, Class type); + List findByIdAndLastName(List ids, String lastName, Class type); // Dynamic Projection List findByLastNameAndId(String lastName, String id, Class type); @@ -64,11 +64,33 @@ public interface PersonRepository

extends AerospikeRepository< // Dynamic Projection List findByFirstNameAndLastName(String firstName, String lastName, Class type); + /** + * Find all entities that have primary key in the given list. + * + * @param ids List of primary keys + */ List

findById(List ids); - List

findByIdAndFirstName(String id, String firstName); + /** + * Find all entities that satisfy the condition "have primary key in the given list and first name equal to the + * specified string". + * + * @param ids List of primary keys + * @param firstName String to compare with + */ + List

findByIdAndFirstName(List ids, String firstName); + + /** + * Find all entities that satisfy the condition "have primary key in the given list and either first name equal to + * the specified string or age equal to the specified integer". + * + * @param ids List of primary keys + * @param firstName String to compare firstName with + * @param age integer to compare age with + */ + List

findByIdAndFirstNameAndAge(List ids, String firstName, int age); - List

findByFirstNameAndId(String firstName, String id); + List

findByIdAndFirstNameOrAge(List ids, String firstName, int age); Page

findByLastNameStartsWithOrderByAgeAsc(String prefix, Pageable pageable);