Skip to content

Commit ce80187

Browse files
committed
Hacking.
1 parent a1c1330 commit ce80187

File tree

7 files changed

+403
-256
lines changed

7 files changed

+403
-256
lines changed

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

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,11 @@ public MappingMongoConverter(DbRefResolver dbRefResolver,
176176
this.idMapper = new QueryMapper(this);
177177

178178
this.spELContext = new SpELContext(DocumentPropertyAccessor.INSTANCE);
179-
this.dbRefProxyHandler = new DefaultDbRefProxyHandler(mappingContext,
180-
(prop, bson, evaluator, path) -> {
179+
this.dbRefProxyHandler = new DefaultDbRefProxyHandler(mappingContext, (prop, bson, evaluator, path) -> {
181180

182-
ConversionContext context = getConversionContext(path);
183-
return MappingMongoConverter.this.getValueInternal(context, prop, bson, evaluator);
184-
}, expressionEvaluatorFactory::create);
181+
ConversionContext context = getConversionContext(path);
182+
return MappingMongoConverter.this.getValueInternal(context, prop, bson, evaluator);
183+
}, expressionEvaluatorFactory::create);
185184

186185
this.referenceLookupDelegate = new ReferenceLookupDelegate(mappingContext, spELContext);
187186
this.documentPointerFactory = new DocumentPointerFactory(conversionService, mappingContext);
@@ -1389,23 +1388,27 @@ protected DBRef createDBRef(Object target, @Nullable MongoPersistentProperty pro
13891388
}
13901389

13911390
MongoPersistentEntity<?> entity = targetEntity;
1392-
13931391
MongoPersistentProperty idProperty = entity.getIdProperty();
1392+
Object id = null;
13941393

1395-
if (idProperty != null) {
1396-
1397-
Object id = target.getClass().equals(idProperty.getType()) ? target
1398-
: entity.getPropertyAccessor(target).getProperty(idProperty);
1394+
if (entity.getType().isInstance(target)) {
13991395

1400-
if (null == id) {
1401-
throw new MappingException("Cannot create a reference to an object with a NULL id");
1396+
if (idProperty == null) {
1397+
throw new MappingException("No id property found on class " + entity.getType());
14021398
}
14031399

1404-
return dbRefResolver.createDbRef(property == null ? null : property.getDBRef(), entity,
1405-
idMapper.convertId(id, idProperty != null ? idProperty.getFieldType() : ObjectId.class));
1400+
id = target.getClass().equals(idProperty.getType()) ? target
1401+
: entity.getPropertyAccessor(target).getProperty(idProperty);
1402+
} else {
1403+
id = target;
1404+
}
1405+
1406+
if (null == id) {
1407+
throw new MappingException("Cannot create a reference to an object with a NULL id");
14061408
}
14071409

1408-
throw new MappingException("No id property found on class " + entity.getType());
1410+
return dbRefResolver.createDbRef(property == null ? null : property.getDBRef(), entity,
1411+
idMapper.convertId(id, idProperty != null ? idProperty.getFieldType() : ObjectId.class));
14091412
}
14101413

14111414
@Nullable

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

Lines changed: 22 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import java.util.Map.Entry;
2727
import java.util.Optional;
2828
import java.util.Set;
29-
import java.util.regex.Matcher;
3029
import java.util.regex.Pattern;
3130
import java.util.stream.Collectors;
3231

@@ -50,7 +49,6 @@
5049
import org.springframework.data.mapping.PersistentProperty;
5150
import org.springframework.data.mapping.PersistentPropertyPath;
5251
import org.springframework.data.mapping.PropertyPath;
53-
import org.springframework.data.mapping.PropertyReferenceException;
5452
import org.springframework.data.mapping.context.InvalidPersistentPropertyPath;
5553
import org.springframework.data.mapping.context.MappingContext;
5654
import org.springframework.data.mapping.model.PropertyValueProvider;
@@ -59,9 +57,9 @@
5957
import org.springframework.data.mongodb.core.aggregation.RelaxedTypeBasedAggregationOperationContext;
6058
import org.springframework.data.mongodb.core.convert.MappingMongoConverter.NestedDocument;
6159
import org.springframework.data.mongodb.core.mapping.FieldName;
60+
import org.springframework.data.mongodb.core.mapping.MongoPath;
6261
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
6362
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
64-
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter;
6563
import org.springframework.data.mongodb.core.query.Query;
6664
import org.springframework.data.mongodb.util.BsonUtils;
6765
import org.springframework.data.mongodb.util.DotPath;
@@ -172,6 +170,8 @@ public Document getMappedObject(Bson query, @Nullable MongoPersistentEntity<?> e
172170

173171
Object theNestedObject = BsonUtils.get(query, key);
174172
Document mappedValue = (Document) getMappedValue(field, theNestedObject);
173+
174+
// TODO: Seems a weird condition. Isn't it rather a comparison of nested values vs. document comparison?
175175
if (!StringUtils.hasText(field.getMappedKey())) {
176176
result.putAll(mappedValue);
177177
} else {
@@ -356,7 +356,8 @@ protected Entry<String, Object> getMappedObjectForField(Field field, Object rawV
356356
return createMapEntry(key, getMappedObject(mongoExpression.toDocument(), field.getEntity()));
357357
}
358358

359-
if (isNestedKeyword(rawValue) && !field.isIdField()) {
359+
// TODO: Seems a weird condition
360+
if (isNestedKeyword(rawValue) && (field.isAssociation() || !field.isIdField())) {
360361
Keyword keyword = new Keyword((Document) rawValue);
361362
value = getMappedKeyword(field, keyword);
362363
} else {
@@ -1139,6 +1140,7 @@ protected static class MetadataBackedField extends Field {
11391140
private final MongoPersistentProperty property;
11401141
private final @Nullable PersistentPropertyPath<MongoPersistentProperty> path;
11411142
private final @Nullable Association<MongoPersistentProperty> association;
1143+
private final MongoPath mongoPath;
11421144

11431145
/**
11441146
* Creates a new {@link MetadataBackedField} with the given name, {@link MongoPersistentEntity} and
@@ -1173,7 +1175,8 @@ public MetadataBackedField(String name, MongoPersistentEntity<?> entity,
11731175
this.entity = entity;
11741176
this.mappingContext = context;
11751177

1176-
this.path = getPath(removePlaceholders(POSITIONAL_PARAMETER_PATTERN, name), property);
1178+
this.mongoPath = MongoPath.parse(name);
1179+
this.path = getPath(mongoPath, property);
11771180
this.property = path == null ? property : path.getLeafProperty();
11781181
this.association = findAssociation();
11791182
}
@@ -1256,11 +1259,16 @@ public Class<?> getFieldType() {
12561259
@Override
12571260
public String getMappedKey() {
12581261

1259-
if (getProperty() != null && getProperty().getMongoField().getName().isKey()) {
1260-
return getProperty().getFieldName();
1262+
// TODO: Switch to MongoPath?!
1263+
if (isAssociation()) {
1264+
return path == null ? name : path.toDotPath(getAssociationConverter());
1265+
}
1266+
1267+
if (entity != null) {
1268+
return mongoPath.applyFieldNames(mappingContext, entity).toString();
12611269
}
12621270

1263-
return path == null ? name : path.toDotPath(isAssociation() ? getAssociationConverter() : getPropertyConverter());
1271+
return name;
12641272
}
12651273

12661274
@Nullable
@@ -1269,23 +1277,21 @@ protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
12691277
}
12701278

12711279
/**
1272-
* Returns the {@link PersistentPropertyPath} for the given {@code pathExpression}.
1280+
* Returns the {@link PersistentPropertyPath} for the given {@code MongoPath}.
12731281
*
1274-
* @param pathExpression
12751282
* @return
12761283
*/
12771284
@Nullable
1278-
private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression,
1285+
private PersistentPropertyPath<MongoPersistentProperty> getPath(MongoPath mongoPath,
12791286
@Nullable MongoPersistentProperty sourceProperty) {
12801287

12811288
if (sourceProperty != null && sourceProperty.getOwner().equals(entity)) {
12821289
return mappingContext.getPersistentPropertyPath(
12831290
PropertyPath.from(Pattern.quote(sourceProperty.getName()), entity.getTypeInformation()));
12841291
}
12851292

1286-
String rawPath = resolvePath(pathExpression);
1293+
PropertyPath path = mongoPath.toPropertyPath(mappingContext, entity);
12871294

1288-
PropertyPath path = forName(rawPath);
12891295
if (path == null || isPathToJavaLangClassProperty(path)) {
12901296
return null;
12911297
}
@@ -1298,9 +1304,8 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpre
12981304

12991305
String types = StringUtils.collectionToDelimitedString(
13001306
path.stream().map(it -> it.getType().getSimpleName()).collect(Collectors.toList()), " -> ");
1301-
QueryMapper.LOGGER.info(String.format(
1302-
"Could not map '%s'; Maybe a fragment in '%s' is considered a simple type; Mapper continues with %s",
1303-
path, types, pathExpression));
1307+
QueryMapper.LOGGER.info("Could not map '" + path + "'; Maybe a fragment in '" + types
1308+
+ "' is considered a simple type; Mapper continues with " + mongoPath);
13041309
}
13051310
return null;
13061311
}
@@ -1318,7 +1323,7 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpre
13181323
}
13191324

13201325
if (associationDetected && !property.isIdProperty()) {
1321-
throw new MappingException(String.format(INVALID_ASSOCIATION_REFERENCE, pathExpression));
1326+
throw new MappingException(String.format(INVALID_ASSOCIATION_REFERENCE, mongoPath));
13221327
}
13231328
}
13241329

@@ -1335,89 +1340,12 @@ private PersistentPropertyPath<MongoPersistentProperty> tryToResolvePersistentPr
13351340
}
13361341
}
13371342

1338-
/**
1339-
* Querydsl happens to map id fields directly to {@literal _id} which breaks {@link PropertyPath} resolution. So if
1340-
* the first attempt fails we try to replace {@literal _id} with just {@literal id} and see if we can resolve if
1341-
* then.
1342-
*
1343-
* @param path
1344-
* @return the path or {@literal null}
1345-
*/
1346-
@Nullable
1347-
private PropertyPath forName(String path) {
1348-
1349-
try {
1350-
1351-
if (entity.getPersistentProperty(path) != null) {
1352-
return PropertyPath.from(Pattern.quote(path), entity.getTypeInformation());
1353-
}
1354-
1355-
return PropertyPath.from(path, entity.getTypeInformation());
1356-
} catch (PropertyReferenceException | InvalidPersistentPropertyPath e) {
1357-
1358-
if (path.endsWith("_id")) {
1359-
return forName(path.substring(0, path.length() - 3) + "id");
1360-
}
1361-
1362-
// Ok give it another try quoting
1363-
try {
1364-
return PropertyPath.from(Pattern.quote(path), entity.getTypeInformation());
1365-
} catch (PropertyReferenceException | InvalidPersistentPropertyPath ex) {
1366-
1367-
}
1368-
1369-
return null;
1370-
}
1371-
}
1372-
13731343
private boolean isPathToJavaLangClassProperty(PropertyPath path) {
13741344

13751345
return (path.getType() == Class.class || path.getType().equals(Object.class))
13761346
&& path.getLeafProperty().getType() == Class.class;
13771347
}
13781348

1379-
private static String resolvePath(String source) {
1380-
1381-
String[] segments = source.split("\\.");
1382-
if (segments.length == 1) {
1383-
return source;
1384-
}
1385-
1386-
List<String> path = new ArrayList<>(segments.length);
1387-
1388-
/* always start from a property, so we can skip the first segment.
1389-
from there remove any position placeholder */
1390-
for (int i = 1; i < segments.length; i++) {
1391-
String segment = segments[i];
1392-
if (segment.startsWith("[") && segment.endsWith("]")) {
1393-
continue;
1394-
}
1395-
if (NUMERIC_SEGMENT.matcher(segment).matches()) {
1396-
continue;
1397-
}
1398-
path.add(segment);
1399-
}
1400-
1401-
// when property is followed only by placeholders eg. 'values.0.3.90'
1402-
// or when there is no difference in the number of segments
1403-
if (path.isEmpty() || segments.length == path.size() + 1) {
1404-
return source;
1405-
}
1406-
1407-
path.add(0, segments[0]);
1408-
return StringUtils.collectionToDelimitedString(path, ".");
1409-
}
1410-
1411-
/**
1412-
* Return the {@link Converter} to be used to created the mapped key. Default implementation will use
1413-
* {@link PropertyToFieldNameConverter}.
1414-
*
1415-
* @return
1416-
*/
1417-
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
1418-
return new PositionParameterRetainingPropertyKeyConverter(name, mappingContext);
1419-
}
1420-
14211349
/**
14221350
* Return the {@link Converter} to use for creating the mapped key of an association. Default implementation is
14231351
* {@link AssociationConverter}.
@@ -1433,29 +1361,6 @@ protected MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProp
14331361
return mappingContext;
14341362
}
14351363

1436-
private static String removePlaceholders(Pattern pattern, String raw) {
1437-
return pattern.matcher(raw).replaceAll("");
1438-
}
1439-
1440-
/**
1441-
* @author Christoph Strobl
1442-
* @since 1.8
1443-
*/
1444-
static class PositionParameterRetainingPropertyKeyConverter implements Converter<MongoPersistentProperty, String> {
1445-
1446-
private final KeyMapper keyMapper;
1447-
1448-
public PositionParameterRetainingPropertyKeyConverter(String rawKey,
1449-
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> ctx) {
1450-
this.keyMapper = new KeyMapper(rawKey, ctx);
1451-
}
1452-
1453-
@Override
1454-
public String convert(MongoPersistentProperty source) {
1455-
return keyMapper.mapPropertyName(source);
1456-
}
1457-
}
1458-
14591364
@Override
14601365
public TypeInformation<?> getTypeHint() {
14611366

@@ -1473,83 +1378,6 @@ public TypeInformation<?> getTypeHint() {
14731378
return NESTED_DOCUMENT;
14741379
}
14751380

1476-
/**
1477-
* @author Christoph Strobl
1478-
* @since 1.8
1479-
*/
1480-
static class KeyMapper {
1481-
1482-
private final Iterator<String> iterator;
1483-
private int currentIndex;
1484-
private final List<String> pathParts;
1485-
1486-
public KeyMapper(String key,
1487-
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
1488-
1489-
this.pathParts = Arrays.asList(key.split("\\."));
1490-
this.iterator = pathParts.iterator();
1491-
this.currentIndex = 0;
1492-
}
1493-
1494-
String nextToken() {
1495-
return pathParts.get(currentIndex + 1);
1496-
}
1497-
1498-
boolean hasNexToken() {
1499-
return pathParts.size() > currentIndex + 1;
1500-
}
1501-
1502-
/**
1503-
* Maps the property name while retaining potential positional operator {@literal $}.
1504-
*
1505-
* @param property
1506-
* @return
1507-
*/
1508-
protected String mapPropertyName(MongoPersistentProperty property) {
1509-
1510-
StringBuilder mappedName = new StringBuilder(PropertyToFieldNameConverter.INSTANCE.convert(property));
1511-
if (!hasNexToken()) {
1512-
return mappedName.toString();
1513-
}
1514-
1515-
String nextToken = nextToken();
1516-
if (isPositionalParameter(nextToken)) {
1517-
1518-
mappedName.append(".").append(nextToken);
1519-
currentIndex += 2;
1520-
return mappedName.toString();
1521-
}
1522-
1523-
if (property.isMap()) {
1524-
1525-
mappedName.append(".").append(nextToken);
1526-
currentIndex += 2;
1527-
return mappedName.toString();
1528-
}
1529-
1530-
currentIndex++;
1531-
return mappedName.toString();
1532-
}
1533-
1534-
static boolean isPositionalParameter(String partial) {
1535-
1536-
if ("$".equals(partial)) {
1537-
return true;
1538-
}
1539-
1540-
Matcher matcher = POSITIONAL_OPERATOR.matcher(partial);
1541-
if (matcher.find()) {
1542-
return true;
1543-
}
1544-
1545-
try {
1546-
Long.valueOf(partial);
1547-
return true;
1548-
} catch (NumberFormatException e) {
1549-
return false;
1550-
}
1551-
}
1552-
}
15531381
}
15541382

15551383
/**

0 commit comments

Comments
 (0)