public QueryMetadata parse(Query query) {
        checkArgument(query.query() instanceof ElasticsearchQueryString);
        final String mainQueryString = query.query().queryString();
        final java.util.stream.Stream<String> queryStringStreams = java.util.stream.Stream.concat(
                java.util.stream.Stream.of(mainQueryString),
                query.searchTypes().stream().flatMap(this::queryStringsFromSearchType)
        );

        return queryStringStreams
                .map(queryStringParser::parse)
                .reduce(QueryMetadata.builder().build(), (meta1, meta2) -> QueryMetadata.builder().usedParameters(
                        Sets.union(meta1.usedParameters(), meta2.usedParameters())
                ).build());
    }
@Test
    public void parseAlsoConsidersWidgetFilters() {
        final SearchType searchType1 = Pivot.builder()
                .id("searchType1")
                .filter(QueryStringFilter.builder().query("source:$bar$").build())
                .series(new ArrayList<>())
                .rollup(false)
                .build();
        final SearchType searchType2 = Pivot.builder()
                .id("searchType2")
                .filter(AndFilter.builder().filters(ImmutableSet.of(
                        QueryStringFilter.builder().query("http_action:$baz$").build(),
                        QueryStringFilter.builder().query("source:localhost").build()
                )).build())
                .series(new ArrayList<>())
                .rollup(false)
                .build();
        final QueryMetadata queryMetadata = queryParser.parse(Query.builder()
                .id("abc123")
                .query(ElasticsearchQueryString.of("user_name:$username$ http_method:$foo$"))
                .timerange(RelativeRange.create(600))
                .searchTypes(ImmutableSet.of(searchType1, searchType2))
                .build());

        assertThat(queryMetadata.usedParameterNames())
                .containsExactlyInAnyOrder("username", "foo", "bar", "baz");
    }