@@ -189,6 +189,90 @@ private void doWithRandomAccessPattern(Consumer<IngestDocument> action) throws E
189189 doWithAccessPattern (randomFrom (IngestPipelineFieldAccessPattern .values ()), action );
190190 }
191191
192+ private void assertPathValid (IngestDocument doc , String path ) {
193+ // The fields being checked do not exist, so they all return false when running hasField
194+ assertFalse (doc .hasField (path ));
195+ }
196+
197+ private void assertPathInvalid (IngestDocument doc , String path , String errorMessage ) {
198+ IllegalArgumentException expected = expectThrows (IllegalArgumentException .class , () -> doc .hasField (path ));
199+ assertThat (expected .getMessage (), equalTo (errorMessage ));
200+ }
201+
202+ public void testPathParsingLogic () throws Exception {
203+ // Force a blank document for this test
204+ document = new IngestDocument ("index" , "id" , 1 , null , null , new HashMap <>());
205+
206+ doWithRandomAccessPattern ((doc ) -> {
207+ assertPathInvalid (doc , null , "path cannot be null nor empty" );
208+ assertPathInvalid (doc , "" , "path cannot be null nor empty" );
209+ assertPathValid (doc , "a" );
210+ assertPathValid (doc , "ab" );
211+ assertPathValid (doc , "abc" );
212+ assertPathValid (doc , "a.b" );
213+ assertPathValid (doc , "a.b.c" );
214+ // Trailing empty strings are trimmed by field path parsing logic
215+ assertPathValid (doc , "a." );
216+ assertPathValid (doc , "a.." );
217+ assertPathValid (doc , "a..." );
218+ // Empty field names are not allowed in the beginning or middle of the path though
219+ assertPathInvalid (doc , ".a.b" , "path [.a.b] is not valid" );
220+ assertPathInvalid (doc , "a..b" , "path [a..b] is not valid" );
221+ });
222+
223+ doWithAccessPattern (CLASSIC , (doc ) -> {
224+ // Classic allows number fields because they are treated as either field names or array indices depending on context
225+ assertPathValid (doc , "a.0" );
226+ // Classic allows square brackets because it is not part of it's syntax
227+ assertPathValid (doc , "a[0]" );
228+ assertPathValid (doc , "a[]" );
229+ assertPathValid (doc , "a][" );
230+ assertPathValid (doc , "[" );
231+ assertPathValid (doc , "a[" );
232+ assertPathValid (doc , "[a" );
233+ assertPathValid (doc , "]" );
234+ assertPathValid (doc , "a]" );
235+ assertPathValid (doc , "]a" );
236+ assertPathValid (doc , "[]" );
237+ assertPathValid (doc , "][" );
238+ assertPathValid (doc , "[a]" );
239+ assertPathValid (doc , "]a[" );
240+ assertPathValid (doc , "[]a" );
241+ assertPathValid (doc , "][a" );
242+ });
243+
244+ doWithAccessPattern (FLEXIBLE , (doc ) -> {
245+ // Flexible has specific handling of square brackets
246+ assertPathInvalid (doc , "a[0]" , "path [a[0]] is not valid" );
247+ assertPathInvalid (doc , "a[]" , "path [a[]] is not valid" );
248+ assertPathInvalid (doc , "a][" , "path [a][] is not valid" );
249+ assertPathInvalid (doc , "[" , "path [[] is not valid" );
250+ assertPathInvalid (doc , "a[" , "path [a[] is not valid" );
251+ assertPathInvalid (doc , "[a" , "path [[a] is not valid" );
252+ assertPathInvalid (doc , "]" , "path []] is not valid" );
253+ assertPathInvalid (doc , "a]" , "path [a]] is not valid" );
254+ assertPathInvalid (doc , "]a" , "path []a] is not valid" );
255+ assertPathInvalid (doc , "[]" , "path [[]] is not valid" );
256+ assertPathInvalid (doc , "][" , "path [][] is not valid" );
257+ assertPathInvalid (doc , "[a]" , "path [[a]] is not valid" );
258+ assertPathInvalid (doc , "]a[" , "path []a[] is not valid" );
259+ assertPathInvalid (doc , "[]a" , "path [[]a] is not valid" );
260+ assertPathInvalid (doc , "][a" , "path [][a] is not valid" );
261+
262+ assertPathInvalid (doc , "a[0].b" , "path [a[0].b] is not valid" );
263+ assertPathInvalid (doc , "a[0].b[1]" , "path [a[0].b[1]] is not valid" );
264+ assertPathInvalid (doc , "a[0].b[1].c" , "path [a[0].b[1].c] is not valid" );
265+ assertPathInvalid (doc , "a[0].b[1].c[2]" , "path [a[0].b[1].c[2]] is not valid" );
266+ assertPathInvalid (doc , "a[0][1].c[2]" , "path [a[0][1].c[2]] is not valid" );
267+ assertPathInvalid (doc , "a[0].b[1][2]" , "path [a[0].b[1][2]] is not valid" );
268+ assertPathInvalid (doc , "a[0][1][2]" , "path [a[0][1][2]] is not valid" );
269+
270+ assertPathInvalid (doc , "a[0][" , "path [a[0][] is not valid" );
271+ assertPathInvalid (doc , "a[0]]" , "path [a[0]]] is not valid" );
272+ assertPathInvalid (doc , "a[0]blahblah" , "path [a[0]blahblah] is not valid" );
273+ });
274+ }
275+
192276 public void testSimpleGetFieldValue () throws Exception {
193277 doWithRandomAccessPattern ((doc ) -> {
194278 assertThat (doc .getFieldValue ("foo" , String .class ), equalTo ("bar" ));
@@ -2034,7 +2118,7 @@ public void testNestedAccessPatternPropagation() {
20342118
20352119 // At the end of the test, there should be neither pipeline ids nor access patterns left in the stack.
20362120 assertThat (document .getPipelineStack (), is (empty ()));
2037- assertThat (document .getCurrentAccessPattern (), is (nullValue () ));
2121+ assertThat (document .getCurrentAccessPattern (). isEmpty () , is (true ));
20382122 }
20392123
20402124 /**
@@ -2082,7 +2166,8 @@ void doTestNestedAccessPatternPropagation(int level, int maxCallDepth, IngestDoc
20822166
20832167 // Assert expected state
20842168 assertThat (document .getPipelineStack ().getFirst (), is (expectedPipelineId ));
2085- assertThat (document .getCurrentAccessPattern (), is (expectedAccessPattern ));
2169+ assertThat (document .getCurrentAccessPattern ().isPresent (), is (true ));
2170+ assertThat (document .getCurrentAccessPattern ().get (), is (expectedAccessPattern ));
20862171
20872172 // Randomly recurse: We recurse only one time per level to avoid hogging test time, but we randomize which
20882173 // pipeline to recurse on, eventually requiring a recursion on the last pipeline run if one hasn't happened yet.
@@ -2099,11 +2184,11 @@ void doTestNestedAccessPatternPropagation(int level, int maxCallDepth, IngestDoc
20992184 assertThat (document .getPipelineStack ().size (), is (equalTo (level )));
21002185 if (level == 0 ) {
21012186 // Top level means access pattern should be empty
2102- assertThat (document .getCurrentAccessPattern (), is (nullValue () ));
2187+ assertThat (document .getCurrentAccessPattern (). isEmpty () , is (true ));
21032188 } else {
21042189 // If we're nested below the top level we should still have an access
21052190 // pattern on the document for the pipeline above us
2106- assertThat (document .getCurrentAccessPattern (), is (not ( nullValue ()) ));
2191+ assertThat (document .getCurrentAccessPattern (). isPresent () , is (true ));
21072192 }
21082193 }
21092194 logger .debug ("LEVEL {}/{}: COMPLETE" , level , maxCallDepth );
0 commit comments