2020import java .util .Map .Entry ;
2121
2222import org .springframework .core .convert .converter .Converter ;
23+ import org .springframework .data .mapping .Association ;
2324import org .springframework .data .mapping .context .MappingContext ;
2425import org .springframework .data .mongodb .core .mapping .MongoPersistentEntity ;
2526import org .springframework .data .mongodb .core .mapping .MongoPersistentProperty ;
@@ -193,18 +194,60 @@ public String getMappedKey() {
193194 */
194195 @ Override
195196 protected Converter <MongoPersistentProperty , String > getPropertyConverter () {
196- return isAssociation () ? new AssociationConverter (getAssociation ()) : new UpdatePropertyConverter (key );
197+ return new UpdatePropertyConverter (key );
198+ }
199+
200+ /*
201+ * (non-Javadoc)
202+ * @see org.springframework.data.mongodb.core.convert.QueryMapper.MetadataBackedField#getAssociationConverter()
203+ */
204+ @ Override
205+ protected Converter <MongoPersistentProperty , String > getAssociationConverter () {
206+ return new UpdateAssociationConverter (getAssociation (), key );
207+ }
208+
209+ /**
210+ * Special mapper handling positional parameter {@literal $} within property names.
211+ *
212+ * @author Christoph Strobl
213+ * @since 1.7
214+ */
215+ private static class UpdateKeyMapper {
216+
217+ private final Iterator <String > iterator ;
218+
219+ protected UpdateKeyMapper (String rawKey ) {
220+
221+ Assert .hasText (rawKey , "Key must not be null or empty!" );
222+
223+ this .iterator = Arrays .asList (rawKey .split ("\\ ." )).iterator ();
224+ this .iterator .next ();
225+ }
226+
227+ /**
228+ * Maps the property name while retaining potential positional operator {@literal $}.
229+ *
230+ * @param property
231+ * @return
232+ */
233+ protected String mapPropertyName (MongoPersistentProperty property ) {
234+
235+ String mappedName = PropertyToFieldNameConverter .INSTANCE .convert (property );
236+ return iterator .hasNext () && iterator .next ().equals ("$" ) ? String .format ("%s.$" , mappedName ) : mappedName ;
237+ }
238+
197239 }
198240
199241 /**
200242 * Special {@link Converter} for {@link MongoPersistentProperty} instances that will concatenate the {@literal $}
201243 * contained in the source update key.
202244 *
203245 * @author Oliver Gierke
246+ * @author Christoph Strobl
204247 */
205248 private static class UpdatePropertyConverter implements Converter <MongoPersistentProperty , String > {
206249
207- private final Iterator < String > iterator ;
250+ private final UpdateKeyMapper mapper ;
208251
209252 /**
210253 * Creates a new {@link UpdatePropertyConverter} with the given update key.
@@ -215,8 +258,7 @@ public UpdatePropertyConverter(String updateKey) {
215258
216259 Assert .hasText (updateKey , "Update key must not be null or empty!" );
217260
218- this .iterator = Arrays .asList (updateKey .split ("\\ ." )).iterator ();
219- this .iterator .next ();
261+ this .mapper = new UpdateKeyMapper (updateKey );
220262 }
221263
222264 /*
@@ -225,9 +267,37 @@ public UpdatePropertyConverter(String updateKey) {
225267 */
226268 @ Override
227269 public String convert (MongoPersistentProperty property ) {
270+ return mapper .mapPropertyName (property );
271+ }
272+ }
228273
229- String mappedName = PropertyToFieldNameConverter .INSTANCE .convert (property );
230- return iterator .hasNext () && iterator .next ().equals ("$" ) ? String .format ("%s.$" , mappedName ) : mappedName ;
274+ /**
275+ * {@link Converter} retaining positional parameter {@literal $} for {@link Association}s.
276+ *
277+ * @author Christoph Strobl
278+ */
279+ protected static class UpdateAssociationConverter extends AssociationConverter {
280+
281+ private final UpdateKeyMapper mapper ;
282+
283+ /**
284+ * Creates a new {@link AssociationConverter} for the given {@link Association}.
285+ *
286+ * @param association must not be {@literal null}.
287+ */
288+ public UpdateAssociationConverter (Association <MongoPersistentProperty > association , String key ) {
289+
290+ super (association );
291+ this .mapper = new UpdateKeyMapper (key );
292+ }
293+
294+ /*
295+ * (non-Javadoc)
296+ * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
297+ */
298+ @ Override
299+ public String convert (MongoPersistentProperty source ) {
300+ return super .convert (source ) == null ? null : mapper .mapPropertyName (source );
231301 }
232302 }
233303 }
0 commit comments