99import org .apache .lucene .analysis .core .LowerCaseFilter ;
1010import org .apache .lucene .analysis .core .WhitespaceTokenizer ;
1111import org .apache .lucene .analysis .synonym .SynonymMap ;
12+ import org .elasticsearch .common .logging .DeprecationLogger ;
1213import org .elasticsearch .common .settings .Settings ;
1314import org .elasticsearch .env .Environment ;
1415import org .elasticsearch .index .IndexSettings ;
1516import org .elasticsearch .index .analysis .AbstractTokenFilterFactory ;
16- import org .elasticsearch .index .analysis .AnalysisRegistry ;
17+ import org .elasticsearch .index .analysis .Analysis ;
18+ import org .elasticsearch .index .analysis .AnalysisMode ;
19+ import org .elasticsearch .index .analysis .CharFilterFactory ;
20+ import org .elasticsearch .index .analysis .CustomAnalyzer ;
21+ import org .elasticsearch .index .analysis .TokenFilterFactory ;
1722import org .elasticsearch .index .analysis .TokenizerFactory ;
18- import org .elasticsearch .indices .analysis .AnalysisModule ;
1923
2024import java .io .IOException ;
2125import java .util .Map ;
2630import java .util .concurrent .TimeUnit ;
2731import java .util .concurrent .atomic .AtomicInteger ;
2832
33+ import java .util .List ;
34+ import java .util .function .Function ;
35+
2936/**
3037 * @author bellszhu
3138 */
3239public class DynamicSynonymTokenFilterFactory extends
3340 AbstractTokenFilterFactory {
3441
42+ private static final DeprecationLogger DEPRECATION_LOGGER
43+ = new DeprecationLogger (LogManager .getLogger (DynamicSynonymTokenFilterFactory .class ));
44+
3545 /**
3646 * Static id generator
3747 */
@@ -43,20 +53,21 @@ public class DynamicSynonymTokenFilterFactory extends
4353 return thread ;
4454 });
4555 private final String location ;
46- private final boolean ignoreCase ;
4756 private final boolean expand ;
57+ private final boolean lenient ;
4858 private final String format ;
4959 private final int interval ;
5060 private volatile ScheduledFuture <?> scheduledFuture ;
5161 private SynonymMap synonymMap ;
5262 private Map <DynamicSynonymFilter , Integer > dynamicSynonymFilters = new WeakHashMap <>();
63+ protected final Environment environment ;
64+ protected final AnalysisMode analysisMode ;
5365
5466 public DynamicSynonymTokenFilterFactory (
5567 IndexSettings indexSettings ,
5668 Environment env ,
5769 String name ,
58- Settings settings ,
59- AnalysisRegistry analysisRegistry
70+ Settings settings
6071 ) throws IOException {
6172
6273 super (indexSettings , name , settings );
@@ -68,58 +79,103 @@ public DynamicSynonymTokenFilterFactory(
6879 }
6980
7081 this .interval = settings .getAsInt ("interval" , 60 );
71- this .ignoreCase = settings .getAsBoolean ("ignore_case" , false );
82+ if (settings .get ("ignore_case" ) != null ) {
83+ DEPRECATION_LOGGER .deprecated (
84+ "The ignore_case option on the synonym_graph filter is deprecated. " +
85+ "Instead, insert a lowercase filter in the filter chain before the synonym_graph filter." );
86+ }
7287 this .expand = settings .getAsBoolean ("expand" , true );
88+ this .lenient = settings .getAsBoolean ("lenient" , false );
7389 this .format = settings .get ("format" , "" );
90+ boolean updateable = settings .getAsBoolean ("updateable" , false );
91+ this .analysisMode = updateable ? AnalysisMode .SEARCH_TIME : AnalysisMode .ALL ;
92+ this .environment = env ;
93+ }
7494
75- String tokenizerName = settings .get ("tokenizer" , "whitespace" );
76-
77- AnalysisModule .AnalysisProvider <TokenizerFactory > tokenizerFactoryFactory =
78- analysisRegistry .getTokenizerProvider (tokenizerName , indexSettings );
79- if (tokenizerFactoryFactory == null ) {
80- throw new IllegalArgumentException ("failed to find tokenizer [" + tokenizerName + "] for synonym token filter" );
81- }
82- final TokenizerFactory tokenizerFactory = tokenizerFactoryFactory .get (indexSettings , env , tokenizerName ,
95+ @ Override
96+ public AnalysisMode getAnalysisMode () {
97+ return this .analysisMode ;
98+ }
8399
84- AnalysisRegistry .getSettingsFromIndexSettings (indexSettings , AnalysisRegistry .INDEX_ANALYSIS_TOKENIZER + "." + tokenizerName ));
85100
101+ @ Override
102+ public TokenStream create (TokenStream tokenStream ) {
103+ throw new IllegalStateException ("Call getChainAwareTokenFilterFactory to specialize this factory for an analysis chain first" );
104+ }
86105
87- Analyzer analyzer = new Analyzer () {
106+ public TokenFilterFactory getChainAwareTokenFilterFactory (TokenizerFactory tokenizer , List <CharFilterFactory > charFilters ,
107+ List <TokenFilterFactory > previousTokenFilters ,
108+ Function <String , TokenFilterFactory > allFilters ) {
109+ final Analyzer analyzer = buildSynonymAnalyzer (tokenizer , charFilters , previousTokenFilters , allFilters );
110+ synonymMap = buildSynonyms (analyzer );
111+ final String name = name ();
112+ return new TokenFilterFactory () {
88113 @ Override
89- protected TokenStreamComponents createComponents (String fieldName ) {
90- Tokenizer tokenizer = tokenizerFactory == null ? new WhitespaceTokenizer () : tokenizerFactory .create ();
91- TokenStream stream = ignoreCase ? new LowerCaseFilter (tokenizer ) : tokenizer ;
92- return new TokenStreamComponents (tokenizer , stream );
114+ public String name () {
115+ return name ;
93116 }
94- };
95117
96- SynonymFile synonymFile ;
97- if (location .startsWith ("http://" ) || location .startsWith ("https://" )) {
98- synonymFile = new RemoteSynonymFile (env , analyzer , expand , format ,
99- location );
100- } else {
101- synonymFile = new LocalSynonymFile (env , analyzer , expand , format ,
102- location );
103- }
104- synonymMap = synonymFile .reloadSynonymMap ();
118+ @ Override
119+ public TokenStream create (TokenStream tokenStream ) {
120+ // fst is null means no synonyms
121+ if (synonymMap .fst == null ) {
122+ return tokenStream ;
123+ }
124+ DynamicSynonymFilter dynamicSynonymFilter = new DynamicSynonymFilter (tokenStream , synonymMap , false );
125+ dynamicSynonymFilters .put (dynamicSynonymFilter , 1 );
105126
106- scheduledFuture = pool .scheduleAtFixedRate (new Monitor (synonymFile ),
107- interval , interval , TimeUnit .SECONDS );
127+ return dynamicSynonymFilter ;
128+ }
129+
130+ @ Override
131+ public TokenFilterFactory getSynonymFilter () {
132+ // In order to allow chained synonym filters, we return IDENTITY here to
133+ // ensure that synonyms don't get applied to the synonym map itself,
134+ // which doesn't support stacked input tokens
135+ return IDENTITY_FILTER ;
136+ }
108137
138+ @ Override
139+ public AnalysisMode getAnalysisMode () {
140+ return analysisMode ;
141+ }
142+ };
109143 }
110144
145+ Analyzer buildSynonymAnalyzer (TokenizerFactory tokenizer , List <CharFilterFactory > charFilters ,
146+ List <TokenFilterFactory > tokenFilters , Function <String , TokenFilterFactory > allFilters ) {
147+ return new CustomAnalyzer ("dynamic_synonym" , tokenizer , charFilters .toArray (new CharFilterFactory [0 ]),
148+ tokenFilters .stream ()
149+ .map (TokenFilterFactory ::getSynonymFilter )
150+ .toArray (TokenFilterFactory []::new ));
151+ }
111152
112- @ Override
113- public TokenStream create ( TokenStream tokenStream ) {
114- // fst is null means no synonyms
115- if ( synonymMap == null || synonymMap . fst == null ) {
116- return tokenStream ;
153+ SynonymMap buildSynonyms ( Analyzer analyzer ) {
154+ try {
155+ return getSynonymFile ( analyzer ). reloadSynonymMap ();
156+ } catch ( Exception e ) {
157+ throw new IllegalArgumentException ( "failed to build synonyms" , e ) ;
117158 }
159+ }
118160
119- DynamicSynonymFilter dynamicSynonymFilter = new DynamicSynonymFilter (tokenStream , synonymMap , ignoreCase );
120- dynamicSynonymFilters .put (dynamicSynonymFilter , 1 );
121-
122- return dynamicSynonymFilter ;
161+ SynonymFile getSynonymFile (Analyzer analyzer ) {
162+ try {
163+ SynonymFile synonymFile ;
164+ if (location .startsWith ("http://" ) || location .startsWith ("https://" )) {
165+ synonymFile = new RemoteSynonymFile (environment , analyzer , expand , format ,
166+ location );
167+ } else {
168+ synonymFile = new LocalSynonymFile (environment , analyzer , expand , format ,
169+ location );
170+ }
171+ if (scheduledFuture == null ) {
172+ scheduledFuture = pool .scheduleAtFixedRate (new Monitor (synonymFile ),
173+ interval , interval , TimeUnit .SECONDS );
174+ }
175+ return synonymFile ;
176+ } catch (Exception e ) {
177+ throw new IllegalArgumentException ("failed to get synonyms : " + location , e );
178+ }
123179 }
124180
125181 public class Monitor implements Runnable {
0 commit comments