From 6cc6cca5131a32ca45150a36870b4d2fc9637b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Obermu=CC=88ller?= Date: Thu, 21 Sep 2023 14:07:01 +0200 Subject: [PATCH] re-add EmptyPropertyFilter and implement series properties --- frontend/src/queries/schema.json | 7 ++ frontend/src/queries/schema.ts | 1 + frontend/src/types.ts | 3 + posthog/hogql_queries/filter_to_query.py | 2 +- .../utils/test/test_filter_to_query.py | 117 +++++++++++++++++- posthog/schema.py | 23 ++++ 6 files changed, 151 insertions(+), 2 deletions(-) diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index ce1e095631cbb..745c742ae2b95 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -126,6 +126,9 @@ }, { "$ref": "#/definitions/HogQLPropertyFilter" + }, + { + "$ref": "#/definitions/EmptyPropertyFilter" } ] }, @@ -523,6 +526,10 @@ "required": ["attributes", "tag_name"], "type": "object" }, + "EmptyPropertyFilter": { + "additionalProperties": false, + "type": "object" + }, "EntityType": { "enum": ["actions", "events", "new_entity"], "type": "string" diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts index 2d0ad2b90c137..4ce25da43bb4e 100644 --- a/frontend/src/queries/schema.ts +++ b/frontend/src/queries/schema.ts @@ -195,6 +195,7 @@ export interface ActionsNode extends EntityNode { kind: NodeKind.ActionsNode id: number } + export interface QueryTiming { /** Key. Shortened to 'k' to save on data. */ k: string diff --git a/frontend/src/types.ts b/frontend/src/types.ts index c7bf68e43b080..2b8b8837abc33 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -585,6 +585,8 @@ export interface HogQLPropertyFilter extends BasePropertyFilter { key: string } +export interface EmptyPropertyFilter {} + export type AnyPropertyFilter = | EventPropertyFilter | PersonPropertyFilter @@ -595,6 +597,7 @@ export type AnyPropertyFilter = | GroupPropertyFilter | FeaturePropertyFilter | HogQLPropertyFilter + | EmptyPropertyFilter export type AnyFilterLike = AnyPropertyFilter | PropertyGroupFilter | PropertyGroupFilterValue diff --git a/posthog/hogql_queries/filter_to_query.py b/posthog/hogql_queries/filter_to_query.py index 3b8d0a61fd690..066812d576e60 100644 --- a/posthog/hogql_queries/filter_to_query.py +++ b/posthog/hogql_queries/filter_to_query.py @@ -19,7 +19,7 @@ def entity_to_node(entity: Entity) -> EventsNode | ActionsNode: shared = { "name": entity.name, "custom_name": entity.custom_name, - # "properties": entity.properties, + "properties": entity._data.get("properties", None), "math": entity.math, "math_property": entity.math_property, "math_hogql": entity.math_hogql, diff --git a/posthog/hogql_queries/utils/test/test_filter_to_query.py b/posthog/hogql_queries/utils/test/test_filter_to_query.py index 1458fe7ee403f..efa62c38da0b0 100644 --- a/posthog/hogql_queries/utils/test/test_filter_to_query.py +++ b/posthog/hogql_queries/utils/test/test_filter_to_query.py @@ -6,9 +6,18 @@ from posthog.schema import ( ActionsNode, BaseMathType, + CohortPropertyFilter, CountPerActorMathType, + ElementPropertyFilter, + EventPropertyFilter, EventsNode, + GroupPropertyFilter, + HogQLPropertyFilter, + Key, + PersonPropertyFilter, PropertyMathType, + PropertyOperator, + SessionPropertyFilter, ) from posthog.test.base import BaseTest from posthog.models.filters.filter import Filter @@ -292,6 +301,7 @@ @pytest.mark.parametrize("insight", test_insights) def test_base_insights(insight): + """smoke test (i.e. filter_to_query should not throw) for real world insights""" filter = Filter(data=insight) filter_to_query(filter) @@ -382,6 +392,7 @@ def test_base_insights(insight): @pytest.mark.parametrize("properties", test_properties) def test_base_properties(properties): + """smoke test (i.e. filter_to_query should not throw) for real world properties""" filter = Filter(data={"properties": properties}) filter_to_query(filter) @@ -586,4 +597,108 @@ def test_series_math(self): ) def test_series_properties(self): - pass # TODO + filter = Filter( + data={ + "events": [ + {"id": "$pageview", "properties": []}, # smoke test + { + "id": "$pageview", + "properties": [{"key": "success", "type": "event", "value": ["true"], "operator": "exact"}], + }, + { + "id": "$pageview", + "properties": [{"key": "email", "type": "person", "value": "is_set", "operator": "is_set"}], + }, + { + "id": "$pageview", + "properties": [{"key": "text", "value": ["some text"], "operator": "exact", "type": "element"}], + }, + { + "id": "$pageview", + "properties": [{"key": "$session_duration", "value": 1, "operator": "gt", "type": "session"}], + }, + {"id": "$pageview", "properties": [{"key": "id", "value": 2, "type": "cohort"}]}, + { + "id": "$pageview", + "properties": [ + { + "key": "name", + "value": ["Hedgebox Inc."], + "operator": "exact", + "type": "group", + "group_type_index": 2, + } + ], + }, + { + "id": "$pageview", + "properties": [ + {"key": "dateDiff('minute', timestamp, now()) < 30", "type": "hogql", "value": None} + ], + }, + { + "id": "$pageview", + "properties": [ + {"key": "$referring_domain", "type": "event", "value": "google", "operator": "icontains"}, + {"key": "utm_source", "type": "event", "value": "is_not_set", "operator": "is_not_set"}, + ], + }, + ] + } + ) + + query = filter_to_query(filter) + + self.assertEqual( + query.series, + [ + EventsNode(event="$pageview", name="$pageview", properties=[]), + EventsNode( + event="$pageview", + name="$pageview", + properties=[EventPropertyFilter(key="success", value=["true"], operator=PropertyOperator.exact)], + ), + EventsNode( + event="$pageview", + name="$pageview", + properties=[PersonPropertyFilter(key="email", value="is_set", operator=PropertyOperator.is_set)], + ), + EventsNode( + event="$pageview", + name="$pageview", + properties=[ + ElementPropertyFilter(key=Key.text, value=["some text"], operator=PropertyOperator.exact) + ], + ), + EventsNode( + event="$pageview", + name="$pageview", + properties=[SessionPropertyFilter(value=1, operator=PropertyOperator.gt)], + ), + EventsNode(event="$pageview", name="$pageview", properties=[CohortPropertyFilter(value=2)]), + EventsNode( + event="$pageview", + name="$pageview", + properties=[ + GroupPropertyFilter( + key="name", value=["Hedgebox Inc."], operator=PropertyOperator.exact, group_type_index=2 + ) + ], + ), + EventsNode( + event="$pageview", + name="$pageview", + properties=[HogQLPropertyFilter(key="dateDiff('minute', timestamp, now()) < 30")], + ), + EventsNode( + event="$pageview", + name="$pageview", + properties=[ + EventPropertyFilter( + key="$referring_domain", value="google", operator=PropertyOperator.icontains + ), + EventPropertyFilter(key="utm_source", value="is_not_set", operator=PropertyOperator.is_not_set), + ], + ), + ], + ) diff --git a/posthog/schema.py b/posthog/schema.py index 16be22feca0d3..f0f8313e12110 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -123,6 +123,13 @@ class ElementType(BaseModel): text: Optional[str] = None +class EmptyPropertyFilter(BaseModel): + pass + model_config = ConfigDict( + extra="forbid", + ) + + class EntityType(str, Enum): actions = "actions" events = "events" @@ -681,6 +688,7 @@ class EventsNode(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ] ] = Field( @@ -709,6 +717,7 @@ class EventsNode(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ] ] = Field(default=None, description="Properties configurable in the interface") @@ -735,6 +744,7 @@ class EventsQuery(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ] ] = Field( @@ -758,6 +768,7 @@ class EventsQuery(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ] ] = Field(default=None, description="Properties configurable in the interface") @@ -783,6 +794,7 @@ class HogQLFilters(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ] ] = None @@ -827,6 +839,7 @@ class PersonsNode(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ] ] = Field( @@ -848,6 +861,7 @@ class PersonsNode(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ] ] = Field(default=None, description="Properties configurable in the interface") @@ -873,6 +887,7 @@ class PropertyGroupFilterValue(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ], ] ] @@ -895,6 +910,7 @@ class ActionsNode(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ] ] = Field( @@ -922,6 +938,7 @@ class ActionsNode(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ] ] = Field(default=None, description="Properties configurable in the interface") @@ -1007,6 +1024,7 @@ class RetentionQuery(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ], PropertyGroupFilter, @@ -1044,6 +1062,7 @@ class StickinessQuery(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ], PropertyGroupFilter, @@ -1083,6 +1102,7 @@ class TrendsQuery(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ], PropertyGroupFilter, @@ -1123,6 +1143,7 @@ class FunnelsQuery(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ], PropertyGroupFilter, @@ -1161,6 +1182,7 @@ class LifecycleQuery(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ], PropertyGroupFilter, @@ -1195,6 +1217,7 @@ class PathsQuery(BaseModel): GroupPropertyFilter, FeaturePropertyFilter, HogQLPropertyFilter, + EmptyPropertyFilter, ] ], PropertyGroupFilter,