Browse Source

Generify the API

issue/5087
Christoph Strobl 3 days ago
parent
commit
7c8efdf280
No known key found for this signature in database
GPG Key ID: E6054036D0C37A4B
  1. 48
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/BulkOperationBase.java
  2. 2
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/BulkOperations.java
  3. 41
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/NamespaceBulkOperations.java
  4. 46
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/NamespacedBulkOperationSupport.java
  5. 116
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsIntegrationTests.java
  6. 146
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/NamespaceBulkOperationIntegrationTests.java

48
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/BulkOperationBase.java

@ -18,6 +18,8 @@ package org.springframework.data.mongodb.core; @@ -18,6 +18,8 @@ package org.springframework.data.mongodb.core;
import java.util.List;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
@ -27,7 +29,7 @@ import org.springframework.data.util.Pair; @@ -27,7 +29,7 @@ import org.springframework.data.util.Pair;
* @author Christoph Strobl
* @since 2026/01
*/
public interface BulkOperationBase {
public interface BulkOperationBase<T> {
/**
* Add a single insert to the bulk operation.
@ -35,7 +37,7 @@ public interface BulkOperationBase { @@ -35,7 +37,7 @@ public interface BulkOperationBase {
* @param documents the document to insert, must not be {@literal null}.
* @return the current {@link BulkOperations} instance with the insert added, will never be {@literal null}.
*/
BulkOperationBase insert(Object documents);
BulkOperationBase<T> insert(T documents);
/**
* Add a list of inserts to the bulk operation.
@ -43,7 +45,11 @@ public interface BulkOperationBase { @@ -43,7 +45,11 @@ public interface BulkOperationBase {
* @param documents List of documents to insert, must not be {@literal null}.
* @return the current {@link BulkOperations} instance with the insert added, will never be {@literal null}.
*/
BulkOperationBase insert(List<? extends Object> documents);
BulkOperationBase<T> insert(List<? extends T> documents);
default BulkOperationBase<T> updateOne(CriteriaDefinition criteria, UpdateDefinition update) {
return updateOne(Query.query(criteria), update);
}
/**
* Add a single update to the bulk operation. For the update request, only the first matching document is updated.
@ -53,7 +59,7 @@ public interface BulkOperationBase { @@ -53,7 +59,7 @@ public interface BulkOperationBase {
* @param update {@link Update} operation to perform, must not be {@literal null}.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
default BulkOperationBase updateOne(Query query, Update update) {
default BulkOperationBase<T> updateOne(Query query, Update update) {
return updateOne(query, (UpdateDefinition) update);
}
@ -66,7 +72,7 @@ public interface BulkOperationBase { @@ -66,7 +72,7 @@ public interface BulkOperationBase {
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
* @since 4.1
*/
BulkOperationBase updateOne(Query query, UpdateDefinition update);
BulkOperationBase<T> updateOne(Query query, UpdateDefinition update);
/**
* Add a list of updates to the bulk operation. For each update request, only the first matching document is updated.
@ -74,8 +80,11 @@ public interface BulkOperationBase { @@ -74,8 +80,11 @@ public interface BulkOperationBase {
* @param updates Update operations to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperationBase updateOne(List<Pair<Query, UpdateDefinition>> updates);
BulkOperationBase<T> updateOne(List<Pair<Query, UpdateDefinition>> updates);
default BulkOperationBase<T> updateMulti(CriteriaDefinition criteria, UpdateDefinition update) {
return updateMulti(Query.query(criteria), update);
}
/**
* Add a single update to the bulk operation. For the update request, all matching documents are updated.
*
@ -83,7 +92,7 @@ public interface BulkOperationBase { @@ -83,7 +92,7 @@ public interface BulkOperationBase {
* @param update Update operation to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
default BulkOperationBase updateMulti(Query query, Update update) {
default BulkOperationBase<T> updateMulti(Query query, Update update) {
return updateMulti(query, (UpdateDefinition) update);
}
@ -95,7 +104,7 @@ public interface BulkOperationBase { @@ -95,7 +104,7 @@ public interface BulkOperationBase {
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
* @since 4.1
*/
BulkOperationBase updateMulti(Query query, UpdateDefinition update);
BulkOperationBase<T> updateMulti(Query query, UpdateDefinition update);
/**
* Add a list of updates to the bulk operation. For each update request, all matching documents are updated.
@ -103,7 +112,11 @@ public interface BulkOperationBase { @@ -103,7 +112,11 @@ public interface BulkOperationBase {
* @param updates Update operations to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperationBase updateMulti(List<Pair<Query, UpdateDefinition>> updates);
BulkOperationBase<T> updateMulti(List<Pair<Query, UpdateDefinition>> updates);
default BulkOperationBase<T> upsert(CriteriaDefinition criteria, UpdateDefinition update) {
return upsert(Query.query(criteria), update);
}
/**
* Add a single upsert to the bulk operation. An upsert is an update if the set of matching documents is not empty,
@ -113,7 +126,7 @@ public interface BulkOperationBase { @@ -113,7 +126,7 @@ public interface BulkOperationBase {
* @param update Update operation to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
default BulkOperationBase upsert(Query query, Update update) {
default BulkOperationBase<T> upsert(Query query, Update update) {
return upsert(query, (UpdateDefinition) update);
}
@ -126,7 +139,7 @@ public interface BulkOperationBase { @@ -126,7 +139,7 @@ public interface BulkOperationBase {
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
* @since 4.1
*/
BulkOperationBase upsert(Query query, UpdateDefinition update);
BulkOperationBase<T> upsert(Query query, UpdateDefinition update);
/**
* Add a list of upserts to the bulk operation. An upsert is an update if the set of matching documents is not empty,
@ -135,15 +148,18 @@ public interface BulkOperationBase { @@ -135,15 +148,18 @@ public interface BulkOperationBase {
* @param updates Updates/insert operations to perform.
* @return the current {@link BulkOperations} instance with the update added, will never be {@literal null}.
*/
BulkOperationBase upsert(List<Pair<Query, Update>> updates);
BulkOperationBase<T> upsert(List<Pair<Query, Update>> updates);
default BulkOperationBase<T> remove(CriteriaDefinition criteria) {
return remove(Query.query(criteria));
}
/**
* Add a single remove operation to the bulk operation.
*
* @param remove the {@link Query} to select the documents to be removed, must not be {@literal null}.
* @return the current {@link BulkOperations} instance with the removal added, will never be {@literal null}.
*/
BulkOperationBase remove(Query remove);
BulkOperationBase<T> remove(Query remove);
/**
* Add a list of remove operations to the bulk operation.
@ -151,7 +167,7 @@ public interface BulkOperationBase { @@ -151,7 +167,7 @@ public interface BulkOperationBase {
* @param removes the remove operations to perform, must not be {@literal null}.
* @return the current {@link BulkOperations} instance with the removal added, will never be {@literal null}.
*/
BulkOperationBase remove(List<Query> removes);
BulkOperationBase<T> remove(List<Query> removes);
/**
* Add a single replace operation to the bulk operation.
@ -162,7 +178,7 @@ public interface BulkOperationBase { @@ -162,7 +178,7 @@ public interface BulkOperationBase {
* @return the current {@link BulkOperations} instance with the replacement added, will never be {@literal null}.
* @since 2.2
*/
default BulkOperationBase replaceOne(Query query, Object replacement) {
default BulkOperationBase<T> replaceOne(Query query, Object replacement) {
return replaceOne(query, replacement, FindAndReplaceOptions.empty());
}
@ -176,6 +192,6 @@ public interface BulkOperationBase { @@ -176,6 +192,6 @@ public interface BulkOperationBase {
* @return the current {@link BulkOperations} instance with the replacement added, will never be {@literal null}.
* @since 2.2
*/
BulkOperationBase replaceOne(Query query, Object replacement, FindAndReplaceOptions options);
BulkOperationBase<T> replaceOne(Query query, Object replacement, FindAndReplaceOptions options);
}

2
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/BulkOperations.java

@ -49,7 +49,7 @@ import com.mongodb.bulk.BulkWriteResult; @@ -49,7 +49,7 @@ import com.mongodb.bulk.BulkWriteResult;
* @author Minsu Kim
* @since 1.9
*/
public interface BulkOperations extends BulkOperationBase {
public interface BulkOperations extends BulkOperationBase<Object> {
/**
* Mode for bulk operation.

41
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/NamespaceBulkOperations.java

@ -32,6 +32,7 @@ @@ -32,6 +32,7 @@
package org.springframework.data.mongodb.core;
import java.util.List;
import java.util.function.Consumer;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
@ -44,55 +45,59 @@ import com.mongodb.client.model.bulk.ClientBulkWriteResult; @@ -44,55 +45,59 @@ import com.mongodb.client.model.bulk.ClientBulkWriteResult;
* @author Christoph Strobl
* @since 2026/01
*/
public interface NamespaceBulkOperations extends BulkOperationBase {
public interface NamespaceBulkOperations {
NamespaceAwareBulkOperations inCollection(Class<?> type);
<S> NamespaceAwareBulkOperations<S> inCollection(Class<S> type);
NamespaceAwareBulkOperations inCollection(String collection);
<S> NamespaceAwareBulkOperations<S> inCollection(Class<S> type, Consumer<BulkOperationBase<S>> bulkActions);
NamespaceAwareBulkOperations<Object> inCollection(String collection);
NamespaceAwareBulkOperations<Object> inCollection(String collection, Consumer<BulkOperationBase<Object>> bulkActions);
NamespaceBulkOperations switchDatabase(String databaseName);
interface NamespaceAwareBulkOperations extends BulkOperationBase, NamespaceBulkOperations {
ClientBulkWriteResult execute();
NamespaceAwareBulkOperations insert(Object document);
interface NamespaceAwareBulkOperations<S> extends BulkOperationBase<S>, NamespaceBulkOperations {
NamespaceAwareBulkOperations<S> insert(S document);
@Override
NamespaceAwareBulkOperations insert(List<? extends Object> documents);
NamespaceAwareBulkOperations<S> insert(List<? extends S> documents);
@Override
NamespaceAwareBulkOperations updateOne(Query query, UpdateDefinition update);
NamespaceAwareBulkOperations<S> updateOne(Query query, UpdateDefinition update);
@Override
NamespaceAwareBulkOperations updateOne(List<Pair<Query, UpdateDefinition>> updates);
NamespaceAwareBulkOperations<S> updateOne(List<Pair<Query, UpdateDefinition>> updates);
@Override
NamespaceAwareBulkOperations updateMulti(Query query, UpdateDefinition update);
NamespaceAwareBulkOperations<S> updateMulti(Query query, UpdateDefinition update);
@Override
NamespaceAwareBulkOperations updateMulti(List<Pair<Query, UpdateDefinition>> updates);
NamespaceAwareBulkOperations<S> updateMulti(List<Pair<Query, UpdateDefinition>> updates);
@Override
NamespaceAwareBulkOperations upsert(Query query, UpdateDefinition update);
NamespaceAwareBulkOperations<S> upsert(Query query, UpdateDefinition update);
@Override
NamespaceAwareBulkOperations upsert(List<Pair<Query, Update>> updates);
NamespaceAwareBulkOperations<S> upsert(List<Pair<Query, Update>> updates);
@Override
NamespaceAwareBulkOperations remove(Query remove);
NamespaceAwareBulkOperations<S> remove(Query remove);
@Override
NamespaceAwareBulkOperations remove(List<Query> removes);
NamespaceAwareBulkOperations<S> remove(List<Query> removes);
@Override
NamespaceAwareBulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options);
NamespaceAwareBulkOperations<S> replaceOne(Query query, Object replacement, FindAndReplaceOptions options);
@Override
default NamespaceAwareBulkOperations upsert(Query query, Update update) {
default NamespaceAwareBulkOperations<S> upsert(Query query, Update update) {
upsert(query, (UpdateDefinition) update);
return this;
}
ClientBulkWriteResult execute();
}
// NamespacedBulkOperations<Object> inNamespace(Namespace namespace);

46
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/NamespacedBulkOperationSupport.java

@ -35,6 +35,7 @@ import java.util.ArrayList; @@ -35,6 +35,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.bson.Document;
import org.jspecify.annotations.Nullable;
@ -76,7 +77,7 @@ import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateOneOptions; @@ -76,7 +77,7 @@ import com.mongodb.internal.client.model.bulk.ConcreteClientUpdateOneOptions;
/**
* @author Christoph Strobl
*/
class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
class NamespacedBulkOperationSupport<T> implements NamespaceAwareBulkOperations<T> {
private final BulkMode bulkMode;
private final NamespacedBulkOperationContext ctx;
@ -94,7 +95,7 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations { @@ -94,7 +95,7 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
}
@Override
public NamespaceAwareBulkOperations insert(Object document) {
public NamespaceAwareBulkOperations<T> insert(T document) {
Assert.notNull(document, "Document must not be null");
@ -108,20 +109,20 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations { @@ -108,20 +109,20 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
}
@Override
public NamespaceAwareBulkOperations insert(List<?> documents) {
public NamespaceAwareBulkOperations<T> insert(List<? extends T> documents) {
documents.forEach(this::insert);
return this;
}
@Override
public NamespaceAwareBulkOperations updateOne(Query query, UpdateDefinition update) {
public NamespaceAwareBulkOperations<T> updateOne(Query query, UpdateDefinition update) {
update(currentNamespace, query, update, false, false);
return this;
}
@Override
public NamespaceAwareBulkOperations updateOne(List<Pair<Query, UpdateDefinition>> updates) {
public NamespaceAwareBulkOperations<T> updateOne(List<Pair<Query, UpdateDefinition>> updates) {
for (Pair<Query, UpdateDefinition> update : updates) {
update(currentNamespace, update.getFirst(), update.getSecond(), false, false);
@ -131,14 +132,14 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations { @@ -131,14 +132,14 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
}
@Override
public NamespaceAwareBulkOperations updateMulti(Query query, UpdateDefinition update) {
public NamespaceAwareBulkOperations<T> updateMulti(Query query, UpdateDefinition update) {
update(currentNamespace, query, update, false, true);
return this;
}
@Override
public NamespaceAwareBulkOperations updateMulti(List<Pair<Query, UpdateDefinition>> updates) {
public NamespaceAwareBulkOperations<T> updateMulti(List<Pair<Query, UpdateDefinition>> updates) {
for (Pair<Query, UpdateDefinition> update : updates) {
update(currentNamespace, update.getFirst(), update.getSecond(), false, true);
@ -148,13 +149,13 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations { @@ -148,13 +149,13 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
}
@Override
public NamespaceAwareBulkOperations upsert(Query query, UpdateDefinition update) {
public NamespaceAwareBulkOperations<T> upsert(Query query, UpdateDefinition update) {
update(currentNamespace, query, update, true, true);
return this;
}
@Override
public NamespaceAwareBulkOperations upsert(List<Pair<Query, Update>> updates) {
public NamespaceAwareBulkOperations<T> upsert(List<Pair<Query, Update>> updates) {
for (Pair<Query, Update> update : updates) {
upsert(update.getFirst(), update.getSecond());
}
@ -162,7 +163,7 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations { @@ -162,7 +163,7 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
}
@Override
public NamespaceAwareBulkOperations remove(Query query) {
public NamespaceAwareBulkOperations<T> remove(Query query) {
ClientDeleteManyOptions deleteOptions = ClientDeleteManyOptions.clientDeleteManyOptions();
query.getCollation().map(Collation::toMongoCollation).ifPresent(deleteOptions::collation);
@ -174,7 +175,7 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations { @@ -174,7 +175,7 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
}
@Override
public NamespaceAwareBulkOperations remove(List<Query> removes) {
public NamespaceAwareBulkOperations<T> remove(List<Query> removes) {
for (Query query : removes) {
remove(query);
}
@ -183,7 +184,7 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations { @@ -183,7 +184,7 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
}
@Override
public NamespaceAwareBulkOperations replaceOne(Query query, Object replacement, FindAndReplaceOptions options) {
public NamespaceAwareBulkOperations<T> replaceOne(Query query, Object replacement, FindAndReplaceOptions options) {
Assert.notNull(query, "Query must not be null");
Assert.notNull(replacement, "Replacement must not be null");
Assert.notNull(options, "Options must not be null");
@ -223,11 +224,29 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations { @@ -223,11 +224,29 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
}
@Override
public NamespaceAwareBulkOperations inCollection(Class<?> type) {
public <S> NamespaceAwareBulkOperations<S> inCollection(Class<S> type, Consumer<BulkOperationBase<S>> bulkActions) {
NamespaceAwareBulkOperations<S> ops = inCollection(type);
bulkActions.accept(ops);
return ops;
}
@Override
@SuppressWarnings("unchecked")
public NamespaceAwareBulkOperations<Object> inCollection(String collection,
Consumer<BulkOperationBase<Object>> bulkActions) {
NamespaceAwareBulkOperations<Object> ops = inCollection(collection);
bulkActions.accept(ops);
return ops;
}
@Override
@SuppressWarnings("unchecked")
public <S> NamespaceAwareBulkOperations<S> inCollection(Class<S> type) {
return inCollection(operations.getCollectionName(type));
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public NamespaceAwareBulkOperations inCollection(String collection) {
this.currentNamespace = new Namespace(currentNamespace.database(), collection);
@ -281,7 +300,6 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations { @@ -281,7 +300,6 @@ class NamespacedBulkOperationSupport implements NamespaceAwareBulkOperations {
* @param update the {@link Update} to perform, must not be {@literal null}.
* @param upsert whether to upsert.
* @param multi whether to issue a multi-update.
* @return the {@link BulkOperations} with the update registered.
*/
private void update(Namespace namespace, Query query, UpdateDefinition update, boolean upsert, boolean multi) {

116
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DefaultBulkOperationsIntegrationTests.java

@ -19,7 +19,6 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -19,7 +19,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.data.domain.Sort.Direction.DESC;
import static org.springframework.data.mongodb.core.Namespace.namespace;
import java.util.ArrayList;
import java.util.Arrays;
@ -27,15 +26,12 @@ import java.util.List; @@ -27,15 +26,12 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.bson.BsonBoolean;
import org.bson.BsonInt32;
import org.bson.Document;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.data.domain.Score;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.BulkOperationException;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
@ -56,15 +52,10 @@ import org.springframework.data.mongodb.test.util.Template; @@ -56,15 +52,10 @@ import org.springframework.data.mongodb.test.util.Template;
import org.springframework.data.util.Pair;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.MongoNamespace;
import com.mongodb.WriteConcern;
import com.mongodb.bulk.BulkWriteResult;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.internal.client.model.bulk.ConcreteClientInsertOneModel;
import com.mongodb.internal.client.model.bulk.ConcreteClientNamespacedInsertOneModel;
import com.mongodb.internal.operation.ClientBulkWriteOperation;
/**
* Integration tests for {@link DefaultBulkOperations}.
@ -374,113 +365,6 @@ public class DefaultBulkOperationsIntegrationTests { @@ -374,113 +365,6 @@ public class DefaultBulkOperationsIntegrationTests {
assertThat(raw).containsEntry("value", target.value);
}
@Test // GH-5087
void bulkWriteMultipleCollections() {
BaseDoc doc1 = new BaseDoc();
doc1.id = "id-doc1";
doc1.value = "value-doc1";
BaseDoc doc2 = new BaseDoc();
doc2.id = "id-doc2";
doc2.value = "value-doc2";
NamespaceBulkOperations bulkOps = operations.bulkOps(BulkMode.ORDERED);
bulkOps
.inCollection(BaseDoc.class)
.insert(doc1)
.insert(doc2)
.upsert(where("_id", "id-doc3"), new Update().set("value", "upserted"))
.inCollection(SpecialDoc.class)
.insert(new SpecialDoc())
.execute();
}
@Test // GH-5087
void apiDesign() {
// NamespaceBulkOperations bulkOps = operations.bulkOps(BulkMode.ORDERED);
// bulkOps
// .inCollection(User.class)
// .insert(List.of(new User()))
// .upsert(Query.query(Criteria.where("name").is("batman")), new Update().set("actor", "..."))
// .switchDatabase("db2")
// .inCollection("sql")
// .insert(List.of(new Document()));
//
//
// NamespaceBulkOperations bulkOps = operations.bulkOps(BulkMode.ORDERED);
// bulkOps
// .inNamespace(namespace("person"))
// .update()
// .one(Criteria.where("name").is("batman"), null)
// .one(Criteria.where("name").is("joker"), null)
// .insert()
// .many(List.of(new Person()))
// .inNamespace(namespace("log"))
// .insert()
// .one(new User())
// .inNamespace(Score.class)
// .insert().many(List.of())
//
// .execute();
// NamespaceBulkOps bulkOps1 = null;
// bulkOps1.inNamespace(namespace("person"))
// .andInNamespace(namespace("log"));
}
// interface NamespaceBulkOps {
// NamespaceBoundBulkOps inNamespace(Namespace namespace);
// CollectionBound
// }
@Test // GH-5087
void exploreItOnClient() {
ClientBulkWriteOperation op = null;
// op.execute()
mongoClient.getDatabase("test").getCollection("pizzas").drop();
mongoClient.getDatabase("test").getCollection("pizzaOrders").drop();
// command:
MongoDatabase db = operations.getDb();
MongoTemplate template = operations;
Document commandDocument = new Document("bulkWrite", new BsonInt32(1)).append("errorsOnly", BsonBoolean.TRUE)
.append("ordered", BsonBoolean.TRUE);
List<Document> bulkOperations = new ArrayList<>();
bulkOperations
.add(Document.parse("{ insert: 0, document: { _id: 5, type: 'sausage', size: 'small', price: 12 } }"));
bulkOperations.add(Document.parse("{ insert: 1, document: { _id: 4, type: 'vegan cheese', number: 16 } }"));
commandDocument.put("ops", bulkOperations);
List<Document> namespaceInfo = new ArrayList<>();
namespaceInfo.add(Document.parse("{ns: '%s.pizzas'}".formatted(template.getDb().getName())));
namespaceInfo.add(Document.parse("{ns: '%s.pizzaOrders'}".formatted(template.getDb().getName())));
commandDocument.put("nsInfo", namespaceInfo);
template.getMongoDatabaseFactory().getMongoDatabase("admin").runCommand(commandDocument);
}
@Test
void hackSomePotentialApi() {
MongoNamespace pizzasNamespace = new MongoNamespace(operations.getDb().getName(), "pizzas");
ConcreteClientNamespacedInsertOneModel insert1 = new ConcreteClientNamespacedInsertOneModel(pizzasNamespace,
new ConcreteClientInsertOneModel(
Document.parse("{ insert: 0, document: { _id: 5, type: 'sausage', size: 'small', price: 12 } }")));
insert1.getNamespace();
insert1.getModel();
}
private void testUpdate(BulkMode mode, boolean multi, int expectedUpdates) {
BulkOperations bulkOps = createBulkOps(mode);

146
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/NamespaceBulkOperationIntegrationTests.java

@ -0,0 +1,146 @@ @@ -0,0 +1,146 @@
/*
* Copyright 2026. 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.
*/
/*
* Copyright 2026 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 static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.test.util.Client;
import org.springframework.data.mongodb.test.util.MongoTestTemplate;
import org.springframework.data.mongodb.test.util.Template;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.bulk.ClientBulkWriteResult;
/**
* @author Christoph Strobl
* @since 2026/01
*/
public class NamespaceBulkOperationIntegrationTests {
static final String COLLECTION_NAME = "bulk_ops";
@Client static MongoClient mongoClient;
@Template(initialEntitySet = BaseDoc.class) //
static MongoTestTemplate operations;
@BeforeEach
public void setUp() {
operations.flushDatabase();
}
@Test // GH-5087
void bulkWriteMultipleCollections() {
/*
NamespaceBulkOperations and NamespaceAwareBulkOperations are new apis that allow to do bulk operations for MongoDB via an api that allows to be used with multiple mongodb collections.
The existing BulkOperations API is tied to a single collection.
You need to create tests for the new API similar to the existing ones in DefaultBulkOperationsIntegrationTests.
The tests need to make sure operations are executed in different collections.
you can see an example of this and how to use the new API in NamespaceBulkOperationIntegrationTests
*/
operations.flushDatabase();
BaseDoc doc1 = new BaseDoc();
doc1.id = "id-doc1";
doc1.value = "value-doc1";
BaseDoc doc2 = new BaseDoc();
doc2.id = "id-doc2";
doc2.value = "value-doc2";
NamespaceBulkOperations bulkOps = operations.bulkOps(BulkMode.ORDERED);
ClientBulkWriteResult result = bulkOps
.inCollection(BaseDoc.class,
ops -> ops.insert(doc1).insert(doc2).upsert(where("_id").is("id-doc3"),
new Update().set("value", "upserted")))
.inCollection(SpecialDoc.class).insert(new SpecialDoc()).execute();
assertThat(result.getUpsertedCount()).isOne();
assertThat(result.getInsertedCount()).isEqualTo(3);
Long inBaseDocCollection = operations.execute(BaseDoc.class, MongoCollection::countDocuments);
Long inSpecialCollection = operations.execute(SpecialDoc.class, MongoCollection::countDocuments);
assertThat(inBaseDocCollection).isEqualTo(3L);
assertThat(inSpecialCollection).isOne();
}
// @Test // GH-5087
// void exploreItOnClient() {
//
// ClientBulkWriteOperation op = null;
// // op.execute()
//
// mongoClient.getDatabase("test").getCollection("pizzas").drop();
// mongoClient.getDatabase("test").getCollection("pizzaOrders").drop();
//
// // command:
// MongoDatabase db = operations.getDb();
// MongoTemplate template = operations;
//
// Document commandDocument = new Document("bulkWrite", new BsonInt32(1)).append("errorsOnly", BsonBoolean.TRUE)
// .append("ordered", BsonBoolean.TRUE);
// List<Document> bulkOperations = new ArrayList<>();
// bulkOperations
// .add(Document.parse("{ insert: 0, document: { _id: 5, type: 'sausage', size: 'small', price: 12 } }"));
// bulkOperations.add(Document.parse("{ insert: 1, document: { _id: 4, type: 'vegan cheese', number: 16 } }"));
// commandDocument.put("ops", bulkOperations);
//
// List<Document> namespaceInfo = new ArrayList<>();
// namespaceInfo.add(Document.parse("{ns: '%s.pizzas'}".formatted(template.getDb().getName())));
// namespaceInfo.add(Document.parse("{ns: '%s.pizzaOrders'}".formatted(template.getDb().getName())));
// commandDocument.put("nsInfo", namespaceInfo);
//
// template.getMongoDatabaseFactory().getMongoDatabase("admin").runCommand(commandDocument);
// }
//
// @Test
// void hackSomePotentialApi() {
//
// MongoNamespace pizzasNamespace = new MongoNamespace(operations.getDb().getName(), "pizzas");
//
// ConcreteClientNamespacedInsertOneModel insert1 = new ConcreteClientNamespacedInsertOneModel(pizzasNamespace,
// new ConcreteClientInsertOneModel(
// Document.parse("{ insert: 0, document: { _id: 5, type: 'sausage', size: 'small', price: 12 } }")));
// insert1.getNamespace();
// insert1.getModel();
// }
}
Loading…
Cancel
Save