Skip to content

feat: Support EventMetadata filter for EventsQuery/TrendsQuery#30232

Merged
danielbachhuber merged 46 commits intomasterfrom
crm/event-metadata
Mar 25, 2025
Merged

feat: Support EventMetadata filter for EventsQuery/TrendsQuery#30232
danielbachhuber merged 46 commits intomasterfrom
crm/event-metadata

Conversation

@danielbachhuber
Copy link
Contributor

@danielbachhuber danielbachhuber commented Mar 20, 2025

See #29881
See https://posthog.slack.com/archives/C0368RPHLQH/p1742417106336409

Changes

Adds support for filtering EventsQuery or TrendsQuery by a new EventMetadata filter:

CleanShot.2025-03-21.at.10.27.10.mp4

The happy path works but I'm not sure how many gotchas I might be missing.

How did you test this code?

Added backend tests.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR adds support for filtering events by metadata properties in EventsQuery and TrendsQuery, enabling direct filtering on fields like distinct_id and timestamp.

Key changes:

  • Added new EventMetadata filter type in frontend/src/types.ts with corresponding taxonomic group support
  • Implemented metadata property handling in posthog/hogql/property.py for direct field access
  • Added test coverage in test_events_query_runner.py and test_trends_query_runner.py for metadata filtering
  • Updated property validation in posthog/models/property/property.py to support event_metadata type
  • Extended taxonomic filter logic with new metadata options and icons in taxonomicFilterLogic.tsx

12 file(s) reviewed, 2 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines 327 to 331
options: eventMetadataOptions,
getIcon: (option: Record<string, string>) => option.icon,
getName: (option: Record<string, string>) => option.name,
getValue: (option: Record<string, string>) => option.value,
getPopoverHeader: () => 'Event metadata',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing type validation for option.icon - could cause runtime errors if icon is not a valid React element

Comment on lines 588 to 595
return _expr_to_compare_op(
expr=ast.Field(chain=[property.key]),
value=value,
operator=operator,
team=team,
property=property,
is_json_field=False,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing boolean value handling for event metadata fields

Copy link
Collaborator

@mariusandra mariusandra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool stuff, left some thoughts inline.

Comment on lines 612 to 613
{ icon: <IconLogomark />, name: 'Distinct ID', value: 'distinct_id' },
{ icon: <IconLogomark />, name: 'Timestamp', value: 'timestamp' },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add event here? Perhaps also person_id if it works well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking out loud...

While I think it makes total sense to have a new event metadata TaxonomicFilterGroupType for this new tab/pill... I wonder if this is the right PropertyFilterType to add. It feels a bit restrictive. More specifically, I wonder if EventMetadataPropertyFilter could be merged with HogQLPropertyFilter.

Looking at this list of options here, we effectively have expression operator value, where the expression comes from a predefined list of SQL expressions. There's no backend validation... so it's effectively a generic SQLValueFilter or something like that.

The existing HogQLPropertyFilter does the expression part as its key. We could just add operator and value into it... or if making a new property filter, to make it more abstract in its name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add event here? Perhaps also person_id if it works well.

Added with 94d3026. Both work fine.

The existing HogQLPropertyFilter does the expression part as its key. We could just add operator and value into it... or if making a new property filter, to make it more abstract in its name.

Doesn't that mean we'd need to apply this logic and this logic in the client?

Copy link
Collaborator

@mariusandra mariusandra Mar 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't that mean we'd need to apply this logic and this logic in the client?

Hmm... well, no... it'd work exactly like the filter you now added works, just renamed to HogQLPropertyValueFilter or something more abstract.

My worry is that people will want to start filtering for all sorts of other and custom expressions. However I do like that this is scoped to just events, making some things a lot easier than if it'd be totally abstract...

@aspicer
Copy link
Contributor

aspicer commented Mar 20, 2025

Do we want to call this "$group_2" in the customer facing UI? I don't think we use that terminology anywhere and it's quite an implementation detail to expose.

"Organization ID" might make more sense? I'm not sure we want to surface that group naming anywhere?

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 1)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@aspicer
Copy link
Contributor

aspicer commented Mar 20, 2025

This looks awesome. Much overdue.

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 1)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2025

Size Change: -194 kB (-1.95%)

Total Size: 9.76 MB

Filename Size Change
frontend/dist/toolbar.js 9.76 MB -194 kB (-1.95%)

compressed-size-action

@danielbachhuber
Copy link
Contributor Author

Do we want to call this "$group_2" in the customer facing UI? I don't think we use that terminology anywhere and it's quite an implementation detail to expose.

"Organization ID" might make more sense? I'm not sure we want to surface that group naming anywhere?

@aspicer My intent was to ease this transition:
CleanShot 2025-03-20 at 16 38 47

Specifically, when the user selects "organization" in the UI and then sees $group_2 in the filter.

It looks like getCoreFilterDefinition() is where the label is fetched:

const coreDefinition = getCoreFilterDefinition(value, type)

export function getCoreFilterDefinition(
value: string | PropertyFilterValue | undefined,
type: TaxonomicFilterGroupType
): CoreFilterDefinition | null {
if (value == undefined) {
return null
}

We could fetch the group type mapping labels dynamically but I don't love the idea of adding a call to a logic. It seems like too many of our components have random dependencies on logics.

I do think $group_2 is the value we should store in the filter, though (not group_type).

Thoughts?

@mariusandra
Copy link
Collaborator

It's definitely better for the user to see a clearly labelled "Organization ID" rather than a cryptic "$group_2", so I think it's worth taking whatever extra steps are needed to make it work. We should have all the group names in memory normally as well.

Copy link
Collaborator

@mariusandra mariusandra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a play around. While it did filter when I entered stuff to the fields, there's no autocomplete:

image

Plus the timestamp field is not treated as a datetime field (no date/time controls, before/after, etc)

image

This is what I'd expect:

image image

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 1)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

@danielbachhuber
Copy link
Contributor Author

I'm willing to 🙈 on the part of it all going through property definitions, however I'd see if there's any way to avoid those "loading"/"missing" states (those kea actions in one of the last gifs).

@mariusandra I think I fixed this with fcfba12 and 0fda1bc (at least, I don't see the erroneous property_definitions?limit=50&properties=event&type=event API request anymore).

Perhaps avoid going through checkOrLoadPropertyDefinition altogether?

I tried this initially with e5f6405 but had to change it in bef7375 because of some bug (don't recall exactly). Is there some alternative you had in mind?

@posthog-bot
Copy link
Contributor

📸 UI snapshots have been updated

1 snapshot changes in total. 0 added, 1 modified, 0 deleted:

  • chromium: 0 added, 1 modified, 0 deleted (diff for shard 1)
  • webkit: 0 added, 0 modified, 0 deleted

Triggered by this commit.

👉 Review this PR's diff of snapshots.

Copy link
Collaborator

@mariusandra mariusandra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The person_id filter dropdown doesn't work:

image

and I left a few other comments inline.

As for skipping checkOrLoadPropertyDefinition, well, just prepopulate propertyDefinitionStorage with the metadata properties and that should be enough, no? One trick you can use is to rename the propertyDefinitionStorage reducer to rawPropertyDefinitionStorage, and then add a new selector called propertyDefinitionStorage that merges rawPropertyDefinitionStorage and eventMetadataPropertyDefinitions into one object.

@danielbachhuber
Copy link
Contributor Author

The person_id filter dropdown doesn't work:

@mariusandra Good catch, fixed with fdf4a12

As for skipping checkOrLoadPropertyDefinition, well, just prepopulate propertyDefinitionStorage with the metadata properties and that should be enough, no? One trick you can use is to rename the propertyDefinitionStorage reducer to rawPropertyDefinitionStorage, and then add a new selector called propertyDefinitionStorage that merges rawPropertyDefinitionStorage and eventMetadataPropertyDefinitions into one object.

Ah, that's quite nice. Thanks for the suggestion! Updated with 070a82d

@danielbachhuber
Copy link
Contributor Author

I think I fixed this with fcfba12 and 0fda1bc (at least, I don't see the erroneous property_definitions?limit=50&properties=event&type=event API request anymore).

Kind of ugly, but I also had to do 1efb7ee. Otherwise, the group filters end up with empty values:

CleanShot 2025-03-24 at 10 44 04@2x

CleanShot 2025-03-24 at 10 44 18@2x

Copy link
Collaborator

@mariusandra mariusandra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been a big undertaking and thanks for putting up with all my comments and remarks. However I think we made it! 🙌

I still left one comment inline about merging the objects in taxonomy.tsx, however the rest LGTM! So merge whenever you're ready! 👍

Comment on lines 287 to 294
event_metadata: {
person_id: {
label: 'Person ID',
description: 'The ID of the person, depending on the person properties mode.',
examples: ['16ff262c4301e5-0aa346c03894bc-39667c0e-1aeaa0-16ff262c431767'],
},
// Other values are assigned below
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... I'm not too sure, but it looks like it was added in this PR

Looking at the code:

image

... it does seem like "distinct_id" was explicitly moved out of the "event_properties" tab because it wasn't a property, but it was more like (event) "metadata". So I think the intent is the same. As for where is it used, I could only find the CORE_FILTER_DEFINITIONS_BY_GROUP.event_properties.distinct_id = CORE_FILTER_DEFINITIONS_BY_GROUP.metadata.distinct_id line right now.

I think it's safe to merge the two... unless we get any immediate typescript errors, probably the worst that can happen is some missing descriptions, which can be fixed later... I also think it's worth merging, as otherwise people will lose time wondering the same question 2 years down the line :D.

@danielbachhuber danielbachhuber merged commit 9d0f8ee into master Mar 25, 2025
111 of 112 checks passed
@danielbachhuber danielbachhuber deleted the crm/event-metadata branch March 25, 2025 13:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants