From 987461a1b7baa1a2136b0dec9602cd7273a94ae1 Mon Sep 17 00:00:00 2001
From: Christoph Strobl <cstrobl@pivotal.io>
Date: Thu, 17 Nov 2016 12:34:27 +0100
Subject: [PATCH 1/3] DATAMONGO-1509 - Inconsistent type alias placement in
 list of classes.

Prepare issue branch.
---
 pom.xml                                  | 2 +-
 spring-data-mongodb-cross-store/pom.xml  | 4 ++--
 spring-data-mongodb-distribution/pom.xml | 2 +-
 spring-data-mongodb-log4j/pom.xml        | 2 +-
 spring-data-mongodb/pom.xml              | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/pom.xml b/pom.xml
index ff93af8ea6..72a16017f2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
 
 	<groupId>org.springframework.data</groupId>
 	<artifactId>spring-data-mongodb-parent</artifactId>
-	<version>2.0.0.BUILD-SNAPSHOT</version>
+	<version>2.0.0.DATAMONGO-1509-SNAPSHOT</version>
 	<packaging>pom</packaging>
 
 	<name>Spring Data MongoDB</name>
diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml
index 4a49168713..c1c263cbbc 100644
--- a/spring-data-mongodb-cross-store/pom.xml
+++ b/spring-data-mongodb-cross-store/pom.xml
@@ -6,7 +6,7 @@
 	<parent>
 		<groupId>org.springframework.data</groupId>
 		<artifactId>spring-data-mongodb-parent</artifactId>
-		<version>2.0.0.BUILD-SNAPSHOT</version>
+		<version>2.0.0.DATAMONGO-1509-SNAPSHOT</version>
 		<relativePath>../pom.xml</relativePath>
 	</parent>
 
@@ -48,7 +48,7 @@
 		<dependency>
 			<groupId>org.springframework.data</groupId>
 			<artifactId>spring-data-mongodb</artifactId>
-			<version>2.0.0.BUILD-SNAPSHOT</version>
+			<version>2.0.0.DATAMONGO-1509-SNAPSHOT</version>
 		</dependency>
 
 		<!-- reactive -->
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index 750ed23aa8..777490f61a 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -13,7 +13,7 @@
 	<parent>
 		<groupId>org.springframework.data</groupId>
 		<artifactId>spring-data-mongodb-parent</artifactId>
-		<version>2.0.0.BUILD-SNAPSHOT</version>
+		<version>2.0.0.DATAMONGO-1509-SNAPSHOT</version>
 		<relativePath>../pom.xml</relativePath>
 	</parent>
 
diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml
index 50d0a6454a..4335e584fc 100644
--- a/spring-data-mongodb-log4j/pom.xml
+++ b/spring-data-mongodb-log4j/pom.xml
@@ -5,7 +5,7 @@
 	<parent>
 		<groupId>org.springframework.data</groupId>
 		<artifactId>spring-data-mongodb-parent</artifactId>
-		<version>2.0.0.BUILD-SNAPSHOT</version>
+		<version>2.0.0.DATAMONGO-1509-SNAPSHOT</version>
 		<relativePath>../pom.xml</relativePath>
 	</parent>
 
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index c8151d6665..6b8ea508bd 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
 	<parent>
 		<groupId>org.springframework.data</groupId>
 		<artifactId>spring-data-mongodb-parent</artifactId>
-		<version>2.0.0.BUILD-SNAPSHOT</version>
+		<version>2.0.0.DATAMONGO-1509-SNAPSHOT</version>
 		<relativePath>../pom.xml</relativePath>
 	</parent>
 

From 0ee87898e0ca435820c4c7fbdefa75cdf8a3ea1c Mon Sep 17 00:00:00 2001
From: Christoph Strobl <cstrobl@pivotal.io>
Date: Wed, 2 Nov 2016 13:33:54 +0100
Subject: [PATCH 2/3] DATAMONGO-1509 - Write type hint as last element of an
 Document.

Always add type hint as last property of document.
This necessary to assure document equality within mongodb in cases where the query contains full document comparisons. Unfortunately this also might break existing stuff since the order of properties within a Document is changed with this commit.
---
 .../core/convert/MappingMongoConverter.java   | 14 +++++++-------
 .../data/mongodb/core/MongoTemplateTests.java | 19 +++++++++++++++++++
 2 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
