Skip to content

Commit

Permalink
feat(hogql): implement include_recordings for funnel actors
Browse files Browse the repository at this point in the history
  • Loading branch information
thmsobrmlr committed Feb 26, 2024
1 parent 83ea987 commit 65ec5f6
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 55 deletions.
92 changes: 43 additions & 49 deletions posthog/hogql_queries/insights/funnels/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,9 @@ def __init__(self, context: FunnelQueryContext):
self._extra_event_fields: List[ColumnName] = []
self._extra_event_properties: List[PropertyName] = []

# TODO: implement with actors query
# if self._filter.include_recordings:
# self._extra_event_fields = ["uuid"]
# self._extra_event_properties = ["$session_id", "$window_id"]
if self.context.actorsQuery and self.context.actorsQuery.includeRecordings:
self._extra_event_fields = ["uuid"]
self._extra_event_properties = ["$session_id", "$window_id"]

def get_query(self) -> ast.SelectQuery:
raise NotImplementedError()
Expand Down Expand Up @@ -620,22 +619,22 @@ def _get_funnel_person_step_condition(self) -> ast.Expr:
return ast.And(exprs=conditions)

def _get_funnel_person_step_events(self) -> List[ast.Expr]:
if self.context.actorsQuery and self.context.actorsQuery.includeRecordings:
step_num = self.context.actorsQuery.funnelStep
# if self._filter.include_final_matching_events:
if False: # TODO: Implement with correlations
# Always returns the user's final step of the funnel
return [parse_expr("final_matching_events as matching_events")]

Check failure on line 627 in posthog/hogql_queries/insights/funnels/base.py

View workflow job for this annotation

GitHub Actions / Python code quality checks

Statement is unreachable
elif step_num is None:
raise ValueError("Missing funnelStep actors query property")
if step_num >= 0:
# None drop off case
matching_events_step_num = step_num - 1
else:
# Drop off case if negative number
matching_events_step_num = abs(step_num) - 2
return [parse_expr(f"step_{matching_events_step_num}_matching_events as matching_events")]
return []
# if self._filter.include_recordings:
# step_num = self._filter.funnel_step
# if self._filter.include_final_matching_events:
# # Always returns the user's final step of the funnel
# return ", final_matching_events as matching_events"
# elif step_num is None:
# raise ValueError("Missing funnel_step filter property")
# if step_num >= 0:
# # None drop off case
# self.params.update({"matching_events_step_num": step_num - 1})
# else:
# # Drop off case if negative number
# self.params.update({"matching_events_step_num": abs(step_num) - 2})
# return ", step_%(matching_events_step_num)s_matching_events as matching_events"
# return ""

def _get_count_columns(self, max_steps: int) -> List[ast.Expr]:
exprs: List[ast.Expr] = []
Expand All @@ -653,41 +652,36 @@ def _get_step_time_names(self, max_steps: int) -> List[ast.Expr]:

return exprs

# def _get_final_matching_event(self, max_steps: int):
# statement = None
# for i in range(max_steps - 1, -1, -1):
# if i == max_steps - 1:
# statement = f"if(isNull(latest_{i}),step_{i-1}_matching_event,step_{i}_matching_event)"
# elif i == 0:
# statement = f"if(isNull(latest_0),(null,null,null,null),{statement})"
# else:
# statement = f"if(isNull(latest_{i}),step_{i-1}_matching_event,{statement})"
# return f",{statement} as final_matching_event" if statement else ""
def _get_final_matching_event(self, max_steps: int) -> List[ast.Expr]:
statement = None
for i in range(max_steps - 1, -1, -1):
if i == max_steps - 1:
statement = f"if(isNull(latest_{i}),step_{i-1}_matching_event,step_{i}_matching_event)"
elif i == 0:
statement = f"if(isNull(latest_0),(null,null,null,null),{statement})"
else:
statement = f"if(isNull(latest_{i}),step_{i-1}_matching_event,{statement})"
return [parse_expr(f"{statement} as final_matching_event")] if statement else []

def _get_matching_events(self, max_steps: int) -> List[ast.Expr]:
# if self._filter.include_recordings:
# events = []
# for i in range(0, max_steps):
# event_fields = ["latest"] + self.extra_event_fields_and_properties
# event_fields_with_step = ", ".join([f'"{field}_{i}"' for field in event_fields])
# event_clause = f"({event_fields_with_step}) as step_{i}_matching_event"
# events.append(event_clause)
# matching_event_select_statements = "," + ", ".join(events)

# final_matching_event_statement = self._get_final_matching_event(max_steps)

# return matching_event_select_statements + final_matching_event_statement

if self.context.actorsQuery and self.context.actorsQuery.includeRecordings:
events = []
for i in range(0, max_steps):
event_fields = ["latest"] + self.extra_event_fields_and_properties
event_fields_with_step = ", ".join([f'"{field}_{i}"' for field in event_fields])
event_clause = f"({event_fields_with_step}) as step_{i}_matching_event"
events.append(parse_expr(event_clause))

return [*events, *self._get_final_matching_event(max_steps)]
return []

def _get_matching_event_arrays(self, max_steps: int) -> List[ast.Expr]:
# select_clause = ""
# if self._filter.include_recordings:
# for i in range(0, max_steps):
# select_clause += f", groupArray(10)(step_{i}_matching_event) as step_{i}_matching_events"
# select_clause += f", groupArray(10)(final_matching_event) as final_matching_events"
# return select_clause
return []
exprs: List[ast.Expr] = []
if self.context.actorsQuery and self.context.actorsQuery.includeRecordings:
for i in range(0, max_steps):
exprs.append(parse_expr(f"groupArray(10)(step_{i}_matching_event) as step_{i}_matching_events"))
exprs.append(parse_expr(f"groupArray(10)(final_matching_event) as final_matching_events"))
return exprs

def _get_step_time_avgs(self, max_steps: int, inner_query: bool = False) -> List[ast.Expr]:
exprs: List[ast.Expr] = []
Expand Down
11 changes: 5 additions & 6 deletions posthog/hogql_queries/insights/funnels/funnel_trends.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,22 +268,21 @@ def get_query(self) -> ast.SelectQuery:
def get_step_counts_without_aggregation_query(
self, *, specific_entrance_period_start: Optional[datetime] = None
) -> ast.SelectQuery:
team, interval = self.context.team, self.context.interval
team, interval, max_steps = self.context.team, self.context.interval, self.context.max_steps

steps_per_person_query = self.funnel_order.get_step_counts_without_aggregation_query()

# event_select_clause = ""
# if self._filter.include_recordings:
# max_steps = len(self._filter.entities)
# event_select_clause = self._get_matching_event_arrays(max_steps)
event_select_clause: List[ast.Expr] = []
if self.context.actorsQuery and self.context.actorsQuery.includeRecordings:
event_select_clause = self._get_matching_event_arrays(max_steps)

breakdown_clause = self._get_breakdown_prop_expr()

select: List[ast.Expr] = [
ast.Field(chain=["aggregation_target"]),
ast.Alias(alias="entrance_period_start", expr=get_start_of_interval_hogql(interval.value, team=team)),
parse_expr("max(steps) AS steps_completed"),
# {event_select_clause}
*event_select_clause,
*breakdown_clause,
]
select_from = ast.JoinExpr(table=steps_per_person_query)
Expand Down

0 comments on commit 65ec5f6

Please sign in to comment.