diff --git a/README.adoc b/README.adoc index 42105d68e..efba0fe75 100644 --- a/README.adoc +++ b/README.adoc @@ -18,24 +18,21 @@ writing a repository style data access layer. == Documentation -* The https://aerospike.github.io/spring-data-aerospike[Documentation Reference] +* Spring Data Aerospike https://aerospike.github.io/spring-data-aerospike[Documentation Reference] * Java code documentation on https://www.javadoc.io/doc/com.aerospike/spring-data-aerospike[javadoc.io] -* https://docs.aerospike.com/[Aerospike documentation] +* Aerospike https://docs.aerospike.com/[Official Documentation] +* If you are new to Spring as well as to Spring Data, look for information +about https://projects.spring.io/[Spring Projects] -If you are new to Spring as well as to Spring Data, look for information -about https://projects.spring.io/[Spring projects]. +== Examples -== Demo Projects - -[arabic] -. Demo project with detailed guides is located -https://github.com/aerospike-community/spring-data-aerospike-demo[here]. . Demo project example with a step-by-step tutorial can be found -https://github.com/aerospike-examples/simple-springboot-aerospike-demo[here]. +https://github.com/aerospike-examples/simple-springboot-aerospike-demo[here] +. Demo project with detailed guides is located +https://github.com/aerospike-community/spring-data-aerospike-demo[here] -== Getting Started blog posts +== Getting Started Blog Posts -[arabic] . https://medium.com/aerospike-developer-blog/simple-web-application-using-java-spring-boot-aerospike-database-and-docker-ad13795e0089?source=friends_link&sk=43d747f5f55e527248125eeb18748d92[Simple Web Application Using Java, Spring Boot, Aerospike and Docker] . https://medium.com/aerospike-developer-blog/how-to-setup-spring-data-aerospike-in-spring-boot-application-afa8bcb59224?source=friends_link&sk=e16a3b69c814bfb22f200634c743e476[How @@ -51,8 +48,11 @@ Data Aerospike: Reactive Repositories] . https://medium.com/aerospike-developer-blog/spring-data-aerospike-projections-951382bc07b5?source=friends_link&sk=d0a3be4fd171bbc9e072d09ccbcf056f[Spring Data Aerospike - Projections] -== Spring Data Aerospike compatibility +== Spring Data Aerospike Compatibility +.Compatibility Table +[%collapsible] +==== [width="100%",cols="<24%,<14%,<18%,<26%,<18%",options="header",] |=== |Spring Data Aerospike |Spring Boot |Aerospike Client |Aerospike Reactor Client |Aerospike Server @@ -86,55 +86,38 @@ Data Aerospike - Projections] |1.2.1.RELEASE |1.5.x |4.1.x | | |=== +==== == Quick Start -=== Maven configuration +=== 1. Maven configuration -Add the Maven dependency: +Add the `spring-data-aerospike` Maven dependency: [source,xml] ---- com.aerospike spring-data-aerospike - 4.5.0 + ${spring-data-aerospike.version} ---- -The Aerospike Spring Data connector depends on the Aerospike Client -project: +Notes: -[source,xml] ----- - - com.aerospike - aerospike-client - ----- - -Dependency will be provided for you by `spring-data-aerospike`, so no -need to declare it additionally. +* It is recommended to use the latest version of Spring Data Aerospike, checkout Spring Data Aerospike +https://github.com/aerospike/spring-data-aerospike/releases[GitHub releases] +* Spring Data Aerospike uses the https://github.com/aerospike/aerospike-client-java[Aerospike Java Client] +(and https://github.com/aerospike/aerospike-client-java-reactive[Aerospike Reactive Java Client]) under the hood -=== AerospikeTemplate +=== 2. AerospikeRepository -`AerospikeTemplate` is the central support class for Aerospike database -operations. It provides: +`AerospikeRepository` is the simplest way to interact with Aerospike using Spring Data Aerospike. -* Basic POJO mapping support to and from Bins -* Convenience methods to interact with the store (insert object, update -objects) and Aerospike specific ones. -* Connection callback -* Exception translation into Spring’s -https://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html#dao-exceptions[technology-agnostic -DAO exception hierarchy]. - -=== Spring Data repositories +Create your own custom repository that extends `AerospikeRepository` which will provide out-of-the-box CRUD operations +and query implementations, so you can easily save, find, delete and query single entities and collections of them. -To simplify the creation of data repositories Spring Data Aerospike -provides a generic repository programming model. It will automatically -create a repository proxy for you that adds implementations of finder -methods you specify on an interface. +Implementation will be determined by the method names automatically, no need to write any implementation. For example, given a `Person` class with first and last name properties, a `PersonRepository` interface that can query for `Person` by last name @@ -150,13 +133,17 @@ public interface PersonRepository extends AerospikeRepository { } ---- -The queries issued on execution will be derived from the method name. -Extending `AerospikeRepository` causes CRUD methods being pulled into -the interface so that you can easily save and find single entities and -collections of them. +For non-blocking reactive API use `ReactiveAerospikeRepository`. + +=== 3. Configuration -You can have Spring automatically create a proxy for the interface by -using the following JavaConfig: +In order to configure Spring Data Aerospike you will need to create a configuration class that extends +`AbstractAerospikeDataConfiguration`, defines the relevant Spring Data Repositories via `@EnableAerospikeRepositories` +annotation and overrides `getHosts()` and `nameSpace()` methods with the required connection details. + +You can optionally override other methods of `AbstractAerospikeDataConfiguration` to customize your configuration. + +Here is a simple example of a configuration class that sets up a connection to a local Aerospike instance: [source,java] ---- @@ -171,69 +158,88 @@ class ApplicationConfig extends AbstractAerospikeDataConfiguration { @Override protected String nameSpace() { - return "TEST"; + return "test"; } } ---- -This sets up a connection to a local Aerospike instance and enables the -detection of Spring Data repositories (through -`@EnableAerospikeRepositories`). +=== Usage + +Below is an example of a service that uses `PersonRepository` operations. -This will find the repository interface and register a proxy object in -the container. You can use it as shown below: +* `deleteAll` and `save` are provided automatically by extending `AerospikeRepository` interface +* `findByFirstnameLike` and `findByLastname` methods were defined in `PersonRepository` but there was no need to write +the actual implementation, it is determined automatically from the method names [source,java] ---- @Service -public class MyService { +public class PersonService { - private final PersonRepository repository; + private final PersonRepository personRepository; @Autowired - public MyService(PersonRepository repository) { - this.repository = repository; + public PersonService(PersonRepository personRepository) { + this.personRepository = personRepository; } - public void doWork() { - repository.deleteAll(); + public void example() { + // Delete all existing persons + personRepository.deleteAll(); Person person = new Person(); - person.setFirstname("Oliver"); - person.setLastname("Gierke"); - repository.save(person); - - List lastNameResults = repository.findByLastname("Gierke"); - List firstNameResults = repository.findByFirstnameLike("Oli*"); + person.setFirstname("John"); + person.setLastname("Smith"); + // Save the new created person + personRepository.save(person); + + // Get all persons whose first name starts with "Jo" + List firstNameResults = personRepository.findByFirstnameLike("Jo*"); + // Get all persons whose last name is equal to "Smith" + List lastNameResults = personRepository.findByLastname("Smith"); } } ---- -== Getting Help +=== AerospikeOperations + +`AerospikeOperations` is the base interface for Aerospike database operations. It is implemented by +`AerospikeTemplate` class. -See <>. +As a lower-level alternative to `AerospikeRepository`, `AerospikeOperations` supports wider variety of operations and +greater flexibility, but requires a bit more code writing and less out-of-the-box functionality. + +Features supported by `AerospikeOperations`: + +* Basic support for mapping POJOs to and from Aerospike bins +* Convenience CRUD (Create, Read, Update and Delete) methods for interacting with Aerospike +* Rich Query API +* Access to the native Aerospike Java Client (reactive and non-reactive) +* Translating exceptions into Spring's +https://docs.spring.io/spring/docs/current/spring-framework-reference/html/dao.html#dao-exceptions[technology-agnostic +DAO exception hierarchy] + +For non-blocking reactive API use `ReactiveAerospikeOperations`. + +== Getting Help -For more detailed questions you can use -https://stackoverflow.com/questions/tagged/spring-data-aerospike[Spring -Data Aerospike on Stackoverflow]. +* See <> +* Ask a specific question using +https://stackoverflow.com/questions/tagged/spring-data-aerospike[Spring Data Aerospike tag on StackOverflow] -== Contributing to Spring Data +== Contributing to Spring Data Aerospike Here are some ways you can get involved: -* Get involved with the Spring community on Stackoverflow and help out -on the -https://stackoverflow.com/questions/tagged/spring-data-aerospike[spring-data-aerospike] -tag by responding to questions and joining the debate. -* Create +* Help out on the StackOverflow https://stackoverflow.com/questions/tagged/spring-data-aerospike[spring-data-aerospike] +tag by responding to questions and joining the debate +* Create a https://github.com/aerospike/spring-data-aerospike/issues[GitHub -issue] for bugs and new features and comment and vote on the ones that -you are interested in. -* GitHub is for social coding: if you want to write code, we encourage -contributions through pull requests from -https://help.github.com/forking/[forks of this repository]. If you want -to contribute code this way, please reference a GitHub ticket as well -covering the specific issue you are addressing. +issue] for a feature request or bug fixing, comment and vote on the ones that +you are interested in +* GitHub is for social coding: we encourage contributions through pull requests from +https://help.github.com/forking/[forks of this repository]. When contributing code, please reference a specific +GitHub issue you are addressing * Watch for upcoming articles by https://www.aerospike.com/forms/subscribe-the-aerospike-standup/[subscribing] -to Aerospike Stand-Up. \ No newline at end of file +to Aerospike Stand-Up \ No newline at end of file diff --git a/src/main/java/org/springframework/data/aerospike/core/AerospikeInternalOperations.java b/src/main/java/org/springframework/data/aerospike/core/AerospikeInternalOperations.java deleted file mode 100644 index c4f0f41ff..000000000 --- a/src/main/java/org/springframework/data/aerospike/core/AerospikeInternalOperations.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.springframework.data.aerospike.core; - -import com.aerospike.client.AerospikeException; -import org.springframework.data.aerospike.query.Qualifier; - -import java.util.Collection; -import java.util.List; - -public interface AerospikeInternalOperations { - - /** - * Find document by providing id, set name will be determined by the given entityClass. - *

- * Documents will be mapped to the given targetClass. - * - * @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. - * @return The document from Aerospike, returned document will be mapped to targetClass's type. - */ - Object findByIdInternal(Object id, Class entityClass, Class targetClass, Qualifier... qualifiers); - - /** - * Find document by providing id with a given set name. - *

