Skip to content

Commit

Permalink
Inject the backend query type map for pluggable backend selection
Browse files Browse the repository at this point in the history
always also bind backends in the generic map binder, even for versioned backends in non-versioned backends, use the map binder directly so the engine can pick the correct backend based on the BackendQuery instance used

for query normalization, ignore non-elasticsearch based queries for now, we will need to address normalizers later to make them type safe (or just live with the instanceof check)

Simplified version of #21073
  • Loading branch information
kroepke committed Dec 4, 2024
1 parent 104c8ca commit bbe47d7
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.google.inject.multibindings.MapBinder;
import org.graylog.plugins.views.ViewsModule;
import org.graylog.plugins.views.search.SearchType;
import org.graylog.plugins.views.search.elasticsearch.ElasticsearchQueryString;
import org.graylog.plugins.views.search.engine.GeneratedQueryContext;
import org.graylog.plugins.views.search.engine.QueryBackend;
import org.graylog.plugins.views.search.export.ExportBackend;
Expand Down Expand Up @@ -86,8 +87,7 @@ public ViewsESBackendModule(SearchVersion supportedSearchVersion) {
protected void configure() {
install(new FactoryModuleBuilder().build(ESGeneratedQueryContext.Factory.class));

bindForVersion(supportedSearchVersion, new TypeLiteral<QueryBackend<? extends GeneratedQueryContext>>() {})
.to(ElasticsearchBackend.class);
registerVersionedQueryBackend(supportedSearchVersion, ElasticsearchQueryString.NAME, ElasticsearchBackend.class);

registerESSearchTypeHandler(MessageList.NAME, ESMessageList.class);
registerESSearchTypeHandler(EventList.NAME, ESEventList.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.google.inject.multibindings.OptionalBinder;
import org.graylog.plugins.views.ViewsModule;
import org.graylog.plugins.views.search.SearchType;
import org.graylog.plugins.views.search.elasticsearch.ElasticsearchQueryString;
import org.graylog.plugins.views.search.engine.GeneratedQueryContext;
import org.graylog.plugins.views.search.engine.QueryBackend;
import org.graylog.plugins.views.search.export.ExportBackend;
Expand Down Expand Up @@ -89,8 +90,7 @@ public ViewsOSBackendModule(SearchVersion supportedSearchVersion) {
protected void configure() {
install(new FactoryModuleBuilder().build(OSGeneratedQueryContext.Factory.class));

bindForVersion(supportedSearchVersion, new TypeLiteral<QueryBackend<? extends GeneratedQueryContext>>() {})
.to(OpenSearchBackend.class);
registerVersionedQueryBackend(supportedSearchVersion, ElasticsearchQueryString.NAME, OpenSearchBackend.class);

registerOSSearchTypeHandler(MessageList.NAME, OSMessageList.class);
registerOSSearchTypeHandler(EventList.NAME, OSEventListDelegate.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,22 @@ protected void registerPivotAggregationFunction(String name, String description,
seriesSpecBinder().addBinding(name).toInstance(SeriesDescription.create(name, description));
}

protected MapBinder<String, QueryBackend<? extends GeneratedQueryContext>> queryBackendBinder(SearchVersion version) {
protected MapBinder<String, QueryBackend<? extends GeneratedQueryContext>> queryBackendBinder() {
return MapBinder.newMapBinder(binder(),
TypeLiteral.get(String.class),
new TypeLiteral<QueryBackend<? extends GeneratedQueryContext>>() {});
new TypeLiteral<>() {});

}

protected ScopedBindingBuilder registerQueryBackend(SearchVersion version, String name, Class<? extends QueryBackend<? extends GeneratedQueryContext>> implementation) {
return queryBackendBinder(version).addBinding(name).to(implementation);
/**
* Use this binder for versioned backends, it also registers the backend in the overall MapBinder in
* @param version
* @param name
* @param implementation
*/
protected void registerVersionedQueryBackend(SearchVersion version, String name, Class<? extends QueryBackend<? extends GeneratedQueryContext>> implementation) {
bindForVersion(version, new TypeLiteral<QueryBackend<? extends GeneratedQueryContext>>() {}).to(implementation);
queryBackendBinder().addBinding(name).to(implementation);
}

protected void registerESQueryDecorator(Class<? extends QueryStringDecorator> esQueryDecorator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ public class QueryEngine {

// TODO proper thread pool with tunable settings
private final Executor queryPool = Executors.newFixedThreadPool(4, new ThreadFactoryBuilder().setNameFormat("query-engine-%d").build());
private final QueryBackend<? extends GeneratedQueryContext> backend;
private final Map<String, QueryBackend<? extends GeneratedQueryContext>> backends;

@Inject
public QueryEngine(QueryBackend<? extends GeneratedQueryContext> backend,
public QueryEngine(Map<String, QueryBackend<? extends GeneratedQueryContext>> backends,
Set<QueryMetadataDecorator> queryMetadataDecorators,
QueryParser queryParser) {
this.backend = backend;
this.backends = backends;
this.queryMetadataDecorators = queryMetadataDecorators;
this.queryParser = queryParser;
}
Expand All @@ -75,6 +75,7 @@ public QueryMetadata parse(Search search, Query query) {
public ExplainResults explain(SearchJob searchJob, Set<SearchError> validationErrors, DateTimeZone timezone) {
final Map<String, ExplainResults.QueryExplainResult> queries = searchJob.getSearch().queries().stream()
.collect(Collectors.toMap(Query::id, q -> {
var backend = backends.get(q.query().type());
final GeneratedQueryContext generatedQueryContext = backend.generate(q, Set.of(), timezone);

return backend.explain(searchJob, q, generatedQueryContext);
Expand Down Expand Up @@ -116,6 +117,7 @@ public SearchJob execute(SearchJob searchJob, Set<SearchError> validationErrors,
}

private QueryResult prepareAndRun(SearchJob searchJob, Query query, Set<SearchError> validationErrors, DateTimeZone timezone) {
final var backend = backends.get(query.query().type());
LOG.debug("[{}] Using {} to generate query", query.id(), backend);
// with all the results done, we can execute the current query and eventually complete our own result
// if any of this throws an exception, the handle in #execute will convert it to an error and return a "failed" result instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public DecorateQueryStringsNormalizer(QueryStringDecorators queryStringDecorator

@Override
public Query normalizeQuery(final Query query, final ParameterProvider parameterProvider) {
// this only makes sense for ElasticsearchQueryString instances, don't touch any other type
if (!(query.query() instanceof ElasticsearchQueryString)) {
return query;
}
return query.toBuilder()
.query(ElasticsearchQueryString.of(this.queryStringDecorators.decorate(query.query().queryString(), parameterProvider, query)))
.filter(normalizeFilter(query.filter(), query, parameterProvider))
Expand Down

0 comments on commit bbe47d7

Please sign in to comment.