2626import java .util .Map .Entry ;
2727import java .util .Optional ;
2828import java .util .Set ;
29- import java .util .regex .Matcher ;
3029import java .util .regex .Pattern ;
3130import java .util .stream .Collectors ;
3231
5049import org .springframework .data .mapping .PersistentProperty ;
5150import org .springframework .data .mapping .PersistentPropertyPath ;
5251import org .springframework .data .mapping .PropertyPath ;
53- import org .springframework .data .mapping .PropertyReferenceException ;
5452import org .springframework .data .mapping .context .InvalidPersistentPropertyPath ;
5553import org .springframework .data .mapping .context .MappingContext ;
5654import org .springframework .data .mapping .model .PropertyValueProvider ;
5957import org .springframework .data .mongodb .core .aggregation .RelaxedTypeBasedAggregationOperationContext ;
6058import org .springframework .data .mongodb .core .convert .MappingMongoConverter .NestedDocument ;
6159import org .springframework .data .mongodb .core .mapping .FieldName ;
60+ import org .springframework .data .mongodb .core .mapping .MongoPath ;
6261import org .springframework .data .mongodb .core .mapping .MongoPersistentEntity ;
6362import org .springframework .data .mongodb .core .mapping .MongoPersistentProperty ;
64- import org .springframework .data .mongodb .core .mapping .MongoPersistentProperty .PropertyToFieldNameConverter ;
6563import org .springframework .data .mongodb .core .query .Query ;
6664import org .springframework .data .mongodb .util .BsonUtils ;
6765import 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