Upgrading analysis plugins fails

When an analysis plugins provides default index settings using `PreBuiltAnalyzerProviderFactory`,  `PreBuiltTokenFilterFactoryFactory` or `PreBuiltTokenizerFactoryFactory` it fails when upgrading it with elasticsearch superior or equal to 0.90.5.

Related issue: #4936

Fix is needed in core. But, in the meantime, analysis plugins developers can fix that issue by overloading default prebuilt factories.

For example:

```java
public class StempelAnalyzerProviderFactory extends PreBuiltAnalyzerProviderFactory {

    private final PreBuiltAnalyzerProvider analyzerProvider;

    public StempelAnalyzerProviderFactory(String name, AnalyzerScope scope, Analyzer analyzer) {
        super(name, scope, analyzer);
        analyzerProvider = new PreBuiltAnalyzerProvider(name, scope, analyzer);
    }

    @Override
    public AnalyzerProvider create(String name, Settings settings) {
        return analyzerProvider;
    }

    public Analyzer analyzer() {
        return analyzerProvider.get();
    }
}
```

And instead of:

```java
    @Inject
    public PolishIndicesAnalysis(Settings settings, IndicesAnalysisService indicesAnalysisService) {
        super(settings);
        indicesAnalysisService.analyzerProviderFactories().put("polish", new PreBuiltAnalyzerProviderFactory("polish", AnalyzerScope.INDICES, new PolishAnalyzer(Lucene.ANALYZER_VERSION)));
    }
```

do

```java
    @Inject
    public PolishIndicesAnalysis(Settings settings, IndicesAnalysisService indicesAnalysisService) {
        super(settings);
        indicesAnalysisService.analyzerProviderFactories().put("polish", new StempelAnalyzerProviderFactory("polish", AnalyzerScope.INDICES, new PolishAnalyzer(Lucene.ANALYZER_VERSION)));
    }
```

Closes #5030
This commit is contained in:
David Pilato 2014-02-06 09:26:07 +01:00
parent 7a84b94e29
commit 8b1c25e11f
18 changed files with 478 additions and 17 deletions

View File

@ -25,8 +25,6 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.indices.analysis.PreBuiltAnalyzers;
import java.util.Locale;
/**
*
*/
@ -42,8 +40,11 @@ public class PreBuiltAnalyzerProviderFactory implements AnalyzerProviderFactory
public AnalyzerProvider create(String name, Settings settings) {
Version indexVersion = settings.getAsVersion(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT);
if (!Version.CURRENT.equals(indexVersion)) {
Analyzer analyzer = PreBuiltAnalyzers.valueOf(name.toUpperCase(Locale.ROOT)).getAnalyzer(indexVersion);
return new PreBuiltAnalyzerProvider(name, AnalyzerScope.INDICES, analyzer);
PreBuiltAnalyzers preBuiltAnalyzers = PreBuiltAnalyzers.getOrDefault(name, null);
if (preBuiltAnalyzers != null) {
Analyzer analyzer = preBuiltAnalyzers.getAnalyzer(indexVersion);
return new PreBuiltAnalyzerProvider(name, AnalyzerScope.INDICES, analyzer);
}
}
return analyzerProvider;

View File

@ -24,8 +24,6 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.indices.analysis.PreBuiltCharFilters;
import java.util.Locale;
public class PreBuiltCharFilterFactoryFactory implements CharFilterFactoryFactory {
private final CharFilterFactory charFilterFactory;
@ -38,9 +36,12 @@ public class PreBuiltCharFilterFactoryFactory implements CharFilterFactoryFactor
public CharFilterFactory create(String name, Settings settings) {
Version indexVersion = settings.getAsVersion(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT);
if (!Version.CURRENT.equals(indexVersion)) {
return PreBuiltCharFilters.valueOf(name.toUpperCase(Locale.ROOT)).getCharFilterFactory(indexVersion);
PreBuiltCharFilters preBuiltCharFilters = PreBuiltCharFilters.getOrDefault(name, null);
if (preBuiltCharFilters != null) {
return preBuiltCharFilters.getCharFilterFactory(indexVersion);
}
}
return charFilterFactory;
}
}
}

