@Override
    public OSGeneratedQueryContext generate(Query query, Set<SearchError> validationErrors, DateTimeZone timezone) {
        final BackendQuery backendQuery = query.query();

        final Set<SearchType> searchTypes = query.searchTypes();

        final QueryBuilder normalizedRootQuery = translateQueryString(backendQuery.queryString());

        final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery()
                .filter(normalizedRootQuery);

        usedSearchFiltersToQueryStringsMapper.map(query.filters())
                .stream()
                .map(this::translateQueryString)
                .forEach(boolQuery::filter);

        // add the optional root query filters
        generateFilterClause(query.filter()).ifPresent(boolQuery::filter);

        final SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(boolQuery)
                .from(0)
                .size(0)
                .trackTotalHits(true);

        final OSGeneratedQueryContext queryContext = queryContextFactory.create(this, searchSourceBuilder, validationErrors, timezone);
        searchTypes.stream()
                .filter(searchType -> !isSearchTypeWithError(queryContext, searchType.id()))
                .forEach(searchType -> {
                    final String type = searchType.type();
                    final Provider<OSSearchTypeHandler<? extends SearchType>> searchTypeHandler = openSearchSearchTypeHandlers.get(type);
                    if (searchTypeHandler == null) {
                        LOG.error("Unknown search type {} for elasticsearch backend, cannot generate query part. Skipping this search type.", type);
                        queryContext.addError(new SearchTypeError(query, searchType.id(), "Unknown search type '" + type + "' for elasticsearch backend, cannot generate query"));
                        return;
                    }

                    final SearchSourceBuilder searchTypeSourceBuilder = queryContext.searchSourceBuilder(searchType);

                    final Set<String> effectiveStreamIds = query.effectiveStreams(searchType);

                    final BoolQueryBuilder searchTypeOverrides = QueryBuilders.boolQuery()
                            .must(searchTypeSourceBuilder.query())
                            .must(
                                    Objects.requireNonNull(
                                            TimeRangeQueryFactory.create(
                                                    query.effectiveTimeRange(searchType)
                                            ),
                                            "Timerange for search type " + searchType.id() + " cannot be found in query or search type."
                                    )
                            );

                    if (effectiveStreamIds.stream().noneMatch(s -> s.startsWith(Stream.DATASTREAM_PREFIX))) {
                        searchTypeOverrides
                                .must(QueryBuilders.termsQuery(Message.FIELD_STREAMS, effectiveStreamIds));
                    }

                    searchType.query().ifPresent(searchTypeQuery -> {
                        final QueryBuilder normalizedSearchTypeQuery = translateQueryString(searchTypeQuery.queryString());
                        searchTypeOverrides.must(normalizedSearchTypeQuery);
                    });

                    usedSearchFiltersToQueryStringsMapper.map(searchType.filters())
                            .stream()
                            .map(this::translateQueryString)
                            .forEach(searchTypeOverrides::must);

                    searchTypeSourceBuilder.query(searchTypeOverrides);

                    searchTypeHandler.get().generateQueryPart(query, searchType, queryContext);
                });

        return queryContext;
    }
@Test
    public void generatedContextHasQueryThatIncludesSearchFilters() {
        final ImmutableList<UsedSearchFilter> usedSearchFilters = ImmutableList.of(
                InlineQueryStringSearchFilter.builder().title("").description("").queryString("method:GET").build(),
                ReferencedQueryStringSearchFilter.create("12345")
        );
        doReturn(ImmutableSet.of("method:GET", "method:POST")).when(usedSearchFiltersToQueryStringsMapper).map(usedSearchFilters);

        final Query query = Query.builder()
                .id("queryWithSearchFilters")
                .query(ElasticsearchQueryString.of(""))
                .filters(usedSearchFilters)
                .timerange(RelativeRange.create(300))
                .build();

        final OSGeneratedQueryContext queryContext = backend.generate(query, Collections.emptySet(), DateTimeZone.UTC);
        final QueryBuilder esQuery = queryContext.searchSourceBuilder(new SearchType.Fallback()).query();
        assertThat(esQuery)
                .isNotNull()
                .isInstanceOf(BoolQueryBuilder.class);

        final List<QueryBuilder> filters = ((BoolQueryBuilder) esQuery).filter();

        //filter for empty ES query
        assertThat(filters)
                .anyMatch(queryBuilder -> queryBuilder instanceof MatchAllQueryBuilder);

        //2 filters from search filters
        assertThat(filters)
                .filteredOn(queryBuilder -> queryBuilder instanceof QueryStringQueryBuilder)
                .extracting(queryBuilder -> (QueryStringQueryBuilder) queryBuilder)
                .extracting(QueryStringQueryBuilder::queryString)
                .contains("method:POST")
                .contains("method:GET");
    }