diff --git a/pom.xml b/pom.xml
index 828f56af2e..e8c9cb8f5d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.2.0-SNAPSHOT
+ 3.2.0-GH-3590-SNAPSHOT
pom
Spring Data MongoDB
diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml
index f0fbb601c8..9a137b81fa 100644
--- a/spring-data-mongodb-benchmarks/pom.xml
+++ b/spring-data-mongodb-benchmarks/pom.xml
@@ -7,7 +7,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.2.0-SNAPSHOT
+ 3.2.0-GH-3590-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index 1a17321782..133908285a 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.2.0-SNAPSHOT
+ 3.2.0-GH-3590-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 0248517caf..3c5c4af594 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.2.0-SNAPSHOT
+ 3.2.0-GH-3590-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
index 89e7d29783..327d282193 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
@@ -689,7 +689,8 @@ Document applyShardKey(MongoPersistentEntity domainType, Document filter,
: mappedDocument != null ? mappedDocument.getDocument() : getMappedUpdate(domainType);
Document filterWithShardKey = new Document(filter);
- getMappedShardKeyFields(domainType).forEach(key -> filterWithShardKey.putIfAbsent(key, shardKeySource.get(key)));
+ getMappedShardKeyFields(domainType)
+ .forEach(key -> filterWithShardKey.putIfAbsent(key, BsonUtils.resolveValue(shardKeySource, key)));
return filterWithShardKey;
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java
index 85525b13b4..9be81f2bd2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java
@@ -385,6 +385,41 @@ public static Document parse(String json, @Nullable CodecRegistryProvider codecR
.orElseGet(() -> new DocumentCodec(codecRegistryProvider.getCodecRegistry())));
}
+ /**
+ * Resolve a the value for a given key. If the given {@link Bson} value contains the key the value is immediately
+ * returned. If not and the key contains a path using the dot ({@code .}) notation it will try to resolve the path by
+ * inspecting the individual parts. If one of the intermediate ones is {@literal null} or cannot be inspected further
+ * (wrong) type, {@literal null} is returned.
+ *
+ * @param bson the source to inspect. Must not be {@literal null}.
+ * @param key the key to lookup. Must not be {@literal null}.
+ * @return can be {@literal null}.
+ */
+ @Nullable
+ public static Object resolveValue(Bson bson, String key) {
+
+ Map source = asMap(bson);
+
+ if (source.containsKey(key) || !key.contains(".")) {
+ return source.get(key);
+ }
+
+ String[] parts = key.split("\\.");
+
+ for (int i = 1; i < parts.length; i++) {
+
+ Object result = source.get(parts[i - 1]);
+
+ if (result == null || !(result instanceof Bson)) {
+ return null;
+ }
+
+ source = asMap((Bson) result);
+ }
+
+ return source.get(parts[parts.length - 1]);
+ }
+
/**
* Returns given object as {@link Collection}. Will return the {@link Collection} as is if the source is a
* {@link Collection} already, will convert an array into a {@link Collection} or simply create a single element
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
index 7006091d11..abf2903105 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
@@ -78,6 +78,7 @@
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
+import org.springframework.data.mongodb.core.mapping.Sharded;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback;
@@ -1954,6 +1955,24 @@ void saveShouldAppendDefaultShardKeyIfNotPresentInFilter() {
verify(findIterable, never()).first();
}
+ @Test // GH-3590
+ void shouldIncludeValueFromNestedShardKeyPath() {
+
+ WithShardKeyPoitingToNested source = new WithShardKeyPoitingToNested();
+ source.id = "id-1";
+ source.value = "v1";
+ source.nested = new WithNamedFields();
+ source.nested.customName = "cname";
+ source.nested.name = "name";
+
+ template.save(source);
+
+ ArgumentCaptor filter = ArgumentCaptor.forClass(Bson.class);
+ verify(collection).replaceOne(filter.capture(), any(), any());
+
+ assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("value", "v1").append("nested.custom-named-field", "cname"));
+ }
+
@Test // DATAMONGO-2341
void saveShouldProjectOnShardKeyWhenLoadingExistingDocument() {
@@ -2313,6 +2332,13 @@ static class Sith {
@Field("firstname") String name;
}
+ @Sharded(shardKey = {"value", "nested.customName"})
+ static class WithShardKeyPoitingToNested {
+ String id;
+ String value;
+ WithNamedFields nested;
+ }
+
static class TypeImplementingIterator implements Iterator {
@Override