Skip to content

Commit b103e4e

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 c4a6c63 commit b103e4e

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
@@ -867,7 +867,7 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpre
867867
* @return
868868
*/
869869
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
870-
return PropertyToFieldNameConverter.INSTANCE;
870+
return new PositionParameterRetainingPropertyKeyConverter(name);
871871
}
872872

873873
/**
@@ -881,6 +881,24 @@ protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
881881
return new AssociationConverter(getAssociation());
882882
}
883883

884+
/**
885+
* @author Christoph Strobl
886+
* @since 1.8
887+
*/
888+
static class PositionParameterRetainingPropertyKeyConverter implements Converter<MongoPersistentProperty, String> {
889+
890+
private final KeyMapper keyMapper;
891+
892+
PositionParameterRetainingPropertyKeyConverter(String rawKey) {
893+
this.keyMapper = new KeyMapper(rawKey);
894+
}
895+
896+
@Override
897+
public String convert(MongoPersistentProperty source) {
898+
return keyMapper.mapPropertyName(source);
899+
}
900+
}
901+
884902
/*
885903
* (non-Javadoc)
886904
* @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#getTypeHint()
@@ -901,6 +919,63 @@ public TypeInformation<?> getTypeHint() {
901919

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

906981
/**

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
@@ -790,6 +790,34 @@ public void intersectsShouldUseGeoJsonRepresentationCorrectly() {
790790
assertThat(dbo, isBsonObject().containing("geoJsonPoint.$geoIntersects.$geometry.coordinates"));
791791
}
792792

793+
/**
794+
* @see DATAMONGO-1269
795+
*/
796+
@Test
797+
public void mappingShouldRetainNumericMapKey() {
798+
799+
Query query = query(where("map.1.stringProperty").is("ba'alzamon"));
800+
801+
DBObject dbo = mapper.getMappedObject(query.getQueryObject(),
802+
context.getPersistentEntity(EntityWithComplexValueTypeMap.class));
803+
804+
assertThat(dbo.containsField("map.1.stringProperty"), is(true));
805+
}
806+
807+
/**
808+
* @see DATAMONGO-1269
809+
*/
810+
@Test
811+
public void mappingShouldRetainNumericPositionInList() {
812+
813+
Query query = query(where("list.1.stringProperty").is("ba'alzamon"));
814+
815+
DBObject dbo = mapper.getMappedObject(query.getQueryObject(),
816+
context.getPersistentEntity(EntityWithComplexValueTypeList.class));
817+
818+
assertThat(dbo.containsField("list.1.stringProperty"), is(true));
819+
}
820+
793821
@Document
794822
public class Foo {
795823
@Id private ObjectId id;
@@ -890,4 +918,18 @@ static class ClassWithGeoTypes {
890918
GeoJsonPoint geoJsonPoint;
891919
@Field("geoJsonPointWithNameViaFieldAnnotation") GeoJsonPoint namedGeoJsonPoint;
892920
}
921+
922+
static class SimpeEntityWithoutId {
923+
924+
String stringProperty;
925+
Integer integerProperty;
926+
}
927+
928+
static class EntityWithComplexValueTypeMap {
929+
Map<Integer, SimpeEntityWithoutId> map;
930+
}
931+
932+
static class EntityWithComplexValueTypeList {
933+
List<SimpeEntityWithoutId> list;
934+
}
893935
}

0 commit comments

Comments
 (0)