Skip to content

Commit

Permalink
feat(trends): use hogql for legacy insight trends api
Browse files Browse the repository at this point in the history
  • Loading branch information
thmsobrmlr committed Jan 2, 2025
1 parent f71e331 commit 2248791
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 5 deletions.
27 changes: 22 additions & 5 deletions posthog/api/insight.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@
apply_dashboard_filters_to_dict,
apply_dashboard_variables_to_dict,
)
from posthog.hogql_queries.legacy_compatibility.feature_flag import (
hogql_insights_replace_filters,
)
from posthog.hogql_queries.legacy_compatibility.feature_flag import hogql_insights_replace_filters, get_query_method
from posthog.hogql_queries.legacy_compatibility.filter_to_query import filter_to_query
from posthog.hogql_queries.legacy_compatibility.flagged_conversion_manager import (
conversion_to_query_based,
)
from posthog.hogql_queries.query_runner import (
ExecutionMode,
execution_mode_from_refresh,
get_query_runner,
shared_insights_execution_mode,
)
from posthog.kafka_client.topics import KAFKA_METRICS_TIME_TO_SEE_DATA
Expand Down Expand Up @@ -180,7 +180,7 @@ def capture_legacy_api_call(request: request.Request, team: Team):
properties = {
"path": request._request.path,
"method": request._request.method,
"use_hogql": False,
"query_method": get_query_method(request=request, team=team),
"filter": get_filter(request=request, team=team),
"was_impersonated": is_impersonated_session(request),
}
Expand Down Expand Up @@ -994,7 +994,11 @@ def trend(self, request: request.Request, *args: Any, **kwargs: Any):
timings = HogQLTimings()
try:
with timings.measure("calculate"):
result = self.calculate_trends(request)
query_method = get_query_method(request=request, team=self.team)
if query_method == "hogql":
result = self.calculate_trends_hogql(request)
else:
result = self.calculate_trends(request)
except ExposedHogQLError as e:
raise ValidationError(str(e))
filter = Filter(request=request, team=self.team)
Expand Down Expand Up @@ -1055,6 +1059,19 @@ def calculate_trends(self, request: request.Request) -> dict[str, Any]:

return {"result": result, "timezone": team.timezone}

@cached_by_filters
def calculate_trends_hogql(self, request: request.Request) -> dict[str, Any]:
team = self.team
filter = Filter(request=request, team=team)
query = filter_to_query(filter.to_dict())
query_runner = get_query_runner(query, team, limit_context=None)

# we use the legacy caching mechanism (@cached_by_filters decorator), no need to cache in the query runner
result = query_runner.run(execution_mode=ExecutionMode.CALCULATE_BLOCKING_ALWAYS)
assert isinstance(result, schema.CachedTrendsQueryResponse)

return {"result": result.results, "timezone": team.timezone}

# ******************************************
# /projects/:id/insights/funnel
# The funnel endpoint is asynchronously processed. When a request is received, the endpoint will
Expand Down
7 changes: 7 additions & 0 deletions posthog/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from posthog.clickhouse.query_tagging import tag_queries
from posthog.models.filters.utils import get_filter
from posthog.utils import refresh_requested_by_client
from posthog.hogql_queries.legacy_compatibility.feature_flag import get_query_method

from .utils import generate_cache_key, get_safe_cache

Expand Down Expand Up @@ -52,7 +53,12 @@ def wrapper(self: U, request: Request) -> T:
return f(self, request)

filter = get_filter(request=request, team=team)
query_method = get_query_method(request=request, team=team)
cache_key = f"{filter.toJSON()}_{team.pk}"

if query_method == "hogql":
cache_key += "_hogql"

if request.data.get("cache_invalidation_key"):
cache_key += f"_{request.data['cache_invalidation_key']}"

Expand Down Expand Up @@ -92,6 +98,7 @@ def wrapper(self: U, request: Request) -> T:
timestamp = now()
fresh_result_package["last_refresh"] = timestamp
fresh_result_package["is_cached"] = False
fresh_result_package["query_method"] = query_method
update_cached_state(team.pk, cache_key, timestamp, fresh_result_package)

return fresh_result_package
Expand Down
36 changes: 36 additions & 0 deletions posthog/hogql_queries/legacy_compatibility/feature_flag.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from typing import Literal
import posthoganalytics
from posthog.models import Team
from rest_framework.request import Request


def hogql_insights_replace_filters(team: Team) -> bool:
Expand Down Expand Up @@ -63,3 +65,37 @@ def insight_funnels_use_udf_trends(team: Team) -> bool:
only_evaluate_locally=False,
send_feature_flag_events=False,
)


def insight_api_use_legacy_queries(team: Team) -> bool:
"""
Use the legacy implementation of insight api calculation endpoints.
"""
return posthoganalytics.feature_enabled(
"insight-api-use-legacy-queries",
str(team.uuid),
groups={
"organization": str(team.organization_id),
"project": str(team.id),
},
group_properties={
"organization": {
"id": str(team.organization_id),
},
"project": {
"id": str(team.id),
},
},
only_evaluate_locally=True,
send_feature_flag_events=False,
)


LegacyAPIQueryMethod = Literal["legacy", "hogql"]


def get_query_method(request: Request, team: Team) -> LegacyAPIQueryMethod:
query_method_param = request.query_params.get("query_method", None)
if query_method_param in ["hogql", "legacy"]:
return query_method_param # type: ignore
return "legacy" if insight_api_use_legacy_queries(team) else "hogql"

0 comments on commit 2248791

Please sign in to comment.