- * Documents will be mapped to the given targetClass. - * - * @param id The id of the document to find. Must not be {@literal null}. - * @param entityClass The class to get the entity properties from (such as expiration). Must not be - * {@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. - * @return The document from Aerospike, returned document will be mapped to targetClass's type. - */ - Object findByIdInternal(Object id, Class entityClass, Class targetClass, String setName, - Qualifier... qualifiers); - - /** - * Find documents by providing multiple ids, set name will be determined by the given entityClass. - *

- * Documents will be mapped to the given targetClass. - * - * @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. - * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document - * exists, an empty list is returned. - */ - List findByIdsInternal(Collection ids, Class entityClass, Class targetClass, - Qualifier... qualifiers); - - /** - * Find documents by providing multiple ids with a given set name. - *

- * Documents will be mapped to the given targetClass. - * - * @param ids The ids of the documents to find. Must not be {@literal null}. - * @param entityClass The class to get the entity properties from (such as expiration). Must not be - * {@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. - * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document - * exists, an empty list is returned. - */ - List findByIdsInternal(Collection ids, Class entityClass, Class targetClass, - String setName, Qualifier... qualifiers); - - /** - * Batch delete documents by providing their ids. Set name will be determined by the given entityClass. - *

- * This operation requires Server version 6.0+. - * - * @param ids The ids of the documents to delete. Must not be {@literal null}. - * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. - * @throws AerospikeException.BatchRecordArray if batch delete results contain errors or null records - */ - void deleteByIdsInternal(Collection ids, Class entityClass); - - /** - * Batch delete documents by providing their ids with a given set name. - *

- * This operation requires Server version 6.0+. - * - * @param ids The ids of the documents to delete. Must not be {@literal null}. - * @param setName Set name to delete the documents from. - * @throws AerospikeException.BatchRecordArray if batch delete results contain errors or null records - */ - void deleteByIdsInternal(Collection ids, String setName); -} 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 694fa2fd6..4ec19c688 100644 --- a/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java +++ b/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java @@ -75,58 +75,6 @@ public interface AerospikeOperations { */ IAerospikeClient getAerospikeClient(); - /** - * Insert a document using {@link com.aerospike.client.policy.RecordExistsAction#CREATE_ONLY} policy. - *

- * If document has version property it will be updated with the server's version after successful operation. - * - * @param document The document to insert. Must not be {@literal null}. - */ - void insert(T document); - - /** - * Insert a document with a given set name (overrides the set associated with the document) using - * {@link com.aerospike.client.policy.RecordExistsAction#CREATE_ONLY} policy. - *

- * If document has version property it will be updated with the server's version after successful operation. - * - * @param document The document to insert. Must not be {@literal null}. - * @param setName Set name to override the set associated with the document. - */ - void insert(T document, String setName); - - /** - * Insert multiple documents in one batch request. The policies are analogous to {@link #insert(Object)}. - *

- * The order of returned results is preserved. The execution order is NOT preserved. - *

- * This operation requires Server version 6.0+. - * - * @param documents Documents to insert. Must not be {@literal null}. - * @throws AerospikeException.BatchRecordArray if batch insert succeeds, but results contain errors or null - * records - * @throws org.springframework.dao.DataAccessException if batch operation failed (see - * {@link DefaultAerospikeExceptionTranslator} for details) - */ - void insertAll(Iterable documents); - - /** - * Insert multiple documents with a given set name (overrides the set associated with the document) in one batch - * request. The policies are analogous to {@link #insert(Object)}. - *

- * The order of returned results is preserved. The execution order is NOT preserved. - *

- * This operation requires Server version 6.0+. - * - * @param documents Documents to insert. Must not be {@literal null}. - * @param setName Set name to override the set associated with the document. - * @throws AerospikeException.BatchRecordArray if batch insert succeeds, but results contain errors or null - * records - * @throws org.springframework.dao.DataAccessException if batch operation failed (see - * {@link DefaultAerospikeExceptionTranslator} for details) - */ - void insertAll(Iterable documents, String setName); - /** * Save a document. *

@@ -200,6 +148,58 @@ public interface AerospikeOperations { */ void saveAll(Iterable documents, String setName); + /** + * Insert a document using {@link com.aerospike.client.policy.RecordExistsAction#CREATE_ONLY} policy. + *

+ * If document has version property it will be updated with the server's version after successful operation. + * + * @param document The document to insert. Must not be {@literal null}. + */ + void insert(T document); + + /** + * Insert a document with a given set name (overrides the set associated with the document) using + * {@link com.aerospike.client.policy.RecordExistsAction#CREATE_ONLY} policy. + *

+ * If document has version property it will be updated with the server's version after successful operation. + * + * @param document The document to insert. Must not be {@literal null}. + * @param setName Set name to override the set associated with the document. + */ + void insert(T document, String setName); + + /** + * Insert multiple documents in one batch request. The policies are analogous to {@link #insert(Object)}. + *

+ * The order of returned results is preserved. The execution order is NOT preserved. + *

+ * This operation requires Server version 6.0+. + * + * @param documents Documents to insert. Must not be {@literal null}. + * @throws AerospikeException.BatchRecordArray if batch insert succeeds, but results contain errors or null + * records + * @throws org.springframework.dao.DataAccessException if batch operation failed (see + * {@link DefaultAerospikeExceptionTranslator} for details) + */ + void insertAll(Iterable documents); + + /** + * Insert multiple documents with a given set name (overrides the set associated with the document) in one batch + * request. The policies are analogous to {@link #insert(Object)}. + *

+ * The order of returned results is preserved. The execution order is NOT preserved. + *

+ * This operation requires Server version 6.0+. + * + * @param documents Documents to insert. Must not be {@literal null}. + * @param setName Set name to override the set associated with the document. + * @throws AerospikeException.BatchRecordArray if batch insert succeeds, but results contain errors or null + * records + * @throws org.springframework.dao.DataAccessException if batch operation failed (see + * {@link DefaultAerospikeExceptionTranslator} for details) + */ + void insertAll(Iterable documents, String setName); + /** * Persist a document using specified WritePolicy. * @@ -321,18 +321,21 @@ public interface AerospikeOperations { boolean delete(Object id, Class entityClass); /** - * Truncate/Delete all the documents in the given entity's set. + * Delete a document. * - * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @param document The document to delete. Must not be {@literal null}. + * @return whether the document existed on server before deletion. */ - void deleteAll(Class entityClass); + boolean delete(T document); /** - * Truncate/Delete all the documents in the given set. + * Delete a document with a given set name. * - * @param setName Set name to truncate/delete all the documents in. + * @param document The document to delete. Must not be {@literal null}. + * @param setName Set name to delete the document from. + * @return whether the document existed on server before deletion. */ - void deleteAll(String setName); + boolean delete(T document, String setName); /** * Delete a document by id, set name will be determined by the given entityClass. @@ -352,23 +355,6 @@ public interface AerospikeOperations { */ boolean deleteById(Object id, String setName); - /** - * Delete a document. - * - * @param document The document to delete. Must not be {@literal null}. - * @return whether the document existed on server before deletion. - */ - boolean delete(T document); - - /** - * Delete a document with a given set name. - * - * @param document The document to delete. Must not be {@literal null}. - * @param setName Set name to delete the document from. - * @return whether the document existed on server before deletion. - */ - boolean delete(T document, String setName); - /** * Delete documents by providing multiple ids using a single batch delete operation, set name will be determined by * the given entityClass. @@ -397,6 +383,28 @@ public interface AerospikeOperations { */ void deleteByIds(Iterable ids, String setName); + /** + * Batch delete documents by providing their ids. Set name will be determined by the given entityClass. + *

+ * This operation requires Server version 6.0+. + * + * @param ids The ids of the documents to delete. Must not be {@literal null}. + * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @throws AerospikeException.BatchRecordArray if batch delete results contain errors or null records + */ + void deleteByIds(Collection ids, Class entityClass); + + /** + * Batch delete documents by providing their ids with a given set name. + *

+ * This operation requires Server version 6.0+. + * + * @param ids The ids of the documents to delete. Must not be {@literal null}. + * @param setName Set name to delete the documents from. + * @throws AerospikeException.BatchRecordArray if batch delete results contain errors or null records + */ + void deleteByIds(Collection ids, String setName); + /** * Executes a single batch delete for several entities. *

@@ -414,94 +422,180 @@ public interface AerospikeOperations { void deleteByIds(GroupedKeys groupedKeys); /** - * Check if a document exists by providing document id and entityClass (set name will be determined by the given - * entityClass). + * Truncate/Delete all the documents in the given entity's set. * - * @param id The id to check if exists. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. - * @return whether the document exists. */ - boolean exists(Object id, Class entityClass); + void deleteAll(Class entityClass); /** - * Check if a document exists by providing document id and a set name. + * Truncate/Delete all the documents in the given set. * - * @param id The id to check if exists. Must not be {@literal null}. - * @param setName Set name to check if document exists. - * @return whether the document exists. + * @param setName Set name to truncate/delete all the documents in. */ - boolean exists(Object id, String setName); + void deleteAll(String setName); /** - * Find documents in the given entityClass's set using a query and map them to the given class type. + * Add integer/double bin values to existing document bin values, read the new modified document and map it back the + * given document class type. * - * @param query The 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. + * @param document The document to extract the Aerospike set from and to map the documents to. Must not be + * {@literal null}. + * @param values a Map of bin names and values to add. Must not be {@literal null}. + * @return Modified document after add operations. */ - Stream find(Query query, Class entityClass); + T add(T document, Map values); /** - * Find documents in the given entityClass's set using a query and map them to the given target class type. + * Add integer/double bin values to existing document bin values with a given set name, read the new modified + * document and map it back to the given document class type. * - * @param query The 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. + * @param document The document to extract the Aerospike set from and to map the documents to. Must not be + * {@literal null}. + * @param setName Set name for the operation. + * @param values a Map of bin names and values to add. Must not be {@literal null}. + * @return Modified document after add operations. */ - Stream find(Query query, Class entityClass, Class targetClass); + T add(T document, String setName, Map values); /** - * Find documents in the given set using a query and map them to the given target class type. + * Add integer/double bin value to existing document bin value, read the new modified document and map it back the + * given document class type. * - * @param query The 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. + * @param document The document to extract the Aerospike set from and to map the documents to. Must not be + * {@literal null}. + * @param binName Bin name to use add operation on. Must not be {@literal null}. + * @param value The value to add. + * @return Modified document after add operation. */ - Stream find(Query query, Class targetClass, String setName); + T add(T document, String binName, long value); /** - * Find all documents in the given entityClass's set and map them to the given class type. + * Add integer/double bin value to existing document bin value with a given set name, read the new modified document + * and map it back to the given document class type. * - * @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. + * @param document The document to extract the Aerospike set from and to map the documents to. Must not be + * {@literal null}. + * @param setName Set name for the operation. + * @param binName Bin name to use add operation on. Must not be {@literal null}. + * @param value The value to add. + * @return Modified document after add operation. */ - Stream findAll(Class entityClass); + T add(T document, String setName, String binName, long value); /** - * Find all documents in the given entityClass's set and map them to the given target class type. + * Append bin string values to existing document bin values, read the new modified document and map it back the + * given document 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}. - * @return A Stream of matching documents, returned documents will be mapped to targetClass's type. + * @param document The document to extract the Aerospike set from and to map the documents to. Must not be + * {@literal null}. + * @param values a Map of bin names and values to append. Must not be {@literal null}. + * @return Modified document after append operations. */ - Stream findAll(Class entityClass, Class targetClass); + T append(T document, Map values); /** - * Find all documents in the given set and map them to the given class type. + * Append bin string values to existing document bin values with a given set name, read the new modified document + * and map it back to the given document class type. * - * @param targetClass The class to map the documents to. Must not be {@literal null}. - * @param setName Set name to find all documents. - * @return A Stream of matching documents, returned documents will be mapped to entityClass's type. + * @param document The document to map the document to. Must not be {@literal null}. + * @param setName Set name for the operation. + * @param values a Map of bin names and values to append. Must not be {@literal null}. + * @return Modified document after append operations. */ - Stream findAll(Class targetClass, String setName); + T append(T document, String setName, Map values); /** - * Find a document by id, set name will be determined by the given entityClass. - *

- * Document will be mapped to the given entityClass. + * Append bin string value to existing document bin value, read the new modified document and map it back the given + * document class type. * - * @param id The id of the document to find. Must not be {@literal null}. - * @param entityClass The class to extract the Aerospike set from and to map the document to. Must not be - * {@literal null}. - * @return The document from Aerospike, returned document will be mapped to entityClass's type, if document doesn't - * exist return null. - */ - T findById(Object id, Class entityClass); - + * @param document The document to extract the Aerospike set from and to map the documents to. Must not be + * {@literal null}. + * @param binName Bin name to use append operation on. + * @param value The value to append. + * @return Modified document after append operation. + */ + T append(T document, String binName, String value); + + /** + * Append bin string value to existing document bin value with a given set name, read the new modified document and + * map it back to the given document class type. + * + * @param document The document to map the document to. Must not be {@literal null}. + * @param setName Set name for the operation. + * @param binName Bin name to use append operation on. + * @param value The value to append. + * @return Modified document after append operation. + */ + T append(T document, String setName, String binName, String value); + + /** + * Prepend bin string values to existing document bin values, read the new modified document and map it back the + * given document class type. + * + * @param document The document to extract the Aerospike set from and to map the documents to. Must not be + * {@literal null}. + * @param values a Map of bin names and values to prepend. Must not be {@literal null}. + * @return Modified document after prepend operations. + */ + T prepend(T document, Map values); + + /** + * Prepend bin string values to existing document bin values with a given set name, read the new modified document + * and map it back to the given document class type. + * + * @param document The document to map the document to. Must not be {@literal null}. + * @param setName Set name for the operation. + * @param values a Map of bin names and values to prepend. Must not be {@literal null}. + * @return Modified document after prepend operations. + */ + T prepend(T document, String setName, Map values); + + /** + * Prepend bin string value to existing document bin value, read the new modified document and map it back the given + * document class type. + * + * @param document The document to extract the Aerospike set from and to map the documents to. Must not be + * {@literal null}. + * @param binName Bin name to use prepend operation on. + * @param value The value to prepend. + * @return Modified document after prepend operation. + */ + T prepend(T document, String binName, String value); + + /** + * Prepend bin string value to existing document bin value with a given set name, read the new modified document and + * map it back to the given document class type. + * + * @param document The document to map the document to. Must not be {@literal null}. + * @param setName Set name for the operation. + * @param binName Bin name to use prepend operation on. + * @param value The value to prepend. + * @return Modified document after prepend operation. + */ + T prepend(T document, String setName, String binName, String value); + + /** + * Execute operation against underlying store. + * + * @param supplier must not be {@literal null}. + * @return Execution result. + */ + T execute(Supplier supplier); + + /** + * Find a document by id, set name will be determined by the given entityClass. + *

+ * Document will be mapped to the given entityClass. + * + * @param id The id of the document to find. Must not be {@literal null}. + * @param entityClass The class to extract the Aerospike set from and to map the document to. Must not be + * {@literal null}. + * @return The document from Aerospike, returned document will be mapped to entityClass's type, if document doesn't + * exist return null. + */ + T findById(Object id, Class entityClass); + /** * Find a document by id with a given set name. *

@@ -611,176 +705,168 @@ public interface AerospikeOperations { GroupedEntities findByIds(GroupedKeys groupedKeys); /** - * Add integer/double bin values to existing document bin values, read the new modified document and map it back the - * given document class type. - * - * @param document The document to extract the Aerospike set from and to map the documents to. Must not be - * {@literal null}. - * @param values a Map of bin names and values to add. Must not be {@literal null}. - * @return Modified document after add operations. - */ - T add(T document, Map values); - - /** - * Add integer/double bin values to existing document bin values with a given set name, read the new modified - * document and map it back the given document class type. + * Find document by providing id, set name will be determined by the given entityClass. + *

+ * Documents will be mapped to the given targetClass. * - * @param document The document to extract the Aerospike set from and to map the documents to. Must not be - * {@literal null}. - * @param setName Set name for the operation. - * @param values a Map of bin names and values to add. Must not be {@literal null}. - * @return Modified document after add operations. + * @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. + * @return The document from Aerospike, returned document will be mapped to targetClass's type. */ - T add(T document, String setName, Map values); + Object findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, + Qualifier... qualifiers); /** - * Add integer/double bin value to existing document bin value, read the new modified document and map it back the - * given document class type. + * Find document by providing id with a given set name. + *

+ * Documents will be mapped to the given targetClass. * - * @param document The document to extract the Aerospike set from and to map the documents to. Must not be - * {@literal null}. - * @param binName Bin name to use add operation on. Must not be {@literal null}. - * @param value The value to add. - * @return Modified document after add operation. + * @param id The id of the document to find. Must not be {@literal null}. + * @param entityClass The class to get the entity properties from (such as expiration). Must not be + * {@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. + * @return The document from Aerospike, returned document will be mapped to targetClass's type. */ - T add(T document, String binName, long value); + Object findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, String setName, + Qualifier... qualifiers); /** - * Add integer/double bin value to existing document bin value with a given set name, read the new modified document - * and map it back the given document class type. + * Find documents by providing multiple ids, set name will be determined by the given entityClass. + *

+ * Documents will be mapped to the given targetClass. * - * @param document The document to extract the Aerospike set from and to map the documents to. Must not be - * {@literal null}. - * @param setName Set name for the operation. - * @param binName Bin name to use add operation on. Must not be {@literal null}. - * @param value The value to add. - * @return Modified document after add operation. + * @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. + * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document + * exists, an empty list is returned. */ - T add(T document, String setName, String binName, long value); + List findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, + Qualifier... qualifiers); /** - * Append bin string values to existing document bin values, read the new modified document and map it back the - * given document class type. + * Find documents by providing multiple ids with a given set name. + *

+ * Documents will be mapped to the given targetClass. * - * @param document The document to extract the Aerospike set from and to map the documents to. Must not be - * {@literal null}. - * @param values a Map of bin names and values to append. Must not be {@literal null}. - * @return Modified document after append operations. + * @param ids The ids of the documents to find. Must not be {@literal null}. + * @param entityClass The class to get the entity properties from (such as expiration). Must not be + * {@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. + * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document + * exists, an empty list is returned. */ - T append(T document, Map values); + List findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, + String setName, Qualifier... qualifiers); /** - * Append bin string values to existing document bin values with a given set name, read the new modified document - * and map it back the given document class type. + * Find documents in the given entityClass's set using a query and map them to the given class type. * - * @param document The document to map the document to. Must not be {@literal null}. - * @param setName Set name for the operation. - * @param values a Map of bin names and values to append. Must not be {@literal null}. - * @return Modified document after append operations. + * @param query The 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. */ - T append(T document, String setName, Map values); + Stream find(Query query, Class entityClass); /** - * Append bin string value to existing document bin value, read the new modified document and map it back the given - * document class type. + * Find documents in the given entityClass's set using a query and map them to the given target class type. * - * @param document The document to extract the Aerospike set from and to map the documents to. Must not be - * {@literal null}. - * @param binName Bin name to use append operation on. - * @param value The value to append. - * @return Modified document after append operation. + * @param query The 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. */ - T append(T document, String binName, String value); + Stream find(Query query, Class entityClass, Class targetClass); /** - * Append bin string value to existing document bin value with a given set name, read the new modified document and - * map it back the given document class type. + * Find documents in the given set using a query and map them to the given target class type. * - * @param document The document to map the document to. Must not be {@literal null}. - * @param setName Set name for the operation. - * @param binName Bin name to use append operation on. - * @param value The value to append. - * @return Modified document after append operation. + * @param query The 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. */ - T append(T document, String setName, String binName, String value); + Stream find(Query query, Class targetClass, String setName); /** - * Prepend bin string values to existing document bin values, read the new modified document and map it back the - * given document class type. + * Find all documents in the given entityClass's set using provided {@link Qualifier}. * - * @param document The document to extract the Aerospike set from and to map the documents to. Must not be - * {@literal null}. - * @param values a Map of bin names and values to prepend. Must not be {@literal null}. - * @return Modified document after prepend operations. + * @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. */ - T prepend(T document, Map values); + Stream findUsingQualifier(Class entityClass, @Nullable Filter filter, Qualifier qualifier); /** - * Prepend bin string values to existing document bin values with a given set name, read the new modified document - * and map it back the given document class type. + * Find all documents in the given entityClass's set using provided {@link Qualifier}. * - * @param document The document to map the document to. Must not be {@literal null}. - * @param setName Set name for the operation. - * @param values a Map of bin names and values to prepend. Must not be {@literal null}. - * @return Modified document after prepend operations. + * @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. */ - T prepend(T document, String setName, Map values); + Stream findUsingQualifier(Class entityClass, Class targetClass, @Nullable Filter filter, + Qualifier qualifier); /** - * Prepend bin string value to existing document bin value, read the new modified document and map it back the given - * document class type. + * Find all documents in the given set using provided {@link Qualifier}. * - * @param document The document to extract the Aerospike set from and to map the documents to. Must not be - * {@literal null}. - * @param binName Bin name to use prepend operation on. - * @param value The value to prepend. - * @return Modified document after prepend operation. + * @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. */ - T prepend(T document, String binName, String value); + Stream findUsingQualifier(Class targetClass, String setName, @Nullable Filter filter, + Qualifier qualifier); /** - * Prepend bin string value to existing document bin value with a given set name, read the new modified document and - * map it back the given document class type. + * Find all documents in the given entityClass's set and map them to the given class type. * - * @param document The document to map the document to. Must not be {@literal null}. - * @param setName Set name for the operation. - * @param binName Bin name to use prepend operation on. - * @param value The value to prepend. - * @return Modified document after prepend operation. + * @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. */ - T prepend(T document, String setName, String binName, String value); + Stream findAll(Class entityClass); /** - * Execute query, apply statement's aggregation function, and return result iterator. + * Find all documents in the given entityClass's set and map them to the given target class type. * - * @param filter The filter to pass to the query. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. - * @param module server package where user defined function resides. - * @param function aggregation function name. - * @param arguments arguments to pass to function name, if any. - * @return Result iterator. - */ - ResultSet aggregate(Filter filter, Class entityClass, String module, String function, List arguments); - - /** - * Execute query with a given set name, apply statement's aggregation function, and return result iterator. - * - * @param filter The filter to pass to the query. - * @param setName Set name to aggregate the documents from. - * @param module server package where user defined function resides. - * @param function aggregation function name. - * @param arguments arguments to pass to function name, if any. - * @return Result iterator. + * @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. */ - ResultSet aggregate(Filter filter, String setName, String module, String function, List arguments); + Stream findAll(Class entityClass, Class targetClass); /** - * Execute operation against underlying store. + * Find all documents in the given set and map them to the given class type. * - * @param supplier must not be {@literal null}. - * @return Execution result. + * @param targetClass The class to map the documents to. Must not be {@literal null}. + * @param setName Set name to find all documents. + * @return A Stream of matching documents, returned documents will be mapped to entityClass's type. */ - T execute(Supplier supplier); + Stream findAll(Class targetClass, String setName); /** * Find all documents in the given entityClass's set using a provided sort and map them to the given class type. @@ -857,6 +943,61 @@ public interface AerospikeOperations { */ Stream findInRange(long offset, long limit, Sort sort, Class targetClass, String setName); + /** + * Check if a document exists by providing document id and entityClass (set name will be determined by the given + * entityClass). + * + * @param id The id to check if exists. Must not be {@literal null}. + * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @return whether the document exists. + */ + boolean exists(Object id, Class entityClass); + + /** + * Check if a document exists by providing document id and a set name. + * + * @param id The id to check if exists. Must not be {@literal null}. + * @param setName Set name to check if document exists. + * @return whether the document exists. + */ + boolean exists(Object id, String setName); + + /** + * Check if any document exists by defining a query and entityClass (set name will be determined by the given + * entityClass). + * + * @param query The query to check if any returned document exists. Must not be {@literal null}. + * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @return whether any document exists. + */ + boolean existsByQuery(Query query, Class entityClass); + + /** + * Check if any document exists by defining a query, entityClass and a given set name. + * + * @param query The query to check if any returned document exists. Must not be {@literal null}. + * @param entityClass The class to translate to returned results into. Must not be {@literal null}. + * @param setName The set name to check if documents exists in. Must not be {@literal null}. + * @return whether any document exists. + */ + boolean existsByQuery(Query query, Class entityClass, String setName); + + /** + * Return the amount of documents in the given entityClass's Aerospike set. + * + * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @return amount of documents in the set (of the given entityClass). + */ + long count(Class entityClass); + + /** + * Return the amount of documents in the given Aerospike set. + * + * @param setName The name of the set to count. Must not be {@literal null}. + * @return amount of documents in the given set. + */ + long count(String setName); + /** * Return the amount of documents in query results. Set name will be determined by the given entityClass. * @@ -876,20 +1017,28 @@ public interface AerospikeOperations { long count(Query query, String setName); /** - * Return the amount of documents in the given Aerospike set. + * Execute query, apply statement's aggregation function, and return result iterator. * - * @param setName The name of the set to count. Must not be {@literal null}. - * @return amount of documents in the given set. + * @param filter The filter to pass to the query. + * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @param module server package where user defined function resides. + * @param function aggregation function name. + * @param arguments arguments to pass to function name, if any. + * @return Result iterator. */ - long count(String setName); + ResultSet aggregate(Filter filter, Class entityClass, String module, String function, List arguments); /** - * Return the amount of documents in the given entityClass's Aerospike set. + * Execute query with a given set name, apply statement's aggregation function, and return result iterator. * - * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. - * @return amount of documents in the set (of the given entityClass). + * @param filter The filter to pass to the query. + * @param setName Set name to aggregate the documents from. + * @param module server package where user defined function resides. + * @param function aggregation function name. + * @param arguments arguments to pass to function name, if any. + * @return Result iterator. */ - long count(Class entityClass); + ResultSet aggregate(Filter filter, String setName, String module, String function, List arguments); /** * Create an index with the specified name in Aerospike. @@ -986,33 +1135,4 @@ void createIndex(String setName, String indexName, String binName, * @return true if exists */ boolean indexExists(String indexName); - - /** - * 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 findAllUsingQuery(Class entityClass, @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 findAllUsingQuery(Class targetClass, String setName, @Nullable Filter filter, - Qualifier qualifier); } 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 d192bb674..eb110fbe5 100644 --- a/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java +++ b/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java @@ -86,7 +86,7 @@ */ @Slf4j public class AerospikeTemplate extends BaseAerospikeTemplate implements AerospikeOperations, - AerospikeInternalOperations, IndexesCacheRefresher { + IndexesCacheRefresher { private static final Pattern INDEX_EXISTS_REGEX_PATTERN = Pattern.compile("^FAIL:(-?\\d+).*$"); private final IAerospikeClient client; @@ -111,130 +111,11 @@ public IAerospikeClient getAerospikeClient() { return client; } - @Override - public void createIndex(Class entityClass, String indexName, - String binName, IndexType indexType) { - Assert.notNull(entityClass, "Class must not be null!"); - createIndex(entityClass, indexName, binName, indexType, IndexCollectionType.DEFAULT); - } - - @Override - public void createIndex(Class entityClass, String indexName, - String binName, IndexType indexType, IndexCollectionType indexCollectionType) { - Assert.notNull(entityClass, "Class must not be null!"); - createIndex(entityClass, indexName, binName, indexType, indexCollectionType, new CTX[0]); - } - - @Override - public void createIndex(Class entityClass, String indexName, - String binName, IndexType indexType, IndexCollectionType indexCollectionType, - CTX... ctx) { - Assert.notNull(entityClass, "Class must not be null!"); - createIndex(getSetName(entityClass), indexName, binName, indexType, indexCollectionType, ctx); - } - - @Override - public void createIndex(String setName, String indexName, - String binName, IndexType indexType) { - createIndex(setName, indexName, binName, indexType, IndexCollectionType.DEFAULT); - } - - @Override - public void createIndex(String setName, String indexName, String binName, IndexType indexType, - IndexCollectionType indexCollectionType) { - createIndex(setName, indexName, binName, indexType, indexCollectionType, new CTX[0]); - } - - @Override - public void createIndex(String setName, String indexName, String binName, - IndexType indexType, IndexCollectionType indexCollectionType, CTX... ctx) { - Assert.notNull(setName, "Set name type must not be null!"); - Assert.notNull(indexName, "Index name must not be null!"); - Assert.notNull(binName, "Bin name must not be null!"); - Assert.notNull(indexType, "Index type must not be null!"); - Assert.notNull(indexCollectionType, "Index collection type must not be null!"); - Assert.notNull(ctx, "Ctx must not be null!"); - - try { - IndexTask task = client.createIndex(null, this.namespace, - setName, indexName, binName, indexType, indexCollectionType, ctx); - if (task != null) { - task.waitTillComplete(); - } - refreshIndexesCache(); - } catch (AerospikeException e) { - throw translateError(e); - } - } - @Override public void refreshIndexesCache() { indexRefresher.refreshIndexes(); } - @Override - public void deleteIndex(Class entityClass, String indexName) { - Assert.notNull(entityClass, "Class must not be null!"); - deleteIndex(getSetName(entityClass), indexName); - } - - @Override - public void deleteIndex(String setName, String indexName) { - Assert.notNull(setName, "Set name must not be null!"); - Assert.notNull(indexName, "Index name must not be null!"); - - try { - IndexTask task = client.dropIndex(null, this.namespace, setName, indexName); - if (task != null) { - task.waitTillComplete(); - } - refreshIndexesCache(); - } catch (AerospikeException e) { - throw translateError(e); - } - } - - @Override - public boolean indexExists(String indexName) { - Assert.notNull(indexName, "Index name must not be null!"); - - try { - Node[] nodes = client.getNodes(); - for (Node node : nodes) { - String response = Info.request(node, "sindex-exists:ns=" + namespace + ";indexname=" + indexName); - if (response == null) throw new AerospikeException("Null node response"); - - if (response.equalsIgnoreCase("true")) { - return true; - } else if (response.equalsIgnoreCase("false")) { - return false; - } else { - Matcher matcher = INDEX_EXISTS_REGEX_PATTERN.matcher(response); - if (matcher.matches()) { - int reason; - try { - reason = Integer.parseInt(matcher.group(1)); - } catch (NumberFormatException e) { - throw new AerospikeException("Unexpected node response, unable to parse ResultCode: " + - response); - } - - // as for Server ver. >= 6.1.0.1 the response containing ResultCode.INVALID_NAMESPACE - // means that the request should be sent to another node - if (reason != ResultCode.INVALID_NAMESPACE) { - throw new AerospikeException(reason); - } - } else { - throw new AerospikeException("Unexpected node response: " + response); - } - } - } - } catch (AerospikeException e) { - throw translateError(e); - } - return false; - } - @Override public void save(T document) { Assert.notNull(document, "Document must not be null!"); @@ -305,25 +186,6 @@ private void checkForErrorsAndUpdateVersion(List> batchWri } } - @Override - public void persist(T document, WritePolicy policy) { - Assert.notNull(document, "Document must not be null!"); - Assert.notNull(policy, "Policy must not be null!"); - persist(document, policy, getSetName(document)); - } - - @Override - public void persist(T document, WritePolicy policy, String setName) { - Assert.notNull(document, "Document must not be null!"); - Assert.notNull(policy, "Policy must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); - - AerospikeWriteData data = writeData(document, setName); - - Operation[] operations = operations(data.getBinsAsArray(), Operation::put); - doPersistAndHandleError(data, policy, operations); - } - @Override public void insert(T document) { Assert.notNull(document, "Document must not be null!"); @@ -376,6 +238,25 @@ public void insertAll(Iterable documents, String setName) { checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, "insert"); } + @Override + public void persist(T document, WritePolicy policy) { + Assert.notNull(document, "Document must not be null!"); + Assert.notNull(policy, "Policy must not be null!"); + persist(document, policy, getSetName(document)); + } + + @Override + public void persist(T document, WritePolicy policy, String setName) { + Assert.notNull(document, "Document must not be null!"); + Assert.notNull(policy, "Policy must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); + + AerospikeWriteData data = writeData(document, setName); + + Operation[] operations = operations(data.getBinsAsArray(), Operation::put); + doPersistAndHandleError(data, policy, operations); + } + @Override public void update(T document) { Assert.notNull(document, "Document must not be null!"); @@ -476,17 +357,20 @@ public boolean delete(Object id, Class entityClass) { } @Override - public void deleteAll(Class entityClass) { - Assert.notNull(entityClass, "Class must not be null!"); - deleteAll(getSetName(entityClass)); + public boolean delete(T document) { + Assert.notNull(document, "Document must not be null!"); + return delete(document, getSetName(document)); } @Override - public void deleteAll(String setName) { + public boolean delete(T document, String setName) { + Assert.notNull(document, "Document must not be null!"); Assert.notNull(setName, "Set name must not be null!"); try { - client.truncate(null, getNamespace(), setName, null); + AerospikeWriteData data = writeData(document, setName); + + return this.client.delete(ignoreGenerationDeletePolicy(), data.getKey()); } catch (AerospikeException e) { throw translateError(e); } @@ -512,26 +396,6 @@ public boolean deleteById(Object id, String setName) { } } - @Override - public boolean delete(T document) { - Assert.notNull(document, "Document must not be null!"); - return delete(document, getSetName(document)); - } - - @Override - public boolean delete(T document, String setName) { - Assert.notNull(document, "Document must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); - - try { - AerospikeWriteData data = writeData(document, setName); - - return this.client.delete(ignoreGenerationDeletePolicy(), data.getKey()); - } catch (AerospikeException e) { - throw translateError(e); - } - } - @Override public void deleteByIds(Iterable ids, Class entityClass) { Assert.notNull(ids, "List of ids must not be null!"); @@ -544,18 +408,18 @@ public void deleteByIds(Iterable ids, String setName) { Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - deleteByIdsInternal(IterableConverter.toList(ids), setName); + deleteByIds(IterableConverter.toList(ids), setName); } @Override - public void deleteByIdsInternal(Collection ids, Class entityClass) { + public void deleteByIds(Collection ids, Class entityClass) { Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(entityClass, "Class must not be null!"); - deleteByIdsInternal(ids, getSetName(entityClass)); + deleteByIds(ids, getSetName(entityClass)); } @Override - public void deleteByIdsInternal(Collection ids, String setName) { + public void deleteByIds(Collection ids, String setName) { Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(setName, "Set name must not be null!"); @@ -567,25 +431,7 @@ public void deleteByIdsInternal(Collection ids, String setName) { .map(id -> getKey(id, setName)) .toArray(Key[]::new); - checkForErrors(client, keys); - } - - private void checkForErrors(IAerospikeClient client, Key[] keys) { - BatchResults results; - try { - // requires server ver. >= 6.0.0 - results = client.delete(null, null, keys); - } catch (AerospikeException e) { - throw translateError(e); - } - - for (int i = 0; i < results.records.length; i++) { - BatchRecord record = results.records[i]; - if (batchRecordFailed(record)) { - throw new AerospikeException.BatchRecordArray(results.records, - new AerospikeException("Errors during batch delete")); - } - } + deleteAndHandleErrors(client, keys); } @Override @@ -594,190 +440,245 @@ public void deleteByIds(GroupedKeys groupedKeys) { Assert.notNull(groupedKeys.getEntitiesKeys(), "Entities keys must not be null!"); Assert.notEmpty(groupedKeys.getEntitiesKeys(), "Entities keys must not be empty!"); - deleteEntitiesByIdsInternal(groupedKeys); + deleteGroupedEntitiesByGroupedKeys(groupedKeys); } - private void deleteEntitiesByIdsInternal(GroupedKeys groupedKeys) { + private void deleteGroupedEntitiesByGroupedKeys(GroupedKeys groupedKeys) { EntitiesKeys entitiesKeys = EntitiesKeys.of(toEntitiesKeyMap(groupedKeys)); - checkForErrors(client, entitiesKeys.getKeys()); + deleteAndHandleErrors(client, entitiesKeys.getKeys()); } @Override - public boolean exists(Object id, Class entityClass) { + public void deleteAll(Class entityClass) { Assert.notNull(entityClass, "Class must not be null!"); - return exists(id, getSetName(entityClass)); + deleteAll(getSetName(entityClass)); } @Override - public boolean exists(Object id, String setName) { - Assert.notNull(id, "Id must not be null!"); + public void deleteAll(String setName) { Assert.notNull(setName, "Set name must not be null!"); try { - Key key = getKey(id, setName); - - Record aeroRecord = this.client.operate(null, key, Operation.getHeader()); - return aeroRecord != null; + client.truncate(null, getNamespace(), setName, null); } catch (AerospikeException e) { throw translateError(e); } } - @Override - public Stream findAll(Class entityClass) { - Assert.notNull(entityClass, "Entity class must not be null!"); + private void deleteAndHandleErrors(IAerospikeClient client, Key[] keys) { + BatchResults results; + try { + // requires server ver. >= 6.0.0 + results = client.delete(null, null, keys); + } catch (AerospikeException e) { + throw translateError(e); + } - return findAll(entityClass, getSetName(entityClass)); + for (int i = 0; i < results.records.length; i++) { + BatchRecord record = results.records[i]; + if (batchRecordFailed(record)) { + throw new AerospikeException.BatchRecordArray(results.records, + new AerospikeException("Errors during batch delete")); + } + } } @Override - public Stream findAll(Class entityClass, Class targetClass) { - Assert.notNull(entityClass, "Entity class must not be null!"); - Assert.notNull(targetClass, "Target class must not be null!"); - - return findAll(targetClass, getSetName(entityClass)); + public T add(T document, Map values) { + return add(document, getSetName(document), values); } @Override - public Stream findAll(Class targetClass, String setName) { - Assert.notNull(targetClass, "Target class must not be null!"); + public T add(T document, String setName, Map values) { + Assert.notNull(document, "Document must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + Assert.notNull(values, "Values must not be null!"); - return findAllUsingQuery(targetClass, setName, null, null); - } + try { + AerospikeWriteData data = writeData(document, setName); + Operation[] ops = operations(values, Operation.Type.ADD, Operation.get()); - @Override - public T findById(Object id, Class entityClass) { - Assert.notNull(id, "Id must not be null!"); - Assert.notNull(entityClass, "Class must not be null!"); - return findById(id, entityClass, getSetName(entityClass)); - } + WritePolicy writePolicy = WritePolicyBuilder.builder(client.getWritePolicyDefault()) + .expiration(data.getExpiration()) + .build(); - @Override - public T findById(Object id, Class entityClass, String setName) { - Assert.notNull(id, "Id must not be null!"); - Assert.notNull(entityClass, "Class must not be null!"); - return findById(id, entityClass, null, setName); + Record aeroRecord = this.client.operate(writePolicy, data.getKey(), ops); + + return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); + } catch (AerospikeException e) { + throw translateError(e); + } } @Override - public S findById(Object id, Class entityClass, Class targetClass) { - Assert.notNull(id, "Id must not be null!"); - Assert.notNull(entityClass, "Class must not be null!"); - return findById(id, entityClass, targetClass, getSetName(entityClass)); + public T add(T document, String binName, long value) { + return add(document, getSetName(document), binName, value); } - @SuppressWarnings("unchecked") @Override - 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) findByIdInternal(id, entityClass, targetClass, setName); + public T add(T document, String setName, String binName, long value) { + Assert.notNull(document, "Document must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); + Assert.notNull(binName, "Bin name must not be null!"); + + try { + AerospikeWriteData data = writeData(document, setName); + + WritePolicy writePolicy = WritePolicyBuilder.builder(client.getWritePolicyDefault()) + .expiration(data.getExpiration()) + .build(); + + Record aeroRecord = this.client.operate(writePolicy, data.getKey(), + Operation.add(new Bin(binName, value)), Operation.get()); + + return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); + } catch (AerospikeException e) { + throw translateError(e); + } } @Override - public Object findByIdInternal(Object id, Class entityClass, Class targetClass, - Qualifier... qualifiers) { - Assert.notNull(id, "Id must not be null!"); - Assert.notNull(entityClass, "Class must not be null!"); - return findByIdInternal(id, entityClass, targetClass, getSetName(entityClass), qualifiers); + public T append(T document, Map values) { + return append(document, getSetName(document), values); } @Override - public Object findByIdInternal(Object id, Class entityClass, Class targetClass, String setName, - Qualifier... qualifiers) { - Assert.notNull(id, "Id must not be null!"); - Assert.notNull(entityClass, "Entity class must not be null!"); + public T append(T document, String setName, Map values) { + Assert.notNull(document, "Document must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + Assert.notNull(values, "Values must not be null!"); try { - AerospikePersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); - Key key = getKey(id, setName); + AerospikeWriteData data = writeData(document, setName); + Operation[] ops = operations(values, Operation.Type.APPEND, Operation.get()); + Record aeroRecord = this.client.operate(null, data.getKey(), ops); - if (targetClass != null && targetClass != entityClass) { - return getRecordMapToTargetClass(entity, key, targetClass, qualifiers); - } - return mapToEntity(key, entityClass, getRecord(entity, key, qualifiers)); + return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); } catch (AerospikeException e) { throw translateError(e); } } @Override - public List findByIdsInternal(Collection ids, Class entityClass, Class targetClass, - Qualifier... qualifiers) { - Assert.notNull(entityClass, "Class must not be null!"); - return findByIdsInternal(ids, entityClass, targetClass, getSetName(entityClass), qualifiers); + public T append(T document, String binName, String value) { + return append(document, getSetName(document), binName, value); } @Override - public List findByIdsInternal(Collection ids, Class entityClass, Class targetClass, - String setName, Qualifier... qualifiers) { - Assert.notNull(ids, "List of ids must not be null!"); - Assert.notNull(entityClass, "Entity class must not be null!"); + public T append(T document, String setName, String binName, String value) { + Assert.notNull(document, "Document must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + Assert.notNull(binName, "Bin name must not be null!"); - if (ids.isEmpty()) { - return Collections.emptyList(); + try { + AerospikeWriteData data = writeData(document, setName); + Record aeroRecord = this.client.operate(null, data.getKey(), + Operation.append(new Bin(binName, value)), + Operation.get(binName)); + + return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); + } catch (AerospikeException e) { + throw translateError(e); } + } - try { - Key[] keys = ids.stream() - .map(id -> getKey(id, setName)) - .toArray(Key[]::new); + @Override + public T prepend(T document, String fieldName, String value) { + return prepend(document, getSetName(document), fieldName, value); + } - BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + @Override + public T prepend(T document, String setName, String fieldName, String value) { + Assert.notNull(document, "Document must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); + Assert.notNull(fieldName, "Field name must not be null!"); - Class target; - Record[] aeroRecords; - if (targetClass != null && targetClass != entityClass) { - String[] binNames = getBinNamesFromTargetClass(targetClass); - aeroRecords = getAerospikeClient().get(policy, keys, binNames); - target = targetClass; - } else { - aeroRecords = getAerospikeClient().get(policy, keys); - target = entityClass; - } + try { + AerospikeWriteData data = writeData(document, setName); + Record aeroRecord = this.client.operate(null, data.getKey(), + Operation.prepend(new Bin(fieldName, value)), + Operation.get(fieldName)); - return IntStream.range(0, keys.length) - .filter(index -> aeroRecords[index] != null) - .mapToObj(index -> mapToEntity(keys[index], target, aeroRecords[index])) - .collect(Collectors.toList()); + return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); } catch (AerospikeException e) { throw translateError(e); } } - private List findByIdsInternalWithoutMapping(Collection ids, String setName, - Class targetClass, - Qualifier... qualifiers) { - Assert.notNull(ids, "Ids must not be null"); - if (ids.isEmpty()) { - return Collections.emptyList(); - } + @Override + public T prepend(T document, Map values) { + return prepend(document, getSetName(document), values); + } + + @Override + public T prepend(T document, String setName, Map values) { + Assert.notNull(document, "Document must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); + Assert.notNull(values, "Values must not be null!"); try { - Key[] keys = getKeys(ids, setName); + AerospikeWriteData data = writeData(document, setName); + Operation[] ops = operations(values, Operation.Type.PREPEND, Operation.get()); + Record aeroRecord = this.client.operate(null, data.getKey(), ops); - BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); + } catch (AerospikeException e) { + throw translateError(e); + } + } - Record[] aeroRecords; - if (targetClass != null) { - String[] binNames = getBinNamesFromTargetClass(targetClass); - aeroRecords = getAerospikeClient().get(policy, keys, binNames); - } else { - aeroRecords = getAerospikeClient().get(policy, keys); - } + @Override + public T execute(Supplier supplier) { + Assert.notNull(supplier, "Supplier must not be null!"); - return IntStream.range(0, keys.length) - .filter(index -> aeroRecords[index] != null) - .mapToObj(index -> new KeyRecord(keys[index], aeroRecords[index])) - .collect(Collectors.toList()); + try { + return supplier.get(); } catch (AerospikeException e) { throw translateError(e); } } + @Override + public T findById(Object id, Class entityClass) { + Assert.notNull(id, "Id must not be null!"); + Assert.notNull(entityClass, "Class must not be null!"); + return findById(id, entityClass, getSetName(entityClass)); + } + + @Override + public T findById(Object id, Class entityClass, String setName) { + Assert.notNull(id, "Id must not be null!"); + Assert.notNull(entityClass, "Class must not be null!"); + return findById(id, entityClass, null, setName); + } + + @Override + public S findById(Object id, Class entityClass, Class targetClass) { + Assert.notNull(id, "Id must not be null!"); + Assert.notNull(entityClass, "Class must not be null!"); + return findById(id, entityClass, targetClass, getSetName(entityClass)); + } + + @SuppressWarnings("unchecked") + @Override + 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); + } + + private Record getRecord(AerospikePersistentEntity entity, Key key, Qualifier... qualifiers) { + Record aeroRecord; + if (entity.isTouchOnRead()) { + Assert.state(!entity.hasExpirationProperty(), "Touch on read is not supported for expiration property"); + aeroRecord = getAndTouch(key, entity.getExpiration(), null); + } else { + Policy policy = getPolicyFilterExp(qualifiers); + aeroRecord = getAerospikeClient().get(policy, key); + } + return aeroRecord; + } + private BatchPolicy getBatchPolicyFilterExp(Qualifier[] qualifiers) { if (qualifiers != null && qualifiers.length > 0) { BatchPolicy policy = new BatchPolicy(getAerospikeClient().getBatchPolicyDefault()); @@ -793,8 +694,8 @@ private Key[] getKeys(Collection ids, String setName) { .toArray(Key[]::new); } - Object getRecordMapToTargetClass(AerospikePersistentEntity entity, Key key, Class targetClass, - Qualifier... qualifiers) { + private Object getRecordMapToTargetClass(AerospikePersistentEntity entity, Key key, Class targetClass, + Qualifier... qualifiers) { Record aeroRecord; String[] binNames = getBinNamesFromTargetClass(targetClass); if (entity.isTouchOnRead()) { @@ -807,6 +708,17 @@ Object getRecordMapToTargetClass(AerospikePersistentEntity entity, Key ke return mapToEntity(key, targetClass, aeroRecord); } + private String[] getBinNamesFromTargetClass(Class targetClass) { + AerospikePersistentEntity targetEntity = mappingContext.getRequiredPersistentEntity(targetClass); + + List binNamesList = new ArrayList<>(); + + targetEntity.doWithProperties((PropertyHandler) property + -> binNamesList.add(property.getFieldName())); + + return binNamesList.toArray(new String[0]); + } + private Policy getPolicyFilterExp(Qualifier[] qualifiers) { if (qualifiers != null && qualifiers.length > 0) { Policy policy = new Policy(getAerospikeClient().getReadPolicyDefault()); @@ -816,19 +728,7 @@ private Policy getPolicyFilterExp(Qualifier[] qualifiers) { return null; } - Record getRecord(AerospikePersistentEntity entity, Key key, Qualifier... qualifiers) { - Record aeroRecord; - if (entity.isTouchOnRead()) { - Assert.state(!entity.hasExpirationProperty(), "Touch on read is not supported for expiration property"); - aeroRecord = getAndTouch(key, entity.getExpiration(), null); - } else { - Policy policy = getPolicyFilterExp(qualifiers); - aeroRecord = getAerospikeClient().get(policy, key); - } - return aeroRecord; - } - - Record getAndTouch(Key key, int expiration, String[] binNames, Qualifier... qualifiers) { + private Record getAndTouch(Key key, int expiration, String[] binNames, Qualifier... qualifiers) { WritePolicyBuilder writePolicyBuilder = WritePolicyBuilder.builder(client.getWritePolicyDefault()) .expiration(expiration); @@ -857,17 +757,6 @@ Record getAndTouch(Key key, int expiration, String[] binNames, Qualifier... qual } } - String[] getBinNamesFromTargetClass(Class targetClass) { - AerospikePersistentEntity targetEntity = mappingContext.getRequiredPersistentEntity(targetClass); - - List binNamesList = new ArrayList<>(); - - targetEntity.doWithProperties((PropertyHandler) property - -> binNamesList.add(property.getFieldName())); - - return binNamesList.toArray(new String[0]); - } - @Override public List findByIds(Iterable ids, Class entityClass) { return findByIds(ids, entityClass, getSetName(entityClass)); @@ -890,7 +779,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) findByIdsInternal(IterableConverter.toList(ids), entityClass, targetClass, setName); + return (List) findByIdsUsingQualifiers(IterableConverter.toList(ids), entityClass, targetClass, setName); } @Override @@ -901,10 +790,10 @@ public GroupedEntities findByIds(GroupedKeys groupedKeys) { return GroupedEntities.builder().build(); } - return findEntitiesByIdsInternal(groupedKeys); + return findGroupedEntitiesByGroupedKeys(groupedKeys); } - private GroupedEntities findEntitiesByIdsInternal(GroupedKeys groupedKeys) { + private GroupedEntities findGroupedEntitiesByGroupedKeys(GroupedKeys groupedKeys) { EntitiesKeys entitiesKeys = EntitiesKeys.of(toEntitiesKeyMap(groupedKeys)); Record[] aeroRecords = client.get(null, entitiesKeys.getKeys()); @@ -912,101 +801,173 @@ private GroupedEntities findEntitiesByIdsInternal(GroupedKeys groupedKeys) { } @Override - public ResultSet aggregate(Filter filter, Class entityClass, - String module, String function, List arguments) { - return aggregate(filter, getSetName(entityClass), module, function, arguments); + public Object findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, + Qualifier... qualifiers) { + Assert.notNull(id, "Id must not be null!"); + Assert.notNull(entityClass, "Class must not be null!"); + return findByIdUsingQualifiers(id, entityClass, targetClass, getSetName(entityClass), qualifiers); } @Override - public ResultSet aggregate(Filter filter, String setName, - String module, String function, List arguments) { + public Object findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, String setName, + Qualifier... qualifiers) { + 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!"); - Statement statement = new Statement(); - if (filter != null) - statement.setFilter(filter); - statement.setSetName(setName); - statement.setNamespace(this.namespace); - ResultSet resultSet; - if (arguments != null && !arguments.isEmpty()) - resultSet = this.client.queryAggregate(null, statement, module, - function, arguments.toArray(new Value[0])); - else - resultSet = this.client.queryAggregate(null, statement); - return resultSet; + try { + AerospikePersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); + Key key = getKey(id, setName); + + if (targetClass != null && targetClass != entityClass) { + return getRecordMapToTargetClass(entity, key, targetClass, qualifiers); + } + return mapToEntity(key, entityClass, getRecord(entity, key, qualifiers)); + } catch (AerospikeException e) { + throw translateError(e); + } } @Override - public Stream findAll(Sort sort, long offset, long limit, Class entityClass) { - return findAll(sort, offset, limit, entityClass, getSetName(entityClass)); + 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); } @Override - public Stream findAll(Sort sort, long offset, long limit, Class entityClass, Class targetClass) { - return findAll(sort, offset, limit, targetClass, getSetName(entityClass)); + public List findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, + String setName, Qualifier... qualifiers) { + Assert.notNull(ids, "List of 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 Collections.emptyList(); + } + + try { + Key[] keys = ids.stream() + .map(id -> getKey(id, setName)) + .toArray(Key[]::new); + + BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + + Class target; + Record[] aeroRecords; + if (targetClass != null && targetClass != entityClass) { + String[] binNames = getBinNamesFromTargetClass(targetClass); + aeroRecords = getAerospikeClient().get(policy, keys, binNames); + target = targetClass; + } else { + aeroRecords = getAerospikeClient().get(policy, keys); + target = entityClass; + } + + return IntStream.range(0, keys.length) + .filter(index -> aeroRecords[index] != null) + .mapToObj(index -> mapToEntity(keys[index], target, aeroRecords[index])) + .collect(Collectors.toList()); + } catch (AerospikeException e) { + throw translateError(e); + } } @Override - public Stream findAll(Sort sort, long offset, long limit, Class targetClass, String setName) { - Assert.notNull(setName, "Set name must not be null!"); + public Stream find(Query query, Class entityClass) { + return find(query, entityClass, getSetName(entityClass)); + } + + @Override + public Stream find(Query query, Class entityClass, Class targetClass) { + return find(query, targetClass, getSetName(entityClass)); + } + + @Override + public Stream find(Query query, Class targetClass, String setName) { + Assert.notNull(query, "Query must not be null!"); Assert.notNull(targetClass, "Target class must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); - return findAllUsingQueryWithPostProcessing(setName, targetClass, sort, offset, limit, - null, null); + return findUsingQueryWithPostProcessing(setName, targetClass, query); } - public boolean exists(Query query, Class entityClass) { - return exists(query, entityClass, getSetName(entityClass)); + @Override + public Stream findUsingQualifier(Class entityClass, Filter filter, + Qualifier qualifier) { + return findUsingQualifier(entityClass, getSetName(entityClass), filter, qualifier); } - public boolean exists(Query query, Class entityClass, String setName) { - Assert.notNull(query, "Query passed in to exist can't be null"); - Assert.notNull(entityClass, "Class must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); + @Override + public Stream findUsingQualifier(Class entityClass, Class targetClass, Filter filter, + Qualifier qualifier) { + return findRecordsUsingQualifiers(getSetName(entityClass), targetClass, filter, qualifier) + .map(keyRecord -> mapToEntity(keyRecord, targetClass)); + } - return find(query, entityClass, setName).findAny().isPresent(); + @Override + public Stream findUsingQualifier(Class targetClass, String setName, Filter filter, + Qualifier qualifier) { + return findRecordsUsingQualifiers(setName, targetClass, filter, qualifier) + .map(keyRecord -> mapToEntity(keyRecord, targetClass)); } @Override - public T execute(Supplier supplier) { - Assert.notNull(supplier, "Supplier must not be null!"); + public Stream findAll(Class entityClass) { + Assert.notNull(entityClass, "Entity class must not be null!"); - try { - return supplier.get(); - } catch (AerospikeException e) { - throw translateError(e); - } + return findAll(entityClass, getSetName(entityClass)); } @Override - public long count(Query query, Class entityClass) { - Assert.notNull(entityClass, "Class must not be null!"); - return count(query, getSetName(entityClass)); + public Stream findAll(Class entityClass, Class targetClass) { + Assert.notNull(entityClass, "Entity class must not be null!"); + Assert.notNull(targetClass, "Target class must not be null!"); + + return findAll(targetClass, getSetName(entityClass)); } @Override - public long count(Query query, String setName) { - Stream results = findAllRecordsUsingQuery(setName, query); - return results.count(); + 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); } @Override - public Stream find(Query query, Class entityClass) { - return find(query, entityClass, getSetName(entityClass)); + public Stream findAll(Sort sort, long offset, long limit, Class entityClass) { + return findAll(sort, offset, limit, entityClass, getSetName(entityClass)); } @Override - public Stream find(Query query, Class entityClass, Class targetClass) { - return find(query, targetClass, getSetName(entityClass)); + public Stream findAll(Sort sort, long offset, long limit, Class entityClass, Class targetClass) { + return findAll(sort, offset, limit, targetClass, getSetName(entityClass)); } @Override - public Stream find(Query query, Class targetClass, String setName) { - Assert.notNull(query, "Query must not be null!"); - Assert.notNull(targetClass, "Target class must not be null!"); + public Stream findAll(Sort sort, long offset, long limit, Class targetClass, String setName) { 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); + } + + 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); + return applyPostProcessingOnResults(results, query); + } - return findAllUsingQueryWithPostProcessing(setName, targetClass, query); + private Stream findUsingQualifiersWithDistinctPredicate(String setName, Class targetClass, + Predicate distinctPredicate, + Qualifier... qualifiers) { + return findRecordsUsingQualifiers(setName, targetClass, null, qualifiers) + .filter(distinctPredicate) + .map(keyRecord -> mapToEntity(keyRecord, targetClass)); } @Override @@ -1016,19 +977,57 @@ public Stream findInRange(long offset, long limit, Sort sort, } @Override - public Stream findInRange(long offset, long limit, Sort sort, - Class entityClass, Class targetClass) { - return findInRange(offset, limit, sort, targetClass, getSetName(entityClass)); + public Stream findInRange(long offset, long limit, Sort sort, + Class entityClass, Class targetClass) { + return findInRange(offset, limit, sort, targetClass, getSetName(entityClass)); + } + + @Override + public Stream findInRange(long offset, long limit, Sort sort, + Class targetClass, String setName) { + 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); + } + + @Override + public boolean exists(Object id, Class entityClass) { + Assert.notNull(id, "Id must not be null!"); + Assert.notNull(entityClass, "Class must not be null!"); + return exists(id, getSetName(entityClass)); + } + + @Override + public boolean exists(Object id, String setName) { + Assert.notNull(id, "Id must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); + + try { + Key key = getKey(id, setName); + + Record aeroRecord = this.client.operate(null, key, Operation.getHeader()); + return aeroRecord != null; + } catch (AerospikeException e) { + throw translateError(e); + } + } + + @Override + public boolean existsByQuery(Query query, Class entityClass) { + Assert.notNull(query, "Query passed in to exist can't be null"); + Assert.notNull(entityClass, "Class must not be null!"); + return existsByQuery(query, entityClass, getSetName(entityClass)); } @Override - public Stream findInRange(long offset, long limit, Sort sort, - Class targetClass, String setName) { - Assert.notNull(targetClass, "Target class must not be null!"); + public boolean existsByQuery(Query query, Class entityClass, String setName) { + Assert.notNull(query, "Query passed in to exist can't be null"); + Assert.notNull(entityClass, "Class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return findAllUsingQueryWithPostProcessing(setName, targetClass, sort, offset, limit, - null, null); + return find(query, entityClass, setName).findAny().isPresent(); } @Override @@ -1057,147 +1056,167 @@ public long count(String setName) { } @Override - public T prepend(T document, String fieldName, String value) { - return prepend(document, getSetName(document), fieldName, value); + public long count(Query query, Class entityClass) { + Assert.notNull(entityClass, "Class must not be null!"); + return count(query, getSetName(entityClass)); } @Override - public T prepend(T document, String setName, String fieldName, String value) { - Assert.notNull(document, "Document must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); - Assert.notNull(fieldName, "Field name must not be null!"); + public long count(Query query, String setName) { + Stream results = findRecordsUsingQuery(setName, query); + return results.count(); + } - try { - AerospikeWriteData data = writeData(document, setName); - Record aeroRecord = this.client.operate(null, data.getKey(), - Operation.prepend(new Bin(fieldName, value)), - Operation.get(fieldName)); + private Stream findRecordsUsingQuery(String setName, Query query) { + Assert.notNull(query, "Query must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); - return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); - } catch (AerospikeException e) { - throw translateError(e); - } + Qualifier qualifier = query.getCriteria().getCriteriaObject(); + return findRecordsUsingQualifiers(setName, null, null, qualifier); } @Override - public T prepend(T document, Map values) { - return prepend(document, getSetName(document), values); + public ResultSet aggregate(Filter filter, Class entityClass, + String module, String function, List arguments) { + return aggregate(filter, getSetName(entityClass), module, function, arguments); } @Override - public T prepend(T document, String setName, Map values) { - Assert.notNull(document, "Document must not be null!"); + public ResultSet aggregate(Filter filter, String setName, + String module, String function, List arguments) { Assert.notNull(setName, "Set name must not be null!"); - Assert.notNull(values, "Values must not be null!"); - try { - AerospikeWriteData data = writeData(document, setName); - Operation[] ops = operations(values, Operation.Type.PREPEND, Operation.get()); - Record aeroRecord = this.client.operate(null, data.getKey(), ops); - - return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); - } catch (AerospikeException e) { - throw translateError(e); - } + Statement statement = new Statement(); + if (filter != null) + statement.setFilter(filter); + statement.setSetName(setName); + statement.setNamespace(this.namespace); + ResultSet resultSet; + if (arguments != null && !arguments.isEmpty()) + resultSet = this.client.queryAggregate(null, statement, module, + function, arguments.toArray(new Value[0])); + else + resultSet = this.client.queryAggregate(null, statement); + return resultSet; } @Override - public T append(T document, Map values) { - return append(document, getSetName(document), values); + public void createIndex(Class entityClass, String indexName, + String binName, IndexType indexType) { + Assert.notNull(entityClass, "Class must not be null!"); + createIndex(entityClass, indexName, binName, indexType, IndexCollectionType.DEFAULT); } @Override - public T append(T document, String setName, Map values) { - Assert.notNull(document, "Document must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); - Assert.notNull(values, "Values must not be null!"); + public void createIndex(Class entityClass, String indexName, + String binName, IndexType indexType, IndexCollectionType indexCollectionType) { + Assert.notNull(entityClass, "Class must not be null!"); + createIndex(entityClass, indexName, binName, indexType, indexCollectionType, new CTX[0]); + } - try { - AerospikeWriteData data = writeData(document, setName); - Operation[] ops = operations(values, Operation.Type.APPEND, Operation.get()); - Record aeroRecord = this.client.operate(null, data.getKey(), ops); + @Override + public void createIndex(Class entityClass, String indexName, + String binName, IndexType indexType, IndexCollectionType indexCollectionType, + CTX... ctx) { + Assert.notNull(entityClass, "Class must not be null!"); + createIndex(getSetName(entityClass), indexName, binName, indexType, indexCollectionType, ctx); + } - return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); - } catch (AerospikeException e) { - throw translateError(e); - } + @Override + public void createIndex(String setName, String indexName, + String binName, IndexType indexType) { + createIndex(setName, indexName, binName, indexType, IndexCollectionType.DEFAULT); } @Override - public T append(T document, String binName, String value) { - return append(document, getSetName(document), binName, value); + public void createIndex(String setName, String indexName, String binName, IndexType indexType, + IndexCollectionType indexCollectionType) { + createIndex(setName, indexName, binName, indexType, indexCollectionType, new CTX[0]); } @Override - public T append(T document, String setName, String binName, String value) { - Assert.notNull(document, "Document must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); + public void createIndex(String setName, String indexName, String binName, + IndexType indexType, IndexCollectionType indexCollectionType, CTX... ctx) { + Assert.notNull(setName, "Set name type must not be null!"); + Assert.notNull(indexName, "Index name must not be null!"); Assert.notNull(binName, "Bin name must not be null!"); + Assert.notNull(indexType, "Index type must not be null!"); + Assert.notNull(indexCollectionType, "Index collection type must not be null!"); + Assert.notNull(ctx, "Ctx must not be null!"); try { - AerospikeWriteData data = writeData(document, setName); - Record aeroRecord = this.client.operate(null, data.getKey(), - Operation.append(new Bin(binName, value)), - Operation.get(binName)); - - return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); + IndexTask task = client.createIndex(null, this.namespace, + setName, indexName, binName, indexType, indexCollectionType, ctx); + if (task != null) { + task.waitTillComplete(); + } + refreshIndexesCache(); } catch (AerospikeException e) { throw translateError(e); } } @Override - public T add(T document, Map values) { - return add(document, getSetName(document), values); + public void deleteIndex(Class entityClass, String indexName) { + Assert.notNull(entityClass, "Class must not be null!"); + deleteIndex(getSetName(entityClass), indexName); } @Override - public T add(T document, String setName, Map values) { - Assert.notNull(document, "Document must not be null!"); + public void deleteIndex(String setName, String indexName) { Assert.notNull(setName, "Set name must not be null!"); - Assert.notNull(values, "Values must not be null!"); + Assert.notNull(indexName, "Index name must not be null!"); try { - AerospikeWriteData data = writeData(document, setName); - Operation[] ops = operations(values, Operation.Type.ADD, Operation.get()); - - WritePolicy writePolicy = WritePolicyBuilder.builder(client.getWritePolicyDefault()) - .expiration(data.getExpiration()) - .build(); - - Record aeroRecord = this.client.operate(writePolicy, data.getKey(), ops); - - return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); + IndexTask task = client.dropIndex(null, this.namespace, setName, indexName); + if (task != null) { + task.waitTillComplete(); + } + refreshIndexesCache(); } catch (AerospikeException e) { throw translateError(e); } } @Override - public T add(T document, String binName, long value) { - return add(document, getSetName(document), binName, value); - } - - @Override - public T add(T document, String setName, String binName, long value) { - Assert.notNull(document, "Document must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); - Assert.notNull(binName, "Bin name must not be null!"); + public boolean indexExists(String indexName) { + Assert.notNull(indexName, "Index name must not be null!"); try { - AerospikeWriteData data = writeData(document, setName); - - WritePolicy writePolicy = WritePolicyBuilder.builder(client.getWritePolicyDefault()) - .expiration(data.getExpiration()) - .build(); + Node[] nodes = client.getNodes(); + for (Node node : nodes) { + String response = Info.request(node, "sindex-exists:ns=" + namespace + ";indexname=" + indexName); + if (response == null) throw new AerospikeException("Null node response"); - Record aeroRecord = this.client.operate(writePolicy, data.getKey(), - Operation.add(new Bin(binName, value)), Operation.get()); + if (response.equalsIgnoreCase("true")) { + return true; + } else if (response.equalsIgnoreCase("false")) { + return false; + } else { + Matcher matcher = INDEX_EXISTS_REGEX_PATTERN.matcher(response); + if (matcher.matches()) { + int reason; + try { + reason = Integer.parseInt(matcher.group(1)); + } catch (NumberFormatException e) { + throw new AerospikeException("Unexpected node response, unable to parse ResultCode: " + + response); + } - return mapToEntity(data.getKey(), getEntityClass(document), aeroRecord); + // as for Server ver. >= 6.1.0.1 the response containing ResultCode.INVALID_NAMESPACE + // means that the request should be sent to another node + if (reason != ResultCode.INVALID_NAMESPACE) { + throw new AerospikeException(reason); + } + } else { + throw new AerospikeException("Unexpected node response: " + response); + } + } + } } catch (AerospikeException e) { throw translateError(e); } + return false; } private void doPersistAndHandleError(AerospikeWriteData data, WritePolicy policy, Operation[] operations) { @@ -1234,50 +1253,15 @@ private Record putAndGetHeader(AerospikeWriteData data, WritePolicy policy, bool return client.operate(policy, key, operations); } - private Stream findAllUsingQueryWithPostProcessing(String setName, Class targetClass, Query query) { - verifyUnsortedWithOffset(query.getSort(), query.getOffset()); - Qualifier qualifier = query.getCriteria().getCriteriaObject(); - Stream results = findAllUsingQueryWithDistinctPredicate(setName, targetClass, - getDistinctPredicate(query), qualifier); - return applyPostProcessingOnResults(results, query); - } - @SuppressWarnings("SameParameterValue") - private Stream findAllUsingQueryWithPostProcessing(String setName, Class targetClass, Sort sort, - long offset, long limit, Filter filter, - Qualifier qualifier) { + private Stream findUsingQualifierWithPostProcessing(String setName, Class targetClass, Sort sort, + long offset, long limit, Filter filter, + Qualifier qualifier) { verifyUnsortedWithOffset(sort, offset); - Stream results = findAllUsingQuery(targetClass, setName, filter, qualifier); + Stream results = findUsingQualifier(targetClass, setName, filter, qualifier); return applyPostProcessingOnResults(results, sort, offset, limit); } - @Override - public Stream findAllUsingQuery(Class entityClass, Filter filter, - Qualifier qualifier) { - return findAllUsingQuery(entityClass, getSetName(entityClass), filter, qualifier); - } - - public Stream findAllUsingQuery(Class entityClass, Class targetClass, Filter filter, - Qualifier qualifier) { - return findAllRecordsUsingQuery(getSetName(entityClass), targetClass, filter, qualifier) - .map(keyRecord -> mapToEntity(keyRecord, targetClass)); - } - - @Override - public Stream findAllUsingQuery(Class targetClass, String setName, Filter filter, - Qualifier qualifier) { - return findAllRecordsUsingQuery(setName, targetClass, filter, qualifier) - .map(keyRecord -> mapToEntity(keyRecord, targetClass)); - } - - private Stream findAllUsingQueryWithDistinctPredicate(String setName, Class targetClass, - Predicate distinctPredicate, - Qualifier... qualifiers) { - return findAllRecordsUsingQuery(setName, targetClass, null, qualifiers) - .filter(distinctPredicate) - .map(keyRecord -> mapToEntity(keyRecord, targetClass)); - } - private Stream applyPostProcessingOnResults(Stream results, Query query) { if (query.getSort() != null && query.getSort().isSorted()) { Comparator comparator = getComparator(query); @@ -1309,23 +1293,15 @@ private Stream applyPostProcessingOnResults(Stream results, Sort sort, return results; } - private Stream findAllRecordsUsingQuery(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 findAllRecordsUsingQuery(setName, null, null, qualifier); - } - - private Stream findAllRecordsUsingQuery(String setName, Class targetClass, Filter filter, - Qualifier... qualifiers) { + 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); if (idQualifier != null) { // a special flow if there is id given - return findByIdsInternalWithoutMapping(getIdValue(idQualifier), setName, targetClass, + return findByIdsWithoutMapping(getIdValue(idQualifier), setName, targetClass, excludeIdQualifier(qualifiers)).stream(); } } @@ -1348,4 +1324,34 @@ private Stream findAllRecordsUsingQuery(String setName, Class } }); } + + private List findByIdsWithoutMapping(Collection ids, String setName, + Class targetClass, + Qualifier... qualifiers) { + Assert.notNull(ids, "Ids must not be null"); + if (ids.isEmpty()) { + return Collections.emptyList(); + } + + try { + Key[] keys = getKeys(ids, setName); + + BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + + Record[] aeroRecords; + if (targetClass != null) { + String[] binNames = getBinNamesFromTargetClass(targetClass); + aeroRecords = getAerospikeClient().get(policy, keys, binNames); + } else { + aeroRecords = getAerospikeClient().get(policy, keys); + } + + return IntStream.range(0, keys.length) + .filter(index -> aeroRecords[index] != null) + .mapToObj(index -> new KeyRecord(keys[index], aeroRecords[index])) + .collect(Collectors.toList()); + } catch (AerospikeException e) { + throw translateError(e); + } + } } diff --git a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeInternalOperations.java b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeInternalOperations.java deleted file mode 100644 index cebba8de0..000000000 --- a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeInternalOperations.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.springframework.data.aerospike.core; - -import org.springframework.data.aerospike.query.Qualifier; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.Collection; - -public interface ReactiveAerospikeInternalOperations { - - /** - * Find document by providing id, set name will be determined by the given entityClass. - *

- * Documents will be mapped to the given targetClass. - * - * @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. - * @return The document from Aerospike, returned document will be mapped to targetClass's type. - */ - Mono findByIdInternal(Object id, Class entityClass, Class targetClass, - Qualifier... qualifiers); - - /** - * Find document by providing id with a given set name. - *

- * Documents will be mapped to the given targetClass. - * - * @param id The id of the document to find. Must not be {@literal null}. - * @param entityClass The class to get the entity properties from (such as expiration). Must not be - * {@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. - * @return The document from Aerospike, returned document will be mapped to targetClass's type. - */ - Mono findByIdInternal(Object id, Class entityClass, Class targetClass, String setName, - Qualifier... qualifiers); - - /** - * Find documents by providing multiple ids, set name will be determined by the given entityClass. - *

- * Documents will be mapped to the given targetClass. - * - * @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. - * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document - * exists, an empty list is returned. - */ - Flux findByIdsInternal(Collection ids, Class entityClass, Class targetClass, - Qualifier... qualifiers); - - /** - * Find documents by providing multiple ids with a given set name. - *

- * Documents will be mapped to the given targetClass. - * - * @param ids The ids of the documents to find. Must not be {@literal null}. - * @param entityClass The class to get the entity properties from (such as expiration). Must not be - * {@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. - * @return The documents from Aerospike, returned documents will be mapped to targetClass's type, if no document - * exists, an empty list is returned. - */ - Flux findByIdsInternal(Collection ids, Class entityClass, Class targetClass, String setName, - Qualifier... qualifiers); -} 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 508744ee5..21c39a1d3 100644 --- a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java +++ b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java @@ -17,6 +17,7 @@ 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; @@ -177,6 +178,28 @@ public interface ReactiveAerospikeOperations { */ Flux insertAll(Iterable documents, String setName); + /** + * Reactively persist a document using specified WritePolicy. + * + * @param document The document to persist. Must not be {@literal null}. + * @param writePolicy The Aerospike write policy for the inner Aerospike put operation. Must not be + * {@literal null}. + * @return A Mono of the new persisted document. + */ + Mono persist(T document, WritePolicy writePolicy); + + /** + * Reactively persist a document using specified WritePolicy and a given set (overrides the set associated with the + * document). + * + * @param document The document to persist. Must not be {@literal null}. + * @param writePolicy The Aerospike write policy for the inner Aerospike put operation. Must not be + * {@literal null}. + * @param setName Set name to override the set associated with the document. + * @return A Mono of the new persisted document. + */ + Mono persist(T document, WritePolicy writePolicy, String setName); + /** * Reactively update document using {@link com.aerospike.client.policy.RecordExistsAction#UPDATE_ONLY} policy * combined with removing bins at first (analogous to @@ -261,6 +284,142 @@ public interface ReactiveAerospikeOperations { */ Flux updateAll(Iterable documents, String setName); + /** + * Reactively truncate/delete all the documents in the given entity's set. + * + * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @deprecated since 4.6.0, use deleteAll(Class entityClass) instead. + */ + Mono delete(Class entityClass); + + /** + * Reactively delete document by id, set name will be determined by the given entityClass. + * + * @param id The id of the document to delete. Must not be {@literal null}. + * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @return A Mono of whether the document existed on server before deletion. + * @deprecated since 4.6.0, use deleteById(Object id, Class entityClass) instead. + */ + Mono delete(Object id, Class entityClass); + + /** + * Reactively delete document. + * + * @param document The document to delete. Must not be {@literal null}. + * @return A Mono of whether the document existed on server before deletion. + */ + Mono delete(T document); + + /** + * Reactively delete document with a given set name. + * + * @param document The document to delete. Must not be {@literal null}. + * @param setName Set name to delete the document. + * @return A Mono of whether the document existed on server before deletion. + */ + Mono delete(T document, String setName); + + /** + * Reactively delete document by id, set name will be determined by the given entityClass. + * + * @param id The id of the document to delete. Must not be {@literal null}. + * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @return A Mono of whether the document existed on server before deletion. + */ + Mono deleteById(Object id, Class entityClass); + + /** + * Reactively delete document by id with a given set name. + * + * @param id The id of the document to delete. Must not be {@literal null}. + * @param setName Set name to delete the document. + * @return A Mono of whether the document existed on server before deletion. + */ + Mono deleteById(Object id, String setName); + + /** + * Reactively delete documents by providing multiple ids using a single batch delete operation, set name will be + * determined by the given entityClass. + *

+ * This operation requires Server version 6.0+. + * + * @param ids The ids of the documents to find. 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 onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain + * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. + */ + Mono deleteByIds(Iterable ids, Class entityClass); + + /** + * Reactively delete documents by providing multiple ids using a single batch delete operation with a given set + * name. + *

+ * This operation requires Server version 6.0+. + * + * @param ids The ids of the documents to find. Must not be {@literal null}. + * @param setName Set name to delete the document. + * @return onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain + * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. + */ + Mono deleteByIds(Iterable ids, String setName); + + /** + * Reactively delete documents by providing multiple ids using a single batch delete operation, set name will be + * determined by the given entityClass. + *

+ * This operation requires Server version 6.0+. + * + * @param ids The ids of the documents to find. 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 onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain + * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. + */ + Mono deleteByIds(Collection ids, Class entityClass); + + /** + * Reactively delete documents by providing multiple ids using a single batch delete operation with a given set + * name. + *

+ * This operation requires Server version 6.0+. + * + * @param ids The ids of the documents to find. Must not be {@literal null}. + * @param setName Set name to delete the document. + * @return onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain + * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. + */ + Mono deleteByIds(Collection ids, String setName); + + /** + * Executes a single batch delete for several entities. + *

+ * Aerospike provides functionality to delete documents from different sets in 1 batch request. The methods allow to + * put grouped keys by entity type as parameter and get result as spring data aerospike entities grouped by entity + * type. + *

+ * This operation requires Server version 6.0+. + * + * @param groupedKeys Must not be {@literal null}. + * @return onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain + * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. + */ + Mono deleteByIds(GroupedKeys groupedKeys); + + /** + * Reactively truncate/delete all the documents in the given entity's set. + * + * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + */ + Mono deleteAll(Class entityClass); + + /** + * Reactively truncate/delete all the documents in the given entity's set. + * + * @param setName Set name to truncate/delete all the documents in. + */ + Mono deleteAll(String setName); + /** * Reactively add integer/double bin values to existing document bin values, read the new modified document and map * it back the given document class type. @@ -406,69 +565,12 @@ public interface ReactiveAerospikeOperations { Mono prepend(T document, String setName, String binName, String value); /** - * Reactively find all documents in the given entityClass's set and map them to the given class type. - * - * @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. - */ - Flux findAll(Class entityClass); - - /** - * Reactively find all documents in the given entityClass's set 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}. - * @return A Flux of matching documents, returned documents will be mapped to targetClass's type. - */ - Flux findAll(Class entityClass, Class targetClass); - - /** - * Reactively find all documents in the given set and map them to the given class type. - * - * @param targetClass The class to map the documents to. Must not be {@literal null}. - * @param setName The set name to find the document. - * @return A Flux of matching documents, returned documents will be mapped to entityClass's type. - */ - Flux findAll(Class targetClass, String setName); - - /** - * Reactively find all documents in the given entityClass's set using a provided sort and map them to the given - * class type. - * - * @param sort The sort to affect the returned iterable documents order. - * @param offset The offset to start the range from. - * @param limit The limit of the range. - * @param entityClass The class to extract the Aerospike set from and to map the documents to. - * @return A Flux of matching documents, returned documents will be mapped to entityClass's type. - */ - Flux findAll(Sort sort, long offset, long limit, Class entityClass); - - /** - * Reactively find all documents in the given entityClass's set using a provided sort and map them to the given - * target class type. - * - * @param sort The sort to affect the returned iterable documents order. - * @param offset The offset to start the range from. - * @param limit The limit of the range. - * @param entityClass The class to extract the Aerospike set from. - * @param targetClass The class to map the documents to. Must not be {@literal null}. - * @return A Flux of matching documents, returned documents will be mapped to targetClass's type. - */ - Flux findAll(Sort sort, long offset, long limit, Class entityClass, Class targetClass); - - /** - * Reactively find all documents in the given entityClass's set using a provided sort and map them to the given - * target class type. + * Reactively execute operation against underlying store. * - * @param sort The sort to affect the returned iterable documents order. - * @param offset The offset to start the range from. - * @param limit The limit of the range. - * @param targetClass The class to map the documents to. Must not be {@literal null}. - * @param setName The set name to find the documents. - * @return A Flux of matching documents, returned documents will be mapped to targetClass's type. + * @param supplier must not be {@literal null}. + * @return A Mono of the execution result. */ - Flux findAll(Sort sort, long offset, long limit, Class targetClass, String setName); + Mono execute(Supplier supplier); /** * Reactively find a document by id, set name will be determined by the given entityClass. @@ -571,6 +673,69 @@ public interface ReactiveAerospikeOperations { */ Mono findByIds(GroupedKeys groupedKeys); + /** + * Find document by providing id, set name will be determined by the given entityClass. + *

+ * Documents will be mapped to the given targetClass. + * + * @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. + * @return The document from Aerospike, returned document will be mapped to targetClass's type. + */ + Mono findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, + Qualifier... qualifiers); + + /** + * Find document by providing id with a given set name. + *

+ * Documents will be mapped to the given targetClass. + * + * @param id The id of the document to find. Must not be {@literal null}. + * @param entityClass The class to get the entity properties from (such as expiration). Must not be + * {@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. + * @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); + + /** + * Find documents by providing multiple ids, set name will be determined by the given entityClass. + *

+ * Documents will be mapped to the given targetClass. + * + * @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. + * @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); + + /** + * Find documents by providing multiple ids with a given set name. + *

+ * Documents will be mapped to the given targetClass. + * + * @param ids The ids of the documents to find. Must not be {@literal null}. + * @param entityClass The class to get the entity properties from (such as expiration). Must not be + * {@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. + * @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); + /** * Reactively find documents in the given entityClass's set using a query and map them to the given class type. * @@ -602,6 +767,111 @@ public interface ReactiveAerospikeOperations { */ 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. + * + * @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. + */ + Flux findAll(Class entityClass); + + /** + * Reactively find all documents in the given entityClass's set 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}. + * @return A Flux of matching documents, returned documents will be mapped to targetClass's type. + */ + Flux findAll(Class entityClass, Class targetClass); + + /** + * Reactively find all documents in the given set and map them to the given class type. + * + * @param targetClass The class to map the documents to. Must not be {@literal null}. + * @param setName The set name to find the document. + * @return A Flux of matching documents, returned documents will be mapped to entityClass's type. + */ + Flux findAll(Class targetClass, String setName); + + /** + * Reactively find all documents in the given entityClass's set using a provided sort and map them to the given + * class type. + * + * @param sort The sort to affect the returned iterable documents order. + * @param offset The offset to start the range from. + * @param limit The limit of the range. + * @param entityClass The class to extract the Aerospike set from and to map the documents to. + * @return A Flux of matching documents, returned documents will be mapped to entityClass's type. + */ + Flux findAll(Sort sort, long offset, long limit, Class entityClass); + + /** + * Reactively find all documents in the given entityClass's set using a provided sort and map them to the given + * target class type. + * + * @param sort The sort to affect the returned iterable documents order. + * @param offset The offset to start the range from. + * @param limit The limit of the range. + * @param entityClass The class to extract the Aerospike set from. + * @param targetClass The class to map the documents to. Must not be {@literal null}. + * @return A Flux of matching documents, returned documents will be mapped to targetClass's type. + */ + Flux findAll(Sort sort, long offset, long limit, Class entityClass, Class targetClass); + + /** + * Reactively find all documents in the given entityClass's set using a provided sort and map them to the given + * target class type. + * + * @param sort The sort to affect the returned iterable documents order. + * @param offset The offset to start the range from. + * @param limit The limit of the range. + * @param targetClass The class to map the documents to. Must not be {@literal null}. + * @param setName The set name to find the documents. + * @return A Flux of matching documents, returned documents will be mapped to targetClass's type. + */ + Flux findAll(Sort sort, long offset, long limit, Class targetClass, String setName); + /** * Reactively find documents in the given entityClass's set using a range (offset, limit) and a sort and map them to * the given class type. @@ -641,40 +911,6 @@ public interface ReactiveAerospikeOperations { */ Flux findInRange(long offset, long limit, Sort sort, Class entityClass, Class targetClass); - /** - * Reactively return the amount of documents in a query results. set name will be determined by the given - * entityClass. - * - * @param query The query that provides the result set for count. - * @param entityClass entityClass The class to extract the Aerospike set from. Must not be {@literal null}. - * @return A Mono of the amount of documents that the given query and entity class supplied. - */ - Mono count(Query query, Class entityClass); - - /** - * Reactively return the amount of documents in the given Aerospike set. - * - * @param setName The name of the set to count. Must not be {@literal null}. - * @return A Mono of the amount of documents in the given set. - */ - Mono count(String setName); - - /** - * Reactively return the amount of documents in the given entityClass's Aerospike set. - * - * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. - * @return A Mono of the amount of documents in the set (of the given entityClass). - */ - Mono count(Class entityClass); - - /** - * Reactively execute operation against underlying store. - * - * @param supplier must not be {@literal null}. - * @return A Mono of the execution result. - */ - Mono execute(Supplier supplier); - /** * Reactively check if document exists by providing document id and entityClass (set name will be determined by the * given entityClass). @@ -695,95 +931,59 @@ public interface ReactiveAerospikeOperations { Mono exists(Object id, String setName); /** - * Reactively truncate/delete all the documents in the given entity's set. + * Reactively check if any document exists by defining a query and entityClass (set name will be determined by the + * given entityClass). * + * @param query The query to check if any returned document exists. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @return A Mono of whether the document exists. */ - Mono deleteAll(Class entityClass); + Mono existsByQuery(Query query, Class entityClass); /** - * Reactively truncate/delete all the documents in the given entity's set. + * Reactively check if any document exists by defining a query, entityClass and a given set name. * - * @param setName Set name to truncate/delete all the documents in. + * @param query The query to check if any returned document exists. Must not be {@literal null}. + * @param entityClass The class to translate to returned results into. Must not be {@literal null}. + * @param setName The set name to check if documents exists in. Must not be {@literal null}. + * @return A Mono of whether the document exists. */ - Mono deleteAll(String setName); + Mono existsByQuery(Query query, Class entityClass, String setName); /** - * Reactively delete document by id, set name will be determined by the given entityClass. + * Reactively return the amount of documents in the given entityClass's Aerospike set. * - * @param id The id of the document to delete. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set from. Must not be {@literal null}. - * @return A Mono of whether the document existed on server before deletion. - */ - Mono deleteById(Object id, Class entityClass); - - /** - * Reactively delete document by id with a given set name. - * - * @param id The id of the document to delete. Must not be {@literal null}. - * @param setName Set name to delete the document. - * @return A Mono of whether the document existed on server before deletion. - */ - Mono deleteById(Object id, String setName); - - /** - * Reactively delete document. - * - * @param document The document to delete. Must not be {@literal null}. - * @return A Mono of whether the document existed on server before deletion. - */ - Mono delete(T document); - - /** - * Reactively delete document with a given set name. - * - * @param document The document to delete. Must not be {@literal null}. - * @param setName Set name to delete the document. - * @return A Mono of whether the document existed on server before deletion. + * @return A Mono of the amount of documents in the set (of the given entityClass). */ - Mono delete(T document, String setName); + Mono count(Class entityClass); /** - * Reactively delete documents by providing multiple ids using a single batch delete operation, set name will be - * determined by the given entityClass. - *

- * This operation requires Server version 6.0+. + * Reactively return the amount of documents in the given Aerospike set. * - * @param ids The ids of the documents to find. 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 onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain - * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. + * @param setName The name of the set to count. Must not be {@literal null}. + * @return A Mono of the amount of documents in the given set. */ - Mono deleteByIds(Iterable ids, Class entityClass); + Mono count(String setName); /** - * Reactively delete documents by providing multiple ids using a single batch delete operation with a given set - * name. - *

- * This operation requires Server version 6.0+. + * Reactively return the amount of documents in a query results. set name will be determined by the given + * entityClass. * - * @param ids The ids of the documents to find. Must not be {@literal null}. - * @param setName Set name to delete the document. - * @return onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain - * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. + * @param query The query that provides the result set for count. + * @param entityClass entityClass The class to extract the Aerospike set from. Must not be {@literal null}. + * @return A Mono of the amount of documents that the given query and entity class supplied. */ - Mono deleteByIds(Iterable ids, String setName); + Mono count(Query query, Class entityClass); /** - * Executes a single batch delete for several entities. - *

- * Aerospike provides functionality to delete documents from different sets in 1 batch request. The methods allow to - * put grouped keys by entity type as parameter and get result as spring data aerospike entities grouped by entity - * type. - *

- * This operation requires Server version 6.0+. + * Reactively return the amount of documents in a query results with a given set name. * - * @param groupedKeys Must not be {@literal null}. - * @return onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain - * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. + * @param query The query that provides the result set for count. + * @param setName The name of the set to count. Must not be {@literal null}. + * @return A Mono of the amount of documents that the given query and set name. */ - Mono deleteByIds(GroupedKeys groupedKeys); + Mono count(Query query, String setName); /** * Reactively create index by specified name in Aerospike. @@ -880,16 +1080,4 @@ Mono createIndex(String setName, String indexName, String binName, * @return Mono of true if exists. */ Mono indexExists(String indexName); - - /** - * 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 findAllUsingQuery(Class entityClass, @Nullable Filter filter, Qualifier qualifier); } 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 3908c92e3..19aa06892 100644 --- a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java +++ b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java @@ -43,10 +43,12 @@ import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.utility.Utils; import org.springframework.data.domain.Sort; +import org.springframework.data.keyvalue.core.IterableConverter; import org.springframework.data.mapping.PropertyHandler; import org.springframework.util.Assert; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -59,7 +61,6 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.StreamSupport; import static com.aerospike.client.ResultCode.KEY_NOT_FOUND_ERROR; import static java.util.Objects.nonNull; @@ -80,7 +81,7 @@ */ @Slf4j public class ReactiveAerospikeTemplate extends BaseAerospikeTemplate implements ReactiveAerospikeOperations, - ReactiveAerospikeInternalOperations, IndexesCacheRefresher { + IndexesCacheRefresher { private static final Pattern INDEX_EXISTS_REGEX_PATTERN = Pattern.compile("^FAIL:(-?\\d+).*$"); private final IAerospikeReactorClient reactorClient; @@ -232,6 +233,25 @@ public Flux insertAll(Iterable documents, String setName) { return batchWriteAndCheckForErrors(batchWriteRecords, batchWriteDataList, "insert"); } + @Override + public Mono persist(T document, WritePolicy policy) { + Assert.notNull(document, "Document must not be null!"); + Assert.notNull(policy, "Policy must not be null!"); + return persist(document, policy, getSetName(document)); + } + + @Override + public Mono persist(T document, WritePolicy policy, String setName) { + Assert.notNull(document, "Document must not be null!"); + Assert.notNull(policy, "Policy must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); + + AerospikeWriteData data = writeData(document, setName); + + Operation[] operations = operations(data.getBinsAsArray(), Operation::put); + return doPersistAndHandleError(document, data, policy, operations); + } + @Override public Mono update(T document) { return update(document, getSetName(document)); @@ -306,51 +326,159 @@ public Flux updateAll(Iterable documents, String setName) { return batchWriteAndCheckForErrors(batchWriteRecords, batchWriteDataList, "update"); } + @Deprecated @Override - public Flux findAll(Class entityClass) { - Assert.notNull(entityClass, "Entity class must not be null!"); + public Mono delete(Class entityClass) { + Assert.notNull(entityClass, "Class must not be null!"); - return findAll(entityClass, getSetName(entityClass)); + try { + String set = getSetName(entityClass); + return Mono.fromRunnable( + () -> reactorClient.getAerospikeClient().truncate(null, this.namespace, set, null)); + } catch (AerospikeException e) { + throw translateError(e); + } } + @Deprecated @Override - public Flux findAll(Class entityClass, Class targetClass) { - Assert.notNull(entityClass, "Entity class must not be null!"); - Assert.notNull(targetClass, "Target class must not be null!"); + public Mono delete(Object id, Class entityClass) { + Assert.notNull(id, "Id must not be null!"); + Assert.notNull(entityClass, "Class must not be null!"); - return findAll(targetClass, getSetName(entityClass)); + AerospikePersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); + + return reactorClient + .delete(ignoreGenerationDeletePolicy(), getKey(id, entity)) + .map(k -> true) + .onErrorMap(this::translateError); } @Override - public Flux findAll(Class targetClass, String setName) { - Assert.notNull(targetClass, "Target class must not be null!"); + public Mono delete(T document) { + return delete(document, getSetName(document)); + } + + @Override + public Mono delete(T document, String setName) { + Assert.notNull(document, "Document must not be null!"); + Assert.notNull(document, "Set name must not be null!"); + + AerospikeWriteData data = writeData(document, setName); + + return reactorClient + .delete(ignoreGenerationDeletePolicy(), data.getKey()) + .map(key -> true) + .onErrorMap(this::translateError); + } + + @Override + public Mono deleteById(Object id, Class entityClass) { + Assert.notNull(id, "Id must not be null!"); + Assert.notNull(entityClass, "Class must not be null!"); + + return deleteById(id, getSetName(entityClass)); + } + + @Override + public Mono deleteById(Object id, String setName) { + Assert.notNull(id, "Id must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return findAllUsingQuery(setName, targetClass, null, (Qualifier[]) null); + return reactorClient + .delete(ignoreGenerationDeletePolicy(), getKey(id, setName)) + .map(k -> true) + .onErrorMap(this::translateError); } @Override - public Flux findAll(Sort sort, long offset, long limit, Class entityClass) { + public Mono deleteByIds(Iterable ids, Class entityClass) { + Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(entityClass, "Class must not be null!"); - return findAll(sort, offset, limit, entityClass, getSetName(entityClass)); + return deleteByIds(ids, getSetName(entityClass)); } @Override - public Flux findAll(Sort sort, long offset, long limit, Class entityClass, Class targetClass) { + public Mono deleteByIds(Iterable ids, String setName) { + Assert.notNull(ids, "List of ids must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); + + return deleteByIds(IterableConverter.toList(ids), setName); + } + + @Override + public Mono deleteByIds(Collection ids, Class entityClass) { + Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(entityClass, "Class must not be null!"); - Assert.notNull(targetClass, "Target class must not be null!"); - return findAll(sort, offset, limit, targetClass, getSetName(entityClass)); + return deleteByIds(ids, getSetName(entityClass)); } @Override - public Flux findAll(Sort sort, long offset, long limit, Class targetClass, String setName) { - Assert.notNull(targetClass, "Target class must not be null!"); + public Mono deleteByIds(Collection ids, String setName) { + Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return findAllUsingQueryWithPostProcessing(setName, targetClass, sort, offset, limit, - null, (Qualifier[]) null); + Key[] keys = ids.stream() + .map(id -> getKey(id, setName)) + .toArray(Key[]::new); + + return batchDeleteAndCheckForErrors(reactorClient, keys); + } + + private Mono batchDeleteAndCheckForErrors(IAerospikeReactorClient reactorClient, Key[] keys) { + Function> checkForErrors = results -> { + for (BatchRecord record : results.records) { + if (batchRecordFailed(record)) { + return Mono.error(new AerospikeException.BatchRecordArray(results.records, + new AerospikeException("Errors during batch delete"))); + } + } + return Mono.empty(); + }; + + // requires server ver. >= 6.0.0 + return reactorClient.delete(null, null, keys) + .onErrorMap(this::translateError) + .flatMap(checkForErrors); + } + + @Override + public Mono deleteByIds(GroupedKeys groupedKeys) { + Assert.notNull(groupedKeys, "Grouped keys must not be null!"); + Assert.notNull(groupedKeys.getEntitiesKeys(), "Entities keys must not be null!"); + Assert.notEmpty(groupedKeys.getEntitiesKeys(), "Entities keys must not be empty!"); + + return deleteEntitiesByGroupedKeys(groupedKeys); + } + + private Mono deleteEntitiesByGroupedKeys(GroupedKeys groupedKeys) { + EntitiesKeys entitiesKeys = EntitiesKeys.of(toEntitiesKeyMap(groupedKeys)); + + reactorClient.delete(null, null, entitiesKeys.getKeys()) + .doOnError(this::translateError); + + return batchDeleteAndCheckForErrors(reactorClient, entitiesKeys.getKeys()); + } + + @Override + public Mono deleteAll(Class entityClass) { + Assert.notNull(entityClass, "Class must not be null!"); + + return deleteAll(getSetName(entityClass)); + } + + @Override + public Mono deleteAll(String setName) { + Assert.notNull(setName, "Set name must not be null!"); + + try { + return Mono.fromRunnable( + () -> reactorClient.getAerospikeClient().truncate(null, this.namespace, setName, null)); + } catch (AerospikeException e) { + throw translateError(e); + } } @Override @@ -472,6 +600,14 @@ private Mono executeOperationsOnValue(T document, AerospikeWriteData data .onErrorMap(this::translateError); } + @Override + public Mono execute(Supplier supplier) { + Assert.notNull(supplier, "Supplier must not be null!"); + + return Mono.fromSupplier(supplier) + .onErrorMap(this::translateError); + } + @Override public Mono findById(Object id, Class entityClass) { Assert.notNull(entityClass, "Class must not be null!"); @@ -534,14 +670,58 @@ public Mono findById(Object id, Class entityClass, Class targetC } @Override - public Mono findByIdInternal(Object id, Class entityClass, Class targetClass, - Qualifier... qualifiers) { - return findByIdInternal(id, entityClass, targetClass, getSetName(entityClass), qualifiers); + public Flux findByIds(Iterable ids, Class entityClass) { + Assert.notNull(entityClass, "Class must not be null!"); + return findByIds(ids, entityClass, getSetName(entityClass)); + } + + @Override + public Flux findByIds(Iterable ids, Class entityClass, Class targetClass) { + Assert.notNull(entityClass, "Class must not be null!"); + return findByIds(ids, targetClass, getSetName(entityClass)); + } + + @Override + public Flux findByIds(Iterable ids, Class targetClass, String setName) { + Assert.notNull(ids, "List of ids must not be null!"); + Assert.notNull(targetClass, "Class must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); + + return Flux.fromIterable(ids) + .map(id -> getKey(id, setName)) + .flatMap(reactorClient::get) + .filter(keyRecord -> nonNull(keyRecord.record)) + .map(keyRecord -> mapToEntity(keyRecord.key, targetClass, keyRecord.record)); } @Override - public Mono findByIdInternal(Object id, Class entityClass, Class targetClass, String setName, - Qualifier... qualifiers) { + public Mono findByIds(GroupedKeys groupedKeys) { + Assert.notNull(groupedKeys, "Grouped keys must not be null!"); + + if (groupedKeys.getEntitiesKeys() == null || groupedKeys.getEntitiesKeys().isEmpty()) { + return Mono.just(GroupedEntities.builder().build()); + } + + return findGroupedEntitiesByGroupedKeys(groupedKeys); + } + + private Mono findGroupedEntitiesByGroupedKeys(GroupedKeys groupedKeys) { + EntitiesKeys entitiesKeys = EntitiesKeys.of(toEntitiesKeyMap(groupedKeys)); + + return reactorClient.get(null, entitiesKeys.getKeys()) + .map(item -> toGroupedEntities(entitiesKeys, item.records)) + .onErrorMap(this::translateError); + } + + @Override + public Mono findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, + Qualifier... qualifiers) { + return findByIdUsingQualifiers(id, entityClass, targetClass, getSetName(entityClass), qualifiers); + } + + @Override + public Mono findByIdUsingQualifiers(Object id, Class entityClass, Class targetClass, String setName, + Qualifier... qualifiers) { AerospikePersistentEntity entity = mappingContext.getRequiredPersistentEntity(entityClass); Key key = getKey(id, setName); @@ -579,40 +759,15 @@ public Mono findByIdInternal(Object id, Class entityClass, Class } @Override - public Flux findByIds(Iterable ids, Class entityClass) { - Assert.notNull(entityClass, "Class must not be null!"); - return findByIds(ids, entityClass, getSetName(entityClass)); - } - - @Override - public Flux findByIds(Iterable ids, Class entityClass, Class targetClass) { - Assert.notNull(entityClass, "Class must not be null!"); - return findByIds(ids, targetClass, getSetName(entityClass)); - } - - @Override - public Flux findByIds(Iterable ids, Class targetClass, String setName) { - Assert.notNull(ids, "List of ids must not be null!"); - Assert.notNull(targetClass, "Class must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); - - return Flux.fromIterable(ids) - .map(id -> getKey(id, setName)) - .flatMap(reactorClient::get) - .filter(keyRecord -> nonNull(keyRecord.record)) - .map(keyRecord -> mapToEntity(keyRecord.key, targetClass, keyRecord.record)); - } - - @Override - public Flux findByIdsInternal(Collection ids, Class entityClass, Class targetClass, - Qualifier... qualifiers) { - return findByIdsInternal(ids, entityClass, targetClass, getSetName(entityClass), qualifiers); + public Flux findByIdsUsingQualifiers(Collection ids, Class entityClass, Class targetClass, + Qualifier... qualifiers) { + return findByIdsUsingQualifiers(ids, entityClass, targetClass, getSetName(entityClass), qualifiers); } @Override - public Flux findByIdsInternal(Collection ids, Class entityClass, Class targetClass, - String setName, - Qualifier... qualifiers) { + 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!"); @@ -636,80 +791,92 @@ public Flux findByIdsInternal(Collection ids, Class entityClass, .map(keyRecord -> mapToEntity(keyRecord.key, target, keyRecord.record)); } - private Flux findByIdsInternalWithoutMapping(Collection ids, String setName, - Class targetClass, - Qualifier... qualifiers) { - Assert.notNull(ids, "List of ids must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); + @Override + public Flux find(Query query, Class entityClass) { + Assert.notNull(entityClass, "Class must not be null!"); + return find(query, entityClass, getSetName(entityClass)); + } - if (ids.isEmpty()) { - return Flux.empty(); - } + @Override + public Flux find(Query query, Class entityClass, Class targetClass) { + Assert.notNull(entityClass, "Class must not be null!"); + return find(query, targetClass, getSetName(entityClass)); + } - BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + @Override + public Flux find(Query query, Class targetClass, String setName) { + Assert.notNull(query, "Query must not be null!"); + Assert.notNull(targetClass, "Target class must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); - return Flux.fromIterable(ids) - .map(id -> getKey(id, setName)) - .flatMap(key -> getFromClient(policy, key, targetClass)) - .filter(keyRecord -> nonNull(keyRecord.record)); + return findUsingQueryWithPostProcessing(setName, targetClass, query); } - private BatchPolicy getBatchPolicyFilterExp(Qualifier[] qualifiers) { - if (qualifiers != null && qualifiers.length > 0) { - BatchPolicy policy = new BatchPolicy(reactorClient.getAerospikeClient().getBatchPolicyDefault()); - policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(qualifiers); - return policy; - } - return null; + @Override + public Flux findUsingQualifier(Class entityClass, Filter filter, + Qualifier qualifier) { + return findUsingQualifier(entityClass, getSetName(entityClass), filter, qualifier); } - private Mono getFromClient(BatchPolicy finalPolicy, Key key, Class targetClass) { - if (targetClass != null) { - String[] binNames = getBinNamesFromTargetClass(targetClass); - return reactorClient.get(finalPolicy, key, binNames); - } else { - return reactorClient.get(finalPolicy, key); - } + @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 Mono findByIds(GroupedKeys groupedKeys) { - Assert.notNull(groupedKeys, "Grouped keys must not be null!"); + 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)); + } - if (groupedKeys.getEntitiesKeys() == null || groupedKeys.getEntitiesKeys().isEmpty()) { - return Mono.just(GroupedEntities.builder().build()); - } + @Override + public Flux findAll(Class entityClass) { + Assert.notNull(entityClass, "Entity class must not be null!"); - return findEntitiesByIdsInternal(groupedKeys); + return findAll(entityClass, getSetName(entityClass)); } - private Mono findEntitiesByIdsInternal(GroupedKeys groupedKeys) { - EntitiesKeys entitiesKeys = EntitiesKeys.of(toEntitiesKeyMap(groupedKeys)); + @Override + public Flux findAll(Class entityClass, Class targetClass) { + Assert.notNull(entityClass, "Entity class must not be null!"); + Assert.notNull(targetClass, "Target class must not be null!"); - return reactorClient.get(null, entitiesKeys.getKeys()) - .map(item -> toGroupedEntities(entitiesKeys, item.records)) - .onErrorMap(this::translateError); + return findAll(targetClass, getSetName(entityClass)); } @Override - public Flux find(Query query, Class entityClass) { + 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); + } + + @Override + public Flux findAll(Sort sort, long offset, long limit, Class entityClass) { Assert.notNull(entityClass, "Class must not be null!"); - return find(query, entityClass, getSetName(entityClass)); + + return findAll(sort, offset, limit, entityClass, getSetName(entityClass)); } @Override - public Flux find(Query query, Class entityClass, Class targetClass) { + public Flux findAll(Sort sort, long offset, long limit, Class entityClass, Class targetClass) { Assert.notNull(entityClass, "Class must not be null!"); - return find(query, targetClass, getSetName(entityClass)); + Assert.notNull(targetClass, "Target class must not be null!"); + + return findAll(sort, offset, limit, targetClass, getSetName(entityClass)); } @Override - public Flux find(Query query, Class targetClass, String setName) { - Assert.notNull(query, "Query must not be null!"); + public Flux findAll(Sort sort, long offset, long limit, Class targetClass, String setName) { Assert.notNull(targetClass, "Target class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return findAllUsingQueryWithPostProcessing(setName, targetClass, query); + return findUsingQualifiersWithPostProcessing(setName, targetClass, sort, offset, limit, + null, (Qualifier[]) null); } @Override @@ -732,60 +899,32 @@ 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 findAllUsingQueryWithPostProcessing(setName, targetClass, sort, offset, limit, + return findUsingQualifiersWithPostProcessing(setName, targetClass, sort, offset, limit, null, (Qualifier[]) null); } - @Override - public Mono count(Query query, Class entityClass) { - Assert.notNull(query, "Query must not be null!"); - Assert.notNull(entityClass, "Class must not be null!"); - - return findAllRecordsUsingQuery(entityClass, query).count(); - } - - @Override - public Mono count(String setName) { - Assert.notNull(setName, "Set for count must not be null!"); - - try { - return Mono.fromCallable(() -> countSet(setName)); - } catch (AerospikeException e) { - throw translateError(e); + private BatchPolicy getBatchPolicyFilterExp(Qualifier[] qualifiers) { + if (qualifiers != null && qualifiers.length > 0) { + BatchPolicy policy = new BatchPolicy(reactorClient.getAerospikeClient().getBatchPolicyDefault()); + policy.filterExp = reactorQueryEngine.getFilterExpressionsBuilder().build(qualifiers); + return policy; } + return null; } - @Override - public Mono count(Class entityClass) { - Assert.notNull(entityClass, "Class must not be null!"); - String setName = getSetName(entityClass); - return count(setName); - } - - private long countSet(String setName) { - Node[] nodes = reactorClient.getAerospikeClient().getNodes(); - - int replicationFactor = Utils.getReplicationFactor(nodes, this.namespace); - - long totalObjects = Arrays.stream(nodes) - .mapToLong(node -> Utils.getObjectsCount(node, this.namespace, setName)) - .sum(); - - return (nodes.length > 1) ? (totalObjects / replicationFactor) : totalObjects; - } - - @Override - public Mono execute(Supplier supplier) { - Assert.notNull(supplier, "Supplier must not be null!"); - - return Mono.fromSupplier(supplier) - .onErrorMap(this::translateError); + private Mono getFromClient(BatchPolicy finalPolicy, Key key, Class targetClass) { + if (targetClass != null) { + String[] binNames = getBinNamesFromTargetClass(targetClass); + return reactorClient.get(finalPolicy, key, binNames); + } else { + return reactorClient.get(finalPolicy, key); + } } @Override public Mono exists(Object id, Class entityClass) { + Assert.notNull(id, "Id must not be null!"); Assert.notNull(entityClass, "Class must not be null!"); - return exists(id, getSetName(entityClass)); } @@ -802,113 +941,72 @@ public Mono exists(Object id, String setName) { } @Override - public Mono deleteAll(Class entityClass) { + public Mono existsByQuery(Query query, Class entityClass) { + Assert.notNull(query, "Query passed in to exist can't be null"); Assert.notNull(entityClass, "Class must not be null!"); - - return deleteAll(getSetName(entityClass)); + return existsByQuery(query, entityClass, getSetName(entityClass)); } @Override - public Mono deleteAll(String setName) { + public Mono existsByQuery(Query query, Class entityClass, String setName) { + Assert.notNull(query, "Query passed in to exist can't be null"); + Assert.notNull(entityClass, "Class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - - try { - return Mono.fromRunnable( - () -> reactorClient.getAerospikeClient().truncate(null, this.namespace, setName, null)); - } catch (AerospikeException e) { - throw translateError(e); - } + return find(query, entityClass, setName).hasElements(); } @Override - public Mono deleteById(Object id, Class entityClass) { - Assert.notNull(id, "Id must not be null!"); + public Mono count(Class entityClass) { Assert.notNull(entityClass, "Class must not be null!"); - - return deleteById(id, getSetName(entityClass)); + String setName = getSetName(entityClass); + return count(setName); } @Override - public Mono deleteById(Object id, String setName) { - Assert.notNull(id, "Id must not be null!"); + public Mono count(String setName) { Assert.notNull(setName, "Set name must not be null!"); - return reactorClient - .delete(ignoreGenerationDeletePolicy(), getKey(id, setName)) - .map(k -> true) - .onErrorMap(this::translateError); + try { + return Mono.fromCallable(() -> countSet(setName)); + } catch (AerospikeException e) { + throw translateError(e); + } } - @Override - public Mono delete(T document) { - return delete(document, getSetName(document)); - } + private long countSet(String setName) { + Node[] nodes = reactorClient.getAerospikeClient().getNodes(); - @Override - public Mono delete(T document, String setName) { - Assert.notNull(document, "Document must not be null!"); - Assert.notNull(document, "Set name must not be null!"); + int replicationFactor = Utils.getReplicationFactor(nodes, this.namespace); - AerospikeWriteData data = writeData(document, setName); + long totalObjects = Arrays.stream(nodes) + .mapToLong(node -> Utils.getObjectsCount(node, this.namespace, setName)) + .sum(); - return reactorClient - .delete(ignoreGenerationDeletePolicy(), data.getKey()) - .map(key -> true) - .onErrorMap(this::translateError); + return (nodes.length > 1) ? (totalObjects / replicationFactor) : totalObjects; } @Override - public Mono deleteByIds(Iterable ids, Class entityClass) { + public Mono count(Query query, Class entityClass) { + Assert.notNull(query, "Query must not be null!"); Assert.notNull(entityClass, "Class must not be null!"); - return deleteByIds(ids, getSetName(entityClass)); + return count(query, getSetName(entityClass)); } @Override - public Mono deleteByIds(Iterable ids, String setName) { - Assert.notNull(ids, "List of ids must not be null!"); - Assert.notNull(setName, "Set name must not be null!"); - - Key[] keys = StreamSupport.stream(ids.spliterator(), false) - .map(id -> getKey(id, setName)) - .toArray(Key[]::new); - - return batchDeleteAndCheckForErrors(reactorClient, keys); - } - - private Mono batchDeleteAndCheckForErrors(IAerospikeReactorClient reactorClient, Key[] keys) { - Function> checkForErrors = results -> { - for (BatchRecord record : results.records) { - if (batchRecordFailed(record)) { - return Mono.error(new AerospikeException.BatchRecordArray(results.records, - new AerospikeException("Errors during batch delete"))); - } - } - return Mono.empty(); - }; - - // requires server ver. >= 6.0.0 - return reactorClient.delete(null, null, keys) - .onErrorMap(this::translateError) - .flatMap(checkForErrors); - } - - @Override - public Mono deleteByIds(GroupedKeys groupedKeys) { - Assert.notNull(groupedKeys, "Grouped keys must not be null!"); - Assert.notNull(groupedKeys.getEntitiesKeys(), "Entities keys must not be null!"); - Assert.notEmpty(groupedKeys.getEntitiesKeys(), "Entities keys must not be empty!"); + public Mono count(Query query, String setName) { + Assert.notNull(query, "Query must not be null!"); + Assert.notNull(setName, "Set for count must not be null!"); - return deleteEntitiesByIdsInternal(groupedKeys); + return findRecordsUsingQuery(setName, query).count(); } - private Mono deleteEntitiesByIdsInternal(GroupedKeys groupedKeys) { - EntitiesKeys entitiesKeys = EntitiesKeys.of(toEntitiesKeyMap(groupedKeys)); - - reactorClient.delete(null, null, entitiesKeys.getKeys()) - .doOnError(this::translateError); + private Flux findRecordsUsingQuery(String setName, Query query) { + Assert.notNull(query, "Query must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); - return batchDeleteAndCheckForErrors(reactorClient, entitiesKeys.getKeys()); + Qualifier qualifier = query.getCriteria().getCriteriaObject(); + return findRecordsUsingQualifiers(setName, null, null, qualifier); } @Override @@ -932,20 +1030,20 @@ public Mono createIndex(Class entityClass, String indexName, @Override public Mono createIndex(String setName, String indexName, - String binName, IndexType indexType) { + String binName, IndexType indexType) { return createIndex(setName, indexName, binName, indexType, IndexCollectionType.DEFAULT); } @Override public Mono createIndex(String setName, String indexName, - String binName, IndexType indexType, IndexCollectionType indexCollectionType) { + String binName, IndexType indexType, IndexCollectionType indexCollectionType) { return createIndex(setName, indexName, binName, indexType, indexCollectionType, new CTX[0]); } @Override public Mono createIndex(String setName, String indexName, - String binName, IndexType indexType, IndexCollectionType indexCollectionType, - CTX... ctx) { + String binName, IndexType indexType, IndexCollectionType indexCollectionType, + CTX... ctx) { Assert.notNull(setName, "Set name must not be null!"); Assert.notNull(indexName, "Index name must not be null!"); Assert.notNull(binName, "Bin name must not be null!"); @@ -1087,21 +1185,21 @@ private Throwable translateError(Throwable e) { return e; } - public Flux findAllUsingQueryWithPostProcessing(String setName, Class targetClass, Query query) { + private Flux findUsingQueryWithPostProcessing(String setName, Class targetClass, Query query) { verifyUnsortedWithOffset(query.getSort(), query.getOffset()); Qualifier qualifier = query.getCriteria().getCriteriaObject(); - Flux results = findAllUsingQueryWithDistinctPredicate(setName, targetClass, + Flux results = findUsingQualifiersWithDistinctPredicate(setName, targetClass, getDistinctPredicate(query), qualifier); results = applyPostProcessingOnResults(results, query); return results; } @SuppressWarnings("SameParameterValue") - public Flux findAllUsingQueryWithPostProcessing(String setName, Class targetClass, Sort sort, - long offset, long limit, Filter filter, - Qualifier... qualifiers) { + private Flux findUsingQualifiersWithPostProcessing(String setName, Class targetClass, Sort sort, + long offset, long limit, Filter filter, + Qualifier... qualifiers) { verifyUnsortedWithOffset(sort, offset); - Flux results = findAllUsingQuery(setName, targetClass, filter, qualifiers); + Flux results = findUsingQualifiers(setName, targetClass, filter, qualifiers); results = applyPostProcessingOnResults(results, sort, offset, limit); return results; } @@ -1145,44 +1243,29 @@ private Flux applyPostProcessingOnResults(Flux results, Sort sort, lon return results; } - @Override - public Flux findAllUsingQuery(Class entityClass, Filter filter, - Qualifier qualifier) { - return findAllRecordsUsingQuery(getSetName(entityClass), entityClass, filter, qualifier) - .map(keyRecord -> mapToEntity(keyRecord.key, entityClass, keyRecord.record)); - } - - private Flux findAllUsingQuery(String setName, Class targetClass, Filter filter, - Qualifier... qualifiers) { - return findAllRecordsUsingQuery(setName, targetClass, filter, qualifiers) + private Flux findUsingQualifiers(String setName, Class targetClass, Filter filter, + Qualifier... qualifiers) { + return findRecordsUsingQualifiers(setName, targetClass, filter, qualifiers) .map(keyRecord -> mapToEntity(keyRecord, targetClass)); } - private Flux findAllUsingQueryWithDistinctPredicate(String setName, Class targetClass, - Predicate distinctPredicate, - Qualifier... qualifiers) { - return findAllRecordsUsingQuery(setName, targetClass, null, qualifiers) + private Flux findUsingQualifiersWithDistinctPredicate(String setName, Class targetClass, + Predicate distinctPredicate, + Qualifier... qualifiers) { + return findRecordsUsingQualifiers(setName, targetClass, null, qualifiers) .filter(distinctPredicate) .map(keyRecord -> mapToEntity(keyRecord, targetClass)); } - private Flux findAllRecordsUsingQuery(Class entityClass, Query query) { - Assert.notNull(query, "Query must not be null!"); - Assert.notNull(entityClass, "Class must not be null!"); - - Qualifier qualifier = query.getCriteria().getCriteriaObject(); - return findAllRecordsUsingQuery(getSetName(entityClass), entityClass, null, qualifier); - } - - private Flux findAllRecordsUsingQuery(String setName, Class targetClass, Filter filter, - Qualifier... qualifiers) { + 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); if (idQualifier != null) { // a special flow if there is id given - return findByIdsInternalWithoutMapping(getIdValue(idQualifier), setName, targetClass, + return findByIdsWithoutMapping(getIdValue(idQualifier), setName, targetClass, excludeIdQualifier(qualifiers)); } } @@ -1194,4 +1277,22 @@ private Flux findAllRecordsUsingQuery(String setName, Class ta return this.reactorQueryEngine.select(this.namespace, setName, filter, qualifiers); } } + + private Flux findByIdsWithoutMapping(Collection ids, String setName, + Class targetClass, + Qualifier... qualifiers) { + Assert.notNull(ids, "List of ids must not be null!"); + Assert.notNull(setName, "Set name must not be null!"); + + if (ids.isEmpty()) { + return Flux.empty(); + } + + BatchPolicy policy = getBatchPolicyFilterExp(qualifiers); + + return Flux.fromIterable(ids) + .map(id -> getKey(id, setName)) + .flatMap(key -> getFromClient(policy, key, targetClass)) + .filter(keyRecord -> nonNull(keyRecord.record)); + } } 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 f059881a0..66fafda54 100644 --- a/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java +++ b/src/main/java/org/springframework/data/aerospike/query/KeyQualifier.java @@ -27,7 +27,7 @@ * * @author peter * @deprecated Since 4.6.0. Use {@link SimpleAerospikeRepository#findById(Object)} or - * {@link SimpleAerospikeRepository#findByQualifier(Qualifier)} with {@link Qualifier#forId(String)} + * {@link SimpleAerospikeRepository#findByQualifier(Qualifier)} 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 d8cc81293..9c45dd3ac 100644 --- a/src/main/java/org/springframework/data/aerospike/query/Qualifier.java +++ b/src/main/java/org/springframework/data/aerospike/query/Qualifier.java @@ -431,24 +431,24 @@ 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 is equal to the given string. * * @param id String value * @return Single id qualifier */ - public static Qualifier forId(String id) { + public static Qualifier idEquals(String id) { return new Qualifier(new IdQualifierBuilder() .setId(id) .setFilterOperation(FilterOperation.EQ)); } /** - * 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 is equal to one of the given strings (logical OR). * * @param ids String values * @return Multiple ids qualifier with OR condition */ - public static Qualifier forIds(String... ids) { + public static Qualifier idIn(String... ids) { return new Qualifier(new IdQualifierBuilder() .setIds(ids) .setFilterOperation(FilterOperation.EQ)); 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 ebf725a8d..3af0b8d3d 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 @@ -15,7 +15,6 @@ */ package org.springframework.data.aerospike.repository.query; -import org.springframework.data.aerospike.core.AerospikeInternalOperations; import org.springframework.data.aerospike.core.AerospikeOperations; import org.springframework.data.aerospike.core.AerospikeTemplate; import org.springframework.data.aerospike.query.Qualifier; @@ -45,7 +44,6 @@ public class AerospikePartTreeQuery extends BaseAerospikePartTreeQuery { private final AerospikeOperations operations; - private final AerospikeInternalOperations internalOperations; public AerospikePartTreeQuery(QueryMethod queryMethod, QueryMethodEvaluationContextProvider evalContextProvider, @@ -54,7 +52,6 @@ public AerospikePartTreeQuery(QueryMethod queryMethod, super(queryMethod, evalContextProvider, queryCreator); this.operations = aerospikeTemplate; - this.internalOperations = aerospikeTemplate; } @Override @@ -105,7 +102,7 @@ public Object execute(Object[] parameters) { protected Object findByIds(Collection ids, Class entityClass, Class targetClass, Qualifier... qualifiers) { - return internalOperations.findByIdsInternal(ids, entityClass, targetClass, qualifiers); + return operations.findByIdsUsingQualifiers(ids, entityClass, targetClass, qualifiers); } private Stream findByQuery(Query query, Class targetClass) { diff --git a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreator.java b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreator.java index 77fae5b30..63bcb5180 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreator.java +++ b/src/main/java/org/springframework/data/aerospike/repository/query/AerospikeQueryCreator.java @@ -44,8 +44,8 @@ import java.util.stream.Collectors; import static org.springframework.data.aerospike.query.FilterOperation.*; -import static org.springframework.data.aerospike.query.Qualifier.forId; -import static org.springframework.data.aerospike.query.Qualifier.forIds; +import static org.springframework.data.aerospike.query.Qualifier.idEquals; +import static org.springframework.data.aerospike.query.Qualifier.idIn; /** * @author Peter Milne @@ -155,9 +155,9 @@ private Qualifier processId(Object value1) { if (value1 instanceof Collection) { // currently id can only be a String List ids = ((Collection) value1).stream().map(String::valueOf).toList(); - qualifier = forIds(ids.toArray(String[]::new)); + qualifier = idIn(ids.toArray(String[]::new)); } else { - qualifier = forId((String) value1); + qualifier = idEquals((String) value1); } return qualifier; } 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 c9bea87d9..54ad01dc0 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 @@ -15,7 +15,6 @@ */ package org.springframework.data.aerospike.repository.query; -import org.springframework.data.aerospike.core.ReactiveAerospikeInternalOperations; import org.springframework.data.aerospike.core.ReactiveAerospikeOperations; import org.springframework.data.aerospike.core.ReactiveAerospikeTemplate; import org.springframework.data.aerospike.query.Qualifier; @@ -38,7 +37,6 @@ public class ReactiveAerospikePartTreeQuery extends BaseAerospikePartTreeQuery { private final ReactiveAerospikeOperations operations; - private final ReactiveAerospikeInternalOperations internalOperations; public ReactiveAerospikePartTreeQuery(QueryMethod queryMethod, QueryMethodEvaluationContextProvider evalContextProvider, @@ -46,7 +44,6 @@ public ReactiveAerospikePartTreeQuery(QueryMethod queryMethod, Class> queryCreator) { super(queryMethod, evalContextProvider, queryCreator); this.operations = aerospikeTemplate; - this.internalOperations = aerospikeTemplate; } @Override @@ -81,6 +78,6 @@ private Flux findByQuery(Query query, Class targetClass) { protected Object findByIds(Collection ids, Class sourceClass, Class targetClass, Qualifier... qualifiers) { - return internalOperations.findByIdsInternal(ids, sourceClass, targetClass, 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 e18b292c9..9657a0b3f 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 @@ -162,6 +162,6 @@ public boolean indexExists(String indexName) { public Iterable findByQualifier(Qualifier qualifier) { Assert.notNull(qualifier, "Qualifier must not be null"); - return operations.findAllUsingQuery(entityInformation.getJavaType(), null, qualifier).toList(); + return operations.findUsingQualifier(entityInformation.getJavaType(), null, qualifier).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 516425172..3845bdace 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 @@ -156,6 +156,6 @@ public void deleteIndex(Class domainType, String indexName) { @Override public Flux findByQualifier(Qualifier qualifier) { Assert.notNull(qualifier, "Qualifiers must not be null"); - return operations.findAllUsingQuery(entityInformation.getJavaType(), null, qualifier); + return operations.findUsingQualifier(entityInformation.getJavaType(), null, qualifier); } } 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 db4957e9f..d65f585f6 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java @@ -543,13 +543,13 @@ public void findAllUsingQuery_shouldRunWithDifferentArgumentsCombinations() { .setValue1(Value.get(fieldValue1)) .build(); Stream result1 = - template.findAllUsingQuery(SampleClasses.CustomCollectionClass.class, null, qualifier); + template.findUsingQualifier(SampleClasses.CustomCollectionClass.class, null, qualifier); assertThat(result1).containsOnly(doc1); // find by a predefined secondary index filter, no qualifiers Filter filter = Filter.equal(fieldName, fieldValue1); Stream result2 = - template.findAllUsingQuery(SampleClasses.CustomCollectionClass.class, filter, null); + template.findUsingQualifier(SampleClasses.CustomCollectionClass.class, filter, null); assertThat(result2).containsOnly(doc1); // find by a complex qualifier @@ -565,12 +565,12 @@ public void findAllUsingQuery_shouldRunWithDifferentArgumentsCombinations() { .build(); Qualifier qualifierOr = Qualifier.or(dataEqFieldValue1, dataEqFieldValue2); Stream result3 = - template.findAllUsingQuery(SampleClasses.CustomCollectionClass.class, null, qualifierOr); + template.findUsingQualifier(SampleClasses.CustomCollectionClass.class, null, qualifierOr); assertThat(result3).containsOnly(doc1, doc2); // no secondary index filter and no qualifiers Stream result4 = - template.findAllUsingQuery(SampleClasses.CustomCollectionClass.class, null, null); + template.findUsingQualifier(SampleClasses.CustomCollectionClass.class, null, null); assertThat(result4).contains(doc1, doc2); additionalAerospikeTestOperations.dropIndex(SampleClasses.CustomCollectionClass.class, 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 9282b9d01..a7c082d05 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java @@ -1320,18 +1320,18 @@ public void findPersonsByQualifiers() { assertThat(result).doesNotContain(carter); // creating a condition "id equals Carter's id" - Qualifier keyEqCartersId = Qualifier.forId(carter.getId()); + Qualifier keyEqCartersId = Qualifier.idEquals(carter.getId()); result = repository.findByQualifier(keyEqCartersId); assertThat(result).containsOnly(carter); // creating a condition "id equals Boyd's id" - Qualifier keyEqBoydsId = Qualifier.forId(boyd.getId()); + Qualifier keyEqBoydsId = Qualifier.idEquals(boyd.getId()); result = repository.findByQualifier(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.forIds(carter.getId(), boyd.getId()); + Qualifier keyEqMultipleIds = Qualifier.idIn(carter.getId(), boyd.getId()); result = repository.findByQualifier(keyEqMultipleIds); assertThat(result).containsOnly(carter, boyd); @@ -1432,8 +1432,8 @@ public void findPersonsByQualifiersMustBeValid() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("Operation STARTS_WITH cannot be applied to metadataField"); - Qualifier keyEqCartersId = Qualifier.forId(carter.getId()); - Qualifier keyEqBoydsId = Qualifier.forId(boyd.getId()); + Qualifier keyEqCartersId = Qualifier.idEquals(carter.getId()); + Qualifier keyEqBoydsId = Qualifier.idEquals(boyd.getId()); // not more than one id qualifier is allowed assertThatThrownBy(() -> repository.findByQualifier(Qualifier.and(keyEqCartersId,