Browse Source

feat(trace-explorer): Use new SearchQueryBuilder (#75979)

This switches the trace explorer to use the new SearchQueryBuilder.
Tony Xiao 7 months ago
parent
commit
d9d5092c5e

+ 101 - 0
static/app/components/performance/spanSearchQueryBuilder.tsx

@@ -0,0 +1,101 @@
+import {useCallback, useMemo} from 'react';
+
+import {fetchSpanFieldValues} from 'sentry/actionCreators/tags';
+import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
+import {SearchQueryBuilder} from 'sentry/components/searchQueryBuilder';
+import type {CallbackSearchState} from 'sentry/components/searchQueryBuilder/types';
+import {t} from 'sentry/locale';
+import type {PageFilters} from 'sentry/types/core';
+import {SavedSearchType, type Tag, type TagCollection} from 'sentry/types/group';
+import {defined} from 'sentry/utils';
+import {isAggregateField, isMeasurement} from 'sentry/utils/discover/fields';
+import {DEVICE_CLASS_TAG_VALUES, isDeviceClass} from 'sentry/utils/fields';
+import useApi from 'sentry/utils/useApi';
+import useOrganization from 'sentry/utils/useOrganization';
+import usePageFilters from 'sentry/utils/usePageFilters';
+import {useSpanFieldSupportedTags} from 'sentry/views/performance/utils/useSpanFieldSupportedTags';
+
+interface SpanSearchQueryBuilderProps {
+  initialQuery: string;
+  searchSource: string;
+  datetime?: PageFilters['datetime'];
+  disableLoadingTags?: boolean;
+  onSearch?: (query: string, state: CallbackSearchState) => void;
+  placeholder?: string;
+  projects?: PageFilters['projects'];
+}
+
+export function SpanSearchQueryBuilder({
+  initialQuery,
+  searchSource,
+  datetime,
+  onSearch,
+  placeholder,
+  projects,
+}: SpanSearchQueryBuilderProps) {
+  const api = useApi();
+  const organization = useOrganization();
+  const {selection} = usePageFilters();
+
+  const placeholderText = useMemo(() => {
+    return placeholder ?? t('Search for spans, users, tags, and more');
+  }, [placeholder]);
+
+  const supportedTags = useSpanFieldSupportedTags({
+    projects: projects ?? selection.projects,
+  });
+
+  const filterTags: TagCollection = useMemo(() => {
+    return {...supportedTags};
+  }, [supportedTags]);
+
+  const filterKeySections = useMemo(() => {
+    return [];
+  }, []);
+
+  const getSpanFilterTagValues = useCallback(
+    async (tag: Tag, queryString: string) => {
+      if (isAggregateField(tag.key) || isMeasurement(tag.key)) {
+        // We can't really auto suggest values for aggregate fields
+        // or measurements, so we simply don't
+        return Promise.resolve([]);
+      }
+      //
+      // device.class is stored as "numbers" in snuba, but we want to suggest high, medium,
+      // and low search filter values because discover maps device.class to these values.
+      if (isDeviceClass(tag.key)) {
+        return Promise.resolve(DEVICE_CLASS_TAG_VALUES);
+      }
+
+      try {
+        const results = await fetchSpanFieldValues({
+          api,
+          orgSlug: organization.slug,
+          fieldKey: tag.key,
+          search: queryString,
+          projectIds: projects?.map(String) ?? selection.projects?.map(String),
+          endpointParams: normalizeDateTimeParams(datetime ?? selection.datetime),
+        });
+        return results.filter(({name}) => defined(name)).map(({name}) => name);
+      } catch (e) {
+        throw new Error(`Unable to fetch event field values: ${e}`);
+      }
+    },
+    [api, organization, datetime, projects, selection.datetime, selection.projects]
+  );
+
+  return (
+    <SearchQueryBuilder
+      placeholder={placeholderText}
+      filterKeys={filterTags}
+      initialQuery={initialQuery}
+      onSearch={onSearch}
+      searchSource={searchSource}
+      filterKeySections={filterKeySections}
+      getTagValues={getSpanFilterTagValues}
+      disallowFreeText
+      disallowUnsupportedFilters
+      recentSearches={SavedSearchType.EVENT}
+    />
+  );
+}

+ 1 - 1
static/app/views/traces/content.tsx

@@ -39,7 +39,7 @@ const DEFAULT_PER_PAGE = 50;
 export default function Wrapper(props) {
   const organization = useOrganization();
 
-  if (organization.features.includes('xvisibility-explore-view')) {
+  if (organization.features.includes('visibility-explore-view')) {
     return <ExploreContent {...props} />;
   }
 

+ 21 - 11
static/app/views/traces/tracesSearchBar.tsx

@@ -2,6 +2,7 @@ import styled from '@emotion/styled';
 
 import {Button} from 'sentry/components/button';
 import SearchBar from 'sentry/components/events/searchBar';
+import {SpanSearchQueryBuilder} from 'sentry/components/performance/spanSearchQueryBuilder';
 import {IconAdd, IconClose} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
@@ -46,17 +47,26 @@ export function TracesSearchBar({
       {localQueries.map((query, index) => (
         <TraceBar key={index}>
           <SpanLetter>{getSpanName(index)}</SpanLetter>
-          <StyledSearchBar
-            searchSource="trace-explorer"
-            query={query}
-            onSearch={(queryString: string) => handleSearch(index, queryString)}
-            placeholder={t('Search for span attributes')}
-            organization={organization}
-            supportedTags={supportedTags}
-            dataset={DiscoverDatasets.SPANS_INDEXED}
-            projectIds={selection.projects}
-            savedSearchType={SavedSearchType.SPAN}
-          />
+          {organization.features.includes('search-query-builder-performance') ? (
+            <SpanSearchQueryBuilder
+              projects={selection.projects}
+              initialQuery={query}
+              onSearch={(queryString: string) => handleSearch(index, queryString)}
+              searchSource="trace-explorer"
+            />
+          ) : (
+            <StyledSearchBar
+              searchSource="trace-explorer"
+              query={query}
+              onSearch={(queryString: string) => handleSearch(index, queryString)}
+              placeholder={t('Search for span attributes')}
+              organization={organization}
+              supportedTags={supportedTags}
+              dataset={DiscoverDatasets.SPANS_INDEXED}
+              projectIds={selection.projects}
+              savedSearchType={SavedSearchType.SPAN}
+            />
+          )}
           <StyledButton
             aria-label={t('Remove Span')}
             icon={<IconClose size="sm" />}