@@ -33,14 +33,19 @@ import { IconSearch } from '@tabler/icons-react';
3333
3434import {
3535 useAllFields ,
36+ useColumns ,
3637 useGetKeyValues ,
3738 useGetValuesDistribution ,
3839 useJsonColumns ,
3940 useTableMetadata ,
4041} from '@/hooks/useMetadata' ;
4142import { useMetadataWithSettings } from '@/hooks/useMetadata' ;
4243import useResizable from '@/hooks/useResizable' ;
43- import { FilterStateHook , usePinnedFilters } from '@/searchFilters' ;
44+ import {
45+ FilterStateHook ,
46+ IS_ROOT_SPAN_COLUMN_NAME ,
47+ usePinnedFilters ,
48+ } from '@/searchFilters' ;
4449import { useSource } from '@/source' ;
4550import { mergePath } from '@/utils' ;
4651
@@ -300,19 +305,19 @@ const FilterRangeDisplay = ({
300305
301306export type FilterGroupProps = {
302307 name : string ;
303- options : { value : string ; label : string } [ ] ;
308+ options : { value : string | boolean ; label : string } [ ] ;
304309 optionsLoading ?: boolean ;
305310 selectedValues ?: {
306- included : Set < string > ;
307- excluded : Set < string > ;
311+ included : Set < string | boolean > ;
312+ excluded : Set < string | boolean > ;
308313 range ?: { min : number ; max : number } ;
309314 } ;
310- onChange : ( value : string ) => void ;
315+ onChange : ( value : string | boolean ) => void ;
311316 onClearClick : VoidFunction ;
312- onOnlyClick : ( value : string ) => void ;
313- onExcludeClick : ( value : string ) => void ;
314- onPinClick : ( value : string ) => void ;
315- isPinned : ( value : string ) => boolean ;
317+ onOnlyClick : ( value : string | boolean ) => void ;
318+ onExcludeClick : ( value : string | boolean ) => void ;
319+ onPinClick : ( value : string | boolean ) => void ;
320+ isPinned : ( value : string | boolean ) => boolean ;
316321 onFieldPinClick ?: VoidFunction ;
317322 isFieldPinned ?: boolean ;
318323 onLoadMore : ( key : string ) => void ;
@@ -355,7 +360,9 @@ export const FilterGroup = ({
355360 // Accordion expanded state
356361 const [ isExpanded , setExpanded ] = useState ( isDefaultExpanded ?? false ) ;
357362 // Track recently moved items for highlight animation
358- const [ recentlyMoved , setRecentlyMoved ] = useState < Set < string > > ( new Set ( ) ) ;
363+ const [ recentlyMoved , setRecentlyMoved ] = useState < Set < string | boolean > > (
364+ new Set ( ) ,
365+ ) ;
359366 // Show what percentage of the data has each value
360367 const [ showDistributions , setShowDistributions ] = useState ( false ) ;
361368 // For live searches, don't refresh percentages when date range changes
@@ -438,7 +445,7 @@ export const FilterGroup = ({
438445 return [
439446 ...Array . from ( selectedSet )
440447 . filter ( value => ! options . find ( option => option . value === value ) )
441- . map ( value => ( { value, label : value } ) ) ,
448+ . map ( value => ( { value, label : value . toString ( ) } ) ) ,
442449 ...options ,
443450 ] ;
444451 } , [ options , selectedValues ] ) ;
@@ -455,11 +462,11 @@ export const FilterGroup = ({
455462 . filter ( option => {
456463 return (
457464 option . value &&
458- option . value . toLowerCase ( ) . includes ( search . toLowerCase ( ) )
465+ option . label . toLowerCase ( ) . includes ( search . toLowerCase ( ) )
459466 ) ;
460467 } )
461468 . toSorted ( ( a , b ) =>
462- a . value . localeCompare ( b . value , undefined , { numeric : true } ) ,
469+ a . label . localeCompare ( b . label , undefined , { numeric : true } ) ,
463470 ) ;
464471 }
465472
@@ -485,14 +492,14 @@ export const FilterGroup = ({
485492 if ( ! aExcluded && bExcluded ) return 1 ;
486493
487494 // Then sort by estimated percentage of rows with this value, if available
488- const aPercentage = distributionData ?. get ( a . value ) ?? 0 ;
489- const bPercentage = distributionData ?. get ( b . value ) ?? 0 ;
495+ const aPercentage = distributionData ?. get ( a . value . toString ( ) ) ?? 0 ;
496+ const bPercentage = distributionData ?. get ( b . value . toString ( ) ) ?? 0 ;
490497 if ( aPercentage !== bPercentage ) {
491498 return bPercentage - aPercentage ;
492499 }
493500
494501 // Finally sort alphabetically/numerically
495- return a . value . localeCompare ( b . value , undefined , { numeric : true } ) ;
502+ return a . label . localeCompare ( b . label , undefined , { numeric : true } ) ;
496503 } ) ;
497504 } , [
498505 search ,
@@ -512,7 +519,7 @@ export const FilterGroup = ({
512519
513520 // Simple highlight animation when checkbox is checked
514521 const handleChange = useCallback (
515- ( value : string ) => {
522+ ( value : string | boolean ) => {
516523 const wasIncluded = selectedValues . included . has ( value ) ;
517524
518525 // If checking (not unchecking), trigger highlight animation
@@ -662,7 +669,7 @@ export const FilterGroup = ({
662669 ) }
663670 { displayedOptions . map ( option => (
664671 < FilterCheckbox
665- key = { option . value }
672+ key = { option . value . toString ( ) }
666673 label = { option . label }
667674 pinned = { isPinned ( option . value ) }
668675 className = {
@@ -684,7 +691,7 @@ export const FilterGroup = ({
684691 isPercentageLoading = { isFetchingDistribution }
685692 percentage = {
686693 showDistributions && distributionData
687- ? ( distributionData . get ( option . value ) ?? 0 )
694+ ? ( distributionData . get ( option . value . toString ( ) ) ?? 0 )
688695 : undefined
689696 }
690697 />
@@ -794,7 +801,7 @@ const DBSearchPageFiltersComponent = ({
794801 const setFilterValue = useCallback (
795802 (
796803 property : string ,
797- value : string ,
804+ value : string | boolean ,
798805 action ?: 'only' | 'exclude' | 'include' | undefined ,
799806 ) => {
800807 return _setFilterValue ( property , value , action ) ;
@@ -821,6 +828,11 @@ const DBSearchPageFiltersComponent = ({
821828 tableName : chartConfig . from . tableName ,
822829 connectionId : chartConfig . connection ,
823830 } ) ;
831+ const { data : columns } = useColumns ( {
832+ databaseName : chartConfig . from . databaseName ,
833+ tableName : chartConfig . from . tableName ,
834+ connectionId : chartConfig . connection ,
835+ } ) ;
824836
825837 const { data : source } = useSource ( { id : sourceId } ) ;
826838 const { data : tableMetadata } = useTableMetadata ( tcFromSource ( source ) ) ;
@@ -913,7 +925,7 @@ const DBSearchPageFiltersComponent = ({
913925 return Array . from ( mergedKeys ) . map ( key => {
914926 const queriedValues = facetsMap . get ( key ) ;
915927 const pinnedValues = pinnedFilters [ key ] ;
916- const mergedValues = new Set < string > ( [
928+ const mergedValues = new Set < string | boolean > ( [
917929 ...( queriedValues ?? [ ] ) ,
918930 ...( pinnedValues ?? [ ] ) ,
919931 ] ) ;
@@ -961,7 +973,7 @@ const DBSearchPageFiltersComponent = ({
961973 ) ;
962974
963975 const shownFacets = useMemo ( ( ) => {
964- const _facets : { key : string ; value : string [ ] } [ ] = [ ] ;
976+ const _facets : { key : string ; value : ( string | boolean ) [ ] } [ ] = [ ] ;
965977 for ( const _facet of facetsWithPinnedValues ?? [ ] ) {
966978 const facet = structuredClone ( _facet ) ;
967979 if ( jsonColumns ?. some ( col => facet . key . startsWith ( col ) ) ) {
@@ -996,7 +1008,10 @@ const DBSearchPageFiltersComponent = ({
9961008 key => ! _facets . some ( facet => facet . key === key ) ,
9971009 ) ;
9981010 for ( const key of remainingFilterState ) {
999- _facets . push ( { key, value : Array . from ( filterState [ key ] . included ) } ) ;
1011+ _facets . push ( {
1012+ key,
1013+ value : Array . from ( filterState [ key ] . included ) . map ( v => v . toString ( ) ) ,
1014+ } ) ;
10001015 }
10011016
10021017 // prioritize facets that are primary keys
@@ -1052,22 +1067,30 @@ const DBSearchPageFiltersComponent = ({
10521067 if ( ! source ?. parentSpanIdExpression ) return ;
10531068
10541069 if ( rootSpansOnly ) {
1055- setFilterValue ( source . parentSpanIdExpression , '' , 'only' ) ;
1070+ if ( columns ?. some ( col => col . name === IS_ROOT_SPAN_COLUMN_NAME ) ) {
1071+ setFilterValue ( IS_ROOT_SPAN_COLUMN_NAME , true , 'only' ) ;
1072+ } else {
1073+ setFilterValue ( source . parentSpanIdExpression , '' , 'only' ) ;
1074+ }
10561075 } else {
10571076 clearFilter ( source . parentSpanIdExpression ) ;
1077+ clearFilter ( IS_ROOT_SPAN_COLUMN_NAME ) ;
10581078 }
10591079 } ,
1060- [ setFilterValue , clearFilter , source ] ,
1080+ [ setFilterValue , clearFilter , source , columns ] ,
10611081 ) ;
10621082
10631083 const isRootSpansOnly = useMemo ( ( ) => {
10641084 if ( ! source ?. parentSpanIdExpression || source . kind !== SourceKind . Trace )
10651085 return false ;
10661086
10671087 const parentSpanIdFilter = filterState ?. [ source ?. parentSpanIdExpression ] ;
1088+ const isRootSpanFilter = filterState ?. [ IS_ROOT_SPAN_COLUMN_NAME ] ;
10681089 return (
1069- parentSpanIdFilter ?. included . size === 1 &&
1070- parentSpanIdFilter ?. included . has ( '' )
1090+ ( parentSpanIdFilter ?. included . size === 1 &&
1091+ parentSpanIdFilter ?. included . has ( '' ) ) ||
1092+ ( isRootSpanFilter ?. included . size === 1 &&
1093+ isRootSpanFilter ?. included . has ( true ) )
10711094 ) ;
10721095 } , [ filterState , source ] ) ;
10731096
@@ -1214,7 +1237,10 @@ const DBSearchPageFiltersComponent = ({
12141237 } ,
12151238 { } as Record <
12161239 string ,
1217- { included : Set < string > ; excluded : Set < string > }
1240+ {
1241+ included : Set < string | boolean > ;
1242+ excluded : Set < string | boolean > ;
1243+ }
12181244 > ,
12191245 ) }
12201246 onChange = { ( key , value ) => {
@@ -1269,7 +1295,7 @@ const DBSearchPageFiltersComponent = ({
12691295 name = { cleanedFacetName ( facet . key ) }
12701296 options = { facet . value . map ( value => ( {
12711297 value,
1272- label : value ,
1298+ label : value . toString ( ) ,
12731299 } ) ) }
12741300 optionsLoading = { isFacetsLoading }
12751301 selectedValues = {
0 commit comments