1919
2020package org .elasticsearch .index .query ;
2121
22+ import org .apache .lucene .index .IndexOptions ;
2223import org .apache .lucene .search .intervals .FilteredIntervalsSource ;
2324import org .apache .lucene .search .intervals .IntervalIterator ;
2425import org .apache .lucene .search .intervals .Intervals ;
2526import org .apache .lucene .search .intervals .IntervalsSource ;
27+ import org .apache .lucene .util .BytesRef ;
2628import org .elasticsearch .Version ;
2729import org .elasticsearch .common .ParseField ;
2830import org .elasticsearch .common .ParsingException ;
@@ -80,6 +82,8 @@ public static IntervalsSourceProvider fromXContent(XContentParser parser) throws
8082 return Combine .fromXContent (parser );
8183 case "prefix" :
8284 return Prefix .fromXContent (parser );
85+ case "wildcard" :
86+ return Wildcard .fromXContent (parser );
8387 }
8488 throw new ParsingException (parser .getTokenLocation (),
8589 "Unknown interval type [" + parser .currentName () + "], expecting one of [match, any_of, all_of, prefix]" );
@@ -446,18 +450,18 @@ public static class Prefix extends IntervalsSourceProvider {
446450
447451 public static final String NAME = "prefix" ;
448452
449- private final String term ;
453+ private final String prefix ;
450454 private final String analyzer ;
451455 private final String useField ;
452456
453- public Prefix (String term , String analyzer , String useField ) {
454- this .term = term ;
457+ public Prefix (String prefix , String analyzer , String useField ) {
458+ this .prefix = prefix ;
455459 this .analyzer = analyzer ;
456460 this .useField = useField ;
457461 }
458462
459463 public Prefix (StreamInput in ) throws IOException {
460- this .term = in .readString ();
464+ this .prefix = in .readString ();
461465 this .analyzer = in .readOptionalString ();
462466 this .useField = in .readOptionalString ();
463467 }
@@ -472,10 +476,10 @@ public IntervalsSource getSource(QueryShardContext context, MappedFieldType fiel
472476 if (useField != null ) {
473477 fieldType = context .fieldMapper (useField );
474478 assert fieldType != null ;
475- source = Intervals .fixField (useField , fieldType .intervals (term , 0 , false , analyzer , true ));
479+ source = Intervals .fixField (useField , fieldType .intervals (prefix , 0 , false , analyzer , true ));
476480 }
477481 else {
478- source = fieldType .intervals (term , 0 , false , analyzer , true );
482+ source = fieldType .intervals (prefix , 0 , false , analyzer , true );
479483 }
480484 return source ;
481485 }
@@ -492,14 +496,14 @@ public boolean equals(Object o) {
492496 if (this == o ) return true ;
493497 if (o == null || getClass () != o .getClass ()) return false ;
494498 Prefix prefix = (Prefix ) o ;
495- return Objects .equals (term , prefix .term ) &&
499+ return Objects .equals (this . prefix , prefix .prefix ) &&
496500 Objects .equals (analyzer , prefix .analyzer ) &&
497501 Objects .equals (useField , prefix .useField );
498502 }
499503
500504 @ Override
501505 public int hashCode () {
502- return Objects .hash (term , analyzer , useField );
506+ return Objects .hash (prefix , analyzer , useField );
503507 }
504508
505509 @ Override
@@ -509,15 +513,15 @@ public String getWriteableName() {
509513
510514 @ Override
511515 public void writeTo (StreamOutput out ) throws IOException {
512- out .writeString (term );
516+ out .writeString (prefix );
513517 out .writeOptionalString (analyzer );
514518 out .writeOptionalString (useField );
515519 }
516520
517521 @ Override
518522 public XContentBuilder toXContent (XContentBuilder builder , Params params ) throws IOException {
519523 builder .startObject (NAME );
520- builder .field ("term " , term );
524+ builder .field ("prefix " , prefix );
521525 if (analyzer != null ) {
522526 builder .field ("analyzer" , analyzer );
523527 }
@@ -535,7 +539,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
535539 return new Prefix (term , analyzer , useField );
536540 });
537541 static {
538- PARSER .declareString (constructorArg (), new ParseField ("term " ));
542+ PARSER .declareString (constructorArg (), new ParseField ("prefix " ));
539543 PARSER .declareString (optionalConstructorArg (), new ParseField ("analyzer" ));
540544 PARSER .declareString (optionalConstructorArg (), new ParseField ("use_field" ));
541545 }
@@ -545,6 +549,123 @@ public static Prefix fromXContent(XContentParser parser) throws IOException {
545549 }
546550 }
547551
552+ public static class Wildcard extends IntervalsSourceProvider {
553+
554+ public static final String NAME = "wildcard" ;
555+
556+ private final String pattern ;
557+ private final String analyzer ;
558+ private final String useField ;
559+
560+ public Wildcard (String pattern , String analyzer , String useField ) {
561+ this .pattern = pattern ;
562+ this .analyzer = analyzer ;
563+ this .useField = useField ;
564+ }
565+
566+ public Wildcard (StreamInput in ) throws IOException {
567+ this .pattern = in .readString ();
568+ this .analyzer = in .readOptionalString ();
569+ this .useField = in .readOptionalString ();
570+ }
571+
572+ @ Override
573+ public IntervalsSource getSource (QueryShardContext context , MappedFieldType fieldType ) {
574+ NamedAnalyzer analyzer = fieldType .searchAnalyzer ();
575+ if (this .analyzer != null ) {
576+ analyzer = context .getMapperService ().getIndexAnalyzers ().get (this .analyzer );
577+ }
578+ IntervalsSource source ;
579+ if (useField != null ) {
580+ fieldType = context .fieldMapper (useField );
581+ assert fieldType != null ;
582+ checkPositions (fieldType );
583+ if (this .analyzer == null ) {
584+ analyzer = fieldType .searchAnalyzer ();
585+ }
586+ BytesRef normalizedTerm = analyzer .normalize (useField , pattern );
587+ // TODO Intervals.wildcard() should take BytesRef
588+ source = Intervals .fixField (useField , Intervals .wildcard (normalizedTerm .utf8ToString ()));
589+ }
590+ else {
591+ checkPositions (fieldType );
592+ BytesRef normalizedTerm = analyzer .normalize (fieldType .name (), pattern );
593+ source = Intervals .wildcard (normalizedTerm .utf8ToString ());
594+ }
595+ return source ;
596+ }
597+
598+ private void checkPositions (MappedFieldType type ) {
599+ if (type .indexOptions ().compareTo (IndexOptions .DOCS_AND_FREQS_AND_POSITIONS ) < 0 ) {
600+ throw new IllegalArgumentException ("Cannot create intervals over field [" + type .name () + "] with no positions indexed" );
601+ }
602+ }
603+
604+ @ Override
605+ public void extractFields (Set <String > fields ) {
606+ if (useField != null ) {
607+ fields .add (useField );
608+ }
609+ }
610+
611+ @ Override
612+ public boolean equals (Object o ) {
613+ if (this == o ) return true ;
614+ if (o == null || getClass () != o .getClass ()) return false ;
615+ Prefix prefix = (Prefix ) o ;
616+ return Objects .equals (pattern , prefix .prefix ) &&
617+ Objects .equals (analyzer , prefix .analyzer ) &&
618+ Objects .equals (useField , prefix .useField );
619+ }
620+
621+ @ Override
622+ public int hashCode () {
623+ return Objects .hash (pattern , analyzer , useField );
624+ }
625+
626+ @ Override
627+ public String getWriteableName () {
628+ return NAME ;
629+ }
630+
631+ @ Override
632+ public void writeTo (StreamOutput out ) throws IOException {
633+ out .writeString (pattern );
634+ out .writeOptionalString (analyzer );
635+ out .writeOptionalString (useField );
636+ }
637+
638+ @ Override
639+ public XContentBuilder toXContent (XContentBuilder builder , Params params ) throws IOException {
640+ builder .startObject (NAME );
641+ builder .field ("pattern" , pattern );
642+ if (analyzer != null ) {
643+ builder .field ("analyzer" , analyzer );
644+ }
645+ if (useField != null ) {
646+ builder .field ("use_field" , useField );
647+ }
648+ builder .endObject ();
649+ return builder ;
650+ }
651+
652+ private static final ConstructingObjectParser <Wildcard , Void > PARSER = new ConstructingObjectParser <>(NAME , args -> {
653+ String term = (String ) args [0 ];
654+ String analyzer = (String ) args [1 ];
655+ String useField = (String ) args [2 ];
656+ return new Wildcard (term , analyzer , useField );
657+ });
658+ static {
659+ PARSER .declareString (constructorArg (), new ParseField ("pattern" ));
660+ PARSER .declareString (optionalConstructorArg (), new ParseField ("analyzer" ));
661+ PARSER .declareString (optionalConstructorArg (), new ParseField ("use_field" ));
662+ }
663+
664+ public static Wildcard fromXContent (XContentParser parser ) throws IOException {
665+ return PARSER .parse (parser , null );
666+ }
667+ }
668+
548669 static class ScriptFilterSource extends FilteredIntervalsSource {
549670
550671 final IntervalFilterScript script ;
0 commit comments