24
24
import java .util .stream .Collectors ;
25
25
26
26
import org .bson .Document ;
27
-
28
27
import org .springframework .data .mapping .PersistentProperty ;
29
28
import org .springframework .data .mapping .context .MappingContext ;
30
29
import org .springframework .data .mongodb .core .convert .MongoConverter ;
45
44
import org .springframework .util .Assert ;
46
45
import org .springframework .util .ClassUtils ;
47
46
import org .springframework .util .CollectionUtils ;
47
+ import org .springframework .util .LinkedMultiValueMap ;
48
48
import org .springframework .util .ObjectUtils ;
49
49
import org .springframework .util .StringUtils ;
50
50
@@ -62,6 +62,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
62
62
private final MongoConverter converter ;
63
63
private final MappingContext <MongoPersistentEntity <?>, MongoPersistentProperty > mappingContext ;
64
64
private final Predicate <JsonSchemaPropertyContext > filter ;
65
+ private final LinkedMultiValueMap <String , Class <?>> mergeProperties ;
65
66
66
67
/**
67
68
* Create a new instance of {@link MappingMongoJsonSchemaCreator}.
@@ -72,23 +73,51 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
72
73
MappingMongoJsonSchemaCreator (MongoConverter converter ) {
73
74
74
75
this (converter , (MappingContext <MongoPersistentEntity <?>, MongoPersistentProperty >) converter .getMappingContext (),
75
- (property ) -> true );
76
+ (property ) -> true , new LinkedMultiValueMap <>() );
76
77
}
77
78
78
79
@ SuppressWarnings ("unchecked" )
79
80
MappingMongoJsonSchemaCreator (MongoConverter converter ,
80
81
MappingContext <MongoPersistentEntity <?>, MongoPersistentProperty > mappingContext ,
81
- Predicate <JsonSchemaPropertyContext > filter ) {
82
+ Predicate <JsonSchemaPropertyContext > filter , LinkedMultiValueMap < String , Class <?>> mergeProperties ) {
82
83
83
84
Assert .notNull (converter , "Converter must not be null!" );
84
85
this .converter = converter ;
85
86
this .mappingContext = mappingContext ;
86
87
this .filter = filter ;
88
+ this .mergeProperties = mergeProperties ;
87
89
}
88
90
89
91
@ Override
90
92
public MongoJsonSchemaCreator filter (Predicate <JsonSchemaPropertyContext > filter ) {
91
- return new MappingMongoJsonSchemaCreator (converter , mappingContext , filter );
93
+ return new MappingMongoJsonSchemaCreator (converter , mappingContext , filter , mergeProperties );
94
+ }
95
+
96
+ @ Override
97
+ public PropertySpecifier specify (String path ) {
98
+ return new PropertySpecifier () {
99
+ @ Override
100
+ public MongoJsonSchemaCreator types (Class <?>... types ) {
101
+ return specifyTypesFor (path , types );
102
+ }
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Specify additional types to be considered wehen rendering the schema for the given path.
108
+ *
109
+ * @param path path the path using {@literal dot '.'} notation.
110
+ * @param types must not be {@literal null}.
111
+ * @return new instance of {@link MongoJsonSchemaCreator}.
112
+ * @since 3.4
113
+ */
114
+ public MongoJsonSchemaCreator specifyTypesFor (String path , Class <?>... types ) {
115
+
116
+ LinkedMultiValueMap <String , Class <?>> clone = mergeProperties .clone ();
117
+ for (Class <?> type : types ) {
118
+ clone .add (path , type );
119
+ }
120
+ return new MappingMongoJsonSchemaCreator (converter , mappingContext , filter , clone );
92
121
}
93
122
94
123
@ Override
@@ -131,9 +160,12 @@ private List<JsonSchemaProperty> computePropertiesForEntity(List<MongoPersistent
131
160
132
161
List <MongoPersistentProperty > currentPath = new ArrayList <>(path );
133
162
134
- if (!filter .test (new PropertyContext (
135
- currentPath .stream ().map (PersistentProperty ::getName ).collect (Collectors .joining ("." )), nested ))) {
136
- continue ;
163
+ String stringPath = currentPath .stream ().map (PersistentProperty ::getName ).collect (Collectors .joining ("." ));
164
+ stringPath = StringUtils .hasText (stringPath ) ? (stringPath + "." + nested .getName ()) : nested .getName ();
165
+ if (!filter .test (new PropertyContext (stringPath , nested ))) {
166
+ if (!mergeProperties .containsKey (stringPath )) {
167
+ continue ;
168
+ }
137
169
}
138
170
139
171
if (path .contains (nested )) { // cycle guard
@@ -151,14 +183,34 @@ private List<JsonSchemaProperty> computePropertiesForEntity(List<MongoPersistent
151
183
152
184
private JsonSchemaProperty computeSchemaForProperty (List <MongoPersistentProperty > path ) {
153
185
186
+ String stringPath = path .stream ().map (MongoPersistentProperty ::getName ).collect (Collectors .joining ("." ));
154
187
MongoPersistentProperty property = CollectionUtils .lastElement (path );
155
188
156
189
boolean required = isRequiredProperty (property );
157
190
Class <?> rawTargetType = computeTargetType (property ); // target type before conversion
158
191
Class <?> targetType = converter .getTypeMapper ().getWriteTargetTypeFor (rawTargetType ); // conversion target type
159
192
160
- if (!isCollection (property ) && property .isEntity () && ObjectUtils .nullSafeEquals (rawTargetType , targetType )) {
161
- return createObjectSchemaPropertyForEntity (path , property , required );
193
+ if (!isCollection (property ) && ObjectUtils .nullSafeEquals (rawTargetType , targetType )) {
194
+ if (property .isEntity () || mergeProperties .containsKey (stringPath )) {
195
+ List <JsonSchemaProperty > targetProperties = new ArrayList <>();
196
+
197
+ if (property .isEntity ()) {
198
+ targetProperties .add (createObjectSchemaPropertyForEntity (path , property , required ));
199
+ }
200
+ if (mergeProperties .containsKey (stringPath )) {
201
+ for (Class <?> theType : mergeProperties .get (stringPath )) {
202
+
203
+ ObjectJsonSchemaProperty target = JsonSchemaProperty .object (property .getName ());
204
+ List <JsonSchemaProperty > nestedProperties = computePropertiesForEntity (path ,
205
+ mappingContext .getRequiredPersistentEntity (theType ));
206
+
207
+ targetProperties .add (createPotentiallyRequiredSchemaProperty (
208
+ target .properties (nestedProperties .toArray (new JsonSchemaProperty [0 ])), required ));
209
+ }
210
+ }
211
+ return targetProperties .size () == 1 ? targetProperties .iterator ().next ()
212
+ : JsonSchemaProperty .combined (targetProperties );
213
+ }
162
214
}
163
215
164
216
String fieldName = computePropertyFieldName (property );
0 commit comments