/*
* Copyright 2015-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 static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.bson.Document;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.util.Pair;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.MongoBulkWriteException;
import com.mongodb.WriteConcern;
import com.mongodb.client.MongoCollection;
/**
* Integration tests for {@link DefaultBulkOperations}.
*
* @author Tobias Trelle
* @author Oliver Gierke
* @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
public class DefaultBulkOperationsIntegrationTests {
static final String COLLECTION_NAME = "bulk_ops";
@Autowired MongoOperations operations;
MongoCollection<Document> collection;
@Before
public void setUp() {
this.collection = this.operations.getCollection(COLLECTION_NAME);
this.collection.deleteMany(new Document());
}
@Test(expected = IllegalArgumentException.class) // DATAMONGO-934
public void rejectsNullMongoOperations() {
new DefaultBulkOperations(null, null, COLLECTION_NAME, null);
}
@Test(expected = IllegalArgumentException.class) // DATAMONGO-934
public void rejectsNullCollectionName() {
new DefaultBulkOperations(operations, null, null, null);
}
@Test(expected = IllegalArgumentException.class) // DATAMONGO-934
public void rejectsEmptyCollectionName() {
new DefaultBulkOperations(operations, null, "", null);
}
@Test // DATAMONGO-934
public void insertOrdered() {
List<BaseDoc> documents = Arrays.asList(newDoc("1"), newDoc("2"));
assertThat(createBulkOps(BulkMode.ORDERED).insert(documents).execute().getInsertedCount(), is(2));
}
@Test // DATAMONGO-934
public void insertOrderedFails() {
List<BaseDoc> documents = Arrays.asList(newDoc("1"), newDoc("1"), newDoc("2"));
try {
createBulkOps(BulkMode.ORDERED).insert(documents).execute();
fail();
} catch (MongoBulkWriteException e) {
assertThat(e.getWriteResult().getInsertedCount(), is(1)); // fails after first error
assertThat(e.getWriteErrors(), notNullValue());
assertThat(e.getWriteErrors().size(), is(1));
}
}
@Test // DATAMONGO-934
public void insertUnOrdered() {
List<BaseDoc> documents = Arrays.asList(newDoc("1"), newDoc("2"));
assertThat(createBulkOps(BulkMode.UNORDERED).insert(documents).execute().getInsertedCount(), is(2));
}
@Test // DATAMONGO-934
public void insertUnOrderedContinuesOnError() {
List<BaseDoc> documents = Arrays.asList(newDoc("1"), newDoc("1"), newDoc("2"));
try {
createBulkOps(BulkMode.UNORDERED).insert(documents).execute();
fail();
} catch (MongoBulkWriteException e) {
assertThat(e.getWriteResult().getInsertedCount(), is(2)); // two docs were inserted
assertThat(e.getWriteErrors(), notNullValue());
assertThat(e.getWriteErrors().size(), is(1));
}
}
@Test // DATAMONGO-934
public void upsertDoesUpdate() {
insertSomeDocuments();
com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED).//
upsert(where("value", "value1"), set("value", "value2")).//
execute();
assertThat(result, notNullValue());
assertThat(result.getMatchedCount(), is(2));
assertThat(result.getModifiedCount(), is(2));
assertThat(result.getInsertedCount(), is(0));
assertThat(result.getUpserts(), is(notNullValue()));
assertThat(result.getUpserts().size(), is(0));
}
@Test // DATAMONGO-934
public void upsertDoesInsert() {
com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED).//
upsert(where("_id", "1"), set("value", "v1")).//
execute();
assertThat(result, notNullValue());
assertThat(result.getMatchedCount(), is(0));
assertThat(result.getModifiedCount(), is(0));
assertThat(result.getUpserts(), is(notNullValue()));
assertThat(result.getUpserts().size(), is(1));
}
@Test // DATAMONGO-934
public void updateOneOrdered() {
testUpdate(BulkMode.ORDERED, false, 2);
}
@Test // DATAMONGO-934
public void updateMultiOrdered() {
testUpdate(BulkMode.ORDERED, true, 4);
}
@Test // DATAMONGO-934
public void updateOneUnOrdered() {
testUpdate(BulkMode.UNORDERED, false, 2);
}
@Test // DATAMONGO-934
public void updateMultiUnOrdered() {
testUpdate(BulkMode.UNORDERED, true, 4);
}
@Test // DATAMONGO-934
public void removeOrdered() {
testRemove(BulkMode.ORDERED);
}
@Test // DATAMONGO-934
public void removeUnordered() {
testRemove(BulkMode.UNORDERED);
}
/**
* If working on the same set of documents, only an ordered bulk operation will yield predictable results.
*/
@Test // DATAMONGO-934
public void mixedBulkOrdered() {
com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED).insert(newDoc("1", "v1")).//
updateOne(where("_id", "1"), set("value", "v2")).//
remove(where("value", "v2")).//
execute();
assertThat(result, notNullValue());
assertThat(result.getInsertedCount(), is(1));
assertThat(result.getModifiedCount(), is(1));
assertThat(result.getDeletedCount(), is(1));
}
/**
* If working on the same set of documents, only an ordered bulk operation will yield predictable results.
*/
@Test
@SuppressWarnings("unchecked")
public void mixedBulkOrderedWithList() {
List<BaseDoc> inserts = Arrays.asList(newDoc("1", "v1"), newDoc("2", "v2"), newDoc("3", "v2"));
List<Pair<Query, Update>> updates = Arrays.asList(Pair.of(where("value", "v2"), set("value", "v3")));
List<Query> removes = Arrays.asList(where("_id", "1"));
com.mongodb.bulk.BulkWriteResult result = createBulkOps(BulkMode.ORDERED).insert(inserts).updateMulti(updates)
.remove(removes).execute();
assertThat(result, notNullValue());
assertThat(result.getInsertedCount(), is(3));
assertThat(result.getModifiedCount(), is(2));
assertThat(result.getDeletedCount(), is(1));
}
@Test // DATAMONGO-1534
public void insertShouldConsiderInheritance() {
SpecialDoc specialDoc = new SpecialDoc();
specialDoc.id = "id-special";
specialDoc.value = "normal-value";
specialDoc.specialValue = "special-value";
createBulkOps(BulkMode.ORDERED).insert(Arrays.asList(specialDoc)).execute();
BaseDoc doc = operations.findOne(where("_id", specialDoc.id), BaseDoc.class, COLLECTION_NAME);
assertThat(doc, notNullValue());
assertThat(doc, instanceOf(SpecialDoc.class));
}
private void testUpdate(BulkMode mode, boolean multi, int expectedUpdates) {
BulkOperations bulkOps = createBulkOps(mode);
insertSomeDocuments();
List<Pair<Query, Update>> updates = new ArrayList<Pair<Query, Update>>();
updates.add(Pair.of(where("value", "value1"), set("value", "value3")));
updates.add(Pair.of(where("value", "value2"), set("value", "value4")));
int modifiedCount = multi ? bulkOps.updateMulti(updates).execute().getModifiedCount()
: bulkOps.updateOne(updates).execute().getModifiedCount();
assertThat(modifiedCount, is(expectedUpdates));
}
private void testRemove(BulkMode mode) {
insertSomeDocuments();
List<Query> removes = Arrays.asList(where("_id", "1"), where("value", "value2"));
assertThat(createBulkOps(mode).remove(removes).execute().getDeletedCount(), is(3));
}
private BulkOperations createBulkOps(BulkMode mode) {
DefaultBulkOperations operations = new DefaultBulkOperations(this.operations, mode, COLLECTION_NAME, null);
operations.setDefaultWriteConcern(WriteConcern.ACKNOWLEDGED);
return operations;
}
private void insertSomeDocuments() {
final MongoCollection<Document> coll = operations.getCollection(COLLECTION_NAME);
coll.insertOne(rawDoc("1", "value1"));
coll.insertOne(rawDoc("2", "value1"));
coll.insertOne(rawDoc("3", "value2"));
coll.insertOne(rawDoc("4", "value2"));
}
private static BaseDoc newDoc(String id) {
BaseDoc doc = new BaseDoc();
doc.id = id;
return doc;
}
private static BaseDoc newDoc(String id, String value) {
BaseDoc doc = newDoc(id);
doc.value = value;
return doc;
}
private static Query where(String field, String value) {
return new Query().addCriteria(Criteria.where(field).is(value));
}
private static Update set(String field, String value) {
return new Update().set(field, value);
}
private static Document rawDoc(String id, String value) {
return new Document("_id", id).append("value", value);
}
}