View File

@ -24,8 +24,6 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.indices.analysis.PreBuiltTokenFilters;
import java.util.Locale;
public class PreBuiltTokenFilterFactoryFactory implements TokenFilterFactoryFactory {
private final TokenFilterFactory tokenFilterFactory;
@ -38,8 +36,11 @@ public class PreBuiltTokenFilterFactoryFactory implements TokenFilterFactoryFact
public TokenFilterFactory create(String name, Settings settings) {
Version indexVersion = settings.getAsVersion(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT);
if (!Version.CURRENT.equals(indexVersion)) {
return PreBuiltTokenFilters.valueOf(name.toUpperCase(Locale.ROOT)).getTokenFilterFactory(indexVersion);
PreBuiltTokenFilters preBuiltTokenFilters = PreBuiltTokenFilters.getOrDefault(name, null);
if (preBuiltTokenFilters != null) {
return preBuiltTokenFilters.getTokenFilterFactory(indexVersion);
}
}
return tokenFilterFactory;
}
}
}

View File

@ -24,8 +24,6 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.indices.analysis.PreBuiltTokenizers;
import java.util.Locale;
public class PreBuiltTokenizerFactoryFactory implements TokenizerFactoryFactory {
private final TokenizerFactory tokenizerFactory;
@ -38,10 +36,12 @@ public class PreBuiltTokenizerFactoryFactory implements TokenizerFactoryFactory
public TokenizerFactory create(String name, Settings settings) {
Version indexVersion = settings.getAsVersion(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT);
if (!Version.CURRENT.equals(indexVersion)) {
TokenizerFactory versionedTokenizerFactory = PreBuiltTokenizers.valueOf(name.toUpperCase(Locale.ROOT)).getTokenizerFactory(indexVersion);
return versionedTokenizerFactory;
PreBuiltTokenizers preBuiltTokenizers = PreBuiltTokenizers.getOrDefault(name, null);
if (preBuiltTokenizers != null) {
return preBuiltTokenizers.getTokenizerFactory(indexVersion);
}
}
return tokenizerFactory;
}
}
}

View File

@ -65,6 +65,8 @@ import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.index.analysis.StandardHtmlStripAnalyzer;
import org.elasticsearch.indices.analysis.PreBuiltCacheFactory.CachingStrategy;
import java.util.Locale;
/**
*
*/
@ -401,4 +403,17 @@ public enum PreBuiltAnalyzers {
return analyzer;
}
/**
* Get a pre built Analyzer by its name or fallback to the default one
* @param name Analyzer name
* @param defaultAnalyzer default Analyzer if name not found
*/
public static PreBuiltAnalyzers getOrDefault(String name, PreBuiltAnalyzers defaultAnalyzer) {
try {
return valueOf(name.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
return defaultAnalyzer;
}
}
}

View File

@ -67,4 +67,17 @@ public enum PreBuiltCharFilters {
return charFilterFactory;
}
/**
* Get a pre built CharFilter by its name or fallback to the default one
* @param name CharFilter name
* @param defaultCharFilter default CharFilter if name not found
*/
public static PreBuiltCharFilters getOrDefault(String name, PreBuiltCharFilters defaultCharFilter) {
try {
return valueOf(name.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
return defaultCharFilter;
}
}
}

View File

@ -309,4 +309,16 @@ public enum PreBuiltTokenFilters {
return factory;
}
/**
* Get a pre built TokenFilter by its name or fallback to the default one
* @param name TokenFilter name
* @param defaultTokenFilter default TokenFilter if name not found
*/
public static PreBuiltTokenFilters getOrDefault(String name, PreBuiltTokenFilters defaultTokenFilter) {
try {
return valueOf(name.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
return defaultTokenFilter;
}
}
}

View File

@ -151,4 +151,16 @@ public enum PreBuiltTokenizers {
return tokenizerFactory;
}
/**
* Get a pre built Tokenizer by its name or fallback to the default one
* @param name Tokenizer name
* @param defaultTokenizer default Tokenizer if name not found
*/
public static PreBuiltTokenizers getOrDefault(String name, PreBuiltTokenizers defaultTokenizer) {
try {
return valueOf(name.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
return defaultTokenizer;
}
}
}

View File

@ -0,0 +1,47 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import org.elasticsearch.index.analysis.AnalysisModule;
/**
*/
public class DummyAnalysisBinderProcessor extends AnalysisModule.AnalysisBinderProcessor {
@Override
public void processAnalyzers(AnalyzersBindings analyzersBindings) {
analyzersBindings.processAnalyzer("dummy", DummyAnalyzerProvider.class);
}
@Override
public void processTokenFilters(TokenFiltersBindings tokenFiltersBindings) {
tokenFiltersBindings.processTokenFilter("dummy_token_filter", DummyTokenFilterFactory.class);
}
@Override
public void processTokenizers(TokenizersBindings tokenizersBindings) {
tokenizersBindings.processTokenizer("dummy_tokenizer", DummyTokenizerFactory.class);
}
@Override
public void processCharFilters(CharFiltersBindings charFiltersBindings) {
charFiltersBindings.processCharFilter("dummy_char_filter", DummyCharFilterFactory.class);
}
}

View File

@ -0,0 +1,55 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import com.google.common.collect.ImmutableList;
import org.elasticsearch.common.inject.Module;
import org.elasticsearch.index.analysis.AnalysisModule;
import org.elasticsearch.plugins.AbstractPlugin;
import java.util.Collection;
public class DummyAnalysisPlugin extends AbstractPlugin {
/**
* The name of the plugin.
*/
@Override
public String name() {
return "analysis-dummy";
}
/**
* The description of the plugin.
*/
@Override
public String description() {
return "Analysis Dummy Plugin";
}
@Override
public Collection<Class<? extends Module>> modules() {
return ImmutableList.<Class<? extends Module>>of(DummyIndicesAnalysisModule.class);
}
public void onModule(AnalysisModule module) {
module.addProcessor(new DummyAnalysisBinderProcessor());
}
}

View File

@ -0,0 +1,37 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import org.apache.lucene.analysis.util.StopwordAnalyzerBase;
import org.apache.lucene.util.Version;
import java.io.Reader;
public class DummyAnalyzer extends StopwordAnalyzerBase {
protected DummyAnalyzer(Version version) {
super(version);
}
@Override
protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
return null;
}
}

View File

@ -0,0 +1,41 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.index.analysis.AnalyzerProvider;
import org.elasticsearch.index.analysis.AnalyzerScope;
public class DummyAnalyzerProvider implements AnalyzerProvider<DummyAnalyzer> {
@Override
public String name() {
return "dummy";
}
@Override
public AnalyzerScope scope() {
return AnalyzerScope.INDICES;
}
@Override
public DummyAnalyzer get() {
return new DummyAnalyzer(Lucene.ANALYZER_VERSION);
}
}

View File

@ -0,0 +1,36 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import org.elasticsearch.index.analysis.CharFilterFactory;
import java.io.Reader;
public class DummyCharFilterFactory implements CharFilterFactory {
@Override
public String name() {
return "dummy_char_filter";
}
@Override
public Reader create(Reader reader) {
return null;
}
}

View File

@ -0,0 +1,43 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.analysis.*;
public class DummyIndicesAnalysis extends AbstractComponent {
@Inject
public DummyIndicesAnalysis(Settings settings, IndicesAnalysisService indicesAnalysisService) {
super(settings);
indicesAnalysisService.analyzerProviderFactories().put("dummy",
new PreBuiltAnalyzerProviderFactory("dummy", AnalyzerScope.INDICES,
new DummyAnalyzer(Lucene.ANALYZER_VERSION)));
indicesAnalysisService.tokenFilterFactories().put("dummy_token_filter",
new PreBuiltTokenFilterFactoryFactory(new DummyTokenFilterFactory()));
indicesAnalysisService.charFilterFactories().put("dummy_char_filter",
new PreBuiltCharFilterFactoryFactory(new DummyCharFilterFactory()));
indicesAnalysisService.tokenizerFactories().put("dummy_tokenizer",
new PreBuiltTokenizerFactoryFactory(new DummyTokenizerFactory()));
}
}

View File

@ -0,0 +1,30 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import org.elasticsearch.common.inject.AbstractModule;
public class DummyIndicesAnalysisModule extends AbstractModule {
@Override
protected void configure() {
bind(DummyIndicesAnalysis.class).asEagerSingleton();
}
}

View File

@ -0,0 +1,33 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import org.apache.lucene.analysis.TokenStream;
import org.elasticsearch.index.analysis.TokenFilterFactory;
public class DummyTokenFilterFactory implements TokenFilterFactory {
@Override public String name() {
return "dummy_token_filter";
}
@Override public TokenStream create(TokenStream tokenStream) {
return null;
}
}

View File

@ -0,0 +1,37 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import org.apache.lucene.analysis.Tokenizer;
import org.elasticsearch.index.analysis.TokenizerFactory;
import java.io.Reader;
public class DummyTokenizerFactory implements TokenizerFactory {
@Override
public String name() {
return "dummy_tokenizer";
}
@Override
public Tokenizer create(Reader reader) {
return null;
}
}

View File

@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.indices.analysis;
import com.google.common.collect.Lists;
@ -41,8 +42,17 @@ import static org.hamcrest.Matchers.notNullValue;
/**
*
*/
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.SUITE)
public class PreBuiltAnalyzerIntegrationTests extends ElasticsearchIntegrationTest {
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return ImmutableSettings.settingsBuilder()
.put("plugin.types", DummyAnalysisPlugin.class.getName())
.put(super.nodeSettings(nodeOrdinal))
.build();
}
@Test
public void testThatPreBuiltAnalyzersAreNotClosedOnIndexClose() throws Exception {
Map<PreBuiltAnalyzers, List<Version>> loadedAnalyzers = Maps.newHashMap();
@ -108,6 +118,43 @@ public class PreBuiltAnalyzerIntegrationTests extends ElasticsearchIntegrationTe
assertLuceneAnalyzersAreNotClosed(loadedAnalyzers);
}
/**
* Test case for #5030: Upgrading analysis plugins fails
* See https://github.com/elasticsearch/elasticsearch/issues/5030
*/
@Test
public void testThatPluginAnalyzersCanBeUpdated() throws Exception {
final XContentBuilder mapping = jsonBuilder().startObject()
.startObject("type")
.startObject("properties")
.startObject("foo")
.field("type", "string")
.field("analyzer", "dummy")
.endObject()
.startObject("bar")
.field("type", "string")
.field("analyzer", "my_dummy")
.endObject()
.endObject()
.endObject()
.endObject();
Settings versionSettings = ImmutableSettings.builder()
.put(IndexMetaData.SETTING_VERSION_CREATED, randomVersion())
.put("index.analysis.analyzer.my_dummy.type", "custom")
.put("index.analysis.analyzer.my_dummy.filter", "my_dummy_token_filter")
.put("index.analysis.analyzer.my_dummy.char_filter", "my_dummy_char_filter")
.put("index.analysis.analyzer.my_dummy.tokenizer", "my_dummy_tokenizer")
.put("index.analysis.tokenizer.my_dummy_tokenizer.type", "dummy_tokenizer")
.put("index.analysis.filter.my_dummy_token_filter.type", "dummy_token_filter")
.put("index.analysis.char_filter.my_dummy_char_filter.type", "dummy_char_filter")
.build();
client().admin().indices().prepareCreate("test-analysis-dummy").addMapping("type", mapping).setSettings(versionSettings).get();
ensureGreen();
}
private void assertThatAnalyzersHaveBeenLoaded(Map<PreBuiltAnalyzers, List<Version>> expectedLoadedAnalyzers) {
for (Map.Entry<PreBuiltAnalyzers, List<Version>> entry : expectedLoadedAnalyzers.entrySet()) {
for (Version version : entry.getValue()) {