@@ -545,4 +545,153 @@ public void testOnlyIncludeExcludePrefix() throws IOException {
545545            assertEquals (!expectedFilter [i ], longBitSet .get (i ));
546546        }
547547    }
548+ 
549+     /** 
550+      * Test case for prefix filter when the prefix doesn't exist and would be inserted beyond all existing terms. 
551+      * This validates the fix for the IndexOutOfBoundsException bug. 
552+      */ 
553+     public  void  testPrefixFilterWithNonExistentPrefixBeyondRange () throws  IOException  {
554+         // Create a regex pattern that will trigger prefix optimization 
555+         // The prefix "zzz" doesn't exist in our doc values and would be inserted after all existing terms 
556+         IncludeExclude  includeExclude  = new  IncludeExclude ("zzz.*" , null );
557+ 
558+         OrdinalsFilter  ordinalsFilter  = includeExclude .convertToOrdinalsFilter (DocValueFormat .RAW );
559+ 
560+         // Create doc values with terms that all come before "zzz" alphabetically 
561+         BytesRef [] bytesRefs  = toBytesRefArray ("aaa" , "bbb" , "ccc" );
562+ 
563+         SortedSetDocValues  sortedSetDocValues  = new  AbstractSortedSetDocValues () {
564+             @ Override 
565+             public  boolean  advanceExact (int  target ) {
566+                 return  false ;
567+             }
568+ 
569+             @ Override 
570+             public  long  nextOrd () {
571+                 return  0 ;
572+             }
573+ 
574+             @ Override 
575+             public  int  docValueCount () {
576+                 return  1 ;
577+             }
578+ 
579+             @ Override 
580+             public  BytesRef  lookupOrd (long  ord ) {
581+                 if  (ord  < 0  || ord  >= bytesRefs .length ) {
582+                     throw  new  IndexOutOfBoundsException ("ord="  + ord  + " is out of bounds [0,"  + bytesRefs .length  + ")" );
583+                 }
584+                 int  ordIndex  = Math .toIntExact (ord );
585+                 return  bytesRefs [ordIndex ];
586+             }
587+ 
588+             @ Override 
589+             public  long  getValueCount () {
590+                 return  bytesRefs .length ;
591+             }
592+         };
593+ 
594+         // This should not throw IndexOutOfBoundsException after the fix 
595+         LongBitSet  acceptedOrds  = ordinalsFilter .acceptedGlobalOrdinals (sortedSetDocValues );
596+ 
597+         // Since "zzz" doesn't exist in the doc values, no ordinals should be accepted 
598+         assertEquals (bytesRefs .length , acceptedOrds .length ());
599+         for  (int  i  = 0 ; i  < bytesRefs .length ; i ++) {
600+             assertFalse ("Ordinal "  + i  + " should not be accepted" , acceptedOrds .get (i ));
601+         }
602+     }
603+ 
604+     /** 
605+      * Test case for prefix filter with exclude pattern when the prefix doesn't exist. 
606+      */ 
607+     public  void  testPrefixFilterWithNonExistentExcludePrefixBeyondRange () throws  IOException  {
608+         // Test with an exclude pattern where the prefix doesn't exist 
609+         IncludeExclude  includeExclude  = new  IncludeExclude (null , "zzz.*" );
610+ 
611+         OrdinalsFilter  ordinalsFilter  = includeExclude .convertToOrdinalsFilter (DocValueFormat .RAW );
612+ 
613+         BytesRef [] bytesRefs  = toBytesRefArray ("aaa" , "bbb" , "ccc" );
614+ 
615+         SortedSetDocValues  sortedSetDocValues  = new  AbstractSortedSetDocValues () {
616+             @ Override 
617+             public  boolean  advanceExact (int  target ) {
618+                 return  false ;
619+             }
620+ 
621+             @ Override 
622+             public  long  nextOrd () {
623+                 return  0 ;
624+             }
625+ 
626+             @ Override 
627+             public  int  docValueCount () {
628+                 return  1 ;
629+             }
630+ 
631+             @ Override 
632+             public  BytesRef  lookupOrd (long  ord ) {
633+                 if  (ord  < 0  || ord  >= bytesRefs .length ) {
634+                     throw  new  IndexOutOfBoundsException ("ord="  + ord  + " is out of bounds [0,"  + bytesRefs .length  + ")" );
635+                 }
636+                 int  ordIndex  = Math .toIntExact (ord );
637+                 return  bytesRefs [ordIndex ];
638+             }
639+ 
640+             @ Override 
641+             public  long  getValueCount () {
642+                 return  bytesRefs .length ;
643+             }
644+         };
645+ 
646+         // This should not throw IndexOutOfBoundsException after the fix 
647+         LongBitSet  acceptedOrds  = ordinalsFilter .acceptedGlobalOrdinals (sortedSetDocValues );
648+ 
649+         // Since "zzz" doesn't exist and we're excluding it, all ordinals should be accepted 
650+         assertEquals (bytesRefs .length , acceptedOrds .length ());
651+         for  (int  i  = 0 ; i  < bytesRefs .length ; i ++) {
652+             assertTrue ("Ordinal "  + i  + " should be accepted" , acceptedOrds .get (i ));
653+         }
654+     }
655+ 
656+     /** 
657+      * Test case for prefix filter when the prefix exists before all terms. 
658+      */ 
659+     public  void  testPrefixFilterWithNonExistentPrefixBeforeRange () throws  IOException  {
660+         // Test with a prefix that would be inserted before all existing terms 
661+         IncludeExclude  includeExclude  = new  IncludeExclude ("aaa.*" , null );
662+ 
663+         OrdinalsFilter  ordinalsFilter  = includeExclude .convertToOrdinalsFilter (DocValueFormat .RAW );
664+ 
665+         // Create doc values where "aaa" would be at the beginning but doesn't exist 
666+         BytesRef [] bytesRefs  = toBytesRefArray ("bbb" , "ccc" , "ddd" );
667+ 
668+         LongBitSet  acceptedOrds  = ordinalsFilter .acceptedGlobalOrdinals (toDocValues (bytesRefs ));
669+ 
670+         // No ordinals should be accepted since "aaa" doesn't exist 
671+         assertEquals (bytesRefs .length , acceptedOrds .length ());
672+         for  (int  i  = 0 ; i  < bytesRefs .length ; i ++) {
673+             assertFalse ("Ordinal "  + i  + " should not be accepted" , acceptedOrds .get (i ));
674+         }
675+     }
676+ 
677+     /** 
678+      * Test case for prefix filter when the prefix matches some terms. 
679+      */ 
680+     public  void  testPrefixFilterWithMatchingPrefix () throws  IOException  {
681+         // Test with a prefix that matches some terms 
682+         IncludeExclude  includeExclude  = new  IncludeExclude ("aa.*" , null );
683+ 
684+         OrdinalsFilter  ordinalsFilter  = includeExclude .convertToOrdinalsFilter (DocValueFormat .RAW );
685+ 
686+         BytesRef [] bytesRefs  = toBytesRefArray ("aaa" , "aab" , "bbb" , "ccc" );
687+ 
688+         LongBitSet  acceptedOrds  = ordinalsFilter .acceptedGlobalOrdinals (toDocValues (bytesRefs ));
689+ 
690+         // Only the first two ordinals should be accepted (matching "aa" prefix) 
691+         assertEquals (bytesRefs .length , acceptedOrds .length ());
692+         assertTrue ("Ordinal 0 (aaa) should be accepted" , acceptedOrds .get (0 ));
693+         assertTrue ("Ordinal 1 (aab) should be accepted" , acceptedOrds .get (1 ));
694+         assertFalse ("Ordinal 2 (bbb) should not be accepted" , acceptedOrds .get (2 ));
695+         assertFalse ("Ordinal 3 (ccc) should not be accepted" , acceptedOrds .get (3 ));
696+     }
548697}
0 commit comments