Skip to content

Commit e0b8f5b

Browse files
christophstroblodrotbohm
authored andcommitted
DATAMONGO-1269 - Retain position parameter in property path.
We now retain position parameters in paths used in queries when mapping the field name. This allows to map "list.1.name" to the name property of the first element in the list. The change also fixes a glitch in mapping java.util.Map like structures having numeric keys. Original pull request: #314.
1 parent 82850d1 commit e0b8f5b

File tree

3 files changed

+121
-101
lines changed

3 files changed

+121
-101
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpre
857857
* @return
858858
*/
859859
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
860-
return PropertyToFieldNameConverter.INSTANCE;
860+
return new PositionParameterRetainingPropertyKeyConverter(name);
861861
}
862862

863863
/**
@@ -871,6 +871,24 @@ protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
871871
return new AssociationConverter(getAssociation());
872872
}
873873

874+
/**
875+
* @author Christoph Strobl
876+
* @since 1.8
877+
*/
878+
static class PositionParameterRetainingPropertyKeyConverter implements Converter<MongoPersistentProperty, String> {
879+
880+
private final KeyMapper keyMapper;
881+
882+
PositionParameterRetainingPropertyKeyConverter(String rawKey) {
883+
this.keyMapper = new KeyMapper(rawKey);
884+
}
885+
886+
@Override
887+
public String convert(MongoPersistentProperty source) {
888+
return keyMapper.mapPropertyName(source);
889+
}
890+
}
891+
874892
/*
875893
* (non-Javadoc)
876894
* @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#getTypeHint()
@@ -891,6 +909,63 @@ public TypeInformation<?> getTypeHint() {
891909

892910
return NESTED_DOCUMENT;
893911
}
912+
913+
/**
914+
* @author Christoph Strobl
915+
* @since 1.8
916+
*/
917+
static class KeyMapper {
918+
919+
Iterator<String> iterator;
920+
921+
public KeyMapper(String key) {
922+
923+
this.iterator = Arrays.asList(key.split("\\.")).iterator();
924+
this.iterator.next();
925+
}
926+
927+
/**
928+
* Maps the property name while retaining potential positional operator {@literal $}.
929+
*
930+
* @param property
931+
* @return
932+
*/
933+
protected String mapPropertyName(MongoPersistentProperty property) {
934+
935+
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
936+
937+
boolean inspect = iterator.hasNext();
938+
while (inspect) {
939+
940+
String partial = iterator.next();
941+
942+
boolean isPositional = (isPositionalParameter(partial) && (property.isMap() || property.isCollectionLike() || property
943+
.isArray()));
944+
if (isPositional) {
945+
mappedName += "." + partial;
946+
}
947+
948+
inspect = isPositional && iterator.hasNext();
949+
}
950+
951+
return mappedName;
952+
}
953+
954+
boolean isPositionalParameter(String partial) {
955+
956+
if (partial.equals("$")) {
957+
return true;
958+
}
959+
960+
try {
961+
Long.valueOf(partial);
962+
return true;
963+
} catch (NumberFormatException e) {
964+
return false;
965+
}
966+
}
967+
}
968+
894969
}
895970

896971
/**

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java

Lines changed: 3 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,18 @@
1515
*/
1616
package org.springframework.data.mongodb.core.convert;
1717

18-
import java.util.Arrays;
19-
import java.util.Iterator;
2018
import java.util.Map.Entry;
2119

2220
import org.springframework.core.convert.converter.Converter;
2321
import org.springframework.data.mapping.Association;
2422
import org.springframework.data.mapping.context.MappingContext;
2523
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2624
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
27-
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
2825
import org.springframework.data.mongodb.core.query.Query;
2926
import org.springframework.data.mongodb.core.query.Update.Modifier;
3027
import org.springframework.data.mongodb.core.query.Update.Modifiers;
3128
import org.springframework.data.util.ClassTypeInformation;
3229
import org.springframework.data.util.TypeInformation;
33-
import org.springframework.util.Assert;
3430

3531
import com.mongodb.BasicDBObject;
3632
import com.mongodb.DBObject;
@@ -213,7 +209,7 @@ public String getMappedKey() {
213209
*/
214210
@Override
215211
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
216-
return new UpdatePropertyConverter(key);
212+
return new PositionParameterRetainingPropertyKeyConverter(key);
217213
}
218214

