diff --git a/QueryPartitionedCollection.md b/QueryPartitionedCollection.md
new file mode 100644
index 00000000..b94ec9fb
--- /dev/null
+++ b/QueryPartitionedCollection.md
@@ -0,0 +1,58 @@
+### How to Query Partitioned DocumentDB Collection
+
+With DocumentDB, you can configure [partition key](https://docs.microsoft.com/en-us/azure/cosmos-db/partition-data) for your collection.
+
+Below is an example about how to query partitioned collection with this spring data module.
+
+#### Example
+
+Given a document entity structure:
+```
+ @Document
+ @Data
+ @AllArgsConstructor
+ public class Address {
+ @Id
+ String postalCode;
+ String street;
+ @PartitionKey
+ String city;
+ }
+```
+
+Write the repository interface:
+```
+ @Repository
+ public interface AddressRepository extends DocumentDbRepository
{
+ // Add query methods here, refer to below
+ }
+```
+
+Query by field name:
+```
+ List findByCity(String city);
+```
+
+Delete by field name:
+```
+ void deleteByStreet(String street);
+```
+
+For `Partitioned collection`, if you want to query records by `findById(id)`, exception will be thrown.
+```
+ // Incorrect for partitioned collection, exception will be thrown
+ Address result = repository.findById(id); // Caution: Works for non-partitioned collection
+```
+
+Instead, you can query records by ID field name with custom query.
+```
+ // Correct, postalCode is the ID field in Address domain
+ @Repository
+ public interface AddressRepository extends DocumentDbRepository {
+ List findByPostalCode(String postalCode);
+ }
+
+ // Query
+ List result = repository.findByPostalCode(postalCode);
+```
+
diff --git a/README.md b/README.md
index 4a0553f1..14b669d9 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@
* [Sample Code](#sample-codes)
* [Feature List](#feature-list)
* [Quick Start](#quick-start)
+* [Query Partitioned Collection](QueryPartitionedCollection.md)
* [Filing Issues](#filing-issues)
* [How to Contribute](#how-to-contribute)
* [Code of Conduct](#code-of-conduct)
@@ -38,7 +39,7 @@ Please refer to [sample project here](./samplecode).
- Custom collection Name.
By default, collection name will be class name of user domain class. To customize it, add annotation `@Document(collection="myCustomCollectionName")` to domain class, that's all.
- Supports [Azure Cosmos DB partition](https://docs.microsoft.com/en-us/azure/cosmos-db/partition-data). To specify a field of domain class to be partition key field, just annotate it with `@PartitionKey`. When you do CRUD operation, pls specify your partition value. For more sample on partition CRUD, pls refer to [test here](./src/test/java/com/microsoft/azure/spring/data/cosmosdb/documentdb/repository/AddressRepositoryIT.java)
-- Supports [Spring Data custom query](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.query-methods.details) find operation.
+- Supports [Spring Data custom query](https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories.query-methods.details) find operation, e.g., `findByAFieldAndBField
- Supports [spring-boot-starter-data-rest](https://projects.spring.io/spring-data-rest/).
- Supports List and nested type in domain class.
@@ -104,7 +105,10 @@ public class User {
private String lastName;
... // setters and getters
-
+
+ public User() {
+ }
+
public User(String id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
diff --git a/pom.xml b/pom.xml
index ac257801..5f18cfb9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -132,6 +132,12 @@
lombok
test
+
+ com.google.guava
+ guava
+ 18.0
+ test
+
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbOperations.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbOperations.java
index f3c2f28f..8351cff7 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbOperations.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbOperations.java
@@ -7,6 +7,7 @@
package com.microsoft.azure.spring.data.documentdb.core;
import com.microsoft.azure.documentdb.DocumentCollection;
+import com.microsoft.azure.documentdb.PartitionKey;
import com.microsoft.azure.spring.data.documentdb.core.convert.MappingDocumentDbConverter;
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
@@ -20,47 +21,42 @@ DocumentCollection createCollectionIfNotExists(String collectionName,
String partitionKeyFieldName,
Integer requestUnit);
- List findAll(Class entityClass,
- String partitionKeyFieldName,
- String partitionKeyFieldValue);
+ List findAll(Class entityClass);
- List findAll(String collectionName,
- Class entityClass,
- String partitionKeyFieldName,
- String partitionKeyFieldValue);
+ List findAll(String collectionName, Class entityClass);
T findById(Object id,
- Class entityClass,
- String partitionKeyFieldValue);
+ Class entityClass);
T findById(String collectionName,
Object id,
- Class entityClass,
- String partitionKeyFieldValue);
+ Class entityClass);
List find(Query query,
Class entityClass,
String collectionName);
- T insert(T objectToSave, String partitionKeyFieldValue);
+ T insert(T objectToSave, PartitionKey partitionKey);
T insert(String collectionName,
T objectToSave,
- String partitionKeyFieldValue);
+ PartitionKey partitionKey);
- void upsert(T object, Object id, String partitionKeyFieldValue);
+ void upsert(T object, Object id, PartitionKey partitionKey);
void upsert(String collectionName,
T object,
Object id,
- String partitionKeyFieldValue);
+ PartitionKey partitionKey);
void deleteById(String collectionName,
Object id,
Class domainClass,
- String partitionKeyFieldValue);
+ PartitionKey partitionKey);
void deleteAll(String collectionName);
+ List delete(Query query, Class entityClass, String collectionName);
+
MappingDocumentDbConverter getConverter();
}
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplate.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplate.java
index 658fb561..1647a5c6 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplate.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplate.java
@@ -11,6 +11,8 @@
import com.microsoft.azure.spring.data.documentdb.DocumentDbFactory;
import com.microsoft.azure.spring.data.documentdb.core.convert.MappingDocumentDbConverter;
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
+import com.microsoft.azure.spring.data.documentdb.repository.support.DocumentDbEntityInformation;
+import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -21,6 +23,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
public class DocumentDbTemplate implements DocumentDbOperations, ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(DocumentDbTemplate.class);
@@ -54,16 +57,16 @@ public DocumentDbTemplate(DocumentClient client,
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
}
- public T insert(T objectToSave, String partitionKeyFieldValue) {
+ public T insert(T objectToSave, PartitionKey partitionKey) {
return insert(getCollectionName(objectToSave.getClass()),
objectToSave,
- partitionKeyFieldValue);
+ partitionKey);
}
public T insert(String collectionName,
T objectToSave,
- String partitionKeyFieldValue) {
+ PartitionKey partitionKey) {
final Document document = new Document();
mappingDocumentDbConverter.write(objectToSave, document);
@@ -75,7 +78,7 @@ public T insert(String collectionName,
try {
documentDbFactory.getDocumentClient()
.createDocument(getCollectionLink(this.databaseName, collectionName), document,
- getRequestOptions(partitionKeyFieldValue, null), false);
+ getRequestOptions(partitionKey, null), false);
return objectToSave;
} catch (DocumentClientException e) {
throw new RuntimeException("insert exception", e);
@@ -83,23 +86,20 @@ public T insert(String collectionName,
}
public T findById(Object id,
- Class entityClass,
- String partitionKeyFieldValue) {
+ Class entityClass) {
return findById(getCollectionName(entityClass),
id,
- entityClass,
- partitionKeyFieldValue);
+ entityClass);
}
public T findById(String collectionName,
Object id,
- Class entityClass,
- String partitionKeyFieldValue) {
+ Class entityClass) {
try {
final Resource resource = documentDbFactory.getDocumentClient()
.readDocument(getDocumentLink(this.databaseName, collectionName, (String) id),
- getRequestOptions(partitionKeyFieldValue, null)).getResource();
+ new RequestOptions()).getResource();
if (resource instanceof Document) {
final Document document = (Document) resource;
@@ -116,12 +116,12 @@ public T findById(String collectionName,
}
}
- public void upsert(T object, Object id, String partitionKeyFieldValue) {
- upsert(getCollectionName(object.getClass()), object, id, partitionKeyFieldValue);
+ public void upsert(T object, Object id, PartitionKey partitionKey) {
+ upsert(getCollectionName(object.getClass()), object, id, partitionKey);
}
- public void upsert(String collectionName, T object, Object id, String partitionKeyFieldValue) {
+ public void upsert(String collectionName, T object, Object id, PartitionKey partitionKey) {
try {
Document originalDoc = new Document();
if (object instanceof Document) {
@@ -138,23 +138,19 @@ public void upsert(String collectionName, T object, Object id, String partit
documentDbFactory.getDocumentClient().upsertDocument(
getCollectionLink(this.databaseName, collectionName),
originalDoc,
- getRequestOptions(partitionKeyFieldValue, null), false);
+ getRequestOptions(partitionKey, null), false);
} catch (DocumentClientException ex) {
throw new RuntimeException("Failed to upsert document to database.", ex);
}
}
- public List findAll(Class entityClass,
- String partitionKeyFieldName,
- String partitionKeyFieldValue) {
- return findAll(getCollectionName(entityClass), entityClass, partitionKeyFieldName, partitionKeyFieldValue);
+ public List findAll(Class entityClass) {
+ return findAll(getCollectionName(entityClass), entityClass);
}
public List findAll(String collectionName,
- final Class entityClass,
- String partitionKeyFieldName,
- String partitionKeyFieldValue) {
+ final Class entityClass) {
final List collections = documentDbFactory.getDocumentClient().
queryCollections(
getDatabaseLink(this.databaseName),
@@ -170,15 +166,10 @@ public List findAll(String collectionName,
final FeedOptions feedOptions = new FeedOptions();
feedOptions.setEnableCrossPartitionQuery(true);
- SqlQuerySpec sqlQuerySpec = new SqlQuerySpec("SELECT * FROM root c");
- if (partitionKeyFieldName != null && !partitionKeyFieldName.isEmpty()) {
- sqlQuerySpec = new SqlQuerySpec("SELECT * FROM root c WHERE c." + partitionKeyFieldName + "=@partition",
- new SqlParameterCollection(new SqlParameter("@partition", partitionKeyFieldValue)));
- feedOptions.setPartitionKey(new PartitionKey(partitionKeyFieldValue));
- }
+ final SqlQuerySpec sqlQuerySpec = new SqlQuerySpec("SELECT * FROM root c");
final List results = documentDbFactory.getDocumentClient()
- .queryDocuments(collections.get(0).getSelfLink(), sqlQuerySpec, feedOptions, partitionKeyFieldName)
+ .queryDocuments(collections.get(0).getSelfLink(), sqlQuerySpec, feedOptions)
.getQueryIterable().toList();
final List entities = new ArrayList<>();
@@ -250,12 +241,6 @@ private Database createDatabaseIfNotExists(String dbName) {
}
}
- public DocumentCollection createCollection(String collectionName,
- RequestOptions collectionOptions,
- String partitionKeyFieldName) {
- return createCollection(this.databaseName, collectionName, collectionOptions, partitionKeyFieldName);
- }
-
public DocumentCollection createCollection(String dbName,
String collectionName,
RequestOptions collectionOptions,
@@ -314,7 +299,7 @@ public DocumentCollection createCollectionIfNotExists(String collectionName,
public void deleteById(String collectionName,
Object id,
Class domainClass,
- String partitionKeyFieldValue) {
+ PartitionKey partitionKey) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("execute deleteById in database {} collection {}", this.databaseName, collectionName);
}
@@ -322,7 +307,7 @@ public void deleteById(String collectionName,
try {
documentDbFactory.getDocumentClient().deleteDocument(
getDocumentLink(this.databaseName, collectionName, id.toString()),
- getRequestOptions(partitionKeyFieldValue, null));
+ getRequestOptions(partitionKey, null));
} catch (DocumentClientException ex) {
throw new RuntimeException("deleteById exception", ex);
@@ -345,14 +330,14 @@ private String getPartitionKeyPath(String partitionKey) {
return "/" + partitionKey;
}
- private RequestOptions getRequestOptions(String partitionKeyValue, Integer requestUnit) {
- if ((partitionKeyValue == null || partitionKeyValue.isEmpty()) && requestUnit == null) {
+ private RequestOptions getRequestOptions(PartitionKey key, Integer requestUnit) {
+ if (key == null && requestUnit == null) {
return null;
}
final RequestOptions requestOptions = new RequestOptions();
- if (!(partitionKeyValue == null || partitionKeyValue.isEmpty())) {
- requestOptions.setPartitionKey(new PartitionKey(partitionKeyValue));
+ if (key != null) {
+ requestOptions.setPartitionKey(key);
}
if (requestUnit != null) {
requestOptions.setOfferThroughput(requestUnit);
@@ -362,24 +347,21 @@ private RequestOptions getRequestOptions(String partitionKeyValue, Integer reque
}
public List find(Query query, Class domainClass, String collectionName) {
- final SqlQuerySpec sqlQuerySpec = createSqlQuerySpec(query);
+ final SqlQuerySpec sqlQuerySpec = createSqlQuerySpec(query, domainClass);
- final List collections = documentDbFactory.getDocumentClient().
- queryCollections(
- getDatabaseLink(this.databaseName),
- new SqlQuerySpec("SELECT * FROM ROOT r WHERE r.id=@id",
- new SqlParameterCollection(new SqlParameter("@id", collectionName))), null)
- .getQueryIterable().toList();
+ // TODO (wepa) Collection link should be created locally without accessing database,
+ // but currently exception will be thrown if not fetching collection url from database.
+ // Run repository integration test to reproduce.
+ final DocumentCollection collection = getDocCollection(collectionName);
+ final FeedOptions feedOptions = new FeedOptions();
- if (collections.size() != 1) {
- throw new RuntimeException("expect only one collection: " + collectionName
- + " in database: " + this.databaseName + ", but found " + collections.size());
+ final Optional partitionKeyValue = getPartitionKeyValue(query, domainClass);
+ if (!partitionKeyValue.isPresent()) {
+ feedOptions.setEnableCrossPartitionQuery(true);
}
- final FeedOptions feedOptions = new FeedOptions();
- feedOptions.setEnableCrossPartitionQuery(true);
final List results = documentDbFactory.getDocumentClient()
- .queryDocuments(collections.get(0).getSelfLink(),
+ .queryDocuments(collection.getSelfLink(),
sqlQuerySpec, feedOptions)
.getQueryIterable().toList();
@@ -392,19 +374,119 @@ public List find(Query query, Class domainClass, String collectionName
return entities;
}
- private static SqlQuerySpec createSqlQuerySpec(Query query) {
- String queryStr = "SELECT * FROM ROOT r WHERE r.";
+ private DocumentCollection getDocCollection(String collectionName) {
+ final List collections = documentDbFactory.getDocumentClient().
+ queryCollections(
+ getDatabaseLink(this.databaseName),
+ new SqlQuerySpec("SELECT * FROM ROOT r WHERE r.id=@id",
+ new SqlParameterCollection(new SqlParameter("@id", collectionName))), null)
+ .getQueryIterable().toList();
+
+ if (collections.size() != 1) {
+ throw new RuntimeException("expect only one collection: " + collectionName
+ + " in database: " + this.databaseName + ", but found " + collections.size());
+ }
+
+ return collections.get(0);
+ }
+
+ private static SqlQuerySpec createSqlQuerySpec(Query query, Class entityClass) {
+ String queryStr = "SELECT * FROM ROOT r WHERE ";
+
final SqlParameterCollection parameterCollection = new SqlParameterCollection();
for (final Map.Entry entry : query.getCriteria().entrySet()) {
- queryStr += entry.getKey() + "=@" + entry.getKey();
+ if (queryStr.contains("=@")) {
+ queryStr += " AND ";
+ }
+
+ String fieldName = entry.getKey();
+ if (isIdField(fieldName, entityClass)) {
+ fieldName = "id";
+ }
+
+ queryStr += "r." + fieldName + "=@" + entry.getKey();
parameterCollection.add(new SqlParameter("@" + entry.getKey(), entry.getValue()));
}
+
return new SqlQuerySpec(queryStr, parameterCollection);
}
+ private static boolean isIdField(String fieldName, Class entityClass) {
+ if (StringUtils.isEmpty(fieldName)) {
+ return false;
+ }
+
+ final DocumentDbEntityInformation entityInfo = new DocumentDbEntityInformation(entityClass);
+ return fieldName.equals(entityInfo.getId().getName());
+ }
+
@Override
public MappingDocumentDbConverter getConverter() {
return this.mappingDocumentDbConverter;
}
+
+ @Override
+ public List delete(Query query, Class entityClass, String collectionName) {
+ final SqlQuerySpec sqlQuerySpec = createSqlQuerySpec(query, entityClass);
+ final Optional partitionKeyValue = getPartitionKeyValue(query, entityClass);
+
+ final DocumentCollection collection = getDocCollection(collectionName);
+ final FeedOptions feedOptions = new FeedOptions();
+ if (!partitionKeyValue.isPresent()) {
+ feedOptions.setEnableCrossPartitionQuery(true);
+ }
+
+ final List results = documentDbFactory.getDocumentClient()
+ .queryDocuments(collection.getSelfLink(), sqlQuerySpec, feedOptions).getQueryIterable().toList();
+
+ final RequestOptions options = new RequestOptions();
+ if (partitionKeyValue.isPresent()) {
+ options.setPartitionKey(new PartitionKey(partitionKeyValue.get()));
+ }
+
+ final List deletedResult = new ArrayList<>();
+ for (final Document document : results) {
+ try {
+ documentDbFactory.getDocumentClient().deleteDocument((document).getSelfLink(), options);
+ deletedResult.add(getConverter().read(entityClass, document));
+ } catch (DocumentClientException e) {
+ throw new IllegalStateException(
+ String.format("Failed to delete document [%s]", (document).getSelfLink()), e);
+ }
+ }
+
+ return deletedResult;
+ }
+
+ private Optional getPartitionKeyValue(Query query, Class domainClass) {
+ if (query == null) {
+ return Optional.empty();
+ }
+
+ final Optional partitionKeyName = getPartitionKeyField(domainClass);
+ if (!partitionKeyName.isPresent()) {
+ return Optional.empty();
+ }
+
+ final Map criteria = query.getCriteria();
+ // TODO (wepa) Only one partition key value is supported now
+ final Optional matchedKey = criteria.keySet().stream()
+ .filter(key -> partitionKeyName.get().equals(key)).findFirst();
+
+ if (!matchedKey.isPresent()) {
+ return Optional.empty();
+ }
+
+ return Optional.of(criteria.get(matchedKey.get()));
+ }
+
+ private Optional getPartitionKeyField(Class domainClass) {
+ final DocumentDbEntityInformation entityInfo = new DocumentDbEntityInformation(domainClass);
+ if (entityInfo.getPartitionKeyFieldName() == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(entityInfo.getPartitionKeyFieldName());
+ }
}
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/core/query/Criteria.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/core/query/Criteria.java
index c83cbdc7..c8e84503 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/core/query/Criteria.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/core/query/Criteria.java
@@ -5,17 +5,25 @@
*/
package com.microsoft.azure.spring.data.documentdb.core.query;
-import java.util.LinkedHashMap;
-
+import java.util.ArrayList;
+import java.util.List;
public class Criteria implements CriteriaDefinition {
private String key;
private Object value;
+ private List criteriaChain;
+
+ public Criteria(String key) {
+ this.criteriaChain = new ArrayList<>();
+ this.criteriaChain.add(this);
+ this.key = key;
+ }
- public Criteria(String key, LinkedHashMap value) {
+ protected Criteria(List criteriaChain, String key) {
+ this.criteriaChain = criteriaChain;
+ this.criteriaChain.add(this);
this.key = key;
- this.value = value.get(key);
}
public Object getCriteriaObject() {
@@ -26,11 +34,20 @@ public String getKey() {
return key;
}
- public static Criteria where(String key, Object value) {
- return new Criteria(key, (LinkedHashMap) value);
+ public static Criteria where(String key) {
+ return new Criteria(key);
}
public Criteria is(Object o) {
+ this.value = o;
return this;
}
+
+ public Criteria and(String key) {
+ return new Criteria(this.criteriaChain, key);
+ }
+
+ public List getCriteriaChain() {
+ return criteriaChain;
+ }
}
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/core/query/Query.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/core/query/Query.java
index 6e4a1897..ae4861a5 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/core/query/Query.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/core/query/Query.java
@@ -6,21 +6,25 @@
package com.microsoft.azure.spring.data.documentdb.core.query;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
public class Query {
private final Map criteria = new LinkedHashMap<>();
- public static Query query(CriteriaDefinition criteriaDefinition) {
- return new Query(criteriaDefinition);
+ public static Query query(Criteria criteria) {
+ return new Query(criteria);
}
public Query() {
}
- public Query(CriteriaDefinition criteriaDefinition) {
- addCriteria(criteriaDefinition);
+ public Query(Criteria criteria) {
+ final List criteriaList = criteria.getCriteriaChain();
+ for (final Criteria c : criteriaList) {
+ addCriteria(c);
+ }
}
public Query addCriteria(CriteriaDefinition criteriaDefinition) {
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/DocumentDbRepository.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/DocumentDbRepository.java
index 6f7e381b..0427c89e 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/DocumentDbRepository.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/DocumentDbRepository.java
@@ -10,16 +10,8 @@
import org.springframework.data.repository.NoRepositoryBean;
import java.io.Serializable;
-import java.util.List;
@NoRepositoryBean
public interface DocumentDbRepository extends CrudRepository {
- List findAll(String partitionKeyValue);
-
- T findOne(ID id, String partitionKeyValue);
-
- void delete(ID id, String partitionKeyValue);
-
- void delete(T entity, String partitionKeyValue);
}
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/AbstractDocumentDbQuery.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/AbstractDocumentDbQuery.java
index 7457a87a..e6c2706b 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/AbstractDocumentDbQuery.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/AbstractDocumentDbQuery.java
@@ -34,9 +34,9 @@ public Object execute(Object[] parameters) {
private DocumentDbQueryExecution getExecution(Query query, DocumentDbParameterAccessor accessor) {
if (isDeleteQuery()) {
- return new DocumentDbQueryExecution.DeleteExecution();
+ return new DocumentDbQueryExecution.DeleteExecution(operations);
} else {
- return new DocumentDbQueryExecution.SingleEntityExecution(operations);
+ return new DocumentDbQueryExecution.MultiEntityExecution(operations);
}
}
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/DocumentDbQueryCreator.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/DocumentDbQueryCreator.java
index 58dfcb29..e905fc3a 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/DocumentDbQueryCreator.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/DocumentDbQueryCreator.java
@@ -8,6 +8,7 @@
import com.microsoft.azure.spring.data.documentdb.core.mapping.DocumentDbPersistentProperty;
import com.microsoft.azure.spring.data.documentdb.core.query.Criteria;
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
+import org.apache.commons.lang3.NotImplementedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
@@ -18,10 +19,7 @@
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.PartTree;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.Iterator;
-import java.util.LinkedHashMap;
public class DocumentDbQueryCreator extends AbstractQueryCreator {
@@ -44,24 +42,22 @@ protected Criteria create(Part part, Iterator iterator) {
final PersistentPropertyPath propertyPath =
mappingContext.getPersistentPropertyPath(part.getProperty());
final DocumentDbPersistentProperty property = propertyPath.getLeafProperty();
- final LinkedHashMap params = new LinkedHashMap();
- final Collection clonedIterator = new ArrayList();
+ final Criteria criteria = from(part, property, Criteria.where(propertyPath.toDotPath()), iterator);
- while (iterator.hasNext()) {
- final Object obj = iterator.next();
- params.put(propertyPath.toDotPath(), obj);
- clonedIterator.add(obj);
- }
-
- final Criteria criteria = from(part, property, Criteria.where(propertyPath.toDotPath(), params),
- clonedIterator.iterator());
return criteria;
}
@Override
protected Criteria and(Part part, Criteria base, Iterator iterator) {
- // not supported yet
- return null;
+ if (base == null) {
+ return create(part, iterator);
+ }
+
+ final PersistentPropertyPath path =
+ mappingContext.getPersistentPropertyPath(part.getProperty());
+ final DocumentDbPersistentProperty property = path.getLeafProperty();
+
+ return from(part, property, base.and(path.toDotPath()), iterator);
}
@Override
@@ -73,7 +69,7 @@ protected Query complete(Criteria criteria, Sort sort) {
@Override
protected Criteria or(Criteria base, Criteria criteria) {
// not supported yet
- return null;
+ throw new NotImplementedException("Criteria or is not supported.");
}
private Criteria from(Part part, DocumentDbPersistentProperty property,
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/DocumentDbQueryExecution.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/DocumentDbQueryExecution.java
index 9a965181..7540cf53 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/DocumentDbQueryExecution.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/DocumentDbQueryExecution.java
@@ -7,8 +7,6 @@
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbOperations;
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
-import sun.reflect.generics.reflectiveObjects.NotImplementedException;
-
public interface DocumentDbQueryExecution {
Object execute(Query query, Class> type, String collection);
@@ -27,11 +25,11 @@ public Object execute(Query query, Class> type, String collection) {
}
}
- final class SingleEntityExecution implements DocumentDbQueryExecution {
+ final class MultiEntityExecution implements DocumentDbQueryExecution {
private final DocumentDbOperations operations;
- public SingleEntityExecution(DocumentDbOperations operations) {
+ public MultiEntityExecution(DocumentDbOperations operations) {
this.operations = operations;
}
@@ -42,11 +40,15 @@ public Object execute(Query query, Class> type, String collection) {
}
final class DeleteExecution implements DocumentDbQueryExecution {
+ private final DocumentDbOperations operations;
+
+ public DeleteExecution(DocumentDbOperations operations) {
+ this.operations = operations;
+ }
@Override
public Object execute(Query query, Class> type, String collection) {
- // deletion not supported yet
- throw new NotImplementedException();
+ return operations.delete(query, type, collection);
}
}
}
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/PartTreeDocumentDbQuery.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/PartTreeDocumentDbQuery.java
index 2dc06799..8d454994 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/PartTreeDocumentDbQuery.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/query/PartTreeDocumentDbQuery.java
@@ -8,6 +8,7 @@
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbOperations;
import com.microsoft.azure.spring.data.documentdb.core.mapping.DocumentDbPersistentProperty;
import com.microsoft.azure.spring.data.documentdb.core.query.Query;
+import org.apache.commons.lang3.NotImplementedException;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.parser.PartTree;
@@ -37,7 +38,7 @@ protected Query createQuery(DocumentDbParameterAccessor accessor) {
final Query query = creator.createQuery();
if (tree.isLimiting()) {
- System.out.println("not implement yet!");
+ throw new NotImplementedException("Limiting is not supported.");
}
return query;
}
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/support/DocumentDbEntityInformation.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/support/DocumentDbEntityInformation.java
index 3795b9b9..e4bc0f66 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/support/DocumentDbEntityInformation.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/support/DocumentDbEntityInformation.java
@@ -8,6 +8,7 @@
import com.microsoft.azure.spring.data.documentdb.core.mapping.Document;
import com.microsoft.azure.spring.data.documentdb.core.mapping.PartitionKey;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.data.annotation.Id;
import org.springframework.data.repository.core.support.AbstractEntityInformation;
@@ -48,6 +49,10 @@ public ID getId(T entity) {
return (ID) ReflectionUtils.getField(id, entity);
}
+ public Field getId() {
+ return this.id;
+ }
+
public Class getIdType() {
return (Class) id.getType();
}
diff --git a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/support/SimpleDocumentDbRepository.java b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/support/SimpleDocumentDbRepository.java
index 20d8a11c..87bd4c61 100644
--- a/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/support/SimpleDocumentDbRepository.java
+++ b/src/main/java/com/microsoft/azure/spring/data/documentdb/repository/support/SimpleDocumentDbRepository.java
@@ -8,7 +8,9 @@
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbOperations;
+import com.microsoft.azure.documentdb.PartitionKey;
import com.microsoft.azure.spring.data.documentdb.repository.DocumentDbRepository;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
@@ -52,17 +54,25 @@ public S save(S entity) {
if (entityInformation.isNew(entity)) {
documentDbOperations.insert(entityInformation.getCollectionName(),
entity,
- entityInformation.getPartitionKeyFieldValue(entity));
+ createKey(entityInformation.getPartitionKeyFieldValue(entity)));
} else {
documentDbOperations.upsert(entityInformation.getCollectionName(),
entity,
entityInformation.getId(entity),
- entityInformation.getPartitionKeyFieldValue(entity));
+ createKey(entityInformation.getPartitionKeyFieldValue(entity)));
}
return entity;
}
+ private PartitionKey createKey(String partitionKeyValue) {
+ if (StringUtils.isEmpty(partitionKeyValue)) {
+ return null;
+ }
+
+ return new PartitionKey(partitionKeyValue);
+ }
+
/**
* batch save entities
*
@@ -85,14 +95,13 @@ public Iterable save(Iterable entities) {
}
/**
- * find all entities from one collection without partition
+ * find all entities from one collection without configuring partition key value
*
* @return
*/
@Override
public Iterable findAll() {
- return documentDbOperations.findAll(entityInformation.getCollectionName(),
- entityInformation.getJavaType(), null, null);
+ return documentDbOperations.findAll(entityInformation.getCollectionName(), entityInformation.getJavaType());
}
/**
@@ -104,6 +113,7 @@ public Iterable findAll() {
@Override
public List findAll(Iterable ids) {
final List entities = new ArrayList();
+
for (final ID id : ids) {
final T entity = findOne(id);
@@ -124,7 +134,7 @@ public List findAll(Iterable ids) {
public T findOne(ID id) {
Assert.notNull(id, "id must not be null");
return documentDbOperations.findById(
- entityInformation.getCollectionName(), id, entityInformation.getJavaType(), null);
+ entityInformation.getCollectionName(), id, entityInformation.getJavaType());
}
/**
@@ -138,7 +148,7 @@ public long count() {
}
/**
- * delete one document per id without partitions
+ * delete one document per id without configuring partition key value
*
* @param id
*/
@@ -151,16 +161,18 @@ public void delete(ID id) {
}
/**
- * delete one document per entity without partitions
+ * delete one document per entity
*
* @param entity
*/
@Override
public void delete(T entity) {
+ final String paritionKeyValue = entityInformation.getPartitionKeyFieldValue(entity);
+
documentDbOperations.deleteById(entityInformation.getCollectionName(),
entityInformation.getId(entity),
entityInformation.getJavaType(),
- null);
+ paritionKeyValue == null ? null : new PartitionKey(paritionKeyValue));
}
/**
@@ -193,62 +205,4 @@ public void delete(Iterable extends T> entities) {
public boolean exists(ID primaryKey) {
return findOne(primaryKey) != null;
}
-
- /**
- * find all entities from one collection with partitions
- *
- * @param partitionKeyValue
- * @return
- */
- public List findAll(String partitionKeyValue) {
- return documentDbOperations.findAll(entityInformation.getCollectionName(),
- entityInformation.getJavaType(),
- entityInformation.getPartitionKeyFieldName(),
- partitionKeyValue);
- }
-
- /**
- * find one entity per id with partitions
- *
- * @param id
- * @param partitionKeyValue
- * @return
- */
- public T findOne(ID id, String partitionKeyValue) {
- Assert.notNull(id, "id must not be null");
- Assert.notNull(partitionKeyValue, "partitionKeyValue must not be null");
-
- return documentDbOperations.findById(
- entityInformation.getCollectionName(),
- id,
- entityInformation.getJavaType(),
- partitionKeyValue);
- }
-
- /**
- * delete an entity per id with partitions
- *
- * @param id
- * @param partitionKeyValue
- */
- public void delete(ID id, String partitionKeyValue) {
- documentDbOperations.deleteById(entityInformation.getCollectionName(),
- id,
- entityInformation.getJavaType(),
- partitionKeyValue);
-
- }
-
- /**
- * delete an entity with partitions
- *
- * @param entity
- * @param partitionKeyValue
- */
- public void delete(T entity, String partitionKeyValue) {
- documentDbOperations.deleteById(entityInformation.getCollectionName(),
- entityInformation.getId(entity),
- entityInformation.getJavaType(),
- partitionKeyValue);
- }
}
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplateIT.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplateIT.java
index 853da6b9..ff5971c0 100644
--- a/src/test/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplateIT.java
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplateIT.java
@@ -43,8 +43,6 @@ public class DocumentDbTemplateIT {
private static final List ADDRESSES = Constants.ADDRESSES;
private static final Person TEST_PERSON = new Person(TEST_ID, "testfirstname", "testlastname", HOBBIES, ADDRESSES);
- private static final String PARTITION_KEY = "lastName";
-
@Value("${documentdb.uri}")
private String documentDbUri;
@Value("${documentdb.key}")
@@ -85,24 +83,13 @@ public void cleanup() {
}
@Test(expected = RuntimeException.class)
- public void testInsertDuplicateId() throws Exception {
+ public void testInsertDuplicateId() {
dbTemplate.insert(Person.class.getSimpleName(), TEST_PERSON, null);
}
@Test
public void testFindAll() {
- final List result = dbTemplate.findAll(Person.class.getSimpleName(), Person.class, null, null);
- assertThat(result.size()).isEqualTo(1);
- assertThat(result.get(0)).isEqualTo(TEST_PERSON);
- }
-
- @Test
- public void testFindAllPartition() {
- setupPartition();
-
- final List result = dbTemplate.findAll(Person.class.getSimpleName(),
- Person.class, PARTITION_KEY, TEST_PERSON.getLastName());
-
+ final List result = dbTemplate.findAll(Person.class.getSimpleName(), Person.class);
assertThat(result.size()).isEqualTo(1);
assertThat(result.get(0)).isEqualTo(TEST_PERSON);
}
@@ -110,24 +97,11 @@ public void testFindAllPartition() {
@Test
public void testFindById() {
final Person result = dbTemplate.findById(Person.class.getSimpleName(),
- TEST_PERSON.getId(), Person.class, null);
- assertTrue(result.equals(TEST_PERSON));
-
- final Person nullResult = dbTemplate.findById(Person.class.getSimpleName(),
- TEST_NOTEXIST_ID, Person.class, null);
- assertThat(nullResult).isNull();
- }
-
- @Test
- public void testFindByIdPartition() {
- setupPartition();
-
- final Person result = dbTemplate.findById(Person.class.getSimpleName(),
- TEST_PERSON.getId(), Person.class, TEST_PERSON.getLastName());
+ TEST_PERSON.getId(), Person.class);
assertTrue(result.equals(TEST_PERSON));
final Person nullResult = dbTemplate.findById(Person.class.getSimpleName(),
- TEST_NOTEXIST_ID, Person.class, TEST_PERSON.getLastName());
+ TEST_NOTEXIST_ID, Person.class);
assertThat(nullResult).isNull();
}
@@ -141,27 +115,7 @@ public void testUpsertNewDocument() {
dbTemplate.upsert(Person.class.getSimpleName(), newPerson, null, null);
- final List result = dbTemplate.findAll(Person.class, null, null);
-
- assertThat(result.size()).isEqualTo(1);
- assertTrue(result.get(0).getFirstName().equals(firstName));
- }
-
- @Test
- public void testUpsertNewDocumentPartition() {
- // Delete first as was inserted in setup
- dbTemplate.deleteById(Person.class.getSimpleName(),
- TEST_PERSON.getId(), Person.class, null);
-
- setupPartition();
-
- final String firstName = "newFirstName_" + UUID.randomUUID().toString();
- final Person newPerson = new Person(null, firstName, "newLastName", null, null);
-
- final String partitionKeyValue = newPerson.getLastName();
- dbTemplate.upsert(Person.class.getSimpleName(), newPerson, null, partitionKeyValue);
-
- final List result = dbTemplate.findAll(Person.class, PARTITION_KEY, partitionKeyValue);
+ final List result = dbTemplate.findAll(Person.class);
assertThat(result.size()).isEqualTo(1);
assertTrue(result.get(0).getFirstName().equals(firstName));
@@ -174,20 +128,7 @@ public void testUpdate() {
dbTemplate.upsert(Person.class.getSimpleName(), updated, updated.getId(), null);
final Person result = dbTemplate.findById(Person.class.getSimpleName(),
- updated.getId(), Person.class, null);
-
- assertTrue(result.equals(updated));
- }
-
- @Test
- public void testUpdatePartition() {
- setupPartition();
- final Person updated = new Person(TEST_PERSON.getId(), "updatedname",
- TEST_PERSON.getLastName(), TEST_PERSON.getHobbies(), TEST_PERSON.getShippingAddresses());
- dbTemplate.upsert(Person.class.getSimpleName(), updated, updated.getId(), updated.getLastName());
-
- final Person result = dbTemplate.findById(Person.class.getSimpleName(),
- updated.getId(), Person.class, updated.getLastName());
+ updated.getId(), Person.class);
assertTrue(result.equals(updated));
}
@@ -196,37 +137,12 @@ public void testUpdatePartition() {
public void testDeleteById() {
final Person person2 = new Person("newid", "newfn", "newln", HOBBIES, ADDRESSES);
dbTemplate.insert(person2, null);
- assertThat(dbTemplate.findAll(Person.class, null, null).size()).isEqualTo(2);
+ assertThat(dbTemplate.findAll(Person.class).size()).isEqualTo(2);
dbTemplate.deleteById(Person.class.getSimpleName(), TEST_PERSON.getId(), null, null);
- final List result = dbTemplate.findAll(Person.class, null, null);
+ final List result = dbTemplate.findAll(Person.class);
assertThat(result.size()).isEqualTo(1);
assertTrue(result.get(0).equals(person2));
}
-
- @Test
- public void testDeleteByIdPartition() {
- setupPartition();
-
- // insert new document with same partition key
- final Person person2 = new Person("newid", "newfn", TEST_PERSON.getLastName(), HOBBIES, ADDRESSES);
- dbTemplate.insert(Person.class.getSimpleName(), person2, person2.getLastName());
-
- assertThat(dbTemplate.findAll(Person.class, PARTITION_KEY, person2.getLastName()).size()).isEqualTo(2);
-
- dbTemplate.deleteById(Person.class.getSimpleName(),
- TEST_PERSON.getId(), Person.class, TEST_PERSON.getLastName());
-
- final List result = dbTemplate.findAll(Person.class, PARTITION_KEY, person2.getLastName());
- assertThat(result.size()).isEqualTo(1);
- assertTrue(result.get(0).equals(person2));
- }
-
- private void setupPartition() {
- cleanup();
-
- dbTemplate.createCollectionIfNotExists(Person.class.getSimpleName(), PARTITION_KEY, 1000);
- dbTemplate.insert(Person.class.getSimpleName(), TEST_PERSON, TEST_PERSON.getLastName());
- }
}
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplatePartitionIT.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplatePartitionIT.java
new file mode 100644
index 00000000..b6a76e88
--- /dev/null
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/DocumentDbTemplatePartitionIT.java
@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for
+ * license information.
+ */
+
+package com.microsoft.azure.spring.data.documentdb.core;
+
+import com.microsoft.azure.documentdb.ConnectionPolicy;
+import com.microsoft.azure.documentdb.ConsistencyLevel;
+import com.microsoft.azure.documentdb.DocumentClient;
+import com.microsoft.azure.documentdb.PartitionKey;
+import com.microsoft.azure.spring.data.documentdb.Constants;
+import com.microsoft.azure.spring.data.documentdb.core.convert.MappingDocumentDbConverter;
+import com.microsoft.azure.spring.data.documentdb.core.mapping.DocumentDbMappingContext;
+import com.microsoft.azure.spring.data.documentdb.core.query.Criteria;
+import com.microsoft.azure.spring.data.documentdb.core.query.Query;
+import com.microsoft.azure.spring.data.documentdb.domain.Address;
+import com.microsoft.azure.spring.data.documentdb.domain.Person;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.domain.EntityScanner;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.data.annotation.Persistent;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import java.util.List;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@PropertySource(value = {"classpath:application.properties"})
+public class DocumentDbTemplatePartitionIT {
+ private static final String TEST_ID = "template_it_id";
+ private static final String TEST_NOTEXIST_ID = "non_exist_id";
+
+ private static final String TEST_DB_NAME = "template_it_db";
+ private static final List HOBBIES = Constants.HOBBIES;
+ private static final List ADDRESSES = Constants.ADDRESSES;
+ private static final Person TEST_PERSON = new Person(TEST_ID, "testfirstname", "testlastname", HOBBIES, ADDRESSES);
+
+ private static final String PARTITION_KEY = "lastName";
+
+ @Value("${documentdb.uri}")
+ private String documentDbUri;
+ @Value("${documentdb.key}")
+ private String documentDbKey;
+
+ private DocumentClient documentClient;
+ private DocumentDbTemplate dbTemplate;
+
+ private MappingDocumentDbConverter dbConverter;
+ private DocumentDbMappingContext mappingContext;
+
+ @Autowired
+ private ApplicationContext applicationContext;
+
+ @Before
+ public void setup() {
+ mappingContext = new DocumentDbMappingContext();
+ try {
+ mappingContext.setInitialEntitySet(new EntityScanner(this.applicationContext)
+ .scan(Persistent.class));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e.getMessage());
+
+ }
+ dbConverter = new MappingDocumentDbConverter(mappingContext);
+ documentClient = new DocumentClient(documentDbUri, documentDbKey,
+ ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);
+
+ dbTemplate = new DocumentDbTemplate(documentClient, dbConverter, TEST_DB_NAME);
+
+ dbTemplate.createCollectionIfNotExists(Person.class.getSimpleName(), PARTITION_KEY, 1000);
+ dbTemplate.insert(Person.class.getSimpleName(), TEST_PERSON, new PartitionKey(TEST_PERSON.getLastName()));
+ }
+
+ @After
+ public void cleanup() {
+ dbTemplate.deleteAll(Person.class.getSimpleName());
+ }
+
+ @Test
+ public void testFindAllByPartition() {
+ final Criteria criteria = new Criteria("lastName");
+ criteria.is(TEST_PERSON.getLastName());
+ final Query query = new Query(criteria);
+
+ final List result = dbTemplate.find(query, Person.class, Person.class.getSimpleName());
+ assertThat(result.size()).isEqualTo(1);
+ assertTrue(result.get(0).equals(TEST_PERSON));
+ }
+
+ @Test
+ public void testFindByIdWithPartition() {
+ final Criteria criteria = new Criteria("id");
+ criteria.is(TEST_PERSON.getId());
+ criteria.and("lastName").is(TEST_PERSON.getLastName());
+ final Query query = new Query(criteria);
+
+ final List result = dbTemplate.find(query, Person.class, Person.class.getSimpleName());
+ assertThat(result.size()).isEqualTo(1);
+ assertTrue(result.get(0).equals(TEST_PERSON));
+ }
+
+ @Test
+ public void testFindByNonExistIdWithPartition() {
+ final Criteria criteria = new Criteria("id");
+ criteria.is(TEST_NOTEXIST_ID);
+ criteria.and("lastName").is(TEST_PERSON.getLastName());
+ final Query query = new Query(criteria);
+
+ final List result = dbTemplate.find(query, Person.class, Person.class.getSimpleName());
+ assertThat(result.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testUpsertNewDocumentPartition() {
+ final String firstName = "newFirstName_" + UUID.randomUUID().toString();
+ final Person newPerson = new Person(null, firstName, "newLastName", null, null);
+
+ final String partitionKeyValue = newPerson.getLastName();
+ dbTemplate.upsert(Person.class.getSimpleName(), newPerson, null, new PartitionKey(partitionKeyValue));
+
+ final List result = dbTemplate.findAll(Person.class);
+
+ assertThat(result.size()).isEqualTo(2);
+
+ final Person person = result.stream()
+ .filter(p -> p.getLastName().equals(partitionKeyValue)).findFirst().get();
+ assertThat(person.getFirstName()).isEqualTo(firstName);
+ }
+
+ @Test
+ public void testUpdatePartition() {
+ final Person updated = new Person(TEST_PERSON.getId(), "updatedname",
+ TEST_PERSON.getLastName(), TEST_PERSON.getHobbies(), TEST_PERSON.getShippingAddresses());
+ dbTemplate.upsert(Person.class.getSimpleName(), updated, updated.getId(),
+ new PartitionKey(updated.getLastName()));
+
+ final List result = dbTemplate.findAll(Person.class);
+ final Person person = result.stream().filter(p -> TEST_PERSON.getId().equals(p.getId())).findFirst().get();
+
+ assertTrue(person.equals(updated));
+ }
+
+ @Test
+ public void testDeleteByIdPartition() {
+ // insert new document with same partition key
+ final Person person2 = new Person("newid", "newfn", TEST_PERSON.getLastName(), HOBBIES, ADDRESSES);
+ dbTemplate.insert(Person.class.getSimpleName(), person2, new PartitionKey(person2.getLastName()));
+
+ final List inserted = dbTemplate.findAll(Person.class);
+ assertThat(inserted.size()).isEqualTo(2);
+ assertThat(inserted.get(0).getLastName()).isEqualTo(TEST_PERSON.getLastName());
+ assertThat(inserted.get(1).getLastName()).isEqualTo(TEST_PERSON.getLastName());
+
+ dbTemplate.deleteById(Person.class.getSimpleName(),
+ TEST_PERSON.getId(), Person.class, new PartitionKey(TEST_PERSON.getLastName()));
+
+ final List result = dbTemplate.findAll(Person.class);
+ assertThat(result.size()).isEqualTo(1);
+ assertTrue(result.get(0).equals(person2));
+ }
+}
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/core/converter/DocumentDbConverterUnitTest.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/converter/DocumentDbConverterUnitTest.java
index c6c9f874..e5180b4d 100644
--- a/src/test/java/com/microsoft/azure/spring/data/documentdb/core/converter/DocumentDbConverterUnitTest.java
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/converter/DocumentDbConverterUnitTest.java
@@ -78,7 +78,7 @@ public void testConvertFromDocumentToEntity() {
assertThat(person.getId()).isEqualTo(id);
assertThat(person.getFirstName()).isEqualTo(firstName);
assertThat(person.getLastName()).isEqualTo(lastName);
- assertThat(person.getHobbies().equals(hobbies));
- assertThat(person.getShippingAddresses().equals(addresses));
+ assertThat(person.getHobbies()).isEqualTo(hobbies);
+ assertThat(person.getShippingAddresses()).isEqualTo(addresses);
}
}
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/core/query/CriteriaUnitTest.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/query/CriteriaUnitTest.java
index 2ecd21cf..ed4d2ea2 100644
--- a/src/test/java/com/microsoft/azure/spring/data/documentdb/core/query/CriteriaUnitTest.java
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/query/CriteriaUnitTest.java
@@ -7,6 +7,7 @@
import org.junit.Test;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
import static org.assertj.core.api.Assertions.assertThat;
@@ -15,9 +16,8 @@ public class CriteriaUnitTest {
@Test
public void testSimpleCriteria() {
- final LinkedHashMap value = new LinkedHashMap();
- value.put("name", "test");
- final Criteria c = new Criteria("name", value);
+ final Criteria c = new Criteria(new ArrayList<>(), "name");
+ c.is("test");
assertThat(c.getKey()).isEqualTo("name");
assertThat(c.getCriteriaObject()).isEqualTo("test");
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/core/query/QueryUnitTest.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/query/QueryUnitTest.java
index 5f03d571..b2ca4f5c 100644
--- a/src/test/java/com/microsoft/azure/spring/data/documentdb/core/query/QueryUnitTest.java
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/core/query/QueryUnitTest.java
@@ -7,6 +7,7 @@
import org.junit.Test;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
import static org.assertj.core.api.Assertions.assertThat;
@@ -15,10 +16,10 @@ public class QueryUnitTest {
@Test
public void testAddCriteria() {
- final LinkedHashMap values = new LinkedHashMap<>();
- values.putIfAbsent("name", "test");
+ final Criteria criteria = new Criteria(new ArrayList<>(), "name");
+ criteria.is("test");
- final Query query = new Query().addCriteria(new Criteria("name", values));
+ final Query query = new Query().addCriteria(criteria);
assertThat(query.getCriteria().size()).isEqualTo(1);
assertThat(query.getCriteria().get("name")).isEqualTo("test");
@@ -26,11 +27,7 @@ public void testAddCriteria() {
@Test
public void testWhere() {
- final LinkedHashMap values = new LinkedHashMap<>();
- values.putIfAbsent("name", "test");
-
- final Query query = new Query((Criteria.where("name", values)));
-
+ final Query query = new Query((Criteria.where("name").is("test")));
assertThat(query.getCriteria().size()).isEqualTo(1);
assertThat(query.getCriteria().get("name")).isEqualTo("test");
}
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/AddressRepository.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/AddressRepository.java
index fdb6517b..d32bbd72 100644
--- a/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/AddressRepository.java
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/AddressRepository.java
@@ -8,6 +8,17 @@
import com.microsoft.azure.spring.data.documentdb.domain.Address;
import org.springframework.stereotype.Repository;
+import java.util.List;
+
@Repository
public interface AddressRepository extends DocumentDbRepository {
+ void deleteByPostalCodeAndCity(String postalCode, String city);
+
+ void deleteByCity(String city);
+
+ List findByPostalCodeAndCity(String postalCode, String city);
+
+ List findByCity(String city);
+
+ List findByPostalCode(String postalCode);
}
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/AddressRepositoryIT.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/AddressRepositoryIT.java
index 527d815f..c3a46c1a 100644
--- a/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/AddressRepositoryIT.java
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/AddressRepositoryIT.java
@@ -8,7 +8,9 @@
import com.microsoft.azure.spring.data.documentdb.domain.Address;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
@@ -26,15 +28,20 @@ public class AddressRepositoryIT {
private static final Address TEST_ADDRESS1_PARTITION1 = new Address("111", "111st avenue", "redmond");
private static final Address TEST_ADDRESS2_PARTITION1 = new Address("222", "98th street", "redmond");
private static final Address TEST_ADDRESS1_PARTITION2 = new Address("333", "103rd street", "bellevue");
+ private static final Address TEST_ADDRESS4_PARTITION3 = new Address("111", "100rd street", "new york");
@Autowired
AddressRepository repository;
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
@Before
public void setup() {
repository.save(TEST_ADDRESS1_PARTITION1);
repository.save(TEST_ADDRESS1_PARTITION2);
repository.save(TEST_ADDRESS2_PARTITION1);
+ repository.save(TEST_ADDRESS4_PARTITION3);
}
@After
@@ -47,38 +54,67 @@ public void testFindAll() {
// findAll cross partition
final List result = toList(repository.findAll());
- assertThat(result.size()).isEqualTo(3);
+ assertThat(result.size()).isEqualTo(4);
+ }
- // findAll per partition
- final List partition1 = repository.findAll(TEST_ADDRESS1_PARTITION1.getCity());
- assertThat(partition1.size()).isEqualTo(2);
+ @Test
+ public void testFindByIdForPartitionedCollection() {
+ final List addresses = repository.findByPostalCode("111");
- final List partition2 = repository.findAll(TEST_ADDRESS1_PARTITION2.getCity());
- assertThat(partition2.size()).isEqualTo(1);
+ assertThat(addresses.size()).isEqualTo(2);
+ assertThat(addresses.get(0).getPostalCode().equals("111"));
+ assertThat(addresses.get(1).getPostalCode().equals("111"));
}
@Test
- public void testCountAndDeleteByID() {
- long count = repository.count();
- assertThat(count).isEqualTo(3);
+ public void testFindByPartitionedCity() {
+ final String city = TEST_ADDRESS1_PARTITION1.getCity();
+ final List result = toList(repository.findByCity(city));
- repository.delete(TEST_ADDRESS1_PARTITION1.getPostalCode(), TEST_ADDRESS1_PARTITION1.getCity());
+ assertThat(result.size()).isEqualTo(2);
+ assertThat(result.get(0).getCity()).isEqualTo(city);
+ assertThat(result.get(1).getCity()).isEqualTo(city);
+ }
- final List result = toList(repository.findAll());
- assertThat(result.size()).isEqualTo(2);
+ @Test
+ public void testCount() {
+ final long count = repository.count();
+ assertThat(count).isEqualTo(4);
+ }
- count = repository.count();
- assertThat(count).isEqualTo(2);
+ @Test
+ public void deleteWithoutPartitionedColumnShouldFail() {
+ expectedException.expect(UnsupportedOperationException.class);
+ expectedException.expectMessage("PartitionKey value must be supplied for this operation.");
+
+ repository.delete(TEST_ADDRESS1_PARTITION1.getPostalCode());
+ }
+
+ @Test
+ public void canDeleteByIdAndPartitionedCity() {
+ final long count = repository.count();
+ assertThat(count).isEqualTo(4);
+
+ repository.deleteByPostalCodeAndCity(
+ TEST_ADDRESS1_PARTITION1.getPostalCode(), TEST_ADDRESS1_PARTITION1.getCity());
+
+ final List result = toList(repository.findAll());
+
+ assertThat(result.size()).isEqualTo(3);
}
@Test
- public void testCountAndDeleteEntity() {
- repository.delete(TEST_ADDRESS1_PARTITION1, TEST_ADDRESS1_PARTITION1.getCity());
+ public void canDeleteByPartitionedCity() {
+ final long count = repository.count();
+ assertThat(count).isEqualTo(4);
+
+ repository.deleteByCity(TEST_ADDRESS1_PARTITION1.getCity());
final List result = toList(repository.findAll());
assertThat(result.size()).isEqualTo(2);
+ assertThat(result.get(0).getCity()).isNotEqualTo(TEST_ADDRESS1_PARTITION1.getCity());
}
@Test
@@ -88,10 +124,12 @@ public void testUpdateEntity() {
repository.save(updatedAddress);
- final Address address = repository.findOne(updatedAddress.getPostalCode(), updatedAddress.getCity());
+ final List results =
+ repository.findByPostalCodeAndCity(updatedAddress.getPostalCode(), updatedAddress.getCity());
- assertThat(address.getStreet()).isEqualTo(updatedAddress.getStreet());
- assertThat(address.getPostalCode()).isEqualTo(updatedAddress.getPostalCode());
+ assertThat(results.size()).isEqualTo(1);
+ assertThat(results.get(0).getStreet()).isEqualTo(updatedAddress.getStreet());
+ assertThat(results.get(0).getPostalCode()).isEqualTo(updatedAddress.getPostalCode());
}
private List toList(Iterable iterable) {
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/domain/PersonRepository.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/PersonRepository.java
similarity index 68%
rename from src/test/java/com/microsoft/azure/spring/data/documentdb/domain/PersonRepository.java
rename to src/test/java/com/microsoft/azure/spring/data/documentdb/repository/PersonRepository.java
index 7004f326..13eba4f4 100644
--- a/src/test/java/com/microsoft/azure/spring/data/documentdb/domain/PersonRepository.java
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/PersonRepository.java
@@ -4,9 +4,9 @@
* license information.
*/
-package com.microsoft.azure.spring.data.documentdb.domain;
+package com.microsoft.azure.spring.data.documentdb.repository;
-import com.microsoft.azure.spring.data.documentdb.repository.DocumentDbRepository;
+import com.microsoft.azure.spring.data.documentdb.domain.Person;
import org.springframework.stereotype.Repository;
@Repository
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/SimpleDocumentDbRepositoryUnitTest.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/SimpleDocumentDbRepositoryUnitTest.java
index b1f7309d..5b784755 100644
--- a/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/SimpleDocumentDbRepositoryUnitTest.java
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/SimpleDocumentDbRepositoryUnitTest.java
@@ -12,8 +12,11 @@
import com.microsoft.azure.spring.data.documentdb.domain.Person;
import com.microsoft.azure.spring.data.documentdb.repository.support.DocumentDbEntityInformation;
import com.microsoft.azure.spring.data.documentdb.repository.support.SimpleDocumentDbRepository;
+import org.assertj.core.util.Lists;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@@ -32,20 +35,24 @@ public class SimpleDocumentDbRepositoryUnitTest {
new Person("test_person", "firstname", "lastname",
Constants.HOBBIES, Constants.ADDRESSES);
+ private static final String PARTITION_VALUE_REQUIRED_MSG =
+ "PartitionKey value must be supplied for this operation.";
+
SimpleDocumentDbRepository repository;
@Mock
DocumentDbOperations dbOperations;
@Mock
DocumentDbEntityInformation entityInformation;
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
@Before
public void setUp() {
when(entityInformation.getJavaType()).thenReturn(Person.class);
when(entityInformation.getCollectionName()).thenReturn(Person.class.getSimpleName());
- when(entityInformation.getPartitionKeyFieldName()).thenReturn("lastName");
when(entityInformation.getRequestUint()).thenReturn(1000);
- when(dbOperations.findAll(anyString(), any(), anyString(), anyString()))
- .thenReturn(Arrays.asList(TEST_PERSON));
+ when(dbOperations.findAll(anyString(), any())).thenReturn(Arrays.asList(TEST_PERSON));
repository = new SimpleDocumentDbRepository(entityInformation, dbOperations);
}
@@ -54,21 +61,34 @@ public void setUp() {
public void testSave() {
repository.save(TEST_PERSON);
- final List result = repository.findAll(TEST_PERSON.getLastName());
+ final List result = Lists.newArrayList(repository.findAll());
assertEquals(1, result.size());
assertEquals(TEST_PERSON, result.get(0));
}
@Test
public void testFindOne() {
- when(dbOperations.findById(anyString(), any(), any(), anyString())).thenReturn(TEST_PERSON);
+ when(dbOperations.findById(anyString(), any(), any())).thenReturn(TEST_PERSON);
repository.save(TEST_PERSON);
- final Person result = repository.findOne(TEST_PERSON.getId(), TEST_PERSON.getLastName());
+ final Person result = repository.findOne(TEST_PERSON.getId());
assertEquals(TEST_PERSON, result);
}
+ @Test
+ public void testFindOneExceptionForPartitioned() {
+ expectedException.expect(UnsupportedOperationException.class);
+ expectedException.expectMessage(PARTITION_VALUE_REQUIRED_MSG);
+
+ repository.save(TEST_PERSON);
+
+ when(dbOperations.findById(anyString(), any(), any()))
+ .thenThrow(new UnsupportedOperationException(PARTITION_VALUE_REQUIRED_MSG));
+
+ final Person result = repository.findOne(TEST_PERSON.getId());
+ }
+
@Test
public void testUpdate() {
final List updatedAddress =
@@ -78,9 +98,9 @@ public void testUpdate() {
Arrays.asList("updated hobbies"), updatedAddress);
repository.save(updatedPerson);
- when(dbOperations.findById(anyString(), any(), any(), anyString())).thenReturn(updatedPerson);
+ when(dbOperations.findById(anyString(), any(), any())).thenReturn(updatedPerson);
- final Person result = repository.findOne(TEST_PERSON.getId(), TEST_PERSON.getLastName());
+ final Person result = repository.findOne(TEST_PERSON.getId());
assertEquals(updatedPerson, result);
}
}
diff --git a/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/support/DocumentDbRepositoryFactoryBeanUnitTest.java b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/support/DocumentDbRepositoryFactoryBeanUnitTest.java
index 8ddf7863..ec593f8a 100644
--- a/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/support/DocumentDbRepositoryFactoryBeanUnitTest.java
+++ b/src/test/java/com/microsoft/azure/spring/data/documentdb/repository/support/DocumentDbRepositoryFactoryBeanUnitTest.java
@@ -6,7 +6,7 @@
package com.microsoft.azure.spring.data.documentdb.repository.support;
import com.microsoft.azure.spring.data.documentdb.core.DocumentDbTemplate;
-import com.microsoft.azure.spring.data.documentdb.domain.PersonRepository;
+import com.microsoft.azure.spring.data.documentdb.repository.PersonRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;