diff --git a/pom.xml b/pom.xml
index cb4e66d37e..5656cb3f41 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.0.0.BUILD-SNAPSHOT
+ 2.0.0.DATAMONGO-1563-SNAPSHOT
pom
Spring Data MongoDB
diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml
index 4a49168713..c2ff64f291 100644
--- a/spring-data-mongodb-cross-store/pom.xml
+++ b/spring-data-mongodb-cross-store/pom.xml
@@ -6,7 +6,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.0.0.BUILD-SNAPSHOT
+ 2.0.0.DATAMONGO-1563-SNAPSHOT
../pom.xml
@@ -48,7 +48,7 @@
org.springframework.data
spring-data-mongodb
- 2.0.0.BUILD-SNAPSHOT
+ 2.0.0.DATAMONGO-1563-SNAPSHOT
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index 750ed23aa8..569160a582 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -13,7 +13,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.0.0.BUILD-SNAPSHOT
+ 2.0.0.DATAMONGO-1563-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml
index 50d0a6454a..526e5bb318 100644
--- a/spring-data-mongodb-log4j/pom.xml
+++ b/spring-data-mongodb-log4j/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.0.0.BUILD-SNAPSHOT
+ 2.0.0.DATAMONGO-1563-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 99ef83a54b..cb73baabd8 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 2.0.0.BUILD-SNAPSHOT
+ 2.0.0.DATAMONGO-1563-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperation.java
new file mode 100644
index 0000000000..2fb319c347
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperation.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationResults;
+import org.springframework.data.util.CloseableIterator;
+
+/**
+ * {@link ExecutableAggregationOperation} allows creation and execution of MongoDB aggregation operations in a fluent
+ * API style.
+ * The starting {@literal domainType} is used for mapping the {@link Aggregation} provided via {@code by} into the
+ * MongoDB specific representation, as well as mapping back the resulting {@link org.bson.Document}. An alternative
+ * input type for mapping the {@link Aggregation} can be provided by using
+ * {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation}.
+ *
+ *
+ *
+ * aggregateAndReturn(Jedi.class)
+ * .by(newAggregation(Human.class, project("These are not the droids you are looking for")))
+ * .get();
+ *
+ *
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+public interface ExecutableAggregationOperation {
+
+ /**
+ * Start creating an aggregation operation that returns results mapped to the given domain type.
+ * Use {@link org.springframework.data.mongodb.core.aggregation.TypedAggregation} to specify a potentially different
+ * input type for he aggregation.
+ *
+ * @param domainType must not be {@literal null}.
+ * @return new instance of {@link AggregationOperation}.
+ * @throws IllegalArgumentException if domainType is {@literal null}.
+ */
+ AggregationOperation aggregateAndReturn(Class domainType);
+
+ /**
+ * Collection override (Optional).
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface AggregationOperationWithCollection {
+
+ /**
+ * Explicitly set the name of the collection to perform the query on.
+ * Skip this step to use the default collection derived from the domain type.
+ *
+ * @param collection must not be {@literal null} nor {@literal empty}.
+ * @return new instance of {@link AggregationOperationWithAggregation}.
+ * @throws IllegalArgumentException if collection is {@literal null}.
+ */
+ AggregationOperationWithAggregation inCollection(String collection);
+ }
+
+ /**
+ * Trigger execution by calling one of the terminating methods.
+ *
+ * @param
+ */
+ interface TerminatingAggregationOperation {
+
+ /**
+ * Apply pipeline operations as specified.
+ *
+ * @return never {@literal null}.
+ */
+ AggregationResults get();
+
+ /**
+ * Apply pipeline operations as specified.
+ * Returns a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.Cursor}
+ *
+ * @return a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.Cursor} that needs to be closed.
+ * Never {@literal null}.
+ */
+ CloseableIterator stream();
+ }
+
+ /**
+ * Define the aggregation with pipeline stages.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface AggregationOperationWithAggregation {
+
+ /**
+ * Set the aggregation to be used.
+ *
+ * @param aggregation must not be {@literal null}.
+ * @return new instance of {@link TerminatingAggregationOperation}.
+ * @throws IllegalArgumentException if aggregation is {@literal null}.
+ */
+ TerminatingAggregationOperation by(Aggregation aggregation);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface AggregationOperation
+ extends AggregationOperationWithCollection, AggregationOperationWithAggregation {}
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java
new file mode 100644
index 0000000000..c4fb33e335
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableAggregationOperationSupport.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import lombok.RequiredArgsConstructor;
+
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationResults;
+import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
+import org.springframework.data.util.CloseableIterator;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Implementation of {@link ExecutableAggregationOperation} operating directly on {@link MongoTemplate}.
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+class ExecutableAggregationOperationSupport implements ExecutableAggregationOperation {
+
+ private final MongoTemplate template;
+
+ /**
+ * Create new instance of ExecutableAggregationOperationSupport.
+ *
+ * @param template must not be {@literal null}.
+ * @throws IllegalArgumentException if template is {@literal null}.
+ */
+ ExecutableAggregationOperationSupport(MongoTemplate template) {
+
+ Assert.notNull(template, "Template must not be null!");
+ this.template = template;
+ }
+
+ @Override
+ public AggregationOperation aggregateAndReturn(Class domainType) {
+
+ Assert.notNull(domainType, "DomainType must not be null!");
+ return new AggregationOperationSupport(template, null, domainType, null);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ @RequiredArgsConstructor
+ static class AggregationOperationSupport
+ implements AggregationOperationWithAggregation, AggregationOperation, TerminatingAggregationOperation {
+
+ private final MongoTemplate template;
+ private final Aggregation aggregation;
+ private final Class domainType;
+ private final String collection;
+
+ @Override
+ public AggregationOperationWithAggregation inCollection(String collection) {
+
+ Assert.hasText(collection, "Collection must not be null nor empty!");
+ return new AggregationOperationSupport(template, aggregation, domainType, collection);
+ }
+
+ @Override
+ public TerminatingAggregationOperation by(Aggregation aggregation) {
+
+ Assert.notNull(aggregation, "Aggregation must not be null!");
+ return new AggregationOperationSupport(template, aggregation, domainType, collection);
+ }
+
+ @Override
+ public AggregationResults get() {
+ return template.aggregate(aggregation, getCollectionName(aggregation), domainType);
+ }
+
+ @Override
+ public CloseableIterator stream() {
+ return template.aggregateStream(aggregation, getCollectionName(aggregation), domainType);
+ }
+
+ private String getCollectionName(Aggregation aggregation) {
+
+ if (StringUtils.hasText(collection)) {
+ return collection;
+ }
+
+ if (aggregation instanceof TypedAggregation) {
+
+ if (((TypedAggregation>) aggregation).getInputType() != null) {
+ return template.determineCollectionName(((TypedAggregation>) aggregation).getInputType());
+ }
+ }
+
+ return template.determineCollectionName(domainType);
+ }
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java
new file mode 100644
index 0000000000..f218c4abb6
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.springframework.data.geo.GeoResults;
+import org.springframework.data.mongodb.core.query.NearQuery;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.util.CloseableIterator;
+
+/**
+ * {@link ExecutableFindOperation} allows creation and execution of MongoDB find operations in a fluent API style.
+ *
+ * The starting {@literal domainType} is used for mapping the {@link Query} provided via {@code matching} into the
+ * MongoDB specific representation. By default this originating {@literal domainType} is also used for mapping back the
+ * result from the {@link org.bson.Document}. However it is possible to define an different {@literal returnType} via
+ * {@code as} that is then used for mapping the result mapping.
+ * The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
+ * via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
+ * collection name for the execution.
+ *
+ *
+ *
+ * query(Human.class)
+ * .inCollection("star-wars")
+ * .as(Jedi.class)
+ * .matching(query(where("firstname").is("luke")))
+ * .all();
+ *
+ *
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+public interface ExecutableFindOperation {
+
+ /**
+ * Start creating a find operation for the given {@literal domainType}.
+ *
+ * @param domainType must not be {@literal null}.
+ * @return new instance of {@link FindOperation}.
+ * @throws IllegalArgumentException if domainType is {@literal null}.
+ */
+ FindOperation query(Class domainType);
+
+ /**
+ * Trigger find execution by calling one of the terminating methods.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface TerminatingFindOperation {
+
+ /**
+ * Get exactly zero or one result.
+ *
+ * @return {@link Optional#empty()} if no match found.
+ * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one match found.
+ */
+ Optional one();
+
+ /**
+ * Get the first or no result.
+ *
+ * @return {@link Optional#empty()} if no match found.
+ */
+ Optional first();
+
+ /**
+ * Get all matching elements.
+ *
+ * @return never {@literal}.
+ */
+ List all();
+
+ /**
+ * Stream all matching elements.
+ *
+ * @return a {@link CloseableIterator} that wraps the a Mongo DB {@link com.mongodb.Cursor} that needs to be closed.
+ * Never {@literal null}.
+ */
+ CloseableIterator stream();
+ }
+
+ /**
+ * Trigger geonear execution by calling one of the terminating methods.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface TerminatingFindNearOperation {
+
+ /**
+ * Find all matching elements and return them as {@link org.springframework.data.geo.GeoResult}.
+ *
+ * @return never {@literal null}.
+ */
+ GeoResults all();
+ }
+
+ /**
+ * Terminating operations invoking the actual query execution.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface FindOperationWithQuery extends TerminatingFindOperation {
+
+ /**
+ * Set the filter query to be used.
+ *
+ * @param query must not be {@literal null}.
+ * @return new instance of {@link TerminatingFindOperation}.
+ * @throws IllegalArgumentException if query is {@literal null}.
+ */
+ TerminatingFindOperation matching(Query query);
+
+ /**
+ * Set the filter query for the geoNear execution.
+ *
+ * @param nearQuery must not be {@literal null}.
+ * @return new instance of {@link TerminatingFindNearOperation}.
+ * @throws IllegalArgumentException if nearQuery is {@literal null}.
+ */
+ TerminatingFindNearOperation near(NearQuery nearQuery);
+ }
+
+ /**
+ * Collection override (Optional).
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface FindOperationWithCollection extends FindOperationWithQuery {
+
+ /**
+ * Explicitly set the name of the collection to perform the query on.
+ * Skip this step to use the default collection derived from the domain type.
+ *
+ * @param collection must not be {@literal null} nor {@literal empty}.
+ * @return new instance of {@link FindOperationWithProjection}.
+ * @throws IllegalArgumentException if collection is {@literal null}.
+ */
+ FindOperationWithProjection inCollection(String collection);
+ }
+
+ /**
+ * Result type override (Optional).
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface FindOperationWithProjection extends FindOperationWithQuery {
+
+ /**
+ * Define the target type fields should be mapped to.
+ * Skip this step if you are anyway only interested in the original domain type.
+ *
+ * @param resultType must not be {@literal null}.
+ * @param result type.
+ * @return new instance of {@link FindOperationWithProjection}.
+ * @throws IllegalArgumentException if resultType is {@literal null}.
+ */
+ FindOperationWithQuery as(Class resultType);
+ }
+
+ /**
+ * {@link FindOperation} provides methods for constructing lookup operations in a fluent way.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface FindOperation extends FindOperationWithCollection, FindOperationWithProjection {
+
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java
new file mode 100644
index 0000000000..4b1943a4b7
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import lombok.RequiredArgsConstructor;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.bson.Document;
+import org.springframework.dao.IncorrectResultSizeDataAccessException;
+import org.springframework.data.mongodb.core.query.BasicQuery;
+import org.springframework.data.mongodb.core.query.NearQuery;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.SerializationUtils;
+import org.springframework.data.util.CloseableIterator;
+import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+import com.mongodb.client.FindIterable;
+
+/**
+ * Implementation of {@link ExecutableFindOperation}.
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+class ExecutableFindOperationSupport implements ExecutableFindOperation {
+
+ private final MongoTemplate template;
+
+ /**
+ * Create new {@link ExecutableFindOperationSupport}.
+ *
+ * @param template must not be {@literal null}.
+ * @throws IllegalArgumentException if template is {@literal null}.
+ */
+ ExecutableFindOperationSupport(MongoTemplate template) {
+
+ Assert.notNull(template, "Template must not be null!");
+
+ this.template = template;
+ }
+
+ @Override
+ public FindOperation query(Class domainType) {
+
+ Assert.notNull(domainType, "DomainType must not be null!");
+
+ return new FindOperationSupport<>(template, domainType, domainType, null, null);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ @RequiredArgsConstructor
+ static class FindOperationSupport implements FindOperation, FindOperationWithCollection,
+ FindOperationWithProjection, FindOperationWithQuery {
+
+ private final MongoTemplate template;
+ private final Class> domainType;
+ private final Class returnType;
+ private final String collection;
+ private final Query query;
+
+ @Override
+ public FindOperationWithProjection inCollection(String collection) {
+
+ Assert.hasText(collection, "Collection name must not be null nor empty!");
+
+ return new FindOperationSupport<>(template, domainType, returnType, collection, query);
+ }
+
+ @Override
+ public FindOperationWithQuery as(Class returnType) {
+
+ Assert.notNull(returnType, "ReturnType must not be null!");
+
+ return new FindOperationSupport<>(template, domainType, returnType, collection, query);
+ }
+
+ @Override
+ public TerminatingFindOperation matching(Query query) {
+
+ Assert.notNull(query, "Query must not be null!");
+
+ return new FindOperationSupport<>(template, domainType, returnType, collection, query);
+ }
+
+ @Override
+ public Optional one() {
+
+ List result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(2));
+
+ if (ObjectUtils.isEmpty(result)) {
+ return Optional.empty();
+ }
+
+ if (result.size() > 1) {
+ throw new IncorrectResultSizeDataAccessException("Query " + asString() + " returned non unique result.", 1);
+ }
+
+ return Optional.of(result.iterator().next());
+ }
+
+ @Override
+ public Optional first() {
+
+ List result = doFind(new DelegatingQueryCursorPreparer(getCursorPreparer(query, null)).limit(1));
+ return ObjectUtils.isEmpty(result) ? Optional.empty() : Optional.of(result.iterator().next());
+ }
+
+ @Override
+ public List all() {
+ return doFind(null);
+ }
+
+ @Override
+ public CloseableIterator stream() {
+ return doStream();
+ }
+
+ @Override
+ public TerminatingFindNearOperation near(NearQuery nearQuery) {
+ return () -> template.geoNear(nearQuery, domainType, getCollectionName(), returnType);
+ }
+
+ private List doFind(CursorPreparer preparer) {
+
+ Document queryObject = query != null ? query.getQueryObject() : new Document();
+ Document fieldsObject = query != null ? query.getFieldsObject() : new Document();
+
+ return template.doFind(getCollectionName(), queryObject, fieldsObject, domainType, returnType,
+ getCursorPreparer(query, preparer));
+ }
+
+ private CloseableIterator doStream() {
+
+ return template.doStream(query != null ? query : new BasicQuery(new Document()), domainType, getCollectionName(),
+ returnType);
+ }
+
+ private CursorPreparer getCursorPreparer(Query query, CursorPreparer preparer) {
+ return query == null || preparer != null ? preparer : template.new QueryCursorPreparer(query, domainType);
+ }
+
+ private String getCollectionName() {
+ return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
+ }
+
+ private String asString() {
+ return SerializationUtils.serializeToJsonSafely(query);
+ }
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ static class DelegatingQueryCursorPreparer implements CursorPreparer {
+
+ private final CursorPreparer delegate;
+ private Optional limit = Optional.empty();
+
+ public DelegatingQueryCursorPreparer(CursorPreparer delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public FindIterable prepare(FindIterable cursor) {
+
+ FindIterable target = delegate.prepare(cursor);
+ return limit.map(it -> target.limit(it)).orElse(target);
+ }
+
+ CursorPreparer limit(int limit) {
+
+ this.limit = Optional.of(limit);
+ return this;
+ }
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperation.java
new file mode 100644
index 0000000000..083f80ea5f
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperation.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import java.util.Collection;
+
+import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
+
+import com.mongodb.bulk.BulkWriteResult;
+
+/**
+ * {@link ExecutableFindOperation} allows creation and execution of MongoDB insert and bulk insert operations in a
+ * fluent API style.
+ * The collection to operate on is by default derived from the initial {@literal domainType} and can be defined there
+ * via {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
+ * collection name for the execution.
+ *
+ *
+ *
+ * insert(Jedi.class)
+ * .inCollection("star-wars")
+ * .one(luke);
+ *
+ *
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+public interface ExecutableInsertOperation {
+
+ /**
+ * Start creating an insert operation for given {@literal domainType}.
+ *
+ * @param domainType must not be {@literal null}.
+ * @return new instance of {@link InsertOperation}.
+ * @throws IllegalArgumentException if domainType is {@literal null}.
+ */
+ InsertOperation insert(Class domainType);
+
+ /**
+ * Trigger insert execution by calling one of the terminating methods.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface TerminatingInsertOperation extends TerminatingBulkInsertOperation {
+
+ /**
+ * Insert exactly one object.
+ *
+ * @param object must not be {@literal null}.
+ * @throws IllegalArgumentException if object is {@literal null}.
+ */
+ void one(T object);
+
+ /**
+ * Insert a collection of objects.
+ *
+ * @param objects must not be {@literal null}.
+ * @throws IllegalArgumentException if objects is {@literal null}.
+ */
+ void all(Collection extends T> objects);
+ }
+
+ /**
+ * Trigger bulk insert execution by calling one of the terminating methods.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface TerminatingBulkInsertOperation {
+
+ /**
+ * Bulk write collection of objects.
+ *
+ * @param objects must not be {@literal null}.
+ * @return resulting {@link BulkWriteResult}.
+ * @throws IllegalArgumentException if objects is {@literal null}.
+ */
+ BulkWriteResult bulk(Collection extends T> objects);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface InsertOperation
+ extends TerminatingInsertOperation, InsertOperationWithCollection, InsertOperationWithBulkMode {
+
+ }
+
+ /**
+ * Collection override (Optional).
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface InsertOperationWithCollection {
+
+ /**
+ * Explicitly set the name of the collection.
+ * Skip this step to use the default collection derived from the domain type.
+ *
+ * @param collection must not be {@literal null} nor {@literal empty}.
+ * @return new instance of {@link InsertOperationWithBulkMode}.
+ * @throws IllegalArgumentException if collection is {@literal null}.
+ */
+ InsertOperationWithBulkMode inCollection(String collection);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface InsertOperationWithBulkMode extends TerminatingInsertOperation {
+
+ /**
+ * Define the {@link BulkMode} to use for bulk insert operation.
+ *
+ * @param mode must not be {@literal null}.
+ * @return new instance of {@link TerminatingBulkInsertOperation}.
+ * @throws IllegalArgumentException if bulkMode is {@literal null}.
+ */
+ TerminatingBulkInsertOperation withBulkMode(BulkMode bulkMode);
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java
new file mode 100644
index 0000000000..83ca96994e
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableInsertOperationSupport.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import lombok.RequiredArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import com.mongodb.bulk.BulkWriteResult;
+
+/**
+ * Implementation of {@link ExecutableInsertOperation}.
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+class ExecutableInsertOperationSupport implements ExecutableInsertOperation {
+
+ private final MongoTemplate template;
+
+ /**
+ * Create new {@link ExecutableInsertOperationSupport}.
+ *
+ * @param template must not be {@literal null}.
+ * @throws IllegalArgumentException if template is {@literal null}.
+ */
+ ExecutableInsertOperationSupport(MongoTemplate template) {
+
+ Assert.notNull(template, "Template must not be null!");
+
+ this.template = template;
+ }
+
+ @Override
+ public InsertOperation insert(Class domainType) {
+
+ Assert.notNull(domainType, "DomainType must not be null!");
+ return new InsertOperationSupport(template, domainType, null, null);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ @RequiredArgsConstructor
+ static class InsertOperationSupport implements InsertOperation {
+
+ private final MongoTemplate template;
+ private final Class domainType;
+ private final String collection;
+ private final BulkMode bulkMode;
+
+ @Override
+ public void one(T object) {
+
+ Assert.notNull(object, "Object must not be null!");
+ template.insert(object, getCollectionName());
+ }
+
+ @Override
+ public void all(Collection extends T> objects) {
+
+ Assert.notNull(objects, "Objects must not be null!");
+ template.insert(objects, getCollectionName());
+ }
+
+ @Override
+ public BulkWriteResult bulk(Collection extends T> objects) {
+
+ Assert.notNull(objects, "Objects must not be null!");
+ return template.bulkOps(bulkMode != null ? bulkMode : BulkMode.ORDERED, domainType, getCollectionName())
+ .insert(new ArrayList<>(objects)).execute();
+ }
+
+ @Override
+ public InsertOperationWithBulkMode inCollection(String collection) {
+
+ Assert.hasText(collection, "Collection must not be null nor empty.");
+ return new InsertOperationSupport(template, domainType, collection, bulkMode);
+ }
+
+ @Override
+ public TerminatingBulkInsertOperation withBulkMode(BulkMode bulkMode) {
+
+ Assert.notNull(bulkMode, "BulkMode must not be null!");
+ return new InsertOperationSupport(template, domainType, collection, bulkMode);
+ }
+
+ private String getCollectionName() {
+ return StringUtils.hasText(collection) ? collection : template.determineCollectionName(domainType);
+ }
+
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperation.java
new file mode 100644
index 0000000000..87be5e0f5f
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperation.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import java.util.List;
+
+import org.springframework.data.mongodb.core.query.Query;
+
+import com.mongodb.client.result.DeleteResult;
+
+/**
+ * {@link ExecutableRemoveOperation} allows creation and execution of MongoDB remove / findAndRemove operations in a
+ * fluent API style.
+ * The starting {@literal domainType} is used for mapping the {@link Query} provided via {@code matching} into the
+ * MongoDB specific representation. The collection to operate on is by default derived from the initial
+ * {@literal domainType} and can be defined there via {@link org.springframework.data.mongodb.core.mapping.Document}.
+ * Using {@code inCollection} allows to override the collection name for the execution.
+ *
+ *
+ *
+ * remove(Jedi.class)
+ * .inCollection("star-wars")
+ * .matching(query(where("firstname").is("luke")))
+ * .all();
+ *
+ *
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+public interface ExecutableRemoveOperation {
+
+ /**
+ * Start creating a remove operation for the given {@literal domainType}.
+ *
+ * @param domainType must not be {@literal null}.
+ * @return new instance of {@link RemoveOperation}.
+ * @throws IllegalArgumentException if domainType is {@literal null}.
+ */
+ RemoveOperation remove(Class domainType);
+
+ /**
+ * Collection override (Optional).
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface RemoveOperationWithCollection extends RemoveOperationWithQuery {
+
+ /**
+ * Explicitly set the name of the collection to perform the query on.
+ * Skip this step to use the default collection derived from the domain type.
+ *
+ * @param collection must not be {@literal null} nor {@literal empty}.
+ * @return new instance of {@link RemoveOperationWithCollection}.
+ * @throws IllegalArgumentException if collection is {@literal null}.
+ */
+ RemoveOperationWithQuery inCollection(String collection);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface TerminatingRemoveOperation {
+
+ /**
+ * Remove all documents matching.
+ *
+ * @return
+ */
+ DeleteResult all();
+
+ /**
+ * Remove and return all matching documents.
+ * NOTE The entire list of documents will be fetched before sending the actual delete commands.
+ * Also, {@link org.springframework.context.ApplicationEvent}s will be published for each and every delete
+ * operation.
+ *
+ * @return empty {@link List} if no match found. Never {@literal null}.
+ */
+ List findAndRemove();
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface RemoveOperationWithQuery extends TerminatingRemoveOperation {
+
+ /**
+ * Define the query filtering elements.
+ *
+ * @param query must not be {@literal null}.
+ * @return new instance of {@link TerminatingRemoveOperation}.
+ * @throws IllegalArgumentException if query is {@literal null}.
+ */
+ TerminatingRemoveOperation matching(Query query);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface RemoveOperation extends RemoveOperationWithCollection {
+
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java
new file mode 100644
index 0000000000..bc38852794
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableRemoveOperationSupport.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import lombok.RequiredArgsConstructor;
+
+import java.util.List;
+
+import org.bson.Document;
+import org.springframework.data.mongodb.core.query.BasicQuery;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import com.mongodb.client.result.DeleteResult;
+
+/**
+ * Implementation of {@link ExecutableRemoveOperation}.
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+class ExecutableRemoveOperationSupport implements ExecutableRemoveOperation {
+
+ private final MongoTemplate tempate;
+
+ /**
+ * Create new {@link ExecutableRemoveOperationSupport}.
+ *
+ * @param template must not be {@literal null}.
+ * @throws IllegalArgumentException if template is {@literal null}.
+ */
+ ExecutableRemoveOperationSupport(MongoTemplate template) {
+
+ Assert.notNull(template, "Template must not be null!");
+ this.tempate = template;
+ }
+
+ @Override
+ public RemoveOperation remove(Class domainType) {
+
+ Assert.notNull(domainType, "DomainType must not be null!");
+ return new RemoveOperationSupport<>(tempate, null, domainType, null);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ @RequiredArgsConstructor
+ static class RemoveOperationSupport implements RemoveOperation, RemoveOperationWithCollection {
+
+ private final MongoTemplate template;
+ private final Query query;
+ private final Class domainType;
+ private final String collection;
+
+ @Override
+ public RemoveOperationWithQuery inCollection(String collection) {
+
+ Assert.hasText(collection, "Collection must not be null nor empty!");
+ return new RemoveOperationSupport<>(template, query, domainType, collection);
+ }
+
+ @Override
+ public TerminatingRemoveOperation matching(Query query) {
+
+ Assert.notNull(query, "Query must not be null!");
+ return new RemoveOperationSupport<>(template, query, domainType, collection);
+ }
+
+ @Override
+ public DeleteResult all() {
+
+ String collectionName = StringUtils.hasText(collection) ? collection
+ : template.determineCollectionName(domainType);
+
+ return template.doRemove(collectionName, query != null ? query : new BasicQuery(new Document()), domainType);
+ }
+
+ @Override
+ public List findAndRemove() {
+
+ String collectionName = StringUtils.hasText(collection) ? collection
+ : template.determineCollectionName(domainType);
+
+ return template.doFindAndDelete(collectionName, query != null ? query : new BasicQuery(new Document()),
+ domainType);
+ }
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java
new file mode 100644
index 0000000000..9d57b02f58
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperation.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import java.util.Optional;
+
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+
+import com.mongodb.client.result.UpdateResult;
+
+/**
+ * {@link ExecutableUpdateOperation} allows creation and execution of MongoDB update / findAndModify operations in a
+ * fluent API style.
+ * The starting {@literal domainType} is used for mapping the {@link Query} provided via {@code matching}, as well as
+ * the {@link Update} via {@code apply} into the MongoDB specific representations. The collection to operate on is by
+ * default derived from the initial {@literal domainType} and can be defined there via
+ * {@link org.springframework.data.mongodb.core.mapping.Document}. Using {@code inCollection} allows to override the
+ * collection name for the execution.
+ *
+ *
+ *
+ * update(Jedi.class)
+ * .inCollection("star-wars")
+ * .matching(query(where("firstname").is("luke")))
+ * .apply(new Update().set("lastname", "skywalker"))
+ * .upsert();
+ *
+ *
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+public interface ExecutableUpdateOperation {
+
+ /**
+ * Start creating an update operation for the given {@literal domainType}.
+ *
+ * @param domainType must not be {@literal null}.
+ * @return
+ * @throws IllegalArgumentException if domainType is {@literal null}.
+ */
+ UpdateOperation update(Class domainType);
+
+ interface UpdateOperation
+ extends UpdateOperationWithCollection, UpdateOperationWithQuery, UpdateOperationWithUpdate {
+
+ }
+
+ /**
+ * Declare the {@link Update} to apply.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface UpdateOperationWithUpdate {
+
+ /**
+ * Set the {@link Update} to be applied.
+ *
+ * @param update must not be {@literal null}.
+ * @return new instance of {@link TerminatingUpdateOperation}.
+ * @throws IllegalArgumentException if update is {@literal null}.
+ */
+ TerminatingUpdateOperation apply(Update update);
+ }
+
+ /**
+ * Explicitly define the name of the collection to perform operation in.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface UpdateOperationWithCollection {
+
+ /**
+ * Explicitly set the name of the collection to perform the query on.
+ * Skip this step to use the default collection derived from the domain type.
+ *
+ * @param collection must not be {@literal null} nor {@literal empty}.
+ * @return new instance of {@link UpdateOperationWithCollection}.
+ * @throws IllegalArgumentException if collection is {@literal null}.
+ */
+ UpdateOperationWithQuery inCollection(String collection);
+ }
+
+ /**
+ * Define a filter query for the {@link Update}.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface UpdateOperationWithQuery extends UpdateOperationWithUpdate {
+
+ /**
+ * Filter documents by given {@literal query}.
+ *
+ * @param query must not be {@literal null}.
+ * @return new instance of {@link UpdateOperationWithQuery}.
+ * @throws IllegalArgumentException if query is {@literal null}.
+ */
+ UpdateOperationWithUpdate matching(Query query);
+ }
+
+ /**
+ * Define {@link FindAndModifyOptions}.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface FindAndModifyWithOptions {
+
+ /**
+ * Explicitly define {@link FindAndModifyOptions} for the {@link Update}.
+ *
+ * @param options must not be {@literal null}.
+ * @return new instance of {@link FindAndModifyWithOptions}.
+ * @throws IllegalArgumentException if options is {@literal null}.
+ */
+ TerminatingFindAndModifyOperation withOptions(FindAndModifyOptions options);
+ }
+
+ /**
+ * Trigger findAndModify execution by calling one of the terminating methods.
+ *
+ * @param
+ */
+ interface TerminatingFindAndModifyOperation {
+
+ /**
+ * Find, modify and return the first matching document.
+ *
+ * @return {@link Optional#empty()} if nothing found.
+ */
+ Optional findAndModify();
+ }
+
+ /**
+ * Trigger update execution by calling one of the terminating methods.
+ *
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ interface TerminatingUpdateOperation extends TerminatingFindAndModifyOperation, FindAndModifyWithOptions {
+
+ /**
+ * Update all matching documents in the collection.
+ *
+ * @return never {@literal null}.
+ */
+ UpdateResult all();
+
+ /**
+ * Update the first document in the collection.
+ *
+ * @return never {@literal null}.
+ */
+ UpdateResult first();
+
+ /**
+ * Creates a new document if no documents match the filter query or updates the matching ones.
+ *
+ * @return never {@literal null}.
+ */
+ UpdateResult upsert();
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java
new file mode 100644
index 0000000000..8856776827
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableUpdateOperationSupport.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+import lombok.RequiredArgsConstructor;
+
+import java.util.Optional;
+
+import org.bson.Document;
+import org.springframework.data.mongodb.core.query.BasicQuery;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import com.mongodb.client.result.UpdateResult;
+
+/**
+ * Implementation of {@link ExecutableUpdateOperation}.
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+class ExecutableUpdateOperationSupport implements ExecutableUpdateOperation {
+
+ private final MongoTemplate template;
+
+ /**
+ * Creates new {@link ExecutableUpdateOperationSupport}.
+ *
+ * @param template must not be {@literal null}.
+ */
+ ExecutableUpdateOperationSupport(MongoTemplate template) {
+
+ Assert.notNull(template, "Template must not be null!");
+ this.template = template;
+ }
+
+ @Override
+ public UpdateOperation update(Class domainType) {
+
+ Assert.notNull(domainType, "DomainType must not be null!");
+ return new UpdateOperationSupport(template, null, domainType, null, null, null);
+ }
+
+ /**
+ * @param
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+ @RequiredArgsConstructor
+ static class UpdateOperationSupport implements UpdateOperation, UpdateOperationWithCollection,
+ UpdateOperationWithQuery, TerminatingUpdateOperation {
+
+ private final MongoTemplate template;
+ private final Query query;
+ private final Class domainType;
+ private final Update update;
+ private final String collection;
+ private final FindAndModifyOptions options;
+
+ @Override
+ public TerminatingUpdateOperation apply(Update update) {
+
+ Assert.notNull(update, "Update must not be null!");
+ return new UpdateOperationSupport(template, query, domainType, update, collection, options);
+ }
+
+ @Override
+ public UpdateOperationWithQuery inCollection(String collection) {
+
+ Assert.hasText(collection, "Collection must not be null nor empty!");
+ return new UpdateOperationSupport(template, query, domainType, update, collection, options);
+ }
+
+ @Override
+ public UpdateResult first() {
+ return doUpdate(false, false);
+ }
+
+ @Override
+ public UpdateResult upsert() {
+ return doUpdate(true, true);
+ }
+
+ @Override
+ public Optional findAndModify() {
+
+ String collectionName = StringUtils.hasText(collection) ? collection
+ : template.determineCollectionName(domainType);
+
+ return Optional.ofNullable(template.findAndModify(query != null ? query : new BasicQuery(new Document()), update,
+ options, domainType, collectionName));
+ }
+
+ @Override
+ public UpdateOperationWithUpdate matching(Query query) {
+
+ Assert.notNull(query, "Query must not be null!");
+ return new UpdateOperationSupport(template, query, domainType, update, collection, options);
+ }
+
+ @Override
+ public UpdateResult all() {
+ return doUpdate(true, false);
+ }
+
+ @Override
+ public TerminatingFindAndModifyOperation withOptions(FindAndModifyOptions options) {
+
+ Assert.notNull(options, "Options must not be null!");
+ return new UpdateOperationSupport(template, query, domainType, update, collection, options);
+ }
+
+ private UpdateResult doUpdate(boolean multi, boolean upsert) {
+
+ String collectionName = StringUtils.hasText(collection) ? collection
+ : template.determineCollectionName(domainType);
+
+ Query query = this.query != null ? this.query : new BasicQuery(new Document());
+
+ return template.doUpdate(collectionName, query, update, domainType, upsert, multi);
+ }
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FluentMongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FluentMongoOperations.java
new file mode 100644
index 0000000000..0daa36e6ce
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/FluentMongoOperations.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core;
+
+/**
+ * Stripped down interface providing access to a fluent API that specifies a basic set of MongoDB operations.
+ *
+ * @author Christoph Strobl
+ * @since 2.0
+ */
+public interface FluentMongoOperations extends ExecutableFindOperation, ExecutableInsertOperation,
+ ExecutableUpdateOperation, ExecutableRemoveOperation, ExecutableAggregationOperation {
+
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java
index cf0c737d14..2935e167de 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java
@@ -58,7 +58,7 @@
* @author Thomas Darimont
* @author Maninder Singh
*/
-public interface MongoOperations {
+public interface MongoOperations extends FluentMongoOperations {
/**
* The collection name used for the specified class by this template.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
index ade633bf5f..01f5120f81 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java
@@ -344,10 +344,16 @@ public CloseableIterator stream(final Query query, final Class entityT
*/
@Override
public CloseableIterator stream(final Query query, final Class entityType, final String collectionName) {
+ return doStream(query, entityType, collectionName, entityType);
+ }
+
+ protected CloseableIterator doStream(final Query query, final Class> entityType, final String collectionName,
+ Class returnType) {
Assert.notNull(query, "Query must not be null!");
Assert.notNull(entityType, "Entity type must not be null!");
Assert.hasText(collectionName, "Collection name must not be null or empty!");
+ Assert.notNull(returnType, "ReturnType must not be null!");
return execute(collectionName, new CollectionCallback>() {
@@ -364,7 +370,7 @@ public CloseableIterator doInCollection(MongoCollection collection)
.prepare(collection.find(mappedQuery).projection(mappedFields));
return new CloseableIterableCursorAdapter(cursor, exceptionTranslator,
- new ReadDocumentCallback(mongoConverter, entityType, collectionName));
+ new ReadDocumentCallback(mongoConverter, returnType, collectionName));
}
});
}
@@ -644,17 +650,21 @@ public GeoResults geoNear(NearQuery near, Class entityClass) {
}
@SuppressWarnings("unchecked")
- public GeoResults geoNear(NearQuery near, Class entityClass, String collectionName) {
+ public GeoResults geoNear(NearQuery near, Class domainType, String collectionName) {
+ return geoNear(near, domainType, collectionName, domainType);
+ }
+
+ public GeoResults geoNear(NearQuery near, Class> domainType, String collectionName, Class returnType) {
if (near == null) {
throw new InvalidDataAccessApiUsageException("NearQuery must not be null!");
}
- if (entityClass == null) {
+ if (domainType == null) {
throw new InvalidDataAccessApiUsageException("Entity class must not be null!");
}
- String collection = StringUtils.hasText(collectionName) ? collectionName : determineCollectionName(entityClass);
+ String collection = StringUtils.hasText(collectionName) ? collectionName : determineCollectionName(domainType);
Document nearDocument = near.toDocument();
Document command = new Document("geoNear", collection);
@@ -662,12 +672,12 @@ public GeoResults geoNear(NearQuery near, Class entityClass, String co
if (nearDocument.containsKey("query")) {
Document query = (Document) nearDocument.get("query");
- command.put("query", queryMapper.getMappedObject(query, getPersistentEntity(entityClass)));
+ command.put("query", queryMapper.getMappedObject(query, getPersistentEntity(domainType)));
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Executing geoNear using: {} for class: {} in collection: {}", serializeToJsonSafely(command),
- entityClass, collectionName);
+ domainType, collectionName);
}
Document commandResult = executeCommand(command, this.readPreference);
@@ -675,7 +685,7 @@ public GeoResults geoNear(NearQuery near, Class entityClass, String co
results = results == null ? Collections.emptyList() : results;
DocumentCallback> callback = new GeoNearResultDocumentCallback(
- new ReadDocumentCallback(mongoConverter, entityClass, collectionName), near.getMetric());
+ new ReadDocumentCallback(mongoConverter, returnType, collectionName), near.getMetric());
List> result = new ArrayList>(results.size());
int index = 0;
@@ -1776,6 +1786,31 @@ public CloseableIterator doInCollection(MongoCollection collection)
});
}
+ @Override
+ public FindOperation query(Class domainType) {
+ return new ExecutableFindOperationSupport(this).query(domainType);
+ }
+
+ @Override
+ public UpdateOperation update(Class domainType) {
+ return new ExecutableUpdateOperationSupport(this).update(domainType);
+ }
+
+ @Override
+ public RemoveOperation remove(Class domainType) {
+ return new ExecutableRemoveOperationSupport(this).remove(domainType);
+ }
+
+ @Override
+ public AggregationOperation aggregateAndReturn(Class domainType) {
+ return new ExecutableAggregationOperationSupport(this).aggregateAndReturn(domainType);
+ }
+
+ @Override
+ public InsertOperation