package org.mongodb.morphia.mapping; import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.DBCollection; import com.mongodb.DBObject; import org.bson.types.ObjectId; import org.junit.Assert; import org.junit.Test; import org.mongodb.morphia.Datastore; import org.mongodb.morphia.Key; import org.mongodb.morphia.annotations.Embedded; import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Id; import org.mongodb.morphia.annotations.Reference; import org.mongodb.morphia.mapping.lazy.ProxyTestBase; import org.mongodb.morphia.query.MorphiaKeyIterator; import org.mongodb.morphia.query.Query; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mongodb.morphia.mapping.lazy.LazyFeatureDependencies.testDependencyFullFilled; /** * @author Gene Trog, (eternal0@github.com) */ public class ReferenceTest extends ProxyTestBase { @Test public void testComplexIds() { Complex complex = new Complex(new ChildId("Bob", 67), "Kelso"); List<Complex> list = asList(new Complex(new ChildId("Turk", 27), "Turk"), new Complex(new ChildId("JD", 26), "Dorian"), new Complex(new ChildId("Carla", 29), "Espinosa")); List<Complex> lazyList = asList(new Complex(new ChildId("Bippity", 67), "Boppity"), new Complex(new ChildId("Cinder", 22), "Ella"), new Complex(new ChildId("Prince", 29), "Charming")); ComplexParent parent = new ComplexParent(); parent.complex = complex; parent.list = list; parent.lazyList = lazyList; getDs().save(complex); getDs().save(list); getDs().save(lazyList); getDs().save(parent); ComplexParent complexParent = getDs().get(ComplexParent.class, parent.id); assertEquals(parent, complexParent); } @Test public void testFindByEntityReference() { final Ref ref = new Ref("refId"); getDs().save(ref); final Container container = new Container(); container.singleRef = ref; getDs().save(container); Assert.assertNotNull(getDs().find(Container.class).filter("singleRef", ref).get()); } @Test public void testIdOnlyReferences() { final List<Ref> refs = asList(new Ref("foo"), new Ref("bar"), new Ref("baz")); final Container c = new Container(refs); // test that we can save it final Key<Container> key = getDs().save(c); getDs().save(refs); // ensure that we're not using DBRef final DBCollection collection = getDs().getCollection(Container.class); final DBObject persisted = collection.findOne(key.getId()); assertNotNull(persisted); assertEquals("foo", persisted.get("singleRef")); assertEquals("foo", persisted.get("lazySingleRef")); final BasicDBList expectedList = new BasicDBList(); expectedList.add("foo"); expectedList.add("bar"); expectedList.add("baz"); assertEquals(expectedList, persisted.get("collectionRef")); assertEquals(expectedList, persisted.get("lazyCollectionRef")); final DBObject expectedMap = new BasicDBObject(); expectedMap.put("0", "foo"); expectedMap.put("1", "bar"); expectedMap.put("2", "baz"); assertEquals(expectedMap, persisted.get("mapRef")); assertEquals(expectedMap, persisted.get("lazyMapRef")); // ensure that we can retrieve it final Container retrieved = getDs().getByKey(Container.class, key); assertEquals(refs.get(0), retrieved.getSingleRef()); if (testDependencyFullFilled()) { assertIsProxy(retrieved.getLazySingleRef()); } assertEquals(refs.get(0), unwrap(retrieved.getLazySingleRef())); final List<Ref> expectedRefList = new ArrayList<Ref>(); final Map<Integer, Ref> expectedRefMap = new LinkedHashMap<Integer, Ref>(); for (int i = 0; i < refs.size(); i++) { expectedRefList.add(refs.get(i)); expectedRefMap.put(i, refs.get(i)); } assertEquals(expectedRefList, retrieved.getCollectionRef()); assertEquals(expectedRefList, unwrapList(retrieved.getLazyCollectionRef())); assertEquals(expectedRefMap, retrieved.getMapRef()); assertEquals(expectedRefMap, unwrapMap(retrieved.getLazyMapRef())); } @Test public void testNullReferences() { Container container = new Container(); container.lazyMapRef = null; container.singleRef = null; container.lazySingleRef = null; container.collectionRef = null; container.lazyCollectionRef = null; container.mapRef = null; container.lazyMapRef = null; getMorphia().getMapper().getOptions().setStoreNulls(true); getDs().save(container); allNull(container); getMorphia().getMapper().getOptions().setStoreNulls(false); getDs().save(container); allNull(container); } @Test public void testReferenceQueryWithoutValidation() { Ref ref = new Ref("no validation"); getDs().save(ref); final Container container = new Container(singletonList(ref)); getDs().save(container); final Query<Container> query = getDs().find(Container.class) .disableValidation() .field("singleRef").equal(ref); Assert.assertNotNull(query.get()); } @Test public void testReferencesWithoutMapping() { Child child1 = new Child(); getDs().save(child1); Parent parent1 = new Parent(); parent1.children.add(child1); getDs().save(parent1); List<Parent> parentList = getDs().find(Parent.class).asList(); Assert.assertEquals(1, parentList.size()); // reset Datastore to reset internal Mapper cache, so Child class // already cached by previous save is cleared Datastore localDs = getMorphia().createDatastore(getMongoClient(), new Mapper(), getDb().getName()); parentList = localDs.find(Parent.class).asList(); Assert.assertEquals(1, parentList.size()); } @Test public void testFetchKeys() { List<Complex> list = asList(new Complex(new ChildId("Turk", 27), "Turk"), new Complex(new ChildId("JD", 26), "Dorian"), new Complex(new ChildId("Carla", 29), "Espinosa")); getDs().save(list); MorphiaKeyIterator<Complex> keys = getDs().find(Complex.class).fetchKeys(); assertTrue(keys.hasNext()); assertEquals(list.get(0).getId(), keys.next().getId()); assertEquals(list.get(1).getId(), keys.next().getId()); assertEquals(list.get(2).getId(), keys.next().getId()); assertFalse(keys.hasNext()); } @Test public void testFetchEmptyEntities() { List<Complex> list = asList(new Complex(new ChildId("Turk", 27), "Turk"), new Complex(new ChildId("JD", 26), "Dorian"), new Complex(new ChildId("Carla", 29), "Espinosa")); getDs().save(list); Iterator<Complex> keys = getDs().find(Complex.class).fetchEmptyEntities(); assertTrue(keys.hasNext()); assertEquals(list.get(0).getId(), keys.next().getId()); assertEquals(list.get(1).getId(), keys.next().getId()); assertEquals(list.get(2).getId(), keys.next().getId()); assertFalse(keys.hasNext()); } private void allNull(final Container container) { Assert.assertNull(container.lazyMapRef); Assert.assertNull(container.singleRef); Assert.assertNull(container.lazySingleRef); Assert.assertNull(container.collectionRef); Assert.assertNull(container.lazyCollectionRef); Assert.assertNull(container.mapRef); } public static class Container { @Id private ObjectId id; @Reference(idOnly = true) private Ref singleRef; @Reference(idOnly = true, lazy = true) private Ref lazySingleRef; @Reference(idOnly = true) private List<Ref> collectionRef; @Reference(idOnly = true, lazy = true) private List<Ref> lazyCollectionRef; @Reference(idOnly = true) private LinkedHashMap<Integer, Ref> mapRef; @Reference(idOnly = true, lazy = true) private LinkedHashMap<Integer, Ref> lazyMapRef; /* required by morphia */ public Container() { } public Container(final List<Ref> refs) { singleRef = refs.get(0); lazySingleRef = refs.get(0); collectionRef = refs; lazyCollectionRef = refs; mapRef = new LinkedHashMap<Integer, Ref>(); lazyMapRef = new LinkedHashMap<Integer, Ref>(); for (int i = 0; i < refs.size(); i++) { mapRef.put(i, refs.get(i)); lazyMapRef.put(i, refs.get(i)); } } ObjectId getId() { return id; } Ref getSingleRef() { return singleRef; } Ref getLazySingleRef() { return lazySingleRef; } List<Ref> getCollectionRef() { return collectionRef; } List<Ref> getLazyCollectionRef() { return lazyCollectionRef; } LinkedHashMap<Integer, Ref> getMapRef() { return mapRef; } LinkedHashMap<Integer, Ref> getLazyMapRef() { return lazyMapRef; } } @Entity public static class Ref { @Id private String id; public Ref() { } public Ref(final String id) { this.id = id; } @Override public int hashCode() { return id.hashCode(); } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof Ref)) { return false; } final Ref ref = (Ref) o; if (id != null ? !id.equals(ref.id) : ref.id != null) { return false; } return true; } @Override public String toString() { return String.format("Ref{id='%s'}", id); } } @Entity(value = "children", noClassnameStored = true) public static class Child { @Id private ObjectId id; } @Entity(value = "parents", noClassnameStored = true) public static class Parent { @Id private ObjectId id; @Reference(lazy = true) private List<Child> children = new ArrayList<Child>(); } private static class ComplexParent { @Id private ObjectId id; @Reference private Complex complex; @Reference private List<Complex> list = new ArrayList<Complex>(); @Reference(lazy = true) private List<Complex> lazyList = new ArrayList<Complex>(); public ObjectId getId() { return id; } public void setId(final ObjectId id) { this.id = id; } public Complex getComplex() { return complex; } public void setComplex(final Complex complex) { this.complex = complex; } public List<Complex> getList() { return list; } public void setList(final List<Complex> list) { this.list = list; } public List<Complex> getLazyList() { return lazyList; } public void setLazyList(final List<Complex> lazyList) { this.lazyList = lazyList; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof ComplexParent)) { return false; } final ComplexParent that = (ComplexParent) o; if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) { return false; } if (getComplex() != null ? !getComplex().equals(that.getComplex()) : that.getComplex() != null) { return false; } if (getList() != null ? !getList().equals(that.getList()) : that.getList() != null) { return false; } return getLazyList() != null ? getLazyList().equals(that.getLazyList()) : that.getLazyList() == null; } @Override public int hashCode() { int result = getId() != null ? getId().hashCode() : 0; result = 31 * result + (getComplex() != null ? getComplex().hashCode() : 0); result = 31 * result + (getList() != null ? getList().hashCode() : 0); result = 31 * result + (getLazyList() != null ? getLazyList().hashCode() : 0); return result; } } @Entity("complex") private static class Complex { @Id @Embedded private ChildId id; private String value; public Complex() { } public Complex(final ChildId id, final String value) { this.id = id; this.value = value; } public ChildId getId() { return id; } public void setId(final ChildId id) { this.id = id; } public String getValue() { return value; } public void setValue(final String value) { this.value = value; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof Complex)) { return false; } final Complex complex = (Complex) o; if (getId() != null ? !getId().equals(complex.getId()) : complex.getId() != null) { return false; } return getValue() != null ? getValue().equals(complex.getValue()) : complex.getValue() == null; } @Override public int hashCode() { int result = getId() != null ? getId().hashCode() : 0; result = 31 * result + (getValue() != null ? getValue().hashCode() : 0); return result; } } @Embedded private static class ChildId { private String name; private int age; public ChildId() { } public ChildId(final String name, final int age) { this.name = name; this.age = age; } public int getAge() { return age; } public String getName() { return name; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof ChildId)) { return false; } final ChildId childId = (ChildId) o; if (getAge() != childId.getAge()) { return false; } return getName() != null ? getName().equals(childId.getName()) : childId.getName() == null; } @Override public int hashCode() { int result = getName() != null ? getName().hashCode() : 0; result = 31 * result + getAge(); return result; } } }