@@ -5,14 +5,19 @@ import {
55} from '@hyperdx/common-utils/dist/core/metadata' ;
66import { BuilderChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types' ;
77
8- import { NOW } from '@/config' ;
8+ import { AUTOCOMPLETE_DATE_RANGE_MS , AUTOCOMPLETE_MIN_CHARS , NOW } from '@/config' ;
99import {
10- deduplicate2dArray ,
1110 useJsonColumns ,
1211 useMultipleAllFields ,
1312 useMultipleGetKeyValues ,
1413} from '@/hooks/useMetadata' ;
15- import { getLastToken , mergePath , stripNegation , toArray } from '@/utils' ;
14+ import {
15+ getLastToken ,
16+ mergePath ,
17+ stripNegation ,
18+ toArray ,
19+ useDebounce ,
20+ } from '@/utils' ;
1621
1722export interface ILanguageFormatter {
1823 formatFieldValue : ( f : Field ) => string ;
@@ -107,22 +112,41 @@ export function useAutoCompleteOptions(
107112 [ searchField , jsonColumns ] ,
108113 ) ;
109114
115+ // Extract the raw value prefix the user is typing after the colon, e.g.
116+ // `ServiceName:"user-ent` → `user-ent`. Empty when no field is active.
117+ const valuePrefix = useMemo ( ( ) => {
118+ if ( ! searchField ) return '' ;
119+ const lastToken = stripNegation ( getLastToken ( value ) ) ;
120+ const colon = lastToken . indexOf ( ':' ) ;
121+ if ( colon < 0 ) return '' ;
122+ let raw = lastToken . slice ( colon + 1 ) ;
123+ if ( raw . startsWith ( '"' ) ) raw = raw . slice ( 1 ) ;
124+ if ( raw . endsWith ( '"' ) ) raw = raw . slice ( 0 , - 1 ) ;
125+ return raw . replace ( / \* / g, '' ) ;
126+ } , [ searchField , value ] ) ;
127+
128+ // Debounce so we don't fire a new ClickHouse query on every keystroke
129+ const debouncedValuePrefix = useDebounce ( valuePrefix , 300 ) ;
130+
110131 // hooks to get key values
111- const chartConfigs : BuilderChartConfigWithDateRange [ ] = toArray (
112- tableConnection ,
113- ) . map ( ( { databaseName, tableName, connectionId } ) => ( {
114- connection : connectionId ,
115- from : {
116- databaseName,
117- tableName,
118- } ,
119- timestampValueExpression : '' ,
120- select : '' ,
121- where : '' ,
122- // TODO: Pull in date for query as arg
123- // just assuming 1/2 day is okay to query over right now
124- dateRange : [ new Date ( NOW - ( 86400 * 1000 ) / 2 ) , new Date ( NOW ) ] ,
125- } ) ) ;
132+ const chartConfigs : BuilderChartConfigWithDateRange [ ] = useMemo ( ( ) => {
133+ const fieldPath =
134+ searchField && debouncedValuePrefix . length >= AUTOCOMPLETE_MIN_CHARS
135+ ? formatter . formatFieldValue ( searchField )
136+ : null ;
137+ // Escape single quotes to prevent SQL injection from the typed prefix
138+ const safePrefix = debouncedValuePrefix . replace ( / ' / g, "''" ) ;
139+ return toArray ( tableConnection ) . map ( ( { databaseName, tableName, connectionId } ) => ( {
140+ connection : connectionId ,
141+ from : { databaseName, tableName } ,
142+ timestampValueExpression : '' ,
143+ select : '' ,
144+ // Push prefix filter into ClickHouse so we aren't limited to the
145+ // top-N values fetched without any value-level filtering
146+ where : fieldPath ? `${ fieldPath } ILIKE '${ safePrefix } %'` : '' ,
147+ dateRange : [ new Date ( NOW - AUTOCOMPLETE_DATE_RANGE_MS ) , new Date ( NOW ) ] ,
148+ } ) ) ;
149+ } , [ tableConnection , searchField , debouncedValuePrefix , formatter ] ) ;
126150
127151 const { data : keyVals } = useMultipleGetKeyValues ( {
128152 chartConfigs,
@@ -183,8 +207,9 @@ export function useAutoCompleteOptions(
183207 return output ;
184208 } , [ fieldCompleteOptions , keyVals , searchField , formatter ] ) ;
185209
186- // combine all autocomplete options
187- return useMemo ( ( ) => {
188- return deduplicate2dArray ( [ fieldCompleteOptions , keyValCompleteOptions ] ) ;
189- } , [ fieldCompleteOptions , keyValCompleteOptions ] ) ;
210+ // When a field is detected and values are loaded, keyValCompleteOptions contains
211+ // only the fetched values. When no field is detected, it falls back to
212+ // fieldCompleteOptions. Returning it directly prevents field names from leaking
213+ // into the dropdown while the user is completing a value (e.g. ServiceName:"user").
214+ return keyValCompleteOptions ;
190215}
0 commit comments