Plugins: Allow to easily plug a custom DSL query/filter parsers, closes #208.

This commit is contained in:
kimchy 2010-06-05 01:07:12 +03:00
parent 9f8644472e
commit d0eb836c4a
8 changed files with 347 additions and 4 deletions

View File

@ -38,6 +38,7 @@
<w>formatters</w>
<w>frac</w>
<w>freqs</w>
<w>guice</w>
<w>hadoop</w>
<w>hdfs</w>
<w>hpux</w>

View File

@ -22,6 +22,8 @@ package org.elasticsearch.index.query;
import org.elasticsearch.util.settings.Settings;
/**
* Guice Factory for custom {@link IndexQueryParser}.
*
* @author kimchy (Shay Banon)
*/
public interface IndexQueryParserFactory {

View File

@ -20,12 +20,14 @@
package org.elasticsearch.index.query;
import org.elasticsearch.index.query.xcontent.*;
import org.elasticsearch.util.collect.Lists;
import org.elasticsearch.util.inject.AbstractModule;
import org.elasticsearch.util.inject.Scopes;
import org.elasticsearch.util.inject.assistedinject.FactoryProvider;
import org.elasticsearch.util.inject.multibindings.MapBinder;
import org.elasticsearch.util.settings.Settings;
import java.util.LinkedList;
import java.util.Map;
/**
@ -33,12 +35,122 @@ import java.util.Map;
*/
public class IndexQueryParserModule extends AbstractModule {
/**
* A custom processor that can be extended to process and bind custom implementations of
* {@link IndexQueryParserFactory}, {@link XContentQueryParserFactory}, and {@link XContentFilterParser}.
*/
public static class QueryParsersProcessor {
/**
* Extension point to bind a custom {@link IndexQueryParserFactory}.
*/
public void processIndexQueryParsers(IndexQueryParsersBindings bindings) {
}
public static class IndexQueryParsersBindings {
private final MapBinder<String, IndexQueryParserFactory> binder;
private final Map<String, Settings> groupSettings;
public IndexQueryParsersBindings(MapBinder<String, IndexQueryParserFactory> binder, Map<String, Settings> groupSettings) {
this.binder = binder;
this.groupSettings = groupSettings;
}
public MapBinder<String, IndexQueryParserFactory> binder() {
return binder;
}
public Map<String, Settings> groupSettings() {
return groupSettings;
}
public void processIndexQueryParser(String name, Class<? extends IndexQueryParser> indexQueryParser) {
if (!groupSettings.containsKey(name)) {
binder.addBinding(name).toProvider(FactoryProvider.newFactory(IndexQueryParserFactory.class, indexQueryParser)).in(Scopes.SINGLETON);
}
}
}
/**
* Extension point to bind a custom {@link XContentQueryParserFactory}.
*/
public void processXContentQueryParsers(XContentQueryParsersBindings bindings) {
}
public static class XContentQueryParsersBindings {
private final MapBinder<String, XContentQueryParserFactory> binder;
private final Map<String, Settings> groupSettings;
public XContentQueryParsersBindings(MapBinder<String, XContentQueryParserFactory> binder, Map<String, Settings> groupSettings) {
this.binder = binder;
this.groupSettings = groupSettings;
}
public MapBinder<String, XContentQueryParserFactory> binder() {
return binder;
}
public Map<String, Settings> groupSettings() {
return groupSettings;
}
public void processXContentQueryParser(String name, Class<? extends XContentQueryParser> xcontentQueryParser) {
if (!groupSettings.containsKey(name)) {
binder.addBinding(name).toProvider(FactoryProvider.newFactory(XContentQueryParserFactory.class, xcontentQueryParser)).in(Scopes.SINGLETON);
}
}
}
/**
* Extension point to bind a custom {@link XContentFilterParserFactory}.
*/
public void processXContentFilterParsers(XContentFilterParsersBindings bindings) {
}
public static class XContentFilterParsersBindings {
private final MapBinder<String, XContentFilterParserFactory> binder;
private final Map<String, Settings> groupSettings;
public XContentFilterParsersBindings(MapBinder<String, XContentFilterParserFactory> binder, Map<String, Settings> groupSettings) {
this.binder = binder;
this.groupSettings = groupSettings;
}
public MapBinder<String, XContentFilterParserFactory> binder() {
return binder;
}
public Map<String, Settings> groupSettings() {
return groupSettings;
}
public void processXContentQueryFilter(String name, Class<? extends XContentFilterParser> xcontentFilterParser) {
if (!groupSettings.containsKey(name)) {
binder.addBinding(name).toProvider(FactoryProvider.newFactory(XContentFilterParserFactory.class, xcontentFilterParser)).in(Scopes.SINGLETON);
}
}
}
}
private final Settings settings;
private final LinkedList<QueryParsersProcessor> processors = Lists.newLinkedList();
public IndexQueryParserModule(Settings settings) {
this.settings = settings;
}
public IndexQueryParserModule addProcessor(QueryParsersProcessor processor) {
processors.addFirst(processor);
return this;
}
@Override protected void configure() {
// handle IndexQueryParsers
@ -57,6 +169,11 @@ public class IndexQueryParserModule extends AbstractModule {
XContentIndexQueryParser.class)).in(Scopes.SINGLETON);
}
QueryParsersProcessor.IndexQueryParsersBindings queryParsersBindings = new QueryParsersProcessor.IndexQueryParsersBindings(qbinder, queryParserGroupSettings);
for (QueryParsersProcessor processor : processors) {
processor.processIndexQueryParsers(queryParsersBindings);
}
// handle XContenQueryParsers
MapBinder<String, XContentQueryParserFactory> queryBinder
= MapBinder.newMapBinder(binder(), String.class, XContentQueryParserFactory.class);
@ -72,6 +189,11 @@ public class IndexQueryParserModule extends AbstractModule {
qSettings.getAsClass("type", null))).in(Scopes.SINGLETON);
}
QueryParsersProcessor.XContentQueryParsersBindings xContentQueryParsersBindings = new QueryParsersProcessor.XContentQueryParsersBindings(queryBinder, xContentQueryParserGroups);
for (QueryParsersProcessor processor : processors) {
processor.processXContentQueryParsers(xContentQueryParsersBindings);
}
// handle XContentFilterParsers
MapBinder<String, XContentFilterParserFactory> filterBinder
= MapBinder.newMapBinder(binder(), String.class, XContentFilterParserFactory.class);
@ -87,6 +209,11 @@ public class IndexQueryParserModule extends AbstractModule {
fSettings.getAsClass("type", null))).in(Scopes.SINGLETON);
}
QueryParsersProcessor.XContentFilterParsersBindings xContentFilterParsersBindings = new QueryParsersProcessor.XContentFilterParsersBindings(filterBinder, xContentFilterParserGroups);
for (QueryParsersProcessor processor : processors) {
processor.processXContentFilterParsers(xContentFilterParsersBindings);
}
bind(IndexQueryParserService.class).asEagerSingleton();
}
}

View File

@ -30,13 +30,13 @@ import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.util.collect.ImmutableMap;
import org.elasticsearch.util.inject.Inject;
import org.elasticsearch.util.settings.ImmutableSettings;
import org.elasticsearch.util.settings.Settings;
import javax.annotation.Nullable;
import java.util.Map;
import static org.elasticsearch.util.collect.Maps.*;
import static org.elasticsearch.util.settings.ImmutableSettings.Builder.*;
/**
* @author kimchy (Shay Banon)
@ -53,7 +53,7 @@ public class IndexQueryParserService extends AbstractIndexComponent {
private final Map<String, IndexQueryParser> indexQueryParsers;
public IndexQueryParserService(Index index, MapperService mapperService, IndexCache indexCache, IndexEngine indexEngine, AnalysisService analysisService) {
this(index, ImmutableSettings.Builder.EMPTY_SETTINGS, mapperService, indexCache, indexEngine, analysisService, null, null);
this(index, EMPTY_SETTINGS, mapperService, indexCache, indexEngine, analysisService, null, null);
}
@Inject public IndexQueryParserService(Index index, @IndexSettings Settings indexSettings,
@ -73,6 +73,9 @@ public class IndexQueryParserService extends AbstractIndexComponent {
for (Map.Entry<String, IndexQueryParserFactory> entry : indexQueryParsersFactories.entrySet()) {
String qparserName = entry.getKey();
Settings qparserSettings = queryParserGroupSettings.get(qparserName);
if (qparserSettings == null) {
qparserSettings = EMPTY_SETTINGS;
}
qparsers.put(qparserName, entry.getValue().create(qparserName, qparserSettings));
}
}

View File

@ -46,6 +46,7 @@ import java.util.List;
import java.util.Map;
import static org.elasticsearch.util.collect.Lists.*;
import static org.elasticsearch.util.settings.ImmutableSettings.Builder.*;
/**
* @author kimchy (shay.banon)
@ -96,7 +97,9 @@ public class XContentIndexQueryParser extends AbstractIndexComponent implements
String queryParserName = entry.getKey();
XContentQueryParserFactory queryParserFactory = entry.getValue();
Settings queryParserSettings = queryParserGroups.get(queryParserName);
if (queryParserSettings == null) {
queryParserSettings = EMPTY_SETTINGS;
}
queryParsers.add(queryParserFactory.create(queryParserName, queryParserSettings));
}
}
@ -108,7 +111,9 @@ public class XContentIndexQueryParser extends AbstractIndexComponent implements
String filterParserName = entry.getKey();
XContentFilterParserFactory filterParserFactory = entry.getValue();
Settings filterParserSettings = filterParserGroups.get(filterParserName);
if (filterParserSettings == null) {
filterParserSettings = EMPTY_SETTINGS;
}
filterParsers.add(filterParserFactory.create(filterParserName, filterParserSettings));
}
}

View File

@ -0,0 +1,83 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.index.query.xcontent.plugin;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNameModule;
import org.elasticsearch.index.analysis.AnalysisModule;
import org.elasticsearch.index.cache.IndexCacheModule;
import org.elasticsearch.index.engine.IndexEngineModule;
import org.elasticsearch.index.query.IndexQueryParserModule;
import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser;
import org.elasticsearch.index.query.xcontent.XContentQueryParserRegistry;
import org.elasticsearch.index.settings.IndexSettingsModule;
import org.elasticsearch.index.similarity.SimilarityModule;
import org.elasticsearch.util.inject.Guice;
import org.elasticsearch.util.inject.Injector;
import org.elasticsearch.util.settings.ImmutableSettings;
import org.elasticsearch.util.settings.Settings;
import org.testng.annotations.Test;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
/**
* @author kimchy (shay.banon)
*/
public class IndexQueryParserPluginTests {
@Test public void testCustomInjection() {
Settings settings = ImmutableSettings.Builder.EMPTY_SETTINGS;
IndexQueryParserModule queryParserModule = new IndexQueryParserModule(settings);
queryParserModule.addProcessor(new IndexQueryParserModule.QueryParsersProcessor() {
@Override public void processXContentQueryParsers(XContentQueryParsersBindings bindings) {
bindings.processXContentQueryParser("my", PluginJsonQueryParser.class);
}
@Override public void processXContentFilterParsers(XContentFilterParsersBindings bindings) {
bindings.processXContentQueryFilter("my", PluginJsonFilterParser.class);
}
});
Index index = new Index("test");
Injector injector = Guice.createInjector(
new IndexSettingsModule(settings),
new IndexCacheModule(settings),
new AnalysisModule(settings),
new IndexEngineModule(settings),
new SimilarityModule(settings),
queryParserModule,
new IndexNameModule(index)
);
IndexQueryParserService indexQueryParserService = injector.getInstance(IndexQueryParserService.class);
XContentQueryParserRegistry parserRegistry = ((XContentIndexQueryParser) indexQueryParserService.defaultIndexQueryParser()).queryParserRegistry();
PluginJsonQueryParser myJsonQueryParser = (PluginJsonQueryParser) parserRegistry.queryParser("my");
assertThat(myJsonQueryParser.names()[0], equalTo("my"));
PluginJsonFilterParser myJsonFilterParser = (PluginJsonFilterParser) parserRegistry.filterParser("my");
assertThat(myJsonFilterParser.names()[0], equalTo("my"));
}
}

View File

@ -0,0 +1,61 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.index.query.xcontent.plugin;
import org.apache.lucene.search.Filter;
import org.elasticsearch.index.AbstractIndexComponent;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.query.QueryParsingException;
import org.elasticsearch.index.query.xcontent.QueryParseContext;
import org.elasticsearch.index.query.xcontent.XContentFilterParser;
import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.util.inject.Inject;
import org.elasticsearch.util.inject.assistedinject.Assisted;
import org.elasticsearch.util.settings.Settings;
import java.io.IOException;
/**
* @author kimchy (Shay Banon)
*/
public class PluginJsonFilterParser extends AbstractIndexComponent implements XContentFilterParser {
private final String name;
private final Settings settings;
@Inject public PluginJsonFilterParser(Index index, @IndexSettings Settings indexSettings, @Assisted String name, @Assisted Settings settings) {
super(index, indexSettings);
this.name = name;
this.settings = settings;
}
@Override public String[] names() {
return new String[]{this.name};
}
@Override public Filter parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
return null;
}
public Settings settings() {
return settings;
}
}

View File

@ -0,0 +1,61 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.index.query.xcontent.plugin;
import org.apache.lucene.search.Query;
import org.elasticsearch.index.AbstractIndexComponent;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.query.QueryParsingException;
import org.elasticsearch.index.query.xcontent.QueryParseContext;
import org.elasticsearch.index.query.xcontent.XContentQueryParser;
import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.util.inject.Inject;
import org.elasticsearch.util.inject.assistedinject.Assisted;
import org.elasticsearch.util.settings.Settings;
import java.io.IOException;
/**
* @author kimchy (Shay Banon)
*/
public class PluginJsonQueryParser extends AbstractIndexComponent implements XContentQueryParser {
private final String name;
private final Settings settings;
@Inject public PluginJsonQueryParser(Index index, @IndexSettings Settings indexSettings, @Assisted String name, @Assisted Settings settings) {
super(index, indexSettings);
this.name = name;
this.settings = settings;
}
@Override public String[] names() {
return new String[]{this.name};
}
@Override public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
return null;
}
public Settings settings() {
return settings;
}
}