/**
* Copyright (C) 2010 Olafur Gauti Gudmundsson
* <p/>
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.mongodb.morphia.indexes;
import com.mongodb.DBObject;
import com.mongodb.DuplicateKeyException;
import org.bson.types.ObjectId;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.TestBase;
import org.mongodb.morphia.annotations.Embedded;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Field;
import org.mongodb.morphia.annotations.Id;
import org.mongodb.morphia.annotations.Index;
import org.mongodb.morphia.annotations.IndexOptions;
import org.mongodb.morphia.annotations.Indexed;
import org.mongodb.morphia.annotations.Indexes;
import org.mongodb.morphia.annotations.NotSaved;
import org.mongodb.morphia.annotations.Property;
import org.mongodb.morphia.entities.IndexOnValue;
import org.mongodb.morphia.entities.NamedIndexOnValue;
import org.mongodb.morphia.entities.UniqueIndexOnValue;
import org.mongodb.morphia.mapping.MappedClass;
import org.mongodb.morphia.mapping.MappingException;
import org.mongodb.morphia.utils.IndexDirection;
import org.mongodb.morphia.utils.IndexType;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mongodb.morphia.testutil.IndexMatcher.doesNotHaveIndexNamed;
import static org.mongodb.morphia.testutil.IndexMatcher.hasIndexNamed;
/**
* @author Scott Hernandez
*/
public class TestIndexed extends TestBase {
@Before
@Override
public void setUp() {
super.setUp();
getMorphia().map(UniqueIndexOnValue.class).map(IndexOnValue.class).map(NamedIndexOnValue.class);
}
@Test
public void shouldNotCreateAnIndexWhenAnIndexedEntityIsMarkedAsNotSaved() {
// given
getMorphia().map(IndexOnValue.class, NoIndexes.class);
Datastore ds = getDs();
// when
ds.ensureIndexes();
ds.save(new IndexOnValue());
ds.save(new NoIndexes());
// then
List<DBObject> indexes = getDb().getCollection("NoIndexes").getIndexInfo();
assertEquals(1, indexes.size());
}
@Test
public void shouldThrowExceptionWhenAddingADuplicateValueForAUniqueIndex() {
getMorphia().map(UniqueIndexOnValue.class);
getDs().ensureIndexes();
long value = 7L;
try {
final UniqueIndexOnValue entityWithUniqueName = new UniqueIndexOnValue();
entityWithUniqueName.setValue(value);
entityWithUniqueName.setUnique(1);
getDs().save(entityWithUniqueName);
final UniqueIndexOnValue entityWithSameName = new UniqueIndexOnValue();
entityWithSameName.setValue(value);
entityWithSameName.setUnique(2);
getDs().save(entityWithSameName);
Assert.fail("Should have gotten a duplicate key exception");
} catch (Exception ignored) {
}
value = 10L;
try {
final UniqueIndexOnValue first = new UniqueIndexOnValue();
first.setValue(1);
first.setUnique(value);
getDs().save(first);
final UniqueIndexOnValue second = new UniqueIndexOnValue();
second.setValue(2);
second.setUnique(value);
getDs().save(second);
Assert.fail("Should have gotten a duplicate key exception");
} catch (Exception ignored) {
}
}
@Test
public void testCanCreate2dSphereIndexes() {
// given
getMorphia().map(Place.class);
// when
getDs().ensureIndexes();
// then
List<DBObject> indexInfo = getDs().getCollection(Place.class).getIndexInfo();
assertThat(indexInfo.size(), is(2));
assertThat(indexInfo, hasIndexNamed("location_2dsphere"));
}
@Test
public void testCanCreate2dSphereIndexesOnLegacyCoordinatePairs() {
// given
getMorphia().map(LegacyPlace.class);
// when
getDs().ensureIndexes();
// then
List<DBObject> indexInfo = getDs().getCollection(LegacyPlace.class).getIndexInfo();
assertThat(indexInfo, hasIndexNamed("location_2dsphere"));
}
@Test
public void testEmbeddedIndex() {
final MappedClass mc = getMorphia().getMapper().addMappedClass(ContainsIndexedEmbed.class);
assertThat(getDb().getCollection(mc.getCollectionName()).getIndexInfo(), doesNotHaveIndexNamed("e.name_-1"));
getDs().ensureIndexes(ContainsIndexedEmbed.class);
assertThat(getDb().getCollection(mc.getCollectionName()).getIndexInfo(), hasIndexNamed("e.name_-1"));
}
@Test
public void testIndexedEntity() throws Exception {
getDs().ensureIndexes();
assertThat(getDs().getCollection(IndexOnValue.class).getIndexInfo(), hasIndexNamed("value_1"));
getDs().save(new IndexOnValue());
getDs().ensureIndexes();
assertThat(getDs().getCollection(IndexOnValue.class).getIndexInfo(), hasIndexNamed("value_1"));
}
@Test
public void testIndexedRecursiveEntity() throws Exception {
final MappedClass mc = getMorphia().getMapper().getMappedClass(CircularEmbeddedEntity.class);
getDs().ensureIndexes();
assertThat(getDb().getCollection(mc.getCollectionName()).getIndexInfo(), hasIndexNamed("a_1"));
}
@Test
public void testIndexes() {
final MappedClass mc = getMorphia().getMapper().addMappedClass(Ad2.class);
assertThat(getDb().getCollection(mc.getCollectionName()).getIndexInfo(), doesNotHaveIndexNamed("active_1_lastMod_-1"));
getDs().ensureIndexes(Ad2.class);
assertThat(getDb().getCollection(mc.getCollectionName()).getIndexInfo(), hasIndexNamed("active_1_lastMod_-1"));
}
@Test
@SuppressWarnings("deprecation")
public void testMultipleIndexedFields() {
final MappedClass mc = getMorphia().getMapper().getMappedClass(Ad.class);
getMorphia().map(Ad.class);
assertThat(getDb().getCollection(mc.getCollectionName()).getIndexInfo(), doesNotHaveIndexNamed("lastMod_1_active_-1"));
getDs().ensureIndex(Ad.class, "lastMod, -active");
assertThat(getDb().getCollection(mc.getCollectionName()).getIndexInfo(), hasIndexNamed("lastMod_1_active_-1"));
}
@Test
public void testNamedIndexEntity() throws Exception {
getDs().ensureIndexes();
assertThat(getDs().getCollection(NamedIndexOnValue.class).getIndexInfo(), hasIndexNamed("value_ascending"));
}
@Test(expected = DuplicateKeyException.class)
public void testUniqueIndexedEntity() throws Exception {
getDs().ensureIndexes();
assertThat(getDs().getCollection(UniqueIndexOnValue.class).getIndexInfo(), hasIndexNamed("l_ascending"));
getDs().save(new UniqueIndexOnValue("a"));
// this should throw...
getDs().save(new UniqueIndexOnValue("v"));
}
@Test(expected = MappingException.class)
public void testMixedIndexDefinitions() throws Exception {
getMorphia().map(MixedIndexDefinitions.class);
getDs().ensureIndexes(MixedIndexDefinitions.class);
}
@SuppressWarnings("unused")
private static class Place {
@Id
private long id;
@Indexed(IndexDirection.GEO2DSPHERE)
private Object location;
}
@SuppressWarnings("unused")
private static class LegacyPlace {
@Id
private long id;
@Indexed(IndexDirection.GEO2DSPHERE)
private double[] location;
}
private static class Ad {
@Id
private long id;
@Property("lastMod")
@Indexed
private long lastModified;
@Indexed
private boolean active;
}
@Indexes(@Index(fields = {@Field("active"), @Field(value = "lastModified", type = IndexType.DESC)},
options = @IndexOptions(unique = true)))
private static class Ad2 {
@Id
private long id;
@Property("lastMod")
@Indexed
private long lastModified;
@Indexed
private boolean active;
}
@Embedded
private static class IndexedEmbed {
@Indexed(IndexDirection.DESC)
private String name;
}
private static class ContainsIndexedEmbed {
@Id
private ObjectId id;
private IndexedEmbed e;
}
private static class CircularEmbeddedEntity {
@Id
private ObjectId id = new ObjectId();
private String name;
@Indexed
private CircularEmbeddedEntity a;
}
@Entity
private static class NoIndexes {
@Id
private ObjectId id;
@NotSaved
private IndexOnValue indexedClass;
}
@Entity
private static class MixedIndexDefinitions {
@Id
private ObjectId id;
@Indexed(unique = true, options = @IndexOptions(dropDups = true))
private String name;
}
}