219215
/*
@@ -225,107 +221,14 @@ protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
225221
return new UpdateAssociationConverter(getAssociation(), key);
226222
}
227223

228-
/**
229-
* Special mapper handling positional parameter {@literal $} within property names.
230-
*
231-
* @author Christoph Strobl
232-
* @since 1.7
233-
*/
234-
private static class UpdateKeyMapper {
235-
236-
private final Iterator<String> iterator;
237-
238-
protected UpdateKeyMapper(String rawKey) {
239-
240-
Assert.hasText(rawKey, "Key must not be null or empty!");
241-
242-
this.iterator = Arrays.asList(rawKey.split("\\.")).iterator();
243-
this.iterator.next();
244-
}
245-
246-
/**
247-
* Maps the property name while retaining potential positional operator {@literal $}.
248-
*
249-
* @param property
250-
* @return
251-
*/
252-
protected String mapPropertyName(MongoPersistentProperty property) {
253-
254-
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
255-
256-
boolean inspect = iterator.hasNext();
257-
while (inspect) {
258-
259-
String partial = iterator.next();
260-
261-
boolean isPositional = isPositionalParameter(partial);
262-
if (isPositional) {
263-
mappedName += "." + partial;
264-
}
265-
266-
inspect = isPositional && iterator.hasNext();
267-
}
268-
269-
return mappedName;
270-
}
271-
272-
boolean isPositionalParameter(String partial) {
273-
274-
if (partial.equals("$")) {
275-
return true;
276-
}
277-
278-
try {
279-
Long.valueOf(partial);
280-
return true;
281-
} catch (NumberFormatException e) {
282-
return false;
283-
}
284-
}
285-
286-
}
287-
288-
/**
289-
* Special {@link Converter} for {@link MongoPersistentProperty} instances that will concatenate the {@literal $}
290-
* contained in the source update key.
291-
*
292-
* @author Oliver Gierke
293-
* @author Christoph Strobl
294-
*/
295-
private static class UpdatePropertyConverter implements Converter<MongoPersistentProperty, String> {
296-
297-
private final UpdateKeyMapper mapper;
298-
299-
/**
300-
* Creates a new {@link UpdatePropertyConverter} with the given update key.
301-
*
302-
* @param updateKey must not be {@literal null} or empty.
303-
*/
304-
public UpdatePropertyConverter(String updateKey) {
305-
306-
Assert.hasText(updateKey, "Update key must not be null or empty!");
307-
308-
this.mapper = new UpdateKeyMapper(updateKey);
309-
}
310-
311-
/*
312-
* (non-Javadoc)
313-
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
314-
*/
315-
@Override
316-
public String convert(MongoPersistentProperty property) {
317-
return mapper.mapPropertyName(property);
318-
}
319-
}
320-
321224
/**
322225
* {@link Converter} retaining positional parameter {@literal $} for {@link Association}s.
323226
*
324227
* @author Christoph Strobl
325228
*/
326229
protected static class UpdateAssociationConverter extends AssociationConverter {
327230

328-
private final UpdateKeyMapper mapper;
231+
private final KeyMapper mapper;
329232

330233
/**
331234
* Creates a new {@link AssociationConverter} for the given {@link Association}.
@@ -335,7 +238,7 @@ protected static class UpdateAssociationConverter extends AssociationConverter {
335238
public UpdateAssociationConverter(Association<MongoPersistentProperty> association, String key) {
336239

337240
super(association);
338-
this.mapper = new UpdateKeyMapper(key);
241+
this.mapper = new KeyMapper(key);
339242
}
340243

341244
/*

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,34 @@ public void withinShouldUseGeoJsonPolygonWhenMappingPolygonOn2DSphereIndex() {
775775
assertThat(dbo, isBsonObject().containing("geoJsonPoint.$geoWithin.$geometry.type", "Polygon"));
776776
}
777777

778+
/**
779+
* @see DATAMONGO-1269
780+
*/
781+
@Test
782+
public void mappingShouldRetainNumericMapKey() {
783+
784+
Query query = query(where("map.1.stringProperty").is("ba'alzamon"));
785+
786+
DBObject dbo = mapper.getMappedObject(query.getQueryObject(),
787+
context.getPersistentEntity(EntityWithComplexValueTypeMap.class));
788+
789+
assertThat(dbo.containsField("map.1.stringProperty"), is(true));
790+
}
791+
792+
/**
793+
* @see DATAMONGO-1269
794+
*/
795+
@Test
796+
public void mappingShouldRetainNumericPositionInList() {
797+
798+
Query query = query(where("list.1.stringProperty").is("ba'alzamon"));
799+
800+
DBObject dbo = mapper.getMappedObject(query.getQueryObject(),
801+
context.getPersistentEntity(EntityWithComplexValueTypeList.class));
802+
803+
assertThat(dbo.containsField("list.1.stringProperty"), is(true));
804+
}
805+
778806
@Document
779807
public class Foo {
780808
@Id private ObjectId id;
@@ -875,4 +903,18 @@ static class ClassWithGeoTypes {
875903
GeoJsonPoint geoJsonPoint;
876904
@Field("geoJsonPointWithNameViaFieldAnnotation") GeoJsonPoint namedGeoJsonPoint;
877905
}
906+
907+
static class SimpeEntityWithoutId {
908+
909+
String stringProperty;
910+
Integer integerProperty;
911+
}
912+
913+
static class EntityWithComplexValueTypeMap {
914+
Map<Integer, SimpeEntityWithoutId> map;
915+
}
916+
917+
static class EntityWithComplexValueTypeList {
918+
List<SimpeEntityWithoutId> list;
919+
}
878920
}

0 commit comments

Comments
 (0)