Skip to content

DATAMONGO-1269 - Retain position parameter in property path. #314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.8.0.BUILD-SNAPSHOT</version>
<version>1.8.0.DATAMONGO-1269-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data MongoDB</name>
Expand Down
4 changes: 2 additions & 2 deletions spring-data-mongodb-cross-store/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.8.0.BUILD-SNAPSHOT</version>
<version>1.8.0.DATAMONGO-1269-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -48,7 +48,7 @@
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.8.0.BUILD-SNAPSHOT</version>
<version>1.8.0.DATAMONGO-1269-SNAPSHOT</version>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.8.0.BUILD-SNAPSHOT</version>
<version>1.8.0.DATAMONGO-1269-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-log4j/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.8.0.BUILD-SNAPSHOT</version>
<version>1.8.0.DATAMONGO-1269-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>1.8.0.BUILD-SNAPSHOT</version>
<version>1.8.0.DATAMONGO-1269-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpre
* @return
*/
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
return PropertyToFieldNameConverter.INSTANCE;
return new PositionParameterRetainingPropertyKeyConverter(name);
}

/**
Expand All @@ -881,6 +881,24 @@ protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
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()
Expand All @@ -901,6 +919,63 @@ public TypeInformation<?> getTypeHint() {

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;
}
}
}

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,18 @@
*/
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;
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;
Expand Down Expand Up @@ -213,7 +209,7 @@ public String getMappedKey() {
*/
@Override
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
return new UpdatePropertyConverter(key);
return new PositionParameterRetainingPropertyKeyConverter(key);
}

/*
Expand All @@ -225,107 +221,14 @@ protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
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.
*
* @author Christoph Strobl
*/
protected static class UpdateAssociationConverter extends AssociationConverter {

private final UpdateKeyMapper mapper;
private final KeyMapper mapper;

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

super(association);
this.mapper = new UpdateKeyMapper(key);
this.mapper = new KeyMapper(key);
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,34 @@ public void intersectsShouldUseGeoJsonRepresentationCorrectly() {
assertThat(dbo, isBsonObject().containing("geoJsonPoint.$geoIntersects.$geometry.coordinates"));
}

/**
* @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;
Expand Down Expand Up @@ -890,4 +918,18 @@ static class ClassWithGeoTypes {
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;
}
}