Browse Source

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.7.x
Christoph Strobl 11 years ago committed by Oliver Gierke
parent
commit
e0b8f5b566
  1. 77
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
  2. 103
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java
  3. 42
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

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

@ -857,7 +857,7 @@ public class QueryMapper { @@ -857,7 +857,7 @@ public class QueryMapper {
* @return
*/
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
return PropertyToFieldNameConverter.INSTANCE;
return new PositionParameterRetainingPropertyKeyConverter(name);
}
/**
@ -871,6 +871,24 @@ public class QueryMapper { @@ -871,6 +871,24 @@ public class QueryMapper {
return new AssociationConverter(getAssociation());
}
/**
* @author Christoph Strobl
* @since 1.8
*/
static class PositionParameterRetainingPropertyKeyConverter implements Converter<MongoPersistentProperty, String> {
private final KeyMapper keyMapper;
PositionParameterRetainingPropertyKeyConverter(String rawKey) {
this.keyMapper = new KeyMapper(rawKey);
}
@Override
public String convert(MongoPersistentProperty source) {
return keyMapper.mapPropertyName(source);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#getTypeHint()
@ -891,6 +909,63 @@ public class QueryMapper { @@ -891,6 +909,63 @@ public class QueryMapper {
return NESTED_DOCUMENT;
}
/**
* @author Christoph Strobl
* @since 1.8
*/
static class KeyMapper {
Iterator<String> iterator;
public KeyMapper(String key) {
this.iterator = Arrays.asList(key.split("\\.")).iterator();
this.iterator.next();
}
/**
* Maps the property name while retaining potential positional operator {@literal $}.
*
* @param property
* @return
*/
protected String mapPropertyName(MongoPersistentProperty property) {
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
boolean inspect = iterator.hasNext();
while (inspect) {
String partial = iterator.next();
boolean isPositional = (isPositionalParameter(partial) && (property.isMap() || property.isCollectionLike() || property
.isArray()));
if (isPositional) {
mappedName += "." + partial;
}
inspect = isPositional && iterator.hasNext();
}
return mappedName;
}
boolean isPositionalParameter(String partial) {
if (partial.equals("$")) {
return true;
}
try {
Long.valueOf(partial);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}
}
/**

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

@ -15,8 +15,6 @@ @@ -15,8 +15,6 @@
*/
package org.springframework.data.mongodb.core.convert;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map.Entry;
import org.springframework.core.convert.converter.Converter;
@ -24,13 +22,11 @@ import org.springframework.data.mapping.Association; @@ -24,13 +22,11 @@ import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update.Modifier;
import org.springframework.data.mongodb.core.query.Update.Modifiers;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@ -213,7 +209,7 @@ public class UpdateMapper extends QueryMapper { @@ -213,7 +209,7 @@ public class UpdateMapper extends QueryMapper {
*/
@Override
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
return new UpdatePropertyConverter(key);
return new PositionParameterRetainingPropertyKeyConverter(key);
}
/*
@ -225,99 +221,6 @@ public class UpdateMapper extends QueryMapper { @@ -225,99 +221,6 @@ public class UpdateMapper extends QueryMapper {
return new UpdateAssociationConverter(getAssociation(), key);
}
/**
* Special mapper handling positional parameter {@literal $} within property names.
*
* @author Christoph Strobl
* @since 1.7
*/
private static class UpdateKeyMapper {
private final Iterator<String> iterator;
protected UpdateKeyMapper(String rawKey) {
Assert.hasText(rawKey, "Key must not be null or empty!");
this.iterator = Arrays.asList(rawKey.split("\\.")).iterator();
this.iterator.next();
}
/**
* Maps the property name while retaining potential positional operator {@literal $}.
*
* @param property
* @return
*/
protected String mapPropertyName(MongoPersistentProperty property) {
String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property);
boolean inspect = iterator.hasNext();
while (inspect) {
String partial = iterator.next();
boolean isPositional = isPositionalParameter(partial);
if (isPositional) {
mappedName += "." + partial;
}
inspect = isPositional && iterator.hasNext();
}
return mappedName;
}
boolean isPositionalParameter(String partial) {
if (partial.equals("$")) {
return true;
}
try {
Long.valueOf(partial);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}
/**
* Special {@link Converter} for {@link MongoPersistentProperty} instances that will concatenate the {@literal $}
* contained in the source update key.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
private static class UpdatePropertyConverter implements Converter<MongoPersistentProperty, String> {
private final UpdateKeyMapper mapper;
/**
* Creates a new {@link UpdatePropertyConverter} with the given update key.
*
* @param updateKey must not be {@literal null} or empty.
*/
public UpdatePropertyConverter(String updateKey) {
Assert.hasText(updateKey, "Update key must not be null or empty!");
this.mapper = new UpdateKeyMapper(updateKey);
}
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public String convert(MongoPersistentProperty property) {
return mapper.mapPropertyName(property);
}
}
/**
* {@link Converter} retaining positional parameter {@literal $} for {@link Association}s.
*
@ -325,7 +228,7 @@ public class UpdateMapper extends QueryMapper { @@ -325,7 +228,7 @@ public class UpdateMapper extends QueryMapper {
*/
protected static class UpdateAssociationConverter extends AssociationConverter {
private final UpdateKeyMapper mapper;
private final KeyMapper mapper;
/**
* Creates a new {@link AssociationConverter} for the given {@link Association}.
@ -335,7 +238,7 @@ public class UpdateMapper extends QueryMapper { @@ -335,7 +238,7 @@ public class UpdateMapper extends QueryMapper {
public UpdateAssociationConverter(Association<MongoPersistentProperty> association, String key) {
super(association);
this.mapper = new UpdateKeyMapper(key);
this.mapper = new KeyMapper(key);
}
/*

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

@ -775,6 +775,34 @@ public class QueryMapperUnitTests { @@ -775,6 +775,34 @@ public class QueryMapperUnitTests {
assertThat(dbo, isBsonObject().containing("geoJsonPoint.$geoWithin.$geometry.type", "Polygon"));
}
/**
* @see DATAMONGO-1269
*/
@Test
public void mappingShouldRetainNumericMapKey() {
Query query = query(where("map.1.stringProperty").is("ba'alzamon"));
DBObject dbo = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(EntityWithComplexValueTypeMap.class));
assertThat(dbo.containsField("map.1.stringProperty"), is(true));
}
/**
* @see DATAMONGO-1269
*/
@Test
public void mappingShouldRetainNumericPositionInList() {
Query query = query(where("list.1.stringProperty").is("ba'alzamon"));
DBObject dbo = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(EntityWithComplexValueTypeList.class));
assertThat(dbo.containsField("list.1.stringProperty"), is(true));
}
@Document
public class Foo {
@Id private ObjectId id;
@ -875,4 +903,18 @@ public class QueryMapperUnitTests { @@ -875,4 +903,18 @@ public class QueryMapperUnitTests {
GeoJsonPoint geoJsonPoint;
@Field("geoJsonPointWithNameViaFieldAnnotation") GeoJsonPoint namedGeoJsonPoint;
}
static class SimpeEntityWithoutId {
String stringProperty;
Integer integerProperty;
}
static class EntityWithComplexValueTypeMap {
Map<Integer, SimpeEntityWithoutId> map;
}
static class EntityWithComplexValueTypeList {
List<SimpeEntityWithoutId> list;
}
}

Loading…
Cancel
Save