Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 78a59c4

Browse files
christophstroblmp911de
authored andcommittedMar 15, 2021
Fix ShardKey lookup for nested paths.
This commit fixes the lookup of shard key values for nested paths using the dot (.) notation. Closes: #3590 Original pull request: #3591.
1 parent dccdfc8 commit 78a59c4

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed
 

‎spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,8 @@ <T> Document applyShardKey(MongoPersistentEntity<T> domainType, Document filter,
658658
: mappedDocument != null ? mappedDocument.getDocument() : getMappedUpdate(domainType);
659659

660660
Document filterWithShardKey = new Document(filter);
661-
getMappedShardKeyFields(domainType).forEach(key -> filterWithShardKey.putIfAbsent(key, shardKeySource.get(key)));
661+
getMappedShardKeyFields(domainType)
662+
.forEach(key -> filterWithShardKey.putIfAbsent(key, BsonUtils.resolveValue(shardKeySource, key)));
662663

663664
return filterWithShardKey;
664665
}

‎spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,41 @@ public static Document parse(String json, @Nullable CodecRegistryProvider codecR
282282
.orElseGet(() -> new DocumentCodec(codecRegistryProvider.getCodecRegistry())));
283283
}
284284

285+
/**
286+
* Resolve a the value for a given key. If the given {@link Bson} value contains the key the value is immediately
287+
* returned. If not and the key contains a path using the dot ({@code .}) notation it will try to resolve the path by
288+
* inspecting the individual parts. If one of the intermediate ones is {@literal null} or cannot be inspected further
289+
* (wrong) type, {@literal null} is returned.
290+
*
291+
* @param bson the source to inspect. Must not be {@literal null}.
292+
* @param key the key to lookup. Must not be {@literal null}.
293+
* @return can be {@literal null}.
294+
*/
295+
@Nullable
296+
public static Object resolveValue(Bson bson, String key) {
297+
298+
Map<String, Object> source = asMap(bson);
299+
300+
if (source.containsKey(key) || !key.contains(".")) {
301+
return source.get(key);
302+
}
303+
304+
String[] parts = key.split("\\.");
305+
306+
for (int i = 1; i < parts.length; i++) {
307+
308+
Object result = source.get(parts[i - 1]);
309+
310+
if (result == null || !(result instanceof Bson)) {
311+
return null;
312+
}
313+
314+
source = asMap((Bson) result);
315+
}
316+
317+
return source.get(parts[parts.length - 1]);
318+
}
319+
285320
@Nullable
286321
private static String toJson(@Nullable Object value) {
287322

‎spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator;
8585
import org.springframework.data.mongodb.core.mapping.Field;
8686
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
87+
import org.springframework.data.mongodb.core.mapping.Sharded;
8788
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
8889
import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback;
8990
import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback;
@@ -1922,6 +1923,24 @@ void saveShouldAppendDefaultShardKeyIfNotPresentInFilter() {
19221923
verify(findIterable, never()).first();
19231924
}
19241925

1926+
@Test // GH-3590
1927+
void shouldIncludeValueFromNestedShardKeyPath() {
1928+
1929+
WithShardKeyPoitingToNested source = new WithShardKeyPoitingToNested();
1930+
source.id = "id-1";
1931+
source.value = "v1";
1932+
source.nested = new WithNamedFields();
1933+
source.nested.customName = "cname";
1934+
source.nested.name = "name";
1935+
1936+
template.save(source);
1937+
1938+
ArgumentCaptor<Bson> filter = ArgumentCaptor.forClass(Bson.class);
1939+
verify(collection).replaceOne(filter.capture(), any(), any());
1940+
1941+
assertThat(filter.getValue()).isEqualTo(new Document("_id", "id-1").append("value", "v1").append("nested.custom-named-field", "cname"));
1942+
}
1943+
19251944
@Test // DATAMONGO-2341
19261945
void saveShouldProjectOnShardKeyWhenLoadingExistingDocument() {
19271946

@@ -2267,6 +2286,13 @@ static class Sith {
22672286
@Field("firstname") String name;
22682287
}
22692288

2289+
@Sharded(shardKey = {"value", "nested.customName"})
2290+
static class WithShardKeyPoitingToNested {
2291+
String id;
2292+
String value;
2293+
WithNamedFields nested;
2294+
}
2295+
22702296
/**
22712297
* Mocks out the {@link MongoTemplate#getDb()} method to return the {@link DB} mock instead of executing the actual
22722298
* behaviour.

0 commit comments

Comments
 (0)
Please sign in to comment.