diff --git a/core/src/main/java/dev/morphia/mapping/experimental/CollectionReference.java b/core/src/main/java/dev/morphia/mapping/experimental/CollectionReference.java index b988cc3ac2e..8767ba84f77 100644 --- a/core/src/main/java/dev/morphia/mapping/experimental/CollectionReference.java +++ b/core/src/main/java/dev/morphia/mapping/experimental/CollectionReference.java @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -28,9 +29,9 @@ */ @SuppressWarnings({"rawtypes", "unchecked"}) public abstract class CollectionReference extends MorphiaReference { + private final Map> collections = new HashMap<>(); private EntityModel entityModel; private List ids; - private final Map> collections = new HashMap<>(); CollectionReference(Datastore datastore, EntityModel entityModel, List ids) { super(datastore); @@ -47,11 +48,6 @@ public abstract class CollectionReference extends MorphiaR } } - abstract void setValues(List ids); - - protected CollectionReference() { - } - static void collate(EntityModel valueType, Map> collections, Object o) { final String collectionName; @@ -72,6 +68,9 @@ static List register(Map> collections, String name) { return collections.computeIfAbsent(name, k -> new ArrayList<>()); } + protected CollectionReference() { + } + /** * Gets the referenced entities. This may require at least one request to the server. * @@ -79,11 +78,6 @@ static List register(Map> collections, String name) { */ public abstract C get(); - @Override - public Class getType() { - return (Class) entityModel.getType(); - } - @Override public List getIds() { List ids = new ArrayList<>(this.ids); @@ -96,6 +90,11 @@ public List getIds() { return ids; } + @Override + public Class getType() { + return (Class) entityModel.getType(); + } + @Override final List getId(Mapper mapper, Datastore datastore, EntityModel entityModel) { if (ids == null) { @@ -106,6 +105,10 @@ final List getId(Mapper mapper, Datastore datastore, EntityModel entityM return ids; } + abstract Collection getValues(); + + abstract void setValues(List ids); + private List extractIds(List list) { List ids = new ArrayList<>(); list.forEach(i -> { @@ -149,8 +152,6 @@ final List find() { return values; } - abstract Collection getValues(); - Map query(String collection, List collectionIds) { final Map idMap = new HashMap<>(); @@ -162,7 +163,7 @@ Map query(String collection, List collectionIds) { idMap.put(getDatastore().getMapper().getId(entity), entity); } - if (!ignoreMissing() && idMap.size() != collectionIds.size()) { + if (!ignoreMissing() && idMap.size() != new HashSet<>(collectionIds).size()) { throw new ReferenceException( Sofia.missingReferencedEntities(entityModel.getType().getSimpleName())); diff --git a/core/src/test/java/dev/morphia/test/mapping/lazy/TestLazyIdOnlyIgnoreMissing.java b/core/src/test/java/dev/morphia/test/mapping/lazy/TestLazyIdOnlyIgnoreMissing.java index e5aa2cfe0c5..0d5b7ad0a53 100644 --- a/core/src/test/java/dev/morphia/test/mapping/lazy/TestLazyIdOnlyIgnoreMissing.java +++ b/core/src/test/java/dev/morphia/test/mapping/lazy/TestLazyIdOnlyIgnoreMissing.java @@ -1,16 +1,39 @@ package dev.morphia.test.mapping.lazy; import dev.morphia.Datastore; +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; import dev.morphia.annotations.Reference; import dev.morphia.test.mapping.ProxyTestBase; import dev.morphia.test.models.TestEntity; import org.bson.types.ObjectId; import org.testng.annotations.Test; +import java.util.List; +import java.util.Objects; + import static dev.morphia.query.experimental.filters.Filters.eq; +import static org.testng.Assert.assertEquals; @Test(groups = "references") public class TestLazyIdOnlyIgnoreMissing extends ProxyTestBase { + @Test + public void testDuplicatesInList() { + getMapper().map(ListReferences.class, ReferencedEntity.class); + ListReferences references = new ListReferences(); + ReferencedEntity entity1 = new ReferencedEntity(); + entity1.foo = "1"; + ReferencedEntity entity2 = new ReferencedEntity(); + entity2.foo = "2"; + getDs().save(List.of(entity1, entity2)); + references.list = List.of(entity1, entity2, entity1); + getDs().save(references); + + ListReferences first = getDs().find(ListReferences.class).first(); + + assertEquals(first, references); + } + public void testSaveAfterReferentIsGone() { checkForProxyTypes(); @@ -47,6 +70,38 @@ public void testSaveAfterReferentIsGone() { datastore.save(root); } + @Entity + private static class ListReferences { + @Id + private ObjectId id; + @Reference + List list; + + private ListReferences() { + } + + private ListReferences(List list) { + this.list = list; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ListReferences)) { + return false; + } + ListReferences that = (ListReferences) o; + return Objects.equals(list, that.list) && Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(list, id); + } + } + public static class ReferencedEntity extends TestEntity { private String foo; @@ -57,6 +112,24 @@ public String getFoo() { public void setFoo(String string) { foo = string; } + + @Override + public int hashCode() { + return Objects.hash(id, foo); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ReferencedEntity)) { + return false; + } + ReferencedEntity that = (ReferencedEntity) o; + return Objects.equals(id, that.id) + && Objects.equals(foo, that.foo); + } } public static class RootEntity extends TestEntity {