5757import org .springframework .data .mongodb .core .aggregation .RelaxedTypeBasedAggregationOperationContext ;
5858import org .springframework .data .mongodb .core .convert .MappingMongoConverter .NestedDocument ;
5959import org .springframework .data .mongodb .core .mapping .FieldName ;
60+ import org .springframework .data .mongodb .core .mapping .MongoPath .MappedMongoPath ;
61+ import org .springframework .data .mongodb .core .mapping .MongoPath .MappedMongoPath .MappedSegment ;
6062import org .springframework .data .mongodb .core .mapping .MongoPath ;
63+ import org .springframework .data .mongodb .core .mapping .MongoPath .PathSegment ;
64+ import org .springframework .data .mongodb .core .mapping .MongoPaths ;
65+ import org .springframework .data .mongodb .core .mapping .MongoPath .RawMongoPath ;
66+ import org .springframework .data .mongodb .core .mapping .MongoPath .RawMongoPath .Segment ;
67+ import org .springframework .data .mongodb .core .mapping .MongoPath .RawMongoPath .TargetType ;
6168import org .springframework .data .mongodb .core .mapping .MongoPersistentEntity ;
6269import org .springframework .data .mongodb .core .mapping .MongoPersistentProperty ;
6370import org .springframework .data .mongodb .core .query .Query ;
@@ -104,6 +111,7 @@ private enum MetaMapping {
104111 private final MappingContext <? extends MongoPersistentEntity <?>, MongoPersistentProperty > mappingContext ;
105112 private final MongoExampleMapper exampleMapper ;
106113 private final MongoJsonSchemaMapper schemaMapper ;
114+ protected final MongoPaths paths ;
107115
108116 /**
109117 * Creates a new {@link QueryMapper} with the given {@link MongoConverter}.
@@ -119,6 +127,7 @@ public QueryMapper(MongoConverter converter) {
119127 this .mappingContext = converter .getMappingContext ();
120128 this .exampleMapper = new MongoExampleMapper (converter );
121129 this .schemaMapper = new MongoJsonSchemaMapper (converter );
130+ this .paths = new MongoPaths (mappingContext );
122131 }
123132
124133 public Document getMappedObject (Bson query , Optional <? extends MongoPersistentEntity <?>> entity ) {
@@ -381,10 +390,10 @@ protected Field createPropertyField(@Nullable MongoPersistentEntity<?> entity, S
381390 }
382391
383392 if (FieldName .ID .name ().equals (key )) {
384- return new MetadataBackedField (key , entity , mappingContext , entity .getIdProperty ());
393+ return new MetadataBackedField (paths . create ( key ) , entity , mappingContext , entity .getIdProperty ());
385394 }
386395
387- return new MetadataBackedField (key , entity , mappingContext );
396+ return new MetadataBackedField (paths . create ( key ) , entity , mappingContext );
388397 }
389398
390399 /**
@@ -1123,67 +1132,120 @@ public Class<?> getFieldType() {
11231132 }
11241133 }
11251134
1135+ /**
1136+ * Create a {@link PropertyPath} starting at {@link MongoPersistentEntity}.
1137+ * <p>
1138+ * Can return {@code null} if the property path contains named segments that are not mapped to the entity.
1139+ *
1140+ * @param persistentEntity
1141+ * @return
1142+ */
1143+ @ Nullable
1144+ public PropertyPath toPropertyPath (
1145+ MongoPath mongoPath , MongoPersistentEntity <?> persistentEntity ) {
1146+
1147+ StringBuilder path = new StringBuilder ();
1148+ MongoPersistentEntity <?> entity = persistentEntity ;
1149+
1150+ for (PathSegment segment : mongoPath .segments ()) {
1151+
1152+ if (segment .isKeyword ()) {
1153+ continue ;
1154+ }
1155+
1156+ if (entity == null ) {
1157+ return null ;
1158+ }
1159+
1160+ MongoPersistentProperty persistentProperty = entity .getPersistentProperty (segment .segment ());
1161+
1162+ if (persistentProperty == null ) {
1163+
1164+ if (segment .isNumeric ()) {
1165+ continue ;
1166+
1167+ }
1168+
1169+ return null ;
1170+ }
1171+
1172+ entity = mappingContext .getPersistentEntity (persistentProperty );
1173+
1174+ String name = segment .segment ();
1175+
1176+ if (!path .isEmpty ()) {
1177+ path .append ("." );
1178+ }
1179+ path .append (Pattern .quote (name ));
1180+ }
1181+
1182+ if (path .isEmpty ()) {
1183+ return null ;
1184+ }
1185+
1186+ return PropertyPath .from (path .toString (), persistentEntity .getType ());
1187+ }
1188+
1189+
11261190 /**
11271191 * Extension of {@link Field} to be backed with mapping metadata.
11281192 *
11291193 * @author Oliver Gierke
11301194 * @author Thomas Darimont
11311195 */
1132- protected static class MetadataBackedField extends Field {
1196+ protected class MetadataBackedField extends Field {
11331197
1134- private static final Pattern POSITIONAL_PARAMETER_PATTERN = Pattern .compile ("\\ .\\ $(\\ [.*?\\ ])?" );
1135- private static final Pattern NUMERIC_SEGMENT = Pattern .compile ("\\ d+" );
11361198 private static final String INVALID_ASSOCIATION_REFERENCE = "Invalid path reference %s; Associations can only be pointed to directly or via their id property" ;
11371199
11381200 private final MongoPersistentEntity <?> entity ;
11391201 private final MappingContext <? extends MongoPersistentEntity <?>, MongoPersistentProperty > mappingContext ;
11401202 private final MongoPersistentProperty property ;
1141- private final @ Nullable PersistentPropertyPath <MongoPersistentProperty > path ;
1203+ private final @ Nullable PersistentPropertyPath <MongoPersistentProperty > propertyPath ;
11421204 private final @ Nullable Association <MongoPersistentProperty > association ;
11431205 private final MongoPath mongoPath ;
11441206
11451207 /**
11461208 * Creates a new {@link MetadataBackedField} with the given name, {@link MongoPersistentEntity} and
11471209 * {@link MappingContext}.
11481210 *
1149- * @param name must not be {@literal null} or empty.
1211+ * @param path must not be {@literal null} or empty.
11501212 * @param entity must not be {@literal null}.
11511213 * @param context must not be {@literal null}.
11521214 */
1153- public MetadataBackedField (String name , MongoPersistentEntity <?> entity ,
1215+ public MetadataBackedField (MongoPath path , MongoPersistentEntity <?> entity ,
11541216 MappingContext <? extends MongoPersistentEntity <?>, MongoPersistentProperty > context ) {
1155- this (name , entity , context , null );
1217+ this (path , entity , context , null );
11561218 }
11571219
11581220 /**
11591221 * Creates a new {@link MetadataBackedField} with the given name, {@link MongoPersistentEntity} and
11601222 * {@link MappingContext} with the given {@link MongoPersistentProperty}.
11611223 *
1162- * @param name must not be {@literal null} or empty.
1224+ * @param path must not be {@literal null} or empty.
11631225 * @param entity must not be {@literal null}.
11641226 * @param context must not be {@literal null}.
11651227 * @param property may be {@literal null}.
11661228 */
1167- public MetadataBackedField (String name , MongoPersistentEntity <?> entity ,
1229+ public MetadataBackedField (MongoPath path , MongoPersistentEntity <?> entity ,
11681230 MappingContext <? extends MongoPersistentEntity <?>, MongoPersistentProperty > context ,
11691231 @ Nullable MongoPersistentProperty property ) {
11701232
1171- super (name );
1233+ super (path . path () );
11721234
11731235 Assert .notNull (entity , "MongoPersistentEntity must not be null" );
11741236
11751237 this .entity = entity ;
11761238 this .mappingContext = context ;
11771239
1178- this .mongoPath = MongoPath . parse ( name ) ;
1179- this .path = getPath (mongoPath , property );
1180- this .property = path == null ? property : path .getLeafProperty ();
1240+ this .mongoPath = path ;
1241+ this .propertyPath = getPath (mongoPath , property );
1242+ this .property = this . propertyPath == null ? property : this . propertyPath .getLeafProperty ();
11811243 this .association = findAssociation ();
11821244 }
11831245
11841246 @ Override
11851247 public MetadataBackedField with (String name ) {
1186- return new MetadataBackedField (name , entity , mappingContext , property );
1248+ return new MetadataBackedField (mongoPath , entity , mappingContext , property );
11871249 }
11881250
11891251 @ Override
@@ -1237,8 +1299,8 @@ public Association<MongoPersistentProperty> getAssociation() {
12371299 @ Nullable
12381300 private Association <MongoPersistentProperty > findAssociation () {
12391301
1240- if (this .path != null ) {
1241- for (MongoPersistentProperty p : this .path ) {
1302+ if (this .propertyPath != null ) {
1303+ for (MongoPersistentProperty p : this .propertyPath ) {
12421304
12431305 Association <MongoPersistentProperty > association = p .getAssociation ();
12441306
@@ -1261,19 +1323,20 @@ public String getMappedKey() {
12611323
12621324 // TODO: Switch to MongoPath?!
12631325 if (isAssociation ()) {
1264- return path == null ? name : path .toDotPath (getAssociationConverter ());
1326+ return propertyPath == null ? name : propertyPath .toDotPath (getAssociationConverter ());
12651327 }
12661328
12671329 if (entity != null ) {
1268- return mongoPath . applyFieldNames ( mappingContext , entity ).toString ();
1330+ return paths . mappedPath ( mongoPath , entity . getTypeInformation () ).toString ();
12691331 }
12701332
12711333 return name ;
12721334 }
12731335
1336+
12741337 @ Nullable
12751338 protected PersistentPropertyPath <MongoPersistentProperty > getPath () {
1276- return path ;
1339+ return propertyPath ;
12771340 }
12781341
12791342 /**
@@ -1290,7 +1353,7 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(MongoPath mongoP
12901353 PropertyPath .from (Pattern .quote (sourceProperty .getName ()), entity .getTypeInformation ()));
12911354 }
12921355
1293- PropertyPath path = mongoPath . toPropertyPath (mappingContext , entity );
1356+ PropertyPath path = toPropertyPath (mongoPath , entity );
12941357
12951358 if (path == null || isPathToJavaLangClassProperty (path )) {
12961359 return null ;
0 commit comments