From aecc5c9f2b4f61008483dafb529d5d9e1c599f64 Mon Sep 17 00:00:00 2001 From: Julian Bez Date: Mon, 29 Jan 2024 10:43:01 +0000 Subject: [PATCH] feat(hogql): Add actors query to live compare (#19999) --- frontend/src/queries/query.ts | 48 ++++++++++++------- frontend/src/scenes/paths/pathsDataLogic.ts | 1 + .../trends/persons-modal/personsModalLogic.ts | 22 ++++++--- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/frontend/src/queries/query.ts b/frontend/src/queries/query.ts index 4ef9b039930ac..11388a394bd21 100644 --- a/frontend/src/queries/query.ts +++ b/frontend/src/queries/query.ts @@ -139,7 +139,8 @@ export async function query( queryNode: N, methodOptions?: ApiMethodOptions, refresh?: boolean, - queryId?: string + queryId?: string, + legacyUrl?: string ): Promise> { if (isTimeToSeeDataSessionsNode(queryNode)) { return query(queryNode.source) @@ -171,6 +172,11 @@ export async function query( featureFlagLogic.findMounted()?.values.featureFlags?.[FEATURE_FLAGS.HOGQL_INSIGHT_LIVE_COMPARE] ) + async function fetchLegacyUrl(): Promise> { + const response = await api.getResponse(legacyUrl!) + return response.json() + } + async function fetchLegacyInsights(): Promise> { if (!isInsightQueryNode(queryNode)) { throw new Error('fetchLegacyInsights called with non-insight query. Should be unreachable.') @@ -206,20 +212,22 @@ export async function query( }, methodOptions ) - } else if (isInsightQueryNode(queryNode)) { + } else if (isInsightQueryNode(queryNode) || (isActorsQuery(queryNode) && !!legacyUrl)) { if ( (hogQLInsightsLifecycleFlagEnabled && isLifecycleQuery(queryNode)) || - (hogQLInsightsPathsFlagEnabled && isPathsQuery(queryNode)) || + (hogQLInsightsPathsFlagEnabled && + (isPathsQuery(queryNode) || (isActorsQuery(queryNode) && !!legacyUrl))) || (hogQLInsightsRetentionFlagEnabled && isRetentionQuery(queryNode)) || (hogQLInsightsTrendsFlagEnabled && isTrendsQuery(queryNode)) || (hogQLInsightsStickinessFlagEnabled && isStickinessQuery(queryNode)) || (hogQLInsightsFunnelsFlagEnabled && isFunnelsQuery(queryNode)) ) { if (hogQLInsightsLiveCompareEnabled) { + const legacyFunction = legacyUrl ? fetchLegacyUrl : fetchLegacyInsights let legacyResponse: any ;[response, legacyResponse] = await Promise.all([ api.query(queryNode, methodOptions, queryId, refresh), - fetchLegacyInsights(), + legacyFunction(), ]) let res1 = response?.result || response?.results @@ -243,6 +251,9 @@ export async function query( action: undefined, persons: undefined, })) + } else if (res2.length > 0 && res2[0].people) { + res2 = res2[0]?.people.map((n: any) => n.id) + res1 = res1.map((n: any) => n[0].id) } const getTimingDiff = (): undefined | { diff: number; legacy: number; hogql: number } => { @@ -265,6 +276,9 @@ export async function query( } } + const almostEqual = (n1: number, n2: number, epsilon: number = 1.0): boolean => + Math.abs(n1 - n2) < epsilon + const timingDiff = getTimingDiff() const results = flattenObject(res1) @@ -276,17 +290,21 @@ export async function query( let matchCount = 0 let mismatchCount = 0 for (const key of sortedKeys) { - if (results[key] === legacyResults[key]) { + let isMatch = false + if ( + results[key] === legacyResults[key] || + (key.includes('average_conversion_time') && almostEqual(results[key], legacyResults[key])) + ) { + isMatch = true + } + + if (isMatch) { matchCount++ } else { mismatchCount++ } - tableData.push([ - results[key] === legacyResults[key] ? '✅' : '🚨', - key, - results[key], - legacyResults[key], - ]) + + tableData.push([isMatch ? '✅' : '🚨', key, results[key], legacyResults[key]]) } if (timingDiff) { @@ -310,12 +328,10 @@ export async function query( legacyResponse, timingDiff, }) + const resultsLabel = mismatchCount === 0 ? '👏' : '⚠️' + const alertLabel = mismatchCount > 0 ? `🚨${mismatchCount}` : '' // eslint-disable-next-line no-console - console.groupCollapsed( - `Results: ${mismatchCount === 0 ? '✅✅✅' : '✅'} ${matchCount}${ - mismatchCount > 0 ? ` 🚨🚨🚨${mismatchCount}` : '' - }` - ) + console.groupCollapsed(`Results: ${resultsLabel} ✅${matchCount} ${alertLabel} ${queryNode.kind}`) // eslint-disable-next-line no-console console.table(tableData) // eslint-disable-next-line no-console diff --git a/frontend/src/scenes/paths/pathsDataLogic.ts b/frontend/src/scenes/paths/pathsDataLogic.ts index 6493fd9f91dae..ff4a931ddab0a 100644 --- a/frontend/src/scenes/paths/pathsDataLogic.ts +++ b/frontend/src/scenes/paths/pathsDataLogic.ts @@ -142,6 +142,7 @@ export const pathsDataLogic = kea([ }, } openPersonsModal({ + url: personsUrl, query: pathsActorsQuery, title: pathsTitle({ label: path_dropoff_key || path_start_key || path_end_key || 'Pageview', diff --git a/frontend/src/scenes/trends/persons-modal/personsModalLogic.ts b/frontend/src/scenes/trends/persons-modal/personsModalLogic.ts index 49c8e66ab7adf..e08a7c2163be5 100644 --- a/frontend/src/scenes/trends/persons-modal/personsModalLogic.ts +++ b/frontend/src/scenes/trends/persons-modal/personsModalLogic.ts @@ -97,7 +97,8 @@ export const personsModalLogic = kea([ if (values.searchTerm) { url += `&search=${values.searchTerm}` } - + } + if (url && !query) { const res = await api.get(url) breakpoint() @@ -105,12 +106,19 @@ export const personsModalLogic = kea([ actions.resetActors() } return res - } else if (query) { - const response = await performQuery({ - ...values.actorsQuery, - limit: RESULTS_PER_PAGE + 1, - offset: offset || 0, - } as ActorsQuery) + } + if (query) { + const response = await performQuery( + { + ...values.actorsQuery, + limit: RESULTS_PER_PAGE + 1, + offset: offset || 0, + } as ActorsQuery, + undefined, + undefined, + undefined, + url ?? undefined + ) breakpoint() const assembledSelectFields = values.selectFields