Skip to content

Commit

Permalink
feat(hogql): edit hogql expressions for tables other than 'events' (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusandra authored Oct 4, 2023
1 parent b82890e commit 1d4d2d4
Show file tree
Hide file tree
Showing 23 changed files with 113 additions and 30 deletions.
8 changes: 6 additions & 2 deletions frontend/src/lib/components/HogQLEditor/HogQLEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { hogQLEditorLogic } from './hogQLEditorLogic'
export interface HogQLEditorProps {
onChange: (value: string) => void
value: string | undefined
hogQLTable?: string
disablePersonProperties?: boolean
disableAutoFocus?: boolean
disableCmdEnter?: boolean
Expand All @@ -20,6 +21,7 @@ let uniqueNode = 0
export function HogQLEditor({
onChange,
value,
hogQLTable,
disablePersonProperties,
disableAutoFocus,
disableCmdEnter,
Expand All @@ -28,7 +30,7 @@ export function HogQLEditor({
}: HogQLEditorProps): JSX.Element {
const [key] = useState(() => `HogQLEditor.${uniqueNode++}`)
const textareaRef = useRef<HTMLTextAreaElement | null>(null)
const logic = hogQLEditorLogic({ key, value, onChange, textareaRef })
const logic = hogQLEditorLogic({ key, value, onChange, hogQLTable, textareaRef })
const { localValue, error, responseLoading } = useValues(logic)
const { setLocalValue, submit } = useActions(logic)

Expand All @@ -53,7 +55,9 @@ export function HogQLEditor({
maxRows={6}
placeholder={
placeholder ??
(disablePersonProperties
(hogQLTable === 'persons'
? "Enter HogQL expression, such as:\n- properties.$geoip_country_name\n- toInt(properties.$browser_version) * 10\n- concat(properties.name, ' <', properties.email, '>')\n- is_identified ? 'user' : 'anon'"
: disablePersonProperties
? "Enter HogQL expression, such as:\n- properties.$current_url\n- toInt(properties.`Long Field Name`) * 10\n- concat(event, ' ', distinct_id)\n- if(1 < 2, 'small', 'large')"
: "Enter HogQL Expression, such as:\n- properties.$current_url\n- person.properties.$geoip_country_name\n- toInt(properties.`Long Field Name`) * 10\n- concat(event, ' ', distinct_id)\n- if(1 < 2, 'small', 'large')")
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/lib/components/HogQLEditor/hogQLEditorLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React from 'react'
export interface HogQLEditorLogicProps {
key: string
value: string | undefined
hogQLTable?: string
onChange: (value: string) => void
textareaRef?: React.MutableRefObject<HTMLTextAreaElement | null>
}
Expand All @@ -32,6 +33,7 @@ export const hogQLEditorLogic = kea<hogQLEditorLogicType>([
const response = await query<HogQLMetadata>({
kind: NodeKind.HogQLMetadata,
expr: values.localValue,
table: props.hogQLTable || 'events',
})
breakpoint()
if (response && Array.isArray(response.errors) && response.errors.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface PropertyFiltersProps {
disablePopover?: boolean
style?: CSSProperties
taxonomicGroupTypes?: TaxonomicFilterGroupType[]
hogQLTable?: string
showNestedArrow?: boolean
eventNames?: string[]
logicalRowDivider?: boolean
Expand All @@ -36,6 +37,7 @@ export function PropertyFilters({
showConditionBadge = false,
disablePopover = false, // use bare PropertyFilter without popover
taxonomicGroupTypes,
hogQLTable,
style = {},
showNestedArrow = false,
eventNames = [],
Expand Down Expand Up @@ -88,6 +90,7 @@ export function PropertyFilters({
onComplete={onComplete}
orFiltering={orFiltering}
taxonomicGroupTypes={taxonomicGroupTypes}
hogQLTable={hogQLTable}
eventNames={eventNames}
propertyGroupType={propertyGroupType}
disablePopover={disablePopover || orFiltering}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export function TaxonomicPropertyFilter({
orFiltering,
addText = 'Add filter',
hasRowOperator,
hogQLTable,
}: PropertyFilterInternalProps): JSX.Element {
const pageKey = useMemo(() => pageKeyInput || `filter-${uniqueMemoizedIndex++}`, [pageKeyInput])
const groupTypes = taxonomicGroupTypes || [
Expand Down Expand Up @@ -98,6 +99,7 @@ export function TaxonomicPropertyFilter({
value={cohortOrOtherValue}
onChange={taxonomicOnChange}
taxonomicGroupTypes={groupTypes}
hogQLTable={hogQLTable}
eventNames={eventNames}
/>
)
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/components/PropertyFilters/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ export interface PropertyFilterInternalProps {
orFiltering?: boolean
addText?: string | null
hasRowOperator?: boolean
hogQLTable?: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ export function InfiniteSelectResults({
const RenderComponent = activeTaxonomicGroup?.render

const listComponent = RenderComponent ? (
<RenderComponent value={value} onChange={(newValue) => selectItem(activeTaxonomicGroup, newValue, newValue)} />
<RenderComponent
{...(activeTaxonomicGroup?.componentProps ?? {})}
value={value}
onChange={(newValue) => selectItem(activeTaxonomicGroup, newValue, newValue)}
/>
) : (
<InfiniteList />
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import { HogQLEditor } from 'lib/components/HogQLEditor/HogQLEditor'
export interface InlineHogQLEditorProps {
value?: TaxonomicFilterValue
onChange: (value: TaxonomicFilterValue) => void
hogQLTable?: string
}

export function InlineHogQLEditor({ value, onChange }: InlineHogQLEditorProps): JSX.Element {
export function InlineHogQLEditor({ value, onChange, hogQLTable }: InlineHogQLEditorProps): JSX.Element {
return (
<div className="px-2">
<HogQLEditor
onChange={onChange}
value={String(value ?? '')}
hogQLTable={hogQLTable}
submitText={value ? 'Update HogQL expression' : 'Add HogQL expression'}
disableAutoFocus // :TRICKY: No autofocus here. It's controlled in the TaxonomicFilter.
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function TaxonomicFilter({
onClose,
taxonomicGroupTypes,
optionsFromProp,
hogQLTable,
eventNames,
height,
width,
Expand Down Expand Up @@ -50,6 +51,7 @@ export function TaxonomicFilter({
popoverEnabled,
selectFirstItem,
excludedProperties,
hogQLTable,
}

const logic = taxonomicFilterLogic(taxonomicFilterLogicProps)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>({
(taxonomicFilterLogicKey) => taxonomicFilterLogicKey,
],
eventNames: [() => [(_, props) => props.eventNames], (eventNames) => eventNames ?? []],
hogQLTable: [() => [(_, props) => props.hogQLTable], (hogQLTable) => hogQLTable ?? 'events'],
excludedProperties: [
() => [(_, props) => props.excludedProperties],
(excludedProperties) => excludedProperties ?? {},
Expand All @@ -146,16 +147,18 @@ export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>({
s.groupAnalyticsTaxonomicGroups,
s.groupAnalyticsTaxonomicGroupNames,
s.eventNames,
s.hogQLTable,
s.excludedProperties,
],
(
teamId,
groupAnalyticsTaxonomicGroups,
groupAnalyticsTaxonomicGroupNames,
eventNames,
hogQLTable,
excludedProperties
): TaxonomicFilterGroup[] => {
const groups = [
const groups: TaxonomicFilterGroup[] = [
{
name: 'Events',
searchPlaceholder: 'events',
Expand Down Expand Up @@ -429,6 +432,7 @@ export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>({
type: TaxonomicFilterGroupType.HogQLExpression,
render: InlineHogQLEditor,
getPopoverHeader: () => 'HogQL',
componentProps: { hogQLTable },
},
...groupAnalyticsTaxonomicGroups,
...groupAnalyticsTaxonomicGroupNames,
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/lib/components/TaxonomicFilter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface TaxonomicFilterProps {
selectFirstItem?: boolean
/** use to filter results in a group by name, currently only working for EventProperties */
excludedProperties?: { [key in TaxonomicFilterGroupType]?: TaxonomicFilterValue[] }
hogQLTable?: string
}

export interface TaxonomicFilterLogicProps extends TaxonomicFilterProps {
Expand Down Expand Up @@ -56,6 +57,8 @@ export interface TaxonomicFilterGroup {
groupTypeIndex?: number
getFullDetailUrl?: (instance: any) => string
excludedProperties?: string[]
/** Passed to the component specified via the `render` key */
componentProps?: Record<string, any>
}

export enum TaxonomicFilterGroupType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface TaxonomicPopoverProps<ValueType extends TaxonomicFilterValue =
allowClear?: boolean
style?: React.CSSProperties
excludedProperties?: { [key in TaxonomicFilterGroupType]?: TaxonomicFilterValue[] }
hogQLTable?: string
}

/** Like TaxonomicPopover, but convenient when you know you will only use string values */
Expand All @@ -45,6 +46,7 @@ export function TaxonomicPopover<ValueType extends TaxonomicFilterValue = Taxono
placeholderClass = 'text-muted',
allowClear = false,
excludedProperties,
hogQLTable,
...buttonPropsRest
}: TaxonomicPopoverProps<ValueType>): JSX.Element {
const [localValue, setLocalValue] = useState<ValueType>(value || ('' as ValueType))
Expand Down Expand Up @@ -85,6 +87,7 @@ export function TaxonomicPopover<ValueType extends TaxonomicFilterValue = Taxono
}}
taxonomicGroupTypes={groupTypes ?? [groupType]}
eventNames={eventNames}
hogQLTable={hogQLTable}
excludedProperties={excludedProperties}
/>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { columnConfiguratorLogic, ColumnConfiguratorLogicProps } from './columnC
import { defaultDataTableColumns, extractExpressionComment, removeExpressionComment } from '../utils'
import { DataTableNode, NodeKind } from '~/queries/schema'
import { LemonModal } from 'lib/lemon-ui/LemonModal'
import { isEventsQuery, taxonomicFilterToHogQl, trimQuotes } from '~/queries/utils'
import { isEventsQuery, taxonomicEventFilterToHogQL, trimQuotes } from '~/queries/utils'
import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter'
import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo'
import { PropertyFilterIcon } from 'lib/components/PropertyFilters/components/PropertyFilterIcon'
Expand Down Expand Up @@ -166,7 +166,7 @@ function ColumnConfiguratorModal({ query }: ColumnConfiguratorProps): JSX.Elemen
]}
value={undefined}
onChange={(group, value) => {
const column = taxonomicFilterToHogQl(group.type, value)
const column = taxonomicEventFilterToHogQL(group.type, value)
if (column !== null) {
selectColumn(column)
}
Expand Down
24 changes: 15 additions & 9 deletions frontend/src/queries/nodes/DataTable/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
import clsx from 'clsx'
import { SessionPlayerModal } from 'scenes/session-recordings/player/modal/SessionPlayerModal'
import { OpenEditorButton } from '~/queries/nodes/Node/OpenEditorButton'
import { isEventsQuery, isHogQlAggregation, isHogQLQuery, taxonomicFilterToHogQl } from '~/queries/utils'
import { isEventsQuery, isHogQlAggregation, isHogQLQuery, taxonomicEventFilterToHogQL } from '~/queries/utils'
import { PersonPropertyFilters } from '~/queries/nodes/PersonsNode/PersonPropertyFilters'
import { PersonsSearch } from '~/queries/nodes/PersonsNode/PersonsSearch'
import { PersonDeleteModal } from 'scenes/persons/PersonDeleteModal'
Expand All @@ -55,7 +55,7 @@ interface DataTableProps {
cachedResults?: AnyResponseType
}

const groupTypes = [
const eventGroupTypes = [
TaxonomicFilterGroupType.HogQLExpression,
TaxonomicFilterGroupType.EventProperties,
TaxonomicFilterGroupType.PersonProperties,
Expand Down Expand Up @@ -121,6 +121,9 @@ export function DataTable({ uniqueKey, query, setQuery, context, cachedResults }
? columnsInResponse ?? columnsInQuery
: columnsInQuery

const groupTypes = eventGroupTypes
const hogQLTable = 'events'

const lemonColumns: LemonTableColumn<DataTableRow, any>[] = [
...columnsInLemonTable.map((key, index) => ({
dataIndex: key as any,
Expand All @@ -144,7 +147,7 @@ export function DataTable({ uniqueKey, query, setQuery, context, cachedResults }
},
sorter: undefined, // using custom sorting code
more:
!isReadOnly && showActions && sourceFeatures.has(QueryFeature.eventActionsColumn) ? (
!isReadOnly && showActions && sourceFeatures.has(QueryFeature.selectAndOrderByColumns) ? (
<>
<div className="px-2 py-1">
<div className="font-mono font-bold">{extractExpressionComment(key)}</div>
Expand All @@ -156,11 +159,13 @@ export function DataTable({ uniqueKey, query, setQuery, context, cachedResults }
<TaxonomicPopover
groupType={TaxonomicFilterGroupType.HogQLExpression}
value={key}
groupTypes={groupTypes}
hogQLTable={hogQLTable}
renderValue={() => <>Edit column</>}
type="tertiary"
fullWidth
onChange={(v, g) => {
const hogQl = taxonomicFilterToHogQl(g, v)
const hogQl = taxonomicEventFilterToHogQL(g, v)
if (setQuery && hogQl && sourceFeatures.has(QueryFeature.selectAndOrderByColumns)) {
// Typecasting to a query type with select and order_by fields.
// The actual query may or may not be an events query.
Expand All @@ -183,7 +188,6 @@ export function DataTable({ uniqueKey, query, setQuery, context, cachedResults }
})
}
}}
groupTypes={groupTypes}
/>
<LemonDivider />
{canSort ? (
Expand Down Expand Up @@ -230,12 +234,14 @@ export function DataTable({ uniqueKey, query, setQuery, context, cachedResults }
<TaxonomicPopover
groupType={TaxonomicFilterGroupType.HogQLExpression}
value={''}
groupTypes={groupTypes}
hogQLTable={hogQLTable}
placeholder={<span className="not-italic">Add column left</span>}
data-attr="datatable-add-column-left"
type="tertiary"
fullWidth
onChange={(v, g) => {
const hogQl = taxonomicFilterToHogQl(g, v)
const hogQl = taxonomicEventFilterToHogQL(g, v)
if (setQuery && hogQl && sourceFeatures.has(QueryFeature.selectAndOrderByColumns)) {
const isAggregation = isHogQlAggregation(hogQl)
const source = query.source as EventsQuery
Expand All @@ -252,17 +258,18 @@ export function DataTable({ uniqueKey, query, setQuery, context, cachedResults }
})
}
}}
groupTypes={groupTypes}
/>
<TaxonomicPopover
groupType={TaxonomicFilterGroupType.HogQLExpression}
value={''}
groupTypes={groupTypes}
hogQLTable={hogQLTable}
placeholder={<span className="not-italic">Add column right</span>}
data-attr="datatable-add-column-right"
type="tertiary"
fullWidth
onChange={(v, g) => {
const hogQl = taxonomicFilterToHogQl(g, v)
const hogQl = taxonomicEventFilterToHogQL(g, v)
if (setQuery && hogQl && sourceFeatures.has(QueryFeature.selectAndOrderByColumns)) {
const isAggregation = isHogQlAggregation(hogQl)
const source = query.source as EventsQuery
Expand All @@ -279,7 +286,6 @@ export function DataTable({ uniqueKey, query, setQuery, context, cachedResults }
})
}
}}
groupTypes={groupTypes}
/>
{columnsInQuery.filter((c) => c !== '*').length > 1 ? (
<>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/queries/nodes/DataTable/renderColumnMeta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { QueryContext, DataTableNode } from '~/queries/schema'
import { isEventsQuery, isHogQLQuery, trimQuotes } from '~/queries/utils'
import { extractExpressionComment } from '~/queries/nodes/DataTable/utils'
import { SortingIndicator } from 'lib/lemon-ui/LemonTable/sorting'
import { getQueryFeatures, QueryFeature } from '~/queries/nodes/DataTable/queryFeatures'

export interface ColumnMeta {
title?: JSX.Element | string
Expand All @@ -13,6 +14,7 @@ export interface ColumnMeta {
export function renderColumnMeta(key: string, query: DataTableNode, context?: QueryContext): ColumnMeta {
let width: number | undefined
let title: JSX.Element | string | undefined
const queryFeatures = getQueryFeatures(query.source)

if (isHogQLQuery(query.source)) {
title = key
Expand All @@ -27,8 +29,6 @@ export function renderColumnMeta(key: string, query: DataTableNode, context?: Qu
title = 'Event'
} else if (key === 'person') {
title = 'Person'
} else if (key === 'url') {
title = 'URL / Screen'
} else if (key.startsWith('properties.')) {
title = <PropertyKeyInfo value={trimQuotes(key.substring(11))} type={PropertyFilterType.Event} disableIcon />
} else if (key.startsWith('context.columns.')) {
Expand All @@ -41,7 +41,7 @@ export function renderColumnMeta(key: string, query: DataTableNode, context?: Qu
// NOTE: PropertyFilterType.Event is not a mistake. PropertyKeyInfo only knows events vs elements ¯\_(ツ)_/¯
title = <PropertyKeyInfo value={trimQuotes(key.substring(18))} type={PropertyFilterType.Event} disableIcon />
} else {
title = isEventsQuery(query.source) ? extractExpressionComment(key) : key
title = queryFeatures.has(QueryFeature.selectAndOrderByColumns) ? extractExpressionComment(key) : key
}

if (isEventsQuery(query.source) && !query.allowSorting) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function PersonPropertyFilters({ query, setQuery }: PersonPropertyFilters
onChange={(value: AnyPropertyFilter[]) => setQuery?.({ ...query, properties: value })}
pageKey={`PersonPropertyFilters.${id}`}
taxonomicGroupTypes={[TaxonomicFilterGroupType.PersonProperties]}
hogQLTable="persons"
style={{ marginBottom: 0, marginTop: 0 }}
/>
) : (
Expand Down
Loading

0 comments on commit 1d4d2d4

Please sign in to comment.