index eb01d1dd8e..d3585d0569 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
@@ -359,20 +359,20 @@ public void write(final Object obj, final Bson bson) {
 			return;
 		}
 
-		Class<?> entityType = obj.getClass();
-		boolean handledByCustomConverter = conversions.getCustomWriteTarget(entityType, Document.class) != null;
+		Class<?> entityType = ClassUtils.getUserClass(obj.getClass());
 		TypeInformation<? extends Object> type = ClassTypeInformation.from(entityType);
 
-		if (!handledByCustomConverter && !(bson instanceof Collection)) {
-			typeMapper.writeType(type, bson);
-		}
-
 		Object target = obj instanceof LazyLoadingProxy ? ((LazyLoadingProxy) obj).getTarget() : obj;
 
 		writeInternal(target, bson, type);
 		if (asMap(bson).containsKey("_is") && asMap(bson).get("_id") == null) {
 			removeFromMap(bson, "_id");
 		}
+
+		boolean handledByCustomConverter = conversions.getCustomWriteTarget(entityType, Document.class) != null;
+		if (!handledByCustomConverter && !(bson instanceof Collection)) {
+			typeMapper.writeType(type, bson);
+		}
 	}
 
 	/**
@@ -529,12 +529,12 @@ protected void writePropertyInternal(Object obj, Bson bson, MongoPersistentPrope
 
 		Object existingValue = accessor.get(prop);
 		Document document = existingValue instanceof Document ? (Document) existingValue : new Document();
-		addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, document);
 
 		MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass())
 				? mappingContext.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type);
 
 		writeInternal(obj, document, entity);
+		addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, document);
 		accessor.put(prop, document);
 	}
 
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
index aca1cab1ac..0c198ede3b 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
@@ -44,6 +44,7 @@
 import org.joda.time.DateTime;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -3468,6 +3469,22 @@ public void onBeforeSave(BeforeSaveEvent<Document> event) {
 		assertThat(document.id, is(notNullValue()));
 	}
 
+
+	/**
+	 * @see DATAMONGO-1509
+	 */
+	@Test
+	public void findsByGnericNestedListElements() {
+
+		List<Model> modelList = Arrays.<Model>asList(new ModelA("value"));
+		DocumentWithCollection dwc = new DocumentWithCollection(modelList);
+
+		template.insert(dwc);
+
+		Query query = query(where("models").is(modelList));
+		assertThat(template.findOne(query, DocumentWithCollection.class), is(equalTo(dwc)));
+	}
+
 	static class TypeWithNumbers {
 
 		@Id String id;
@@ -3547,6 +3564,7 @@ static class DocumentWithDBRefCollection {
 		@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) public Map<String, Sample> lazyDbRefAnnotatedMap;
 	}
 
+	@EqualsAndHashCode
 	static class DocumentWithCollection {
 
 		@Id String id;
@@ -3599,6 +3617,7 @@ static interface Model {
 		String id();
 	}
 
+	@EqualsAndHashCode
 	static class ModelA implements Model {
 
 		@Id String id;

From c9573edf999e8fc562f106f562123dddafddc715 Mon Sep 17 00:00:00 2001
From: Christoph Strobl <cstrobl@pivotal.io>
Date: Wed, 16 Nov 2016 12:02:25 +0200
Subject: [PATCH 3/3] DATAMONGO-1509 - Add tests.

---
 .../data/mongodb/core/DocumentTestUtils.java  | 20 +++++++++++++++++++
 .../MappingMongoConverterUnitTests.java       | 19 +++++++++++-------
 2 files changed, 32 insertions(+), 7 deletions(-)

diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DocumentTestUtils.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DocumentTestUtils.java
index 5331467535..f6c0f83846 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DocumentTestUtils.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/DocumentTestUtils.java
@@ -18,6 +18,7 @@
 import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
+import java.util.Iterator;
 import java.util.List;
 
 import org.bson.Document;
@@ -80,4 +81,23 @@ public static <T> T getTypedValue(Document source, String key, Class<T> type) {
 
 		return (T) value;
 	}
+
+	public static void assertTypeHint(Document document, Class<?> type) {
+		assertTypeHint(document, type.getName());
+	}
+
+	public static void assertTypeHint(Document document, String expectedTypeString) {
+
+		Iterator<String> keyIterator = document.keySet().iterator();
+		while (keyIterator.hasNext()) {
+			String key = keyIterator.next();
+			if (key.equals("_class")) {
+				assertThat((String) document.get(key), is(equalTo(expectedTypeString)));
+				assertThat(keyIterator.hasNext(), is(false));
+				return;
+			}
+		}
+
+		fail(String.format("Expected to find type info %s in %s.", document, expectedTypeString));
+	}
 }
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java
index 75b65ff869..adff1b3336 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java
@@ -526,6 +526,9 @@ public void maybeConvertHandlesNullValuesCorrectly() {
 		assertThat(converter.convertToMongoType(null), is(nullValue()));
 	}
 
+	/**
+	 * @see DATAMONGO-1509
+	 */
 	@Test
 	public void writesGenericTypeCorrectly() {
 
@@ -537,7 +540,7 @@ public void writesGenericTypeCorrectly() {
 		converter.write(type, result);
 
 		org.bson.Document content = (org.bson.Document) result.get("content");
-		assertThat(content.get("_class"), is(notNullValue()));
+		assertTypeHint(content, Address.class);
 		assertThat(content.get("city"), is(notNullValue()));
 	}
 
@@ -1272,6 +1275,7 @@ public void eagerlyReturnsDBRefObjectIfTargetAlreadyIsOne() {
 
 	/**
 	 * @see DATAMONGO-523
+	 * @see DATAMONGO-1509
 	 */
 	@Test
 	public void considersTypeAliasAnnotation() {
@@ -1282,9 +1286,7 @@ public void considersTypeAliasAnnotation() {
 		org.bson.Document result = new org.bson.Document();
 		converter.write(aliased, result);
 
-		Object type = result.get("_class");
-		assertThat(type, is(notNullValue()));
-		assertThat(type.toString(), is("_"));
+		assertTypeHint(result, "_");
 	}
 
 	/**
@@ -1409,6 +1411,7 @@ public void writesProjectingTypeCorrectly() {
 	/**
 	 * @see DATAMONGO-812
 	 * @see DATAMONGO-893
+	 * @see DATAMONGO-1509
 	 */
 	@Test
 	public void convertsListToBasicDBListAndRetainsTypeInformationForComplexObjects() {
@@ -1424,7 +1427,7 @@ public void convertsListToBasicDBListAndRetainsTypeInformationForComplexObjects(
 
 		List<Object> dbList = (List<Object>) result;
 		assertThat(dbList, hasSize(1));
-		assertThat(getTypedValue(getAsDocument(dbList, 0), "_class", String.class), equalTo(Address.class.getName()));
+		assertTypeHint(getAsDocument(dbList, 0), Address.class);
 	}
 
 	/**
@@ -1444,6 +1447,7 @@ public void convertsListToBasicDBListWithoutTypeInformationForSimpleTypes() {
 
 	/**
 	 * @see DATAMONGO-812
+	 * @see DATAMONGO-1509
 	 */
 	@Test
 	public void convertsArrayToBasicDBListAndRetainsTypeInformationForComplexObjects() {
@@ -1458,7 +1462,7 @@ public void convertsArrayToBasicDBListAndRetainsTypeInformationForComplexObjects
 
 		List<Object> dbList = (List<Object>) result;
 		assertThat(dbList, hasSize(1));
-		assertThat(getTypedValue(getAsDocument(dbList, 0), "_class", String.class), equalTo(Address.class.getName()));
+		assertTypeHint(getAsDocument(dbList, 0), Address.class);
 	}
 
 	/**
@@ -1802,6 +1806,7 @@ public void shouldIncludeTextScorePropertyWhenReading() {
 
 	/**
 	 * @see DATAMONGO-1001
+	 * @see DATAMONGO-1509
 	 */
 	@Test
 	public void shouldWriteCglibProxiedClassTypeInformationCorrectly() {
@@ -1814,7 +1819,7 @@ public void shouldWriteCglibProxiedClassTypeInformationCorrectly() {
 		org.bson.Document document = new org.bson.Document();
 		converter.write(proxied, document);
 
-		assertThat(document.get("_class"), is((Object) GenericType.class.getName()));
+		assertTypeHint(document, GenericType.class);
 	}
 
 	/**