@@ -114,16 +114,21 @@ public static class Builder extends ParametrizedFieldMapper.Builder {
114114 = Parameter .analyzerParam ("analyzer" , true , m -> toType (m ).analyzer , () -> Lucene .KEYWORD_ANALYZER );
115115 final Parameter <NamedAnalyzer > searchAnalyzer
116116 = Parameter .analyzerParam ("search_analyzer" , true , m -> toType (m ).searchAnalyzer , analyzer ::getValue );
117-
118117 final Parameter <Boolean > index = Parameter .boolParam ("index" , false , m -> toType (m ).index , true );
118+ final Parameter <String > required = Parameter .stringParam ("required" , true , m -> toType (m ).required , null )
119+ .setValidator (value -> {
120+ if (value == null ) {
121+ throw new IllegalArgumentException ("field [required] must be specified" );
122+ }
123+ });
119124
120125 protected Builder (String name ) {
121126 super (name );
122127 }
123128
124129 @ Override
125130 protected List <Parameter <?>> getParameters () {
126- return List .of (fixed , fixed2 , variable , index , wrapper , intValue , analyzer , searchAnalyzer );
131+ return List .of (fixed , fixed2 , variable , index , wrapper , intValue , analyzer , searchAnalyzer , required );
127132 }
128133
129134 @ Override
@@ -153,6 +158,7 @@ public static class TestMapper extends ParametrizedFieldMapper {
153158 private final NamedAnalyzer analyzer ;
154159 private final NamedAnalyzer searchAnalyzer ;
155160 private final boolean index ;
161+ private final String required ;
156162
157163 protected TestMapper (String simpleName , String fullName , MultiFields multiFields , CopyTo copyTo ,
158164 ParametrizedMapperTests .Builder builder ) {
@@ -165,6 +171,7 @@ protected TestMapper(String simpleName, String fullName, MultiFields multiFields
165171 this .analyzer = builder .analyzer .getValue ();
166172 this .searchAnalyzer = builder .searchAnalyzer .getValue ();
167173 this .index = builder .index .getValue ();
174+ this .required = builder .required .getValue ();
168175 }
169176
170177 @ Override
@@ -173,7 +180,7 @@ public Builder getMergeBuilder() {
173180 }
174181
175182 @ Override
176- protected void parseCreateField (ParseContext context ) throws IOException {
183+ protected void parseCreateField (ParseContext context ) {
177184
178185 }
179186
@@ -211,7 +218,7 @@ private static TestMapper fromMapping(String mapping) {
211218
212219 // defaults - create empty builder config, and serialize with and without defaults
213220 public void testDefaults () throws IOException {
214- String mapping = "{\" type\" :\" test_mapper\" }" ;
221+ String mapping = "{\" type\" :\" test_mapper\" , \" required \" : \" value \" }" ;
215222 TestMapper mapper = fromMapping (mapping );
216223
217224 assertTrue (mapper .fixed );
@@ -226,17 +233,18 @@ public void testDefaults() throws IOException {
226233 builder .endObject ();
227234 assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" fixed\" :true," +
228235 "\" fixed2\" :false,\" variable\" :\" default\" ,\" index\" :true," +
229- "\" wrapper\" :\" default\" ,\" int_value\" :5,\" analyzer\" :\" _keyword\" ,\" search_analyzer\" :\" _keyword\" }}" ,
236+ "\" wrapper\" :\" default\" ,\" int_value\" :5,\" analyzer\" :\" _keyword\" ," +
237+ "\" search_analyzer\" :\" _keyword\" ,\" required\" :\" value\" }}" ,
230238 Strings .toString (builder ));
231239 }
232240
233241 // merging - try updating 'fixed' and 'fixed2' should get an error, try updating 'variable' and verify update
234242 public void testMerging () {
235- String mapping = "{\" type\" :\" test_mapper\" ,\" fixed\" :false}" ;
243+ String mapping = "{\" type\" :\" test_mapper\" ,\" fixed\" :false, \" required \" : \" value \" }" ;
236244 TestMapper mapper = fromMapping (mapping );
237245 assertEquals ("{\" field\" :" + mapping + "}" , Strings .toString (mapper ));
238246
239- TestMapper badMerge = fromMapping ("{\" type\" :\" test_mapper\" ,\" fixed\" :true,\" fixed2\" :true}" );
247+ TestMapper badMerge = fromMapping ("{\" type\" :\" test_mapper\" ,\" fixed\" :true,\" fixed2\" :true, \" required \" : \" value \" }" );
240248 IllegalArgumentException e = expectThrows (IllegalArgumentException .class , () -> mapper .merge (badMerge ));
241249 String expectedError = "Mapper for [field] conflicts with existing mapper:\n " +
242250 "\t Cannot update parameter [fixed] from [false] to [true]\n " +
@@ -246,56 +254,61 @@ public void testMerging() {
246254 assertEquals ("{\" field\" :" + mapping + "}" , Strings .toString (mapper )); // original mapping is unaffected
247255
248256 // TODO: should we have to include 'fixed' here? Or should updates take as 'defaults' the existing values?
249- TestMapper goodMerge = fromMapping ("{\" type\" :\" test_mapper\" ,\" fixed\" :false,\" variable\" :\" updated\" }" );
257+ TestMapper goodMerge = fromMapping ("{\" type\" :\" test_mapper\" ,\" fixed\" :false,\" variable\" :\" updated\" , \" required \" : \" value \" }" );
250258 TestMapper merged = (TestMapper ) mapper .merge (goodMerge );
251259
252260 assertEquals ("{\" field\" :" + mapping + "}" , Strings .toString (mapper )); // original mapping is unaffected
253- assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" fixed\" :false,\" variable\" :\" updated\" }}" , Strings . toString ( merged ));
254-
261+ assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" fixed\" :false,\" variable\" :\" updated\" , \" required \" : \" value \" }}" ,
262+ Strings . toString ( merged ));
255263 }
256264
257265 // add multifield, verify, add second multifield, verify, overwrite second multifield
258266 public void testMultifields () {
259- String mapping = "{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ,\" fields\" :{\" sub\" :{\" type\" :\" keyword\" }}}" ;
267+ String mapping = "{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ,\" required\" :\" value\" ," +
268+ "\" fields\" :{\" sub\" :{\" type\" :\" keyword\" }}}" ;
260269 TestMapper mapper = fromMapping (mapping );
261270 assertEquals ("{\" field\" :" + mapping + "}" , Strings .toString (mapper ));
262271
263- String addSubField = "{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ,\" fields\" :{\" sub2\" :{\" type\" :\" keyword\" }}}" ;
272+ String addSubField = "{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ,\" required\" :\" value\" " +
273+ ",\" fields\" :{\" sub2\" :{\" type\" :\" keyword\" }}}" ;
264274 TestMapper toMerge = fromMapping (addSubField );
265275 TestMapper merged = (TestMapper ) mapper .merge (toMerge );
266- assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ," +
276+ assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ,\" required \" : \" value \" , " +
267277 "\" fields\" :{\" sub\" :{\" type\" :\" keyword\" },\" sub2\" :{\" type\" :\" keyword\" }}}}" , Strings .toString (merged ));
268278
269- String badSubField = "{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ,\" fields\" :{\" sub2\" :{\" type\" :\" binary\" }}}" ;
279+ String badSubField = "{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ,\" required\" :\" value\" ," +
280+ "\" fields\" :{\" sub2\" :{\" type\" :\" binary\" }}}" ;
270281 TestMapper badToMerge = fromMapping (badSubField );
271282 IllegalArgumentException e = expectThrows (IllegalArgumentException .class , () -> merged .merge (badToMerge ));
272283 assertEquals ("mapper [field.sub2] cannot be changed from type [keyword] to [binary]" , e .getMessage ());
273284 }
274285
275286 // add copy_to, verify
276287 public void testCopyTo () {
277- String mapping = "{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ,\" copy_to\" :[\" other\" ]}" ;
288+ String mapping = "{\" type\" :\" test_mapper\" ,\" variable\" :\" foo\" ,\" required \" : \" value \" , \" copy_to\" :[\" other\" ]}" ;
278289 TestMapper mapper = fromMapping (mapping );
279290 assertEquals ("{\" field\" :" + mapping + "}" , Strings .toString (mapper ));
280291
281292 // On update, copy_to is completely replaced
282293
283- TestMapper toMerge = fromMapping ("{\" type\" :\" test_mapper\" ,\" variable\" :\" updated\" ,\" copy_to\" :[\" foo\" ,\" bar\" ]}" );
294+ TestMapper toMerge = fromMapping ("{\" type\" :\" test_mapper\" ,\" variable\" :\" updated\" ,\" required\" :\" value\" ," +
295+ "\" copy_to\" :[\" foo\" ,\" bar\" ]}" );
284296 TestMapper merged = (TestMapper ) mapper .merge (toMerge );
285- assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" variable\" :\" updated\" ,\" copy_to\" :[\" foo\" ,\" bar\" ]}}" ,
297+ assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" variable\" :\" updated\" ,\" required\" :\" value\" ," +
298+ "\" copy_to\" :[\" foo\" ,\" bar\" ]}}" ,
286299 Strings .toString (merged ));
287300
288- TestMapper removeCopyTo = fromMapping ("{\" type\" :\" test_mapper\" ,\" variable\" :\" updated\" }" );
301+ TestMapper removeCopyTo = fromMapping ("{\" type\" :\" test_mapper\" ,\" variable\" :\" updated\" , \" required \" : \" value \" }" );
289302 TestMapper noCopyTo = (TestMapper ) merged .merge (removeCopyTo );
290- assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" variable\" :\" updated\" }}" , Strings .toString (noCopyTo ));
303+ assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" variable\" :\" updated\" , \" required \" : \" value \" }}" , Strings .toString (noCopyTo ));
291304 }
292305
293306 public void testNullables () {
294- String mapping = "{\" type\" :\" test_mapper\" ,\" fixed\" :null}" ;
307+ String mapping = "{\" type\" :\" test_mapper\" ,\" fixed\" :null, \" required \" : \" value \" }" ;
295308 MapperParsingException e = expectThrows (MapperParsingException .class , () -> fromMapping (mapping ));
296309 assertEquals ("[fixed] on mapper [field] of type [test_mapper] must not have a [null] value" , e .getMessage ());
297310
298- String fine = "{\" type\" :\" test_mapper\" ,\" variable\" :null}" ;
311+ String fine = "{\" type\" :\" test_mapper\" ,\" variable\" :null, \" required \" : \" value \" }" ;
299312 TestMapper mapper = fromMapping (fine );
300313 assertEquals ("{\" field\" :" + fine + "}" , Strings .toString (mapper ));
301314 }
@@ -321,82 +334,93 @@ public void testObjectSerialization() throws IOException {
321334
322335 // test custom serializer
323336 public void testCustomSerialization () {
324- String mapping = "{\" type\" :\" test_mapper\" ,\" wrapper\" :\" wrapped value\" }" ;
337+ String mapping = "{\" type\" :\" test_mapper\" ,\" wrapper\" :\" wrapped value\" , \" required \" : \" value \" }" ;
325338 TestMapper mapper = fromMapping (mapping );
326339 assertEquals ("wrapped value" , mapper .wrapper .name );
327340 assertEquals ("{\" field\" :" + mapping + "}" , Strings .toString (mapper ));
328341 }
329342
330343 // test validator
331344 public void testParameterValidation () {
332- String mapping = "{\" type\" :\" test_mapper\" ,\" int_value\" :10}" ;
345+ String mapping = "{\" type\" :\" test_mapper\" ,\" int_value\" :10, \" required \" : \" value \" }" ;
333346 TestMapper mapper = fromMapping (mapping );
334347 assertEquals (10 , mapper .intValue );
335348 assertEquals ("{\" field\" :" + mapping + "}" , Strings .toString (mapper ));
336349
337350 IllegalArgumentException e = expectThrows (IllegalArgumentException .class ,
338- () -> fromMapping ("{\" type\" :\" test_mapper\" ,\" int_value\" :60}" ));
351+ () -> fromMapping ("{\" type\" :\" test_mapper\" ,\" int_value\" :60, \" required \" : \" value \" }" ));
339352 assertEquals ("Value of [n] cannot be greater than 50" , e .getMessage ());
340-
341353 }
342354
343355 // test deprecations
344356 public void testDeprecatedParameterName () {
345- String mapping = "{\" type\" :\" test_mapper\" ,\" fixed2_old\" :true}" ;
357+ String mapping = "{\" type\" :\" test_mapper\" ,\" fixed2_old\" :true, \" required \" : \" value \" }" ;
346358 TestMapper mapper = fromMapping (mapping );
347359 assertTrue (mapper .fixed2 );
348360 assertWarnings ("Parameter [fixed2_old] on mapper [field] is deprecated, use [fixed2]" );
349- assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" fixed2\" :true}}" , Strings .toString (mapper ));
361+ assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" fixed2\" :true, \" required \" : \" value \" }}" , Strings .toString (mapper ));
350362 }
351363
352364 public void testAnalyzers () {
353- String mapping = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" _standard\" }" ;
365+ String mapping = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" _standard\" , \" required \" : \" value \" }" ;
354366 TestMapper mapper = fromMapping (mapping );
355367 assertEquals (mapper .analyzer , Lucene .STANDARD_ANALYZER );
356368 assertEquals ("{\" field\" :" + mapping + "}" , Strings .toString (mapper ));
357369
358- String withDef = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" default\" }" ;
370+ String withDef = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" default\" , \" required \" : \" value \" }" ;
359371 mapper = fromMapping (withDef );
360372 assertEquals (mapper .analyzer .name (), "default" );
361373 assertThat (mapper .analyzer .analyzer (), instanceOf (StandardAnalyzer .class ));
362374 assertEquals ("{\" field\" :" + withDef + "}" , Strings .toString (mapper ));
363375
364- String badAnalyzer = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" wibble\" }" ;
376+ String badAnalyzer = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" wibble\" , \" required \" : \" value \" }" ;
365377 IllegalArgumentException e = expectThrows (IllegalArgumentException .class , () -> fromMapping (badAnalyzer ));
366378 assertEquals ("analyzer [wibble] has not been configured in mappings" , e .getMessage ());
367379 }
368380
369- public void testDeprecatedParameters () throws IOException {
381+ public void testDeprecatedParameters () {
370382 // 'index' is declared explicitly, 'store' is not, but is one of the previously always-accepted params
371- String mapping = "{\" type\" :\" test_mapper\" ,\" index\" :false,\" store\" :true}" ;
383+ String mapping = "{\" type\" :\" test_mapper\" ,\" index\" :false,\" store\" :true, \" required \" : \" value \" }" ;
372384 TestMapper mapper = fromMapping (mapping , Version .V_7_8_0 );
373385 assertWarnings ("Parameter [store] has no effect on type [test_mapper] and will be removed in future" );
374386 assertFalse (mapper .index );
375- assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" index\" :false}}" , Strings .toString (mapper ));
387+ assertEquals ("{\" field\" :{\" type\" :\" test_mapper\" ,\" index\" :false, \" required \" : \" value \" }}" , Strings .toString (mapper ));
376388
377389 MapperParsingException e = expectThrows (MapperParsingException .class , () -> fromMapping (mapping , Version .V_8_0_0 ));
378390 assertEquals ("unknown parameter [store] on mapper [field] of type [test_mapper]" , e .getMessage ());
379391 }
380392
381393 public void testLinkedAnalyzers () {
382- String mapping = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" _standard\" }" ;
394+ String mapping = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" _standard\" , \" required \" : \" value \" }" ;
383395 TestMapper mapper = fromMapping (mapping );
384396 assertEquals ("_standard" , mapper .analyzer .name ());
385397 assertEquals ("_standard" , mapper .searchAnalyzer .name ());
386398 assertEquals ("{\" field\" :" + mapping + "}" , Strings .toString (mapper ));
387399
388- String mappingWithSA = "{\" type\" :\" test_mapper\" ,\" search_analyzer\" :\" _standard\" }" ;
400+ String mappingWithSA = "{\" type\" :\" test_mapper\" ,\" search_analyzer\" :\" _standard\" , \" required \" : \" value \" }" ;
389401 mapper = fromMapping (mappingWithSA );
390402 assertEquals ("_keyword" , mapper .analyzer .name ());
391403 assertEquals ("_standard" , mapper .searchAnalyzer .name ());
392404 assertEquals ("{\" field\" :" + mappingWithSA + "}" , Strings .toString (mapper ));
393405
394- String mappingWithBoth = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" default\" ,\" search_analyzer\" :\" _standard\" }" ;
406+ String mappingWithBoth = "{\" type\" :\" test_mapper\" ,\" analyzer\" :\" default\" ," +
407+ "\" search_analyzer\" :\" _standard\" ,\" required\" :\" value\" }" ;
395408 mapper = fromMapping (mappingWithBoth );
396409 assertEquals ("default" , mapper .analyzer .name ());
397410 assertEquals ("_standard" , mapper .searchAnalyzer .name ());
398411 assertEquals ("{\" field\" :" + mappingWithBoth + "}" , Strings .toString (mapper ));
399-
400412 }
401413
414+ public void testRequiredField () {
415+ {
416+ String mapping = "{\" type\" :\" test_mapper\" }" ;
417+ IllegalArgumentException iae = expectThrows (IllegalArgumentException .class , () -> fromMapping (mapping ));
418+ assertEquals ("field [required] must be specified" , iae .getMessage ());
419+ }
420+ {
421+ String mapping = "{\" type\" :\" test_mapper\" ,\" required\" :null}" ;
422+ MapperParsingException exc = expectThrows (MapperParsingException .class , () -> fromMapping (mapping ));
423+ assertEquals ("[required] on mapper [field] of type [test_mapper] must not have a [null] value" , exc .getMessage ());
424+ }
425+ }
402426}
0 commit comments