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 /**
@@ -1011,8 +1020,6 @@ public boolean isJsonSchema() {
10111020 */
10121021 protected static class Field {
10131022
1014- protected static final Pattern POSITIONAL_OPERATOR = Pattern .compile ("\\ $\\ [.*\\ ]" );
1015-
10161023 protected final String name ;
10171024
10181025 /**
@@ -1123,67 +1130,120 @@ public Class<?> getFieldType() {
11231130 }
11241131 }
11251132
1133+ /**
1134+ * Create a {@link PropertyPath} starting at {@link MongoPersistentEntity}.
1135+ * <p>
1136+ * Can return {@code null} if the property path contains named segments that are not mapped to the entity.
1137+ *
1138+ * @param persistentEntity
1139+ * @return
1140+ */
1141+ @ Nullable
1142+ public PropertyPath toPropertyPath (
1143+ MongoPath mongoPath , MongoPersistentEntity <?> persistentEntity ) {
1144+
1145+ StringBuilder path = new StringBuilder ();
1146+ MongoPersistentEntity <?> entity = persistentEntity ;
1147+
1148+ for (PathSegment segment : mongoPath .segments ()) {
1149+
1150+ if (segment .isKeyword ()) {
1151+ continue ;
1152+ }
1153+
1154+ if (entity == null ) {
1155+ return null ;
1156+ }
1157+
1158+ MongoPersistentProperty persistentProperty = entity .getPersistentProperty (segment .segment ());
1159+
1160+ if (persistentProperty == null ) {
1161+
1162+ if (segment .isNumeric ()) {
1163+ continue ;
1164+
1165+ }
1166+
1167+ return null ;
1168+ }
1169+
1170+ entity = mappingContext .getPersistentEntity (persistentProperty );
1171+
1172+ String name = segment .segment ();
1173+
1174+ if (!path .isEmpty ()) {
1175+ path .append ("." );
1176+ }
1177+ path .append (Pattern .quote (name ));
1178+ }
1179+
1180+ if (path .isEmpty ()) {
1181+ return null ;
1182+ }
1183+
1184+ return PropertyPath .from (path .toString (), persistentEntity .getType ());
1185+ }
1186+
1187+
11261188 /**
11271189 * Extension of {@link Field} to be backed with mapping metadata.
11281190 *
11291191 * @author Oliver Gierke
11301192 * @author Thomas Darimont
11311193 */
1132- protected static class MetadataBackedField extends Field {
1194+ protected class MetadataBackedField extends Field {
11331195
1134- private static final Pattern POSITIONAL_PARAMETER_PATTERN = Pattern .compile ("\\ .\\ $(\\ [.*?\\ ])?" );
1135- private static final Pattern NUMERIC_SEGMENT = Pattern .compile ("\\ d+" );
11361196 private static final String INVALID_ASSOCIATION_REFERENCE = "Invalid path reference %s; Associations can only be pointed to directly or via their id property" ;
11371197
11381198 private final MongoPersistentEntity <?> entity ;
11391199 private final MappingContext <? extends MongoPersistentEntity <?>, MongoPersistentProperty > mappingContext ;
11401200 private final MongoPersistentProperty property ;
1141- private final @ Nullable PersistentPropertyPath <MongoPersistentProperty > path ;
1201+ private final @ Nullable PersistentPropertyPath <MongoPersistentProperty > propertyPath ;
11421202 private final @ Nullable Association <MongoPersistentProperty > association ;
11431203 private final MongoPath mongoPath ;
11441204
11451205 /**
11461206 * Creates a new {@link MetadataBackedField} with the given name, {@link MongoPersistentEntity} and
11471207 * {@link MappingContext}.
11481208 *
1149- * @param name must not be {@literal null} or empty.
1209+ * @param path must not be {@literal null} or empty.
11501210 * @param entity must not be {@literal null}.
11511211 * @param context must not be {@literal null}.
11521212 */
1153- public MetadataBackedField (String name , MongoPersistentEntity <?> entity ,
1213+ public MetadataBackedField (MongoPath path , MongoPersistentEntity <?> entity ,
11541214 MappingContext <? extends MongoPersistentEntity <?>, MongoPersistentProperty > context ) {
1155- this (name , entity , context , null );
1215+ this (path , entity , context , null );
11561216 }
11571217
11581218 /**
11591219 * Creates a new {@link MetadataBackedField} with the given name, {@link MongoPersistentEntity} and
11601220 * {@link MappingContext} with the given {@link MongoPersistentProperty}.
11611221 *
1162- * @param name must not be {@literal null} or empty.
1222+ * @param path must not be {@literal null} or empty.
11631223 * @param entity must not be {@literal null}.
11641224 * @param context must not be {@literal null}.
11651225 * @param property may be {@literal null}.
11661226 */
1167- public MetadataBackedField (String name , MongoPersistentEntity <?> entity ,
1227+ public MetadataBackedField (MongoPath path , MongoPersistentEntity <?> entity ,
11681228 MappingContext <? extends MongoPersistentEntity <?>, MongoPersistentProperty > context ,
11691229 @ Nullable MongoPersistentProperty property ) {
11701230
1171- super (name );
1231+ super (path . path () );
11721232
11731233 Assert .notNull (entity , "MongoPersistentEntity must not be null" );
11741234
11751235 this .entity = entity ;
11761236 this .mappingContext = context ;
11771237
1178- this .mongoPath = MongoPath . parse ( name ) ;
1179- this .path = getPath (mongoPath , property );
1180- this .property = path == null ? property : path .getLeafProperty ();
1238+ this .mongoPath = path ;
1239+ this .propertyPath = getPath (mongoPath , property );
1240+ this .property = this . propertyPath == null ? property : this . propertyPath .getLeafProperty ();
11811241 this .association = findAssociation ();
11821242 }
11831243
11841244 @ Override
11851245 public MetadataBackedField with (String name ) {
1186- return new MetadataBackedField (name , entity , mappingContext , property );
1246+ return new MetadataBackedField (mongoPath , entity , mappingContext , property );
11871247 }
11881248
11891249 @ Override
@@ -1237,8 +1297,8 @@ public Association<MongoPersistentProperty> getAssociation() {
12371297 @ Nullable
12381298 private Association <MongoPersistentProperty > findAssociation () {
12391299
1240- if (this .path != null ) {
1241- for (MongoPersistentProperty p : this .path ) {
1300+ if (this .propertyPath != null ) {
1301+ for (MongoPersistentProperty p : this .propertyPath ) {
12421302
12431303 Association <MongoPersistentProperty > association = p .getAssociation ();
12441304
@@ -1261,19 +1321,20 @@ public String getMappedKey() {
12611321
12621322 // TODO: Switch to MongoPath?!
12631323 if (isAssociation ()) {
1264- return path == null ? name : path .toDotPath (getAssociationConverter ());
1324+ return propertyPath == null ? name : propertyPath .toDotPath (getAssociationConverter ());
12651325 }
12661326
12671327 if (entity != null ) {
1268- return mongoPath . applyFieldNames ( mappingContext , entity ).toString ();
1328+ return paths . mappedPath ( mongoPath , entity . getTypeInformation () ).toString ();
12691329 }
12701330
12711331 return name ;
12721332 }
12731333
1334+
12741335 @ Nullable
12751336 protected PersistentPropertyPath <MongoPersistentProperty > getPath () {
1276- return path ;
1337+ return propertyPath ;
12771338 }
12781339
12791340 /**
@@ -1290,7 +1351,7 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(MongoPath mongoP
12901351 PropertyPath .from (Pattern .quote (sourceProperty .getName ()), entity .getTypeInformation ()));
12911352 }
12921353
1293- PropertyPath path = mongoPath . toPropertyPath (mappingContext , entity );
1354+ PropertyPath path = toPropertyPath (mongoPath , entity );
12941355
12951356 if (path == null || isPathToJavaLangClassProperty (path )) {
12961357 return null ;
0 commit comments