/*
* Copyright (c) 2008-2014 MongoDB, Inc.
*
* 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 com.mongodb.acceptancetest.core;
import com.mongodb.Block;
import com.mongodb.Function;
import com.mongodb.MongoNamespace;
import com.mongodb.WriteConcern;
import com.mongodb.client.DatabaseTestCase;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.RenameCollectionOptions;
import org.bson.BsonArray;
import org.bson.BsonDocument;
import org.bson.BsonInt32;
import org.bson.BsonRegularExpression;
import org.bson.BsonString;
import org.bson.BsonTimestamp;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.types.Binary;
import org.bson.types.Code;
import org.bson.types.CodeWithScope;
import org.bson.types.MaxKey;
import org.bson.types.MinKey;
import org.bson.types.ObjectId;
import org.junit.Ignore;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Documents the basic functionality of MongoDB Collections available via the Java driver.
*/
public class CollectionAcceptanceTest extends DatabaseTestCase {
@Test
public void shouldBeAbleToIterateOverACollection() {
int numberOfDocuments = 10;
initialiseCollectionWithDocuments(numberOfDocuments);
int countOfDocumentsInIterator = 0;
for (final Document document : collection.find()) {
assertThat(document, is(notNullValue()));
countOfDocumentsInIterator++;
}
assertThat(countOfDocumentsInIterator, is(numberOfDocuments));
}
@Test
public void shouldBeAbleToIterateOverACursor() {
int numberOfDocuments = 10;
initialiseCollectionWithDocuments(numberOfDocuments);
MongoCursor<Document> cursor = collection.find().iterator();
int countOfDocumentsInIterator = 0;
try {
while (cursor.hasNext()) {
assertThat(cursor.next(), is(notNullValue()));
countOfDocumentsInIterator++;
}
} finally {
cursor.close();
}
assertThat(countOfDocumentsInIterator, is(numberOfDocuments));
}
@Test
public void shouldCountNumberOfDocumentsInCollection() {
assertThat(collection.count(), is(0L));
collection.insertOne(new Document("myField", "myValue"));
assertThat(collection.count(), is(1L));
}
@Test
public void shouldDropExistingCollection() {
String collectionName = "shouldDropExistingCollection";
database.createCollection(collectionName);
MongoCollection<Document> newCollection = database.getCollection(collectionName);
assertThat(database.listCollectionNames().into(new ArrayList<String>()).contains(collectionName), is(true));
newCollection.drop();
assertThat(database.listCollectionNames().into(new ArrayList<String>()).contains(collectionName), is(false));
}
@Test
public void shouldAcceptDocumentsWithAllValidValueTypes() {
Document doc = new Document();
doc.append("_id", new ObjectId());
doc.append("bool", true);
doc.append("int", 3);
doc.append("long", 5L);
doc.append("str", "Hello MongoDB");
doc.append("double", 1.1);
doc.append("date", new Date());
doc.append("ts", new BsonTimestamp(5, 1));
doc.append("pattern", new BsonRegularExpression("abc"));
doc.append("minKey", new MinKey());
doc.append("maxKey", new MaxKey());
doc.append("js", new Code("code"));
doc.append("jsWithScope", new CodeWithScope("code", new Document()));
doc.append("null", null);
doc.append("binary", new Binary((byte) 42, new byte[]{10, 11, 12}));
doc.append("list", Arrays.asList(7, 8, 9));
doc.append("doc list", Arrays.asList(new Document("x", 1), new Document("x", 2)));
collection.insertOne(doc);
Document found = collection.find().first();
assertNotNull(found);
assertEquals(ObjectId.class, found.get("_id").getClass());
assertEquals(Boolean.class, found.get("bool").getClass());
assertEquals(Integer.class, found.get("int").getClass());
assertEquals(Long.class, found.get("long").getClass());
assertEquals(String.class, found.get("str").getClass());
assertEquals(Double.class, found.get("double").getClass());
assertEquals(Date.class, found.get("date").getClass());
assertEquals(BsonTimestamp.class, found.get("ts").getClass());
assertEquals(BsonRegularExpression.class, found.get("pattern").getClass());
assertEquals(MinKey.class, found.get("minKey").getClass());
assertEquals(MaxKey.class, found.get("maxKey").getClass());
assertEquals(Code.class, found.get("js").getClass());
assertEquals(CodeWithScope.class, found.get("jsWithScope").getClass());
assertNull(found.get("null"));
assertEquals(Binary.class, found.get("binary").getClass());
assertTrue(found.get("list") instanceof List);
assertTrue(found.get("doc list") instanceof List);
}
@Test(expected = IllegalArgumentException.class)
public void shouldRejectDocumentsWithFieldNamesContainingDots() {
collection.insertOne(new Document("x.y", 1));
}
@Test(expected = IllegalArgumentException.class)
public void shouldRejectNestedDocumentsWithFieldNamesContainingDots() {
collection.insertOne(new Document("x", new Document("a.b", 1)));
}
@Test
public void shouldIterateOverAllDocumentsInCollection() {
initialiseCollectionWithDocuments(10);
List<Document> iteratedDocuments = new ArrayList<Document>();
for (final Document cur : collection.find()) {
iteratedDocuments.add(cur);
}
assertEquals(10, iteratedDocuments.size());
}
@Test
public void shouldForEachOverAllDocumentsInCollection() {
initialiseCollectionWithDocuments(10);
final List<Document> iteratedDocuments = new ArrayList<Document>();
collection.find().forEach(new Block<Document>() {
@Override
public void apply(final Document document) {
iteratedDocuments.add(document);
}
});
assertEquals(10, iteratedDocuments.size());
}
@Test
public void shouldAddAllDocumentsIntoListWhenUsingFind() {
initialiseCollectionWithDocuments(10);
List<Document> iteratedDocuments = collection.find().into(new ArrayList<Document>());
assertEquals(10, iteratedDocuments.size());
}
@Test
public void shouldMapAllDocumentsIntoListWhenUsingFind() {
initialiseCollectionWithDocuments(5);
List<String> iteratedDocuments = collection.find().map(new Function<Document, String>() {
@Override
public String apply(final Document document) {
return document.getInteger("_id").toString();
}
}).into(new ArrayList<String>());
Collections.sort(iteratedDocuments);
assertEquals(asList("0", "1", "2", "3", "4"), iteratedDocuments);
}
@Test
public void shouldSortDocumentsWhenUsingAggregate() {
List<Document> documents = insertAggregationTestDocuments();
List<Document> sorted = collection.aggregate(asList(new Document("$sort", new Document("_id", 1)))).into(new ArrayList<Document>());
assertEquals(documents, sorted);
}
@Test
public void shouldSkipDocumentsWhenUsingAggregate() {
List<Document> documents = insertAggregationTestDocuments();
List<Document> skipped = collection.aggregate(asList(new Document("$sort", new Document("_id", 1)),
new Document("$skip", 1))).into(new ArrayList<Document>());
assertEquals(documents.subList(1, 3), skipped);
}
@Test
public void shouldLimitDocumentsWhenUsingAggregate() {
List<Document> documents = insertAggregationTestDocuments();
List<Document> limited = collection.aggregate(asList(new Document("$sort", new Document("_id", 1)),
new Document("$limit", 2))).into(new ArrayList<Document>());
assertEquals(documents.subList(0, 2), limited);
}
@Test
public void shouldFindDocumentsWhenUsingAggregate() {
List<Document> documents = insertAggregationTestDocuments();
List<Document> matched = collection.aggregate(asList(new Document("$match", new Document("_id", "10012"))))
.into(new ArrayList<Document>());
assertEquals(documents.subList(1, 2), matched);
}
@Test
public void shouldProjectDocumentsWhenUsingAggregate() {
insertAggregationTestDocuments();
List<Document> sorted = collection.aggregate(asList(new Document("$sort", new Document("_id", 1)),
new Document("$project", new Document("_id", 0).append("zip", "$_id")))
).into(new ArrayList<Document>());
assertEquals(asList(new Document("zip", "01778"), new Document("zip", "10012"), new Document("zip", "94301")), sorted);
}
@Test
public void shouldUnwindDocumentsWhenUsingAggregate() {
insertAggregationTestDocuments();
List<Document> unwound = collection.aggregate(asList(new Document("$sort", new Document("_id", 1)),
new Document("$project", new Document("_id", 0).append("tags", 1)),
new Document("$unwind", "$tags"))
).into(new ArrayList<Document>());
assertEquals(asList(new Document("tags", "driver"),
new Document("tags", "driver"),
new Document("tags", "SA"),
new Document("tags", "CE"),
new Document("tags", "kernel"),
new Document("tags", "driver"),
new Document("tags", "SA"),
new Document("tags", "CE")),
unwound);
}
@Test
public void shouldGroupDocumentsWhenUsingAggregate() {
insertAggregationTestDocuments();
List<Document> grouped = collection.aggregate(asList(new Document("$sort", new Document("_id", 1)),
new Document("$project", new Document("_id", 0).append("tags", 1)),
new Document("$unwind", "$tags"),
new Document("$group", new Document("_id", "$tags")),
new Document("$sort", new Document("_id", 1))))
.into(new ArrayList<Document>());
assertEquals(asList(new Document("_id", "CE"),
new Document("_id", "SA"),
new Document("_id", "driver"),
new Document("_id", "kernel")),
grouped);
}
@SuppressWarnings("unchecked")
@Test
public void shouldBeAbleToUseBsonValueToDistinctDocumentsOfVaryingTypes() {
List<Object> mixedList = new ArrayList<Object>();
mixedList.add(2);
mixedList.add("d");
mixedList.add(new Document("e", 3));
collection.drop();
collection.insertMany(asList(new Document("id", "a"), new Document("id", 1),
new Document("id", new Document("b", "c")),
new Document("id", new Document("list", mixedList))));
List<BsonValue> distinct = collection.distinct("id", BsonValue.class).into(new ArrayList<BsonValue>());
assertTrue(distinct.containsAll(asList(new BsonString("a"), new BsonInt32(1), new BsonDocument("b", new BsonString("c")),
new BsonDocument("list", new BsonArray(asList(new BsonInt32(2), new BsonString("d"),
new BsonDocument("e", new BsonInt32(3))))))));
distinct = collection.distinct("id", new Document("id", new Document("$ne", 1)), BsonValue.class).into(new ArrayList<BsonValue>());
assertTrue(distinct.containsAll(asList(new BsonString("a"), new BsonDocument("b", new BsonString("c")),
new BsonDocument("list", new BsonArray(asList(new BsonInt32(2), new BsonString("d"),
new BsonDocument("e", new BsonInt32(3))))))));
}
@SuppressWarnings("unchecked")
@Test
public void shouldBeAbleToHandleNullValuesWhenUsingDistinct() {
collection.drop();
collection.insertMany(asList(new Document("id", "a"), new Document("id", "b"), new Document("id", null)));
List<String> distinctStrings = collection.distinct("id", String.class).into(new ArrayList<String>());
assertTrue(distinctStrings.containsAll(asList("a", "b", null)));
collection.drop();
collection.insertMany(asList(new Document("id", 1), new Document("id", 2), new Document("id", null)));
List<Integer> distinctInts = collection.distinct("id", Integer.class).into(new ArrayList<Integer>());
assertTrue(distinctInts.containsAll(asList(1, 2, null)));
}
private void initialiseCollectionWithDocuments(final int numberOfDocuments) {
MongoCollection<Document> collection = database.getCollection(getCollectionName()).withWriteConcern(WriteConcern.ACKNOWLEDGED);
for (int i = 0; i < numberOfDocuments; i++) {
collection.insertOne(new Document("_id", i));
}
}
public List<Document> insertAggregationTestDocuments() {
List<Document> documents = new ArrayList<Document>();
documents.add(new Document("_id", "01778").append("city", "WAYLAND").append("state", "MA").append("population", 13100)
.append("loc", asList(42.3635, 71.3619)).append("tags", asList("driver")));
documents.add(new Document("_id", "10012").append("city", "NEW YORK CITY")
.append("state", "NY")
.append("population", 8245000)
.append("loc", asList(40.7260, 71.3619))
.append("tags", asList("driver", "SA", "CE", "kernel")));
documents.add(new Document("_id", "94301").append("city", "PALO ALTO")
.append("state", "CA")
.append("population", 65412)
.append("loc", asList(37.4419, 122.1419))
.append("tags", asList("driver", "SA", "CE")));
List<Document> shuffledDocuments = new ArrayList<Document>(documents);
Collections.shuffle(shuffledDocuments);
collection.insertMany(shuffledDocuments);
return documents;
}
@Test
public void shouldChangeACollectionNameWhenRenameIsCalled() {
//given
collection.insertOne(new Document("someKey", "someValue"));
assertThat(database.listCollectionNames().into(new ArrayList<String>()).contains(getCollectionName()), is(true));
//when
String newCollectionName = "TheNewCollectionName";
collection.renameCollection(new MongoNamespace(getDatabaseName(), newCollectionName));
//then
assertThat(database.listCollectionNames().into(new ArrayList<String>()).contains(getCollectionName()), is(false));
assertThat(database.listCollectionNames().into(new ArrayList<String>()).contains(newCollectionName), is(true));
MongoCollection<Document> renamedCollection = database.getCollection(newCollectionName);
assertThat("Renamed collection should have the same number of documents as original",
renamedCollection.count(), is(1L));
}
@Test
public void shouldBeAbleToRenameCollectionToAnExistingCollectionNameAndReplaceItWhenDropIsTrue() {
//given
String existingCollectionName = "anExistingCollection";
String keyInOriginalCollection = "someKey";
String valueInOriginalCollection = "someValue";
collection.insertOne(new Document(keyInOriginalCollection, valueInOriginalCollection));
MongoCollection<Document> existingCollection = database.getCollection(existingCollectionName);
String keyInExistingCollection = "aDifferentDocument";
String valueInExistingCollection = "withADifferentValue";
existingCollection.insertOne(new Document(keyInExistingCollection, valueInExistingCollection));
assertThat(database.listCollectionNames().into(new ArrayList<String>()).contains(getCollectionName()), is(true));
assertThat(database.listCollectionNames().into(new ArrayList<String>()).contains(existingCollectionName), is(true));
//when
collection.renameCollection(new MongoNamespace(getDatabaseName(), existingCollectionName),
new RenameCollectionOptions().dropTarget(true));
//then
assertThat(database.listCollectionNames().into(new ArrayList<String>()).contains(getCollectionName()), is(false));
assertThat(database.listCollectionNames().into(new ArrayList<String>()).contains(existingCollectionName), is(true));
MongoCollection<Document> replacedCollection = database.getCollection(existingCollectionName);
assertThat(replacedCollection.find().first().get(keyInExistingCollection), is(nullValue()));
assertThat(replacedCollection.find().first().get(keyInOriginalCollection).toString(), is(valueInOriginalCollection));
}
@Test
@Ignore("not implemented")
public void shouldFailRenameIfSharded() {
}
}