mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-25 09:28:27 +00:00
Plugins: Allow to easily plug a custom DSL query/filter parsers, closes #208.
This commit is contained in:
parent
9f8644472e
commit
d0eb836c4a
1
.idea/dictionaries/kimchy.xml
generated
1
.idea/dictionaries/kimchy.xml
generated
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user