From f1d1551a018a877220e1c7248af02eba9c0843ae Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Wed, 13 Nov 2024 18:13:16 +0000 Subject: [PATCH 01/17] feat: allow hogql property queries in replay filtering --- .../src/lib/components/UniversalFilters/UniversalFilters.tsx | 4 ++++ .../lib/components/UniversalFilters/universalFiltersLogic.ts | 2 +- .../session-recordings/filters/RecordingsUniversalFilters.tsx | 4 ++++ .../playlist/sessionRecordingsPlaylistLogic.ts | 4 +++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/components/UniversalFilters/UniversalFilters.tsx b/frontend/src/lib/components/UniversalFilters/UniversalFilters.tsx index 117c6b678c59e..a253805e75260 100644 --- a/frontend/src/lib/components/UniversalFilters/UniversalFilters.tsx +++ b/frontend/src/lib/components/UniversalFilters/UniversalFilters.tsx @@ -3,6 +3,7 @@ import { LemonButton, LemonButtonProps, LemonDropdown, Popover } from '@posthog/ import { BindLogic, useActions, useValues } from 'kea' import { useState } from 'react' +import { AnyDataNode } from '~/queries/schema' import { UniversalFiltersGroup, UniversalFilterValue } from '~/types' import { TaxonomicPropertyFilter } from '../PropertyFilters/components/TaxonomicPropertyFilter' @@ -75,12 +76,14 @@ const Value = ({ onChange, onRemove, initiallyOpen = false, + metadataSource, }: { index: number filter: UniversalFilterValue onChange: (property: UniversalFilterValue) => void onRemove: () => void initiallyOpen?: boolean + metadataSource?: AnyDataNode }): JSX.Element => { const { rootKey, taxonomicPropertyFilterGroupTypes } = useValues(universalFiltersLogic) @@ -103,6 +106,7 @@ const Value = ({ onChange={(properties) => onChange({ ...filter, properties })} disablePopover taxonomicGroupTypes={[TaxonomicFilterGroupType.EventProperties]} + metadataSource={metadataSource} /> ) : isEditable ? ( ([ newValues.push(newFeatureFlagFilter) } else { const propertyType = - item.propertyFilterType ?? taxonomicFilterTypeToPropertyFilterType(taxonomicGroup.type) + item?.propertyFilterType ?? taxonomicFilterTypeToPropertyFilterType(taxonomicGroup.type) if (propertyKey && propertyType) { const newPropertyFilter = createDefaultPropertyFilter( {}, diff --git a/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx b/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx index 6dfa6d007949f..4f834c4252310 100644 --- a/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx +++ b/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx @@ -11,6 +11,7 @@ import { TestAccountFilter } from 'scenes/insights/filters/TestAccountFilter' import { actionsModel } from '~/models/actionsModel' import { cohortsModel } from '~/models/cohortsModel' import { AndOrFilterSelect } from '~/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect' +import { NodeKind } from '~/queries/schema' import { RecordingUniversalFilters, UniversalFiltersGroup } from '~/types' import { DurationFilter } from './DurationFilter' @@ -109,6 +110,8 @@ export const RecordingsUniversalFilters = ({ TaxonomicFilterGroupType.Cohorts, TaxonomicFilterGroupType.PersonProperties, TaxonomicFilterGroupType.SessionProperties, + TaxonomicFilterGroupType.FeatureFlags, + TaxonomicFilterGroupType.HogQLExpression, ]} onChange={(filterGroup) => setFilters({ filter_group: filterGroup })} > @@ -144,6 +147,7 @@ const RecordingsUniversalFilterGroup = (): JSX.Element => { onRemove={() => removeGroupValue(index)} onChange={(value) => replaceGroupValue(index, value)} initiallyOpen={allowInitiallyOpen} + metadataSource={{ kind: NodeKind.RecordingsQuery }} /> ) })} diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts index a74ae5fc08b09..617310ca0abff 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts @@ -4,7 +4,7 @@ import { loaders } from 'kea-loaders' import { actionToUrl, router, urlToAction } from 'kea-router' import { subscriptions } from 'kea-subscriptions' import api from 'lib/api' -import { isAnyPropertyfilter } from 'lib/components/PropertyFilters/utils' +import { isAnyPropertyfilter, isHogQLPropertyFilter } from 'lib/components/PropertyFilters/utils' import { DEFAULT_UNIVERSAL_GROUP_FILTER } from 'lib/components/UniversalFilters/universalFiltersLogic' import { isActionFilter, @@ -120,6 +120,8 @@ export function convertUniversalFiltersToRecordingsQuery(universalFilters: Recor actions.push(f) } else if (isLogEntryPropertyFilter(f)) { console_log_filters.push(f) + } else if (isHogQLPropertyFilter(f)) { + properties.push(f) } else if (isAnyPropertyfilter(f)) { if (isRecordingPropertyFilter(f)) { if (f.key === 'visited_page') { From 416c61bd3c4a9e1c3372863675a402c8fe483e5a Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Wed, 13 Nov 2024 18:49:21 +0000 Subject: [PATCH 02/17] Schema change so that recordingsquery can be consider --- frontend/src/queries/schema.json | 3 +++ frontend/src/queries/schema.ts | 1 + posthog/schema.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index e4a03b57c35f6..de422b7f8af9e 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -369,6 +369,9 @@ }, { "$ref": "#/definitions/ExperimentTrendsQuery" + }, + { + "$ref": "#/definitions/RecordingsQuery" } ] }, diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts index 4ba75628bb47c..5cf31193b6bbb 100644 --- a/frontend/src/queries/schema.ts +++ b/frontend/src/queries/schema.ts @@ -133,6 +133,7 @@ export type AnyDataNode = | ErrorTrackingQuery | ExperimentFunnelsQuery | ExperimentTrendsQuery + | RecordingsQuery /** * @discriminator kind diff --git a/posthog/schema.py b/posthog/schema.py index faf2e1739247f..6825203d1cc30 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -6617,6 +6617,7 @@ class HogQLAutocomplete(BaseModel): ErrorTrackingQuery, ExperimentFunnelsQuery, ExperimentTrendsQuery, + RecordingsQuery, ] ] = Field(default=None, description="Query in whose context to validate.") startPosition: int = Field(..., description="Start position of the editor word") @@ -6661,6 +6662,7 @@ class HogQLMetadata(BaseModel): ErrorTrackingQuery, ExperimentFunnelsQuery, ExperimentTrendsQuery, + RecordingsQuery, ] ] = Field( default=None, From d3d8c2ff7ac324d88241ce377c0853d62bd9a91a Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 09:04:09 +0000 Subject: [PATCH 03/17] flag the new filter types --- frontend/src/lib/constants.tsx | 2 ++ .../filters/RecordingsUniversalFilters.tsx | 30 ++++++++++++------- .../playlist/SessionRecordingsPlaylist.tsx | 10 ++++++- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx index d9d84f586c12e..163b978f924ce 100644 --- a/frontend/src/lib/constants.tsx +++ b/frontend/src/lib/constants.tsx @@ -231,6 +231,8 @@ export const FEATURE_FLAGS = { CUSTOM_CHANNEL_TYPE_RULES: 'custom-channel-type-rules', // owner: @robbie-c #team-web-analytics SELF_SERVE_CREDIT_OVERRIDE: 'self-serve-credit-override', // owner: @zach EXPERIMENTS_MIGRATION_DISABLE_UI: 'experiments-migration-disable-ui', // owner: @jurajmajerik #team-experiments + REPLAY_FLAGS_FILTERS: 'replay-flags-filters', // owner: @pauldambra #team-replay + REPLAY_HOGQL_FILTERS: 'replay-hogql-filters', // owner: @pauldambra #team-replay } as const export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS] diff --git a/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx b/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx index 4f834c4252310..3b7ccc04d9343 100644 --- a/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx +++ b/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx @@ -20,16 +20,35 @@ export const RecordingsUniversalFilters = ({ filters, setFilters, className, + allowReplayFlagsFilters = false, + allowReplayHogQLFilters = false, }: { filters: RecordingUniversalFilters setFilters: (filters: Partial) => void className?: string + allowReplayFlagsFilters?: boolean + allowReplayHogQLFilters?: boolean }): JSX.Element => { useMountedLogic(cohortsModel) useMountedLogic(actionsModel) const durationFilter = filters.duration[0] + const taxonomicGroupTypes = [ + TaxonomicFilterGroupType.Replay, + TaxonomicFilterGroupType.Events, + TaxonomicFilterGroupType.Actions, + TaxonomicFilterGroupType.Cohorts, + TaxonomicFilterGroupType.PersonProperties, + TaxonomicFilterGroupType.SessionProperties, + ] + if (allowReplayFlagsFilters) { + taxonomicGroupTypes.push(TaxonomicFilterGroupType.FeatureFlags) + } + if (allowReplayHogQLFilters) { + taxonomicGroupTypes.push(TaxonomicFilterGroupType.HogQLExpression) + } + return (
@@ -103,16 +122,7 @@ export const RecordingsUniversalFilters = ({ setFilters({ filter_group: filterGroup })} > diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx index 65e08fc65f37b..eac443ba71181 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx @@ -118,6 +118,8 @@ export function SessionRecordingsPlaylist(props: SessionRecordingPlaylistLogicPr const { featureFlags } = useValues(featureFlagLogic) const isTestingSaved = featureFlags[FEATURE_FLAGS.SAVED_NOT_PINNED] === 'test' + const allowReplayFlagsFilters = !!featureFlags[FEATURE_FLAGS.REPLAY_FLAGS_FILTERS] + const allowReplayHogQLFilters = !!featureFlags[FEATURE_FLAGS.REPLAY_HOGQL_FILTERS] const pinnedDescription = isTestingSaved ? 'Saved' : 'Pinned' @@ -163,7 +165,13 @@ export function SessionRecordingsPlaylist(props: SessionRecordingPlaylistLogicPr
{!notebookNode && ( - + )} Date: Sat, 23 Nov 2024 10:36:53 +0000 Subject: [PATCH 04/17] add failing test --- posthog/hogql/property.py | 2 + .../session_recording_list_from_filters.py | 6 +- ...est_session_recording_list_from_filters.py | 109 ++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/posthog/hogql/property.py b/posthog/hogql/property.py index e372b446b5c91..ad8f58b9a66b9 100644 --- a/posthog/hogql/property.py +++ b/posthog/hogql/property.py @@ -389,6 +389,8 @@ def property_to_expr( chain = ["session"] elif property.type == "session" and scope == "session": chain = ["sessions"] + elif property.type == "feature" and scope == "replay": + chain = ["events", "properties"] else: chain = ["properties"] diff --git a/posthog/session_recordings/queries/session_recording_list_from_filters.py b/posthog/session_recordings/queries/session_recording_list_from_filters.py index 46fb0664c56b7..e9bebcd097b30 100644 --- a/posthog/session_recordings/queries/session_recording_list_from_filters.py +++ b/posthog/session_recordings/queries/session_recording_list_from_filters.py @@ -24,7 +24,11 @@ def is_event_property(p: Property) -> bool: - return p.type == "event" or (p.type == "hogql" and bool(re.search(r"(? bool: diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py index 3633092a40e25..012d27714e7dd 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py @@ -4110,3 +4110,112 @@ def test_top_level_event_host_property_test_account_filter(self): "ongoing": 1, } ] + + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_feature_filter(self): + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=["user2"], + properties={"email": "not-the-other-one"}, + ) + + session_id_with_flag = str(uuid4()) + produce_replay_summary( + distinct_id="user", + session_id=session_id_with_flag, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ensure_analytics_event_in_session=False, + ) + # this session needs to have multiple matching + for _ in range(10): + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": session_id_with_flag, + "$window_id": "1", + "$host": "localhost", + "$feature/replay-flags-testing": True, + }, + ) + + session_id_without_flag = str(uuid4()) + produce_replay_summary( + distinct_id="user", + session_id=session_id_without_flag, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ensure_analytics_event_in_session=False, + ) + # this session needs to have multiple not matching + for _ in range(10): + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": session_id_without_flag, + "$window_id": "1", + "$host": "localhost", + "$feature/replay-flags-testing": False, + }, + ) + + # and this session does not have the flag at all + session_with_flag_not_set = str(uuid4()) + produce_replay_summary( + distinct_id="user", + session_id=session_with_flag_not_set, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + click_count=10, + ensure_analytics_event_in_session=False, + ) + + for _ in range(10): + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": session_with_flag_not_set, + "$window_id": "1", + "$host": "example.com", + }, + ) + + # there are three states, flag is true, flag is false, and flag is not set + (session_recordings, _, _) = self._filter_recordings_by( + {"properties": [{"key": "replay-flags-testing", "value": ["true"], "operator": "exact", "type": "feature"}]} + ) + assert [sr["session_id"] for sr in session_recordings] == [session_id_with_flag] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + {"key": "replay-flags-testing", "value": ["false"], "operator": "exact", "type": "feature"} + ] + } + ) + assert [sr["session_id"] for sr in session_recordings] == [session_id_without_flag] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + {"key": "replay-hogql-filters", "value": "is_not_set", "operator": "is_not_set", "type": "feature"} + ] + } + ) + assert [sr["session_id"] for sr in session_recordings] == [session_with_flag_not_set] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + {"key": "replay-hogql-filters", "value": "is_set", "operator": "is_set", "type": "feature"} + ] + } + ) + assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( + [session_id_with_flag, session_id_without_flag] + ) From b630acd265265b6a3edc42b28e82ea9f527c8e9a Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 10:41:01 +0000 Subject: [PATCH 05/17] first pass removing flag filtering --- frontend/src/lib/constants.tsx | 1 - .../filters/RecordingsUniversalFilters.tsx | 5 +- .../playlist/SessionRecordingsPlaylist.tsx | 2 - posthog/hogql/property.py | 2 - .../session_recording_list_from_filters.py | 6 +- ...est_session_recording_list_from_filters.py | 109 ------------------ 6 files changed, 2 insertions(+), 123 deletions(-) diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx index 163b978f924ce..fe7dd3971a7a4 100644 --- a/frontend/src/lib/constants.tsx +++ b/frontend/src/lib/constants.tsx @@ -231,7 +231,6 @@ export const FEATURE_FLAGS = { CUSTOM_CHANNEL_TYPE_RULES: 'custom-channel-type-rules', // owner: @robbie-c #team-web-analytics SELF_SERVE_CREDIT_OVERRIDE: 'self-serve-credit-override', // owner: @zach EXPERIMENTS_MIGRATION_DISABLE_UI: 'experiments-migration-disable-ui', // owner: @jurajmajerik #team-experiments - REPLAY_FLAGS_FILTERS: 'replay-flags-filters', // owner: @pauldambra #team-replay REPLAY_HOGQL_FILTERS: 'replay-hogql-filters', // owner: @pauldambra #team-replay } as const export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS] diff --git a/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx b/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx index 3b7ccc04d9343..989b726851070 100644 --- a/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx +++ b/frontend/src/scenes/session-recordings/filters/RecordingsUniversalFilters.tsx @@ -20,7 +20,6 @@ export const RecordingsUniversalFilters = ({ filters, setFilters, className, - allowReplayFlagsFilters = false, allowReplayHogQLFilters = false, }: { filters: RecordingUniversalFilters @@ -42,9 +41,7 @@ export const RecordingsUniversalFilters = ({ TaxonomicFilterGroupType.PersonProperties, TaxonomicFilterGroupType.SessionProperties, ] - if (allowReplayFlagsFilters) { - taxonomicGroupTypes.push(TaxonomicFilterGroupType.FeatureFlags) - } + if (allowReplayHogQLFilters) { taxonomicGroupTypes.push(TaxonomicFilterGroupType.HogQLExpression) } diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx index eac443ba71181..2441f749b387b 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx @@ -118,7 +118,6 @@ export function SessionRecordingsPlaylist(props: SessionRecordingPlaylistLogicPr const { featureFlags } = useValues(featureFlagLogic) const isTestingSaved = featureFlags[FEATURE_FLAGS.SAVED_NOT_PINNED] === 'test' - const allowReplayFlagsFilters = !!featureFlags[FEATURE_FLAGS.REPLAY_FLAGS_FILTERS] const allowReplayHogQLFilters = !!featureFlags[FEATURE_FLAGS.REPLAY_HOGQL_FILTERS] const pinnedDescription = isTestingSaved ? 'Saved' : 'Pinned' @@ -170,7 +169,6 @@ export function SessionRecordingsPlaylist(props: SessionRecordingPlaylistLogicPr setFilters={setFilters} className="border" allowReplayHogQLFilters={allowReplayHogQLFilters} - allowReplayFlagsFilters={allowReplayFlagsFilters} /> )} bool: - return ( - p.type == "event" - or (p.type == "hogql" and bool(re.search(r"(? bool: diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py index 012d27714e7dd..3633092a40e25 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py @@ -4110,112 +4110,3 @@ def test_top_level_event_host_property_test_account_filter(self): "ongoing": 1, } ] - - @freeze_time("2021-01-21T20:00:00.000Z") - @snapshot_clickhouse_queries - def test_feature_filter(self): - Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) - Person.objects.create( - team=self.team, - distinct_ids=["user2"], - properties={"email": "not-the-other-one"}, - ) - - session_id_with_flag = str(uuid4()) - produce_replay_summary( - distinct_id="user", - session_id=session_id_with_flag, - first_timestamp=self.an_hour_ago, - team_id=self.team.id, - ensure_analytics_event_in_session=False, - ) - # this session needs to have multiple matching - for _ in range(10): - self.create_event( - "user", - self.an_hour_ago, - properties={ - "$session_id": session_id_with_flag, - "$window_id": "1", - "$host": "localhost", - "$feature/replay-flags-testing": True, - }, - ) - - session_id_without_flag = str(uuid4()) - produce_replay_summary( - distinct_id="user", - session_id=session_id_without_flag, - first_timestamp=self.an_hour_ago, - team_id=self.team.id, - ensure_analytics_event_in_session=False, - ) - # this session needs to have multiple not matching - for _ in range(10): - self.create_event( - "user", - self.an_hour_ago, - properties={ - "$session_id": session_id_without_flag, - "$window_id": "1", - "$host": "localhost", - "$feature/replay-flags-testing": False, - }, - ) - - # and this session does not have the flag at all - session_with_flag_not_set = str(uuid4()) - produce_replay_summary( - distinct_id="user", - session_id=session_with_flag_not_set, - first_timestamp=self.an_hour_ago + relativedelta(seconds=30), - team_id=self.team.id, - click_count=10, - ensure_analytics_event_in_session=False, - ) - - for _ in range(10): - self.create_event( - "user", - self.an_hour_ago, - properties={ - "$session_id": session_with_flag_not_set, - "$window_id": "1", - "$host": "example.com", - }, - ) - - # there are three states, flag is true, flag is false, and flag is not set - (session_recordings, _, _) = self._filter_recordings_by( - {"properties": [{"key": "replay-flags-testing", "value": ["true"], "operator": "exact", "type": "feature"}]} - ) - assert [sr["session_id"] for sr in session_recordings] == [session_id_with_flag] - - (session_recordings, _, _) = self._filter_recordings_by( - { - "properties": [ - {"key": "replay-flags-testing", "value": ["false"], "operator": "exact", "type": "feature"} - ] - } - ) - assert [sr["session_id"] for sr in session_recordings] == [session_id_without_flag] - - (session_recordings, _, _) = self._filter_recordings_by( - { - "properties": [ - {"key": "replay-hogql-filters", "value": "is_not_set", "operator": "is_not_set", "type": "feature"} - ] - } - ) - assert [sr["session_id"] for sr in session_recordings] == [session_with_flag_not_set] - - (session_recordings, _, _) = self._filter_recordings_by( - { - "properties": [ - {"key": "replay-hogql-filters", "value": "is_set", "operator": "is_set", "type": "feature"} - ] - } - ) - assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( - [session_id_with_flag, session_id_without_flag] - ) From 3b82878b9dd63a83fe8aa29c3312ce2b5deffe88 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 11:20:26 +0000 Subject: [PATCH 06/17] flag the new query route --- frontend/src/lib/constants.tsx | 1 + .../playlist/sessionRecordingsPlaylistLogic.ts | 16 +++++++++++++++- .../session_recordings/session_recording_api.py | 10 +++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx index fe7dd3971a7a4..6b84e1c89fd38 100644 --- a/frontend/src/lib/constants.tsx +++ b/frontend/src/lib/constants.tsx @@ -232,6 +232,7 @@ export const FEATURE_FLAGS = { SELF_SERVE_CREDIT_OVERRIDE: 'self-serve-credit-override', // owner: @zach EXPERIMENTS_MIGRATION_DISABLE_UI: 'experiments-migration-disable-ui', // owner: @jurajmajerik #team-experiments REPLAY_HOGQL_FILTERS: 'replay-hogql-filters', // owner: @pauldambra #team-replay + REPLAY_LIST_RECORDINGS_AS_QUERY: 'replay-list-recordings-as-query', // owner: @pauldambra #team-replay } as const export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS] diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts index 617310ca0abff..c550d14f3bc8c 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts @@ -12,6 +12,7 @@ import { isLogEntryPropertyFilter, isRecordingPropertyFilter, } from 'lib/components/UniversalFilters/utils' +import { FEATURE_FLAGS } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { objectClean, objectsEqual } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' @@ -331,7 +332,9 @@ export const sessionRecordingsPlaylistLogic = kea { - const params: RecordingsQuery = { + // as_query is a temporary parameter as a flag + // to let the backend know not to convert the query to a legacy filter when processing + const params: RecordingsQuery & { as_query?: boolean } = { ...convertUniversalFiltersToRecordingsQuery(values.filters), person_uuid: props.personUUID ?? '', limit: RECORDINGS_LIMIT, @@ -349,6 +352,10 @@ export const sessionRecordingsPlaylistLogic = kea [(_, props) => props], (props): SessionRecordingPlaylistLogicProps => props], + listAPIAsQuery: [ + (s) => [s.featureFlags], + (featureFlags) => { + return !!featureFlags[FEATURE_FLAGS.REPLAY_LIST_RECORDINGS_AS_QUERY] + }, + ], + matchingEventsMatchType: [ (s) => [s.filters], (filters): MatchingEventsMatchType => { diff --git a/posthog/session_recordings/session_recording_api.py b/posthog/session_recordings/session_recording_api.py index 3961109fc365f..6a5f211b23851 100644 --- a/posthog/session_recordings/session_recording_api.py +++ b/posthog/session_recordings/session_recording_api.py @@ -322,9 +322,13 @@ def safely_get_object(self, queryset) -> SessionRecording: return recording def list(self, request: request.Request, *args: Any, **kwargs: Any) -> Response: - filter = SessionRecordingsFilter(request=request, team=self.team) - self._maybe_report_recording_list_filters_changed(request, team=self.team) - return list_recordings_response(filter, request, self.get_serializer_context()) + use_query_type = request.GET.get("as_query", False) + if use_query_type: + raise ValueError("as_query is not supported for session recordings") + else: + filter = SessionRecordingsFilter(request=request, team=self.team) + self._maybe_report_recording_list_filters_changed(request, team=self.team) + return list_recordings_response(filter, request, self.get_serializer_context()) @extend_schema( exclude=True, From 9f5b54c1922b4e2c67e7cda4bc68596524b0c709 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 12:11:18 +0000 Subject: [PATCH 07/17] more --- .../session_recording_playlist.py | 5 +- .../session_recording_list_from_query.py | 658 +++ .../test_session_recording_list_from_query.py | 4113 +++++++++++++++++ .../session_recording_api.py | 140 +- .../test/test_session_recordings.py | 4 +- 5 files changed, 4910 insertions(+), 10 deletions(-) create mode 100644 posthog/session_recordings/queries/session_recording_list_from_query.py create mode 100644 posthog/session_recordings/queries/test/test_session_recording_list_from_query.py diff --git a/ee/session_recordings/session_recording_playlist.py b/ee/session_recordings/session_recording_playlist.py index 8947e1c270ee4..1a7d6e68250fb 100644 --- a/ee/session_recordings/session_recording_playlist.py +++ b/ee/session_recordings/session_recording_playlist.py @@ -34,7 +34,7 @@ ClickHouseBurstRateThrottle, ClickHouseSustainedRateThrottle, ) -from posthog.session_recordings.session_recording_api import list_recordings_response +from posthog.session_recordings.session_recording_api import list_recordings_response, list_recordings from posthog.utils import relative_date_parse logger = structlog.get_logger(__name__) @@ -227,7 +227,8 @@ def recordings(self, request: request.Request, *args: Any, **kwargs: Any) -> res filter = SessionRecordingsFilter(request=request, team=self.team) filter = filter.shallow_clone({SESSION_RECORDINGS_FILTER_IDS: json.dumps(playlist_items)}) - return list_recordings_response(filter, request, self.get_serializer_context()) + # TODO this needs converting to Query as well + return list_recordings_response(list_recordings(filter, request, context=self.get_serializer_context())) # As of now, you can only "update" a session recording by adding or removing a recording from a static playlist @action( diff --git a/posthog/session_recordings/queries/session_recording_list_from_query.py b/posthog/session_recordings/queries/session_recording_list_from_query.py new file mode 100644 index 0000000000000..340e11422a927 --- /dev/null +++ b/posthog/session_recordings/queries/session_recording_list_from_query.py @@ -0,0 +1,658 @@ +import re +from typing import Any, NamedTuple, cast, Optional, Union +from datetime import datetime, timedelta + +import posthoganalytics + +from posthog.hogql import ast +from posthog.hogql.ast import CompareOperation +from posthog.hogql.parser import parse_select +from posthog.hogql.property import entity_to_expr, property_to_expr +from posthog.hogql.query import execute_hogql_query +from posthog.hogql_queries.insights.paginators import HogQLHasMorePaginator +from posthog.models import Team, Property +from posthog.models.filters.session_recordings_filter import SessionRecordingsFilter +from posthog.models.filters.mixins.utils import cached_property +from posthog.models.property import PropertyGroup +from posthog.schema import QueryTiming, HogQLQueryModifiers, PersonsOnEventsMode, RecordingsQuery +from posthog.session_recordings.queries.session_replay_events import ttl_days +from posthog.constants import TREND_FILTER_TYPE_ACTIONS + +import structlog + +logger = structlog.get_logger(__name__) + + +def is_event_property(p: Property) -> bool: + return p.type == "event" or (p.type == "hogql" and bool(re.search(r"(? bool: + return p.type == "person" or (p.type == "hogql" and "person.properties" in p.key) + + +def is_group_property(p: Property) -> bool: + return p.type == "group" + + +def is_cohort_property(p: Property) -> bool: + return "cohort" in p.type + + +class SessionRecordingQueryResult(NamedTuple): + results: list + has_more_recording: bool + timings: list[QueryTiming] | None = None + + +class UnexpectedQueryProperties(Exception): + def __init__(self, remaining_properties: PropertyGroup | None): + self.remaining_properties = remaining_properties + super().__init__(f"Unexpected properties in query: {remaining_properties}") + + +class SessionRecordingListFromQuery: + SESSION_RECORDINGS_DEFAULT_LIMIT = 50 + + _team: Team + _query: RecordingsQuery + + BASE_QUERY: str = """ + SELECT s.session_id, + any(s.team_id), + any(s.distinct_id), + min(s.min_first_timestamp) as start_time, + max(s.max_last_timestamp) as end_time, + dateDiff('SECOND', start_time, end_time) as duration, + argMinMerge(s.first_url) as first_url, + sum(s.click_count) as click_count, + sum(s.keypress_count) as keypress_count, + sum(s.mouse_activity_count) as mouse_activity_count, + sum(s.active_milliseconds)/1000 as active_seconds, + (duration - active_seconds) as inactive_seconds, + sum(s.console_log_count) as console_log_count, + sum(s.console_warn_count) as console_warn_count, + sum(s.console_error_count) as console_error_count, + {ongoing_selection}, + round(( + ((sum(s.active_milliseconds) / 1000 + sum(s.click_count) + sum(s.keypress_count) + sum(s.console_error_count))) -- intent + / + ((sum(s.mouse_activity_count) + dateDiff('SECOND', start_time, end_time) + sum(s.console_error_count) + sum(s.console_log_count) + sum(s.console_warn_count))) + * 100 + ), 2) as activity_score + FROM raw_session_replay_events s + WHERE {where_predicates} + GROUP BY session_id + HAVING {having_predicates} + ORDER BY {order_by} DESC + """ + + @staticmethod + def _data_to_return(results: list[Any] | None) -> list[dict[str, Any]]: + default_columns = [ + "session_id", + "team_id", + "distinct_id", + "start_time", + "end_time", + "duration", + "first_url", + "click_count", + "keypress_count", + "mouse_activity_count", + "active_seconds", + "inactive_seconds", + "console_log_count", + "console_warn_count", + "console_error_count", + "ongoing", + "activity_score", + ] + + return [ + { + **dict(zip(default_columns, row[: len(default_columns)])), + } + for row in results or [] + ] + + def __init__( + self, + team: Team, + query: RecordingsQuery, + hogql_query_modifiers: Optional[HogQLQueryModifiers], + **_, + ): + self._team = team + self._query = query + self._paginator = HogQLHasMorePaginator( + limit=query.limit or self.SESSION_RECORDINGS_DEFAULT_LIMIT, offset=query.offset or 0 + ) + self._hogql_query_modifiers = hogql_query_modifiers + + @property + def ttl_days(self): + return ttl_days(self._team) + + def run(self) -> SessionRecordingQueryResult: + query = self.get_query() + + paginated_response = self._paginator.execute_hogql_query( + # TODO I guess the paginator needs to know how to handle union queries or all callers are supposed to collapse them or .... 🤷 + query=cast(ast.SelectQuery, query), + team=self._team, + query_type="SessionRecordingListQuery", + modifiers=self._hogql_query_modifiers, + ) + + return SessionRecordingQueryResult( + results=(self._data_to_return(self._paginator.results)), + has_more_recording=self._paginator.has_more(), + timings=paginated_response.timings, + ) + + def get_query(self): + return parse_select( + self.BASE_QUERY, + { + # Check if the most recent _timestamp is within five minutes of the current time + # proxy for a live session + "ongoing_selection": ast.Alias( + alias="ongoing", + expr=ast.CompareOperation( + left=ast.Call(name="max", args=[ast.Field(chain=["s", "_timestamp"])]), + right=ast.Constant( + # provided in a placeholder, so we can pass now from python to make tests easier 🙈 + value=datetime.utcnow() - timedelta(minutes=5), + ), + op=ast.CompareOperationOp.GtEq, + ), + ), + "order_by": self._order_by_clause(), + "where_predicates": self._where_predicates(), + "having_predicates": self._having_predicates(), + }, + ) + + def _order_by_clause(self) -> ast.Field: + return ast.Field(chain=[self._query.order]) + + def _where_predicates(self) -> Union[ast.And, ast.Or]: + exprs: list[ast.Expr] = [ + ast.CompareOperation( + op=ast.CompareOperationOp.GtEq, + left=ast.Field(chain=["s", "min_first_timestamp"]), + right=ast.Constant(value=datetime.utcnow() - timedelta(days=self.ttl_days)), + ) + ] + + person_id_compare_operation = PersonsIdCompareOperation(self._team, self._query, self.ttl_days).get_operation() + if person_id_compare_operation: + exprs.append(person_id_compare_operation) + + # we check for session_ids type not for truthiness since we want to allow empty lists + if isinstance(self._query.session_ids, list): + exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.In, + left=ast.Field(chain=["session_id"]), + right=ast.Constant(value=self._query.session_ids), + ) + ) + + if self._query.date_from: + exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.GtEq, + left=ast.Field(chain=["s", "min_first_timestamp"]), + right=ast.Constant(value=self._query.date_from), + ) + ) + if self._query.date_to: + exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.LtEq, + left=ast.Field(chain=["s", "min_first_timestamp"]), + right=ast.Constant(value=self._query.date_to), + ) + ) + + optional_exprs: list[ast.Expr] = [] + + # if in PoE mode then we should be pushing person property queries into here + events_sub_query = ReplayFiltersEventsSubQuery(self._team, self._query).get_query_for_session_id_matching() + if events_sub_query: + optional_exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.In, + left=ast.Field(chain=["s", "session_id"]), + right=events_sub_query, + ) + ) + + # we want to avoid a join to persons since we don't ever need to select from them, + # so we create our own persons sub query here + # if PoE mode is on then this will be handled in the events subquery, and we don't need to do anything here + person_subquery = PersonsPropertiesSubQuery(self._team, self._query, self.ttl_days).get_query() + if person_subquery: + optional_exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.In, + left=ast.Field(chain=["s", "distinct_id"]), + right=person_subquery, + ) + ) + + cohort_subquery = CohortPropertyGroupsSubQuery(self._team, self._query, self.ttl_days).get_query() + if cohort_subquery: + optional_exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.In, + left=ast.Field(chain=["s", "distinct_id"]), + right=cohort_subquery, + ) + ) + + remaining_properties = self._strip_person_and_event_and_cohort_properties(self._query.property_groups) + if remaining_properties: + posthoganalytics.capture_exception(UnexpectedQueryProperties(remaining_properties)) + optional_exprs.append(property_to_expr(remaining_properties, team=self._team, scope="replay")) + + if self._query.console_log_filters and self._query.console_log_filters.values: + console_logs_subquery = ast.SelectQuery( + select=[ast.Field(chain=["log_source_id"])], + select_from=ast.JoinExpr(table=ast.Field(chain=["console_logs_log_entries"])), + where=ast.And( + exprs=[ + self._query.ast_operand( + exprs=[ + property_to_expr(self._query.console_log_filters, team=self._team), + ] + ), + ast.CompareOperation( + op=ast.CompareOperationOp.Eq, + left=ast.Field(chain=["log_source"]), + right=ast.Constant(value="session_replay"), + ), + ] + ), + ) + + optional_exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.In, + left=ast.Field(chain=["session_id"]), + right=console_logs_subquery, + ) + ) + + if optional_exprs: + exprs.append(self._query.ast_operand(exprs=optional_exprs)) + + return ast.And(exprs=exprs) + + def _having_predicates(self) -> ast.Expr: + return property_to_expr(self._query.having_predicates, team=self._team, scope="replay") + + def _strip_person_and_event_and_cohort_properties(self, property_group: PropertyGroup) -> PropertyGroup | None: + property_groups_to_keep = [ + g + for g in property_group.flat + if not is_event_property(g) + and not is_person_property(g) + and not is_group_property(g) + and not is_cohort_property(g) + ] + + return ( + PropertyGroup( + type=self._query.property_operand, + values=property_groups_to_keep, + ) + if property_groups_to_keep + else None + ) + + +def poe_is_active(team: Team) -> bool: + return team.person_on_events_mode is not None and team.person_on_events_mode != PersonsOnEventsMode.DISABLED + + +class PersonsPropertiesSubQuery: + _team: Team + _query: RecordingsQuery + _ttl_days: int + + def __init__(self, team: Team, query: RecordingsQuery, ttl_days: int): + self._team = team + self._query = query + self._ttl_days = ttl_days + + def get_query(self) -> ast.SelectQuery | ast.SelectSetQuery | None: + if self.person_properties and not poe_is_active(self._team): + return parse_select( + """ + SELECT distinct_id + FROM person_distinct_ids + WHERE {where_predicates} + """, + { + "where_predicates": self._where_predicates, + }, + ) + else: + return None + + @cached_property + def person_properties(self) -> PropertyGroup | None: + person_property_groups = [g for g in self._query.property_groups.flat if is_person_property(g)] + return ( + PropertyGroup( + type=self._query.property_operand, + values=person_property_groups, + ) + if person_property_groups + else None + ) + + @cached_property + def _where_predicates(self) -> ast.Expr: + return ( + property_to_expr(self.person_properties, team=self._team) + if self.person_properties + else ast.Constant(value=True) + ) + + +class CohortPropertyGroupsSubQuery: + _team: Team + _query: RecordingsQuery + _ttl_days: int + + raw_cohort_to_distinct_id = """ + SELECT + distinct_id +FROM raw_person_distinct_ids +WHERE distinct_id in (SELECT distinct_id FROM raw_person_distinct_ids WHERE 1=1 AND {cohort_predicate}) +GROUP BY distinct_id +HAVING argMax(is_deleted, version) = 0 AND {cohort_predicate} + """ + + def __init__(self, team: Team, query: RecordingsQuery, ttl_days: int): + self._team = team + self._query = query + self._ttl_days = ttl_days + + def get_query(self) -> ast.SelectQuery | ast.SelectSetQuery | None: + if self.cohort_properties: + return parse_select( + self.raw_cohort_to_distinct_id, + {"cohort_predicate": property_to_expr(self.cohort_properties, team=self._team, scope="replay")}, + ) + + return None + + @cached_property + def cohort_properties(self) -> PropertyGroup | None: + cohort_property_groups = [g for g in self._query.property_groups.flat if is_cohort_property(g)] + return ( + PropertyGroup( + type=self._query.property_operand, + values=cohort_property_groups, + ) + if cohort_property_groups + else None + ) + + +class PersonsIdCompareOperation: + _team: Team + _query: RecordingsQuery + _ttl_days: int + + def __init__(self, team: Team, query: RecordingsQuery, ttl_days: int): + self._team = team + self._query = query + self._ttl_days = ttl_days + + def get_operation(self) -> CompareOperation | None: + q = self.get_query() + if not q: + return None + + if poe_is_active(self._team): + return ast.CompareOperation( + op=ast.CompareOperationOp.In, + left=ast.Field(chain=["session_id"]), + right=q, + ) + else: + return ast.CompareOperation( + op=ast.CompareOperationOp.In, + left=ast.Field(chain=["distinct_id"]), + right=q, + ) + + def get_query(self) -> ast.SelectQuery | ast.SelectSetQuery | None: + if not self._query.person_uuid: + return None + + # anchor to python now so that tests can freeze time + now = datetime.utcnow() + + if poe_is_active(self._team): + return parse_select( + """ + select + distinct `$session_id` + from + events + where + person_id = {person_id} + and timestamp <= {now} + and timestamp >= {ttl_date} + and timestamp >= {date_from} + and timestamp <= {date_to} + and notEmpty(`$session_id`) + """, + { + "person_id": ast.Constant(value=self._query.person_uuid), + "ttl_days": ast.Constant(value=self._ttl_days), + "date_from": ast.Constant(value=self._query.date_from), + "date_to": ast.Constant(value=self._query.date_to), + "now": ast.Constant(value=now), + "ttl_date": ast.Constant(value=now - timedelta(days=self._ttl_days)), + }, + ) + else: + return parse_select( + """ + SELECT distinct_id + FROM person_distinct_ids + WHERE person_id = {person_id} + """, + { + "person_id": ast.Constant(value=self._query.person_uuid), + }, + ) + + +class ReplayFiltersEventsSubQuery: + _team: Team + _query: RecordingsQuery + + @property + def ttl_days(self): + return ttl_days(self._team) + + def __init__( + self, + team: Team, + filter: SessionRecordingsFilter, + hogql_query_modifiers: Optional[HogQLQueryModifiers] = None, + ): + self._team = team + self._query = filter + self._hogql_query_modifiers = hogql_query_modifiers + + @cached_property + def _event_predicates(self): + event_exprs: list[ast.Expr] = [] + event_names: set[int | str] = set() + + for entity in self._query.entities: + if entity.type == TREND_FILTER_TYPE_ACTIONS: + action = entity.get_action() + event_names.update([ae for ae in action.get_step_events() if ae and ae not in event_names]) + else: + if entity.id and entity.id not in event_names: + event_names.add(entity.id) + + # TODO: we're not passing the "right" type in here - should we change the signature or do something else? + entity_exprs = [entity_to_expr(entity=entity)] # type: ignore + + if entity.property_groups: + entity_exprs.append(property_to_expr(entity.property_groups, team=self._team, scope="replay_entity")) + + event_exprs.append(ast.And(exprs=entity_exprs)) + + return event_exprs, list(event_names) + + def _select_from_events(self, select_expr: ast.Expr) -> ast.SelectQuery: + return ast.SelectQuery( + select=[select_expr], + select_from=ast.JoinExpr( + table=ast.Field(chain=["events"]), + ), + where=self._where_predicates(), + having=self._having_predicates(), + group_by=[ast.Field(chain=["$session_id"])], + ) + + def get_query_for_session_id_matching(self) -> ast.SelectQuery | ast.SelectSetQuery | None: + use_poe = poe_is_active(self._team) and self.person_properties + if self._query.entities or self.event_properties or self.group_properties or use_poe: + return self._select_from_events(ast.Alias(alias="session_id", expr=ast.Field(chain=["$session_id"]))) + else: + return None + + def get_query_for_event_id_matching(self) -> ast.SelectQuery | ast.SelectSetQuery: + return self._select_from_events(ast.Call(name="groupUniqArray", args=[ast.Field(chain=["uuid"])])) + + def get_event_ids_for_session(self) -> SessionRecordingQueryResult: + query = self.get_query_for_event_id_matching() + + hogql_query_response = execute_hogql_query( + query=query, + team=self._team, + query_type="SessionRecordingMatchingEventsForSessionQuery", + modifiers=self._hogql_query_modifiers, + ) + + flattened_results = [str(uuid) for row in hogql_query_response.results for uuid in row[0]] + + return SessionRecordingQueryResult( + results=flattened_results, + has_more_recording=False, + timings=hogql_query_response.timings, + ) + + def _where_predicates(self) -> ast.Expr: + exprs: list[ast.Expr] = [ + ast.Call( + name="notEmpty", + args=[ast.Field(chain=["$session_id"])], + ), + # regardless of any other filters limit between TTL and current time + ast.CompareOperation( + op=ast.CompareOperationOp.GtEq, + left=ast.Field(chain=["timestamp"]), + right=ast.Constant(value=datetime.now() - timedelta(days=self.ttl_days)), + ), + ast.CompareOperation( + op=ast.CompareOperationOp.LtEq, + left=ast.Field(chain=["timestamp"]), + right=ast.Call(name="now", args=[]), + ), + ] + + # TRICKY: we're adding a buffer to the date range to ensure we get all the events + # you can start sending us events before the session starts + if self._query.date_from: + exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.GtEq, + left=ast.Field(chain=["timestamp"]), + right=ast.Constant(value=self._query.date_from - timedelta(minutes=2)), + ) + ) + + # but we don't want to include events after date_to if provided + if self._query.date_to: + exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.LtEq, + left=ast.Field(chain=["timestamp"]), + right=ast.Constant(value=self._query.date_to), + ) + ) + + (event_where_exprs, _) = self._event_predicates + if event_where_exprs: + # we OR all events in the where and use hasAll / hasAny in the HAVING clause + exprs.append(ast.Or(exprs=event_where_exprs)) + + if self.event_properties: + exprs.append(property_to_expr(self.event_properties, team=self._team, scope="replay")) + + if self.group_properties: + exprs.append(property_to_expr(self.group_properties, team=self._team)) + + if self._team.person_on_events_mode and self.person_properties: + exprs.append(property_to_expr(self.person_properties, team=self._team, scope="event")) + + if self._query.session_ids: + exprs.append( + ast.CompareOperation( + op=ast.CompareOperationOp.In, + left=ast.Field(chain=["$session_id"]), + right=ast.Constant(value=self._query.session_ids), + ) + ) + + return ast.And(exprs=exprs) + + def _having_predicates(self) -> ast.Expr: + (_, event_names) = self._event_predicates + + if event_names: + return ast.Call( + name="hasAll" if self._query._operand == "AND" else "hasAny", + args=[ + ast.Call(name="groupUniqArray", args=[ast.Field(chain=["event"])]), + # KLUDGE: sorting only so that snapshot tests are consistent + ast.Constant(value=sorted(event_names)), + ], + ) + + return ast.Constant(value=True) + + @cached_property + def event_properties(self): + return [g for g in self._query.property_groups.flat if is_event_property(g)] + + @cached_property + def group_properties(self): + return [g for g in self._query.property_groups.flat if is_group_property(g)] + + @cached_property + def person_properties(self) -> PropertyGroup | None: + person_property_groups = [g for g in self._query.property_groups.flat if is_person_property(g)] + return ( + PropertyGroup( + type=self._query.property_operand, + values=person_property_groups, + ) + if person_property_groups + else None + ) diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py new file mode 100644 index 0000000000000..cec778e5cca82 --- /dev/null +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py @@ -0,0 +1,4113 @@ +from datetime import datetime +from unittest.mock import ANY +from uuid import uuid4 + +from dateutil.relativedelta import relativedelta +from django.utils.timezone import now +from freezegun import freeze_time +from posthog.clickhouse.client import sync_execute +from posthog.clickhouse.log_entries import TRUNCATE_LOG_ENTRIES_TABLE_SQL +from posthog.constants import AvailableFeature +from posthog.models import Cohort, GroupTypeMapping, Person +from posthog.models.action import Action +from posthog.models.group.util import create_group +from posthog.models.team import Team +from posthog.schema import RecordingsQuery +from posthog.session_recordings.queries.session_recording_list_from_filters import ( + SessionRecordingListFromFilters, + SessionRecordingQueryResult, +) +from posthog.session_recordings.queries.session_recording_list_from_query import SessionRecordingListFromQuery +from posthog.session_recordings.queries.session_replay_events import ttl_days +from posthog.session_recordings.queries.test.session_replay_sql import ( + produce_replay_summary, +) +from posthog.session_recordings.sql.session_replay_event_sql import ( + TRUNCATE_SESSION_REPLAY_EVENTS_TABLE_SQL, +) +from posthog.test.base import ( + APIBaseTest, + ClickhouseTestMixin, + _create_event, + also_test_with_materialized_columns, + flush_persons_and_events, + snapshot_clickhouse_queries, +) + + +@freeze_time("2021-01-01T13:46:23") +class TestSessionRecordingsListFromQuery(ClickhouseTestMixin, APIBaseTest): + def setUp(self): + super().setUp() + sync_execute(TRUNCATE_SESSION_REPLAY_EVENTS_TABLE_SQL()) + sync_execute(TRUNCATE_LOG_ENTRIES_TABLE_SQL) + + def create_action(self, name, team_id=None, properties=None): + if team_id is None: + team_id = self.team.pk + if properties is None: + properties = [] + action = Action.objects.create( + team_id=team_id, + name=name, + steps_json=[ + { + "event": name, + "properties": properties, + } + ], + ) + return action + + def create_event( + self, + distinct_id, + timestamp, + team=None, + event_name="$pageview", + properties=None, + ): + if team is None: + team = self.team + if properties is None: + properties = {"$os": "Windows 95", "$current_url": "aloha.com/2"} + return _create_event( + team=team, + event=event_name, + timestamp=timestamp, + distinct_id=distinct_id, + properties=properties, + ) + + def _filter_recordings_by(self, recordings_filter: dict) -> SessionRecordingQueryResult: + the_query = RecordingsQuery(team=self.team, data=recordings_filter) + session_recording_list_instance = SessionRecordingListFromQuery( + query=the_query, team=self.team, hogql_query_modifiers=None + ) + return session_recording_list_instance.run() + + @property + def an_hour_ago(self): + return (now() - relativedelta(hours=1)).replace(microsecond=0, second=0) + + @snapshot_clickhouse_queries + def test_basic_query(self): + user = "test_basic_query-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_one = f"test_basic_query-{str(uuid4())}" + session_id_two = f"test_basic_query-{str(uuid4())}" + + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=self.an_hour_ago.isoformat().replace("T", " "), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=20)).isoformat().replace("T", " "), + distinct_id=user, + first_url="https://example.io/home", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=50 * 1000 * 0.5, # 50% of the total expected duration + ) + + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=(self.an_hour_ago + relativedelta(seconds=10)), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=50)), + distinct_id=user, + first_url="https://a-different-url.com", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=0, # 30% of the total expected duration + ) + + produce_replay_summary( + session_id=session_id_two, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=(self.an_hour_ago + relativedelta(seconds=20)), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=2000)), + distinct_id=user, + first_url="https://another-url.com", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=1980 * 1000 * 0.4, # 40% of the total expected duration + ) + + session_recordings, more_recordings_available, _ = self._filter_recordings_by({"no_filter": None}) + + assert session_recordings == [ + { + "session_id": session_id_two, + "activity_score": 40.16, + "team_id": self.team.pk, + "distinct_id": user, + "click_count": 2, + "keypress_count": 2, + "mouse_activity_count": 2, + "duration": 1980, + "active_seconds": 792.0, + "inactive_seconds": 1188.0, + "start_time": self.an_hour_ago + relativedelta(seconds=20), + "end_time": self.an_hour_ago + relativedelta(seconds=2000), + "first_url": "https://another-url.com", + "console_log_count": 0, + "console_warn_count": 0, + "console_error_count": 0, + "ongoing": 1, + }, + { + "session_id": session_id_one, + "activity_score": 61.11, + "team_id": self.team.pk, + "distinct_id": user, + "click_count": 4, + "keypress_count": 4, + "mouse_activity_count": 4, + "duration": 50, + "active_seconds": 25.0, + "inactive_seconds": 25.0, + "start_time": self.an_hour_ago, + "end_time": self.an_hour_ago + relativedelta(seconds=50), + "first_url": "https://example.io/home", + "console_log_count": 0, + "console_warn_count": 0, + "console_error_count": 0, + "ongoing": 1, + }, + ] + + assert more_recordings_available is False + + @snapshot_clickhouse_queries + def test_basic_query_active_sessions( + self, + ): + user = "test_basic_query-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_total_is_61 = f"test_basic_query_active_sessions-total-{str(uuid4())}" + session_id_active_is_61 = f"test_basic_query_active_sessions-active-{str(uuid4())}" + session_id_inactive_is_61 = f"test_basic_query_active_sessions-inactive-{str(uuid4())}" + + produce_replay_summary( + session_id=session_id_total_is_61, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=self.an_hour_ago.isoformat().replace("T", " "), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=61)).isoformat().replace("T", " "), + distinct_id=user, + first_url="https://example.io/home", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=59000, + ) + + produce_replay_summary( + session_id=session_id_active_is_61, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=59)), + distinct_id=user, + first_url="https://a-different-url.com", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=61000, + ) + + produce_replay_summary( + session_id=session_id_inactive_is_61, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=61)), + distinct_id=user, + first_url="https://a-different-url.com", + click_count=0, + keypress_count=0, + mouse_activity_count=0, + active_milliseconds=0, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "having_predicates": '[{"type":"recording","key":"duration","value":60,"operator":"gt"}]', + } + ) + + assert sorted( + [(s["session_id"], s["duration"], s["active_seconds"]) for s in session_recordings], + key=lambda x: x[0], + ) == [ + (session_id_inactive_is_61, 61, 0.0), + (session_id_total_is_61, 61, 59.0), + ] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "having_predicates": '[{"type":"recording","key":"active_seconds","value":"60","operator":"gt"}]', + } + ) + + assert [(s["session_id"], s["duration"], s["active_seconds"]) for s in session_recordings] == [ + (session_id_active_is_61, 59, 61.0) + ] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "having_predicates": '[{"type":"recording","key":"inactive_seconds","value":"60","operator":"gt"}]', + } + ) + + assert [(s["session_id"], s["duration"], s["inactive_seconds"]) for s in session_recordings] == [ + (session_id_inactive_is_61, 61, 61.0) + ] + + @snapshot_clickhouse_queries + def test_sessions_with_current_data( + self, + ): + user = "test_sessions_with_current_data-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_inactive = f"test_sessions_with_current_data-inactive-{str(uuid4())}" + session_id_active = f"test_sessions_with_current_data-active-{str(uuid4())}" + + produce_replay_summary( + session_id=session_id_inactive, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago, + last_timestamp=self.an_hour_ago + relativedelta(seconds=60), + distinct_id=user, + first_url="https://example.io/home", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=59000, + kafka_timestamp=(datetime.utcnow() - relativedelta(minutes=6)), + ) + + produce_replay_summary( + session_id=session_id_active, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago, + last_timestamp=self.an_hour_ago + relativedelta(seconds=60), + distinct_id=user, + first_url="https://a-different-url.com", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=61000, + kafka_timestamp=(datetime.utcnow() - relativedelta(minutes=3)), + ) + + (session_recordings, _, _) = self._filter_recordings_by({}) + assert sorted( + [(s["session_id"], s["ongoing"]) for s in session_recordings], + key=lambda x: x[0], + ) == [ + (session_id_active, 1), + (session_id_inactive, 0), + ] + + @snapshot_clickhouse_queries + def test_basic_query_with_paging(self): + user = "test_basic_query_with_paging-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_one = f"id_one_test_basic_query_with_paging-{str(uuid4())}" + session_id_two = f"id_two_test_basic_query_with_paging-{str(uuid4())}" + + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=self.an_hour_ago.isoformat().replace("T", " "), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=20)).isoformat().replace("T", " "), + distinct_id=user, + first_url="https://example.io/home", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=50 * 1000 * 0.5, # 50% of the total expected duration + ) + + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=(self.an_hour_ago + relativedelta(seconds=10)), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=50)), + distinct_id=user, + first_url="https://a-different-url.com", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=0, # 30% of the total expected duration + ) + + produce_replay_summary( + session_id=session_id_two, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=(self.an_hour_ago + relativedelta(seconds=20)), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=2000)), + distinct_id=user, + first_url="https://another-url.com", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=1980 * 1000 * 0.4, # 40% of the total expected duration + ) + + (session_recordings, more_recordings_available, _) = self._filter_recordings_by( + {"no_filter": None, "limit": 1, "offset": 0} + ) + + assert session_recordings == [ + { + "activity_score": 40.16, + "session_id": session_id_two, + "team_id": self.team.pk, + "distinct_id": user, + "click_count": 2, + "keypress_count": 2, + "mouse_activity_count": 2, + "duration": 1980, + "active_seconds": 792.0, + "inactive_seconds": 1188.0, + "start_time": self.an_hour_ago + relativedelta(seconds=20), + "end_time": self.an_hour_ago + relativedelta(seconds=2000), + "first_url": "https://another-url.com", + "console_log_count": 0, + "console_warn_count": 0, + "console_error_count": 0, + "ongoing": 1, + } + ] + + assert more_recordings_available is True + + (session_recordings, more_recordings_available, _) = self._filter_recordings_by( + {"no_filter": None, "limit": 1, "offset": 1} + ) + + assert session_recordings == [ + { + "session_id": session_id_one, + "activity_score": 61.11, + "team_id": self.team.pk, + "distinct_id": user, + "click_count": 4, + "keypress_count": 4, + "mouse_activity_count": 4, + "duration": 50, + "active_seconds": 25.0, + "inactive_seconds": 25.0, + "start_time": self.an_hour_ago, + "end_time": self.an_hour_ago + relativedelta(seconds=50), + "first_url": "https://example.io/home", + "console_log_count": 0, + "console_warn_count": 0, + "console_error_count": 0, + "ongoing": 1, + }, + ] + + assert more_recordings_available is False + + (session_recordings, more_recordings_available, _) = self._filter_recordings_by( + {"no_filter": None, "limit": 1, "offset": 2} + ) + + assert session_recordings == [] + + assert more_recordings_available is False + + @snapshot_clickhouse_queries + def test_basic_query_with_ordering(self): + user = "test_basic_query_with_ordering-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_one = f"test_basic_query_with_ordering-session-1-{str(uuid4())}" + session_id_two = f"test_basic_query_with_ordering-session-2-{str(uuid4())}" + + session_one_start = self.an_hour_ago + relativedelta(seconds=10) + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=session_one_start, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=50)), + distinct_id=user, + console_error_count=1000, + active_milliseconds=1, # most errors, but the least activity + ) + + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=session_one_start, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=50)), + distinct_id=user, + console_error_count=12, + active_milliseconds=1, # most errors, but the least activity + ) + + session_two_start = self.an_hour_ago + produce_replay_summary( + session_id=session_id_two, + team_id=self.team.pk, + # starts before session one + first_timestamp=session_two_start, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=50)), + distinct_id=user, + console_error_count=430, + active_milliseconds=1000, # most activity, but the least errors + ) + + (session_recordings) = self._filter_recordings_by( + {"no_filter": None, "limit": 3, "offset": 0, "order": "active_seconds"} + ) + + ordered_by_activity = [(r["session_id"], r["active_seconds"]) for r in session_recordings.results] + assert ordered_by_activity == [(session_id_two, 1.0), (session_id_one, 0.002)] + + (session_recordings) = self._filter_recordings_by( + {"no_filter": None, "limit": 3, "offset": 0, "order": "console_error_count"} + ) + + ordered_by_errors = [(r["session_id"], r["console_error_count"]) for r in session_recordings.results] + assert ordered_by_errors == [(session_id_one, 1012), (session_id_two, 430)] + + (session_recordings) = self._filter_recordings_by( + {"no_filter": None, "limit": 3, "offset": 0, "order": "start_time"} + ) + + ordered_by_default = [(r["session_id"], r["start_time"]) for r in session_recordings.results] + assert ordered_by_default == [(session_id_one, session_one_start), (session_id_two, session_two_start)] + + def test_first_url_selection(self): + user = "test_first_url_selection-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_one = f"first-url-on-first-event-{str(uuid4())}" + session_id_two = f"first-url-not-on-first-event-{str(uuid4())}" + session_id_three = f"no-url-from-many-{str(uuid4())}" + session_id_four = f"events-inserted-out-of-order-{str(uuid4())}" + + # session one has the first url on the first event + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=self.an_hour_ago, + last_timestamp=self.an_hour_ago + relativedelta(seconds=20), + first_url="https://on-first-event.com", + ) + + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago + relativedelta(seconds=10), + last_timestamp=self.an_hour_ago + relativedelta(seconds=20), + first_url="https://on-second-event.com", + ) + + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago + relativedelta(seconds=20), + last_timestamp=self.an_hour_ago + relativedelta(seconds=40), + first_url="https://on-third-event.com", + ) + + # session two has no URL on the first event + produce_replay_summary( + session_id=session_id_two, + team_id=self.team.pk, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=10)), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=50)), + first_url=None, + ) + + produce_replay_summary( + session_id=session_id_two, + team_id=self.team.pk, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=20)), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + first_url="https://first-is-on-second-event.com", + ) + + produce_replay_summary( + session_id=session_id_two, + team_id=self.team.pk, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=25)), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + first_url="https://another-on-the-session.com", + ) + + # session three has no URLs + produce_replay_summary( + session_id=session_id_three, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago, + last_timestamp=self.an_hour_ago + relativedelta(seconds=50), + distinct_id=user, + first_url=None, + ) + + produce_replay_summary( + session_id=session_id_three, + team_id=self.team.pk, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=10)), + last_timestamp=self.an_hour_ago + relativedelta(seconds=50), + distinct_id=user, + first_url=None, + ) + + produce_replay_summary( + session_id=session_id_three, + team_id=self.team.pk, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=20)), + last_timestamp=self.an_hour_ago + relativedelta(seconds=60), + distinct_id=user, + first_url=None, + ) + + # session four events are received out of order + produce_replay_summary( + session_id=session_id_four, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago + relativedelta(seconds=20), + last_timestamp=self.an_hour_ago + relativedelta(seconds=25), + first_url="https://on-first-received-event.com", + ) + produce_replay_summary( + session_id=session_id_four, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago + relativedelta(seconds=10), + last_timestamp=self.an_hour_ago + relativedelta(seconds=25), + first_url="https://on-second-received-event-but-actually-first.com", + ) + + session_recordings, more_recordings_available, _ = self._filter_recordings_by({"no_filter": None}) + + assert sorted( + [{"session_id": r["session_id"], "first_url": r["first_url"]} for r in session_recordings], + key=lambda x: x["session_id"], + ) == sorted( + [ + { + "session_id": session_id_one, + "first_url": "https://on-first-event.com", + }, + { + "session_id": session_id_two, + "first_url": "https://first-is-on-second-event.com", + }, + { + "session_id": session_id_three, + "first_url": None, + }, + { + "session_id": session_id_four, + "first_url": "https://on-second-received-event-but-actually-first.com", + }, + ], + # mypy unhappy about this lambda when first_url can be None 🤷️ + key=lambda x: x["session_id"], # type: ignore + ) + + def test_recordings_dont_leak_data_between_teams(self): + another_team = Team.objects.create(organization=self.organization) + user = "test_recordings_dont_leak_data_between_teams-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + Person.objects.create(team=another_team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_one = f"test_recordings_dont_leak_data_between_teams-1-{str(uuid4())}" + session_id_two = f"test_recordings_dont_leak_data_between_teams-2-{str(uuid4())}" + + produce_replay_summary( + session_id=session_id_one, + team_id=another_team.pk, + distinct_id=user, + first_timestamp=self.an_hour_ago, + last_timestamp=self.an_hour_ago + relativedelta(seconds=20), + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=20 * 1000 * 0.5, # 50% of the total expected duration + ) + + produce_replay_summary( + session_id=session_id_two, + team_id=self.team.pk, + distinct_id=user, + first_timestamp=self.an_hour_ago, + last_timestamp=self.an_hour_ago + relativedelta(seconds=20), + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=20 * 1000 * 0.5, # 50% of the total expected duration + ) + + (session_recordings, _, _) = self._filter_recordings_by({"no_filter": None}) + + assert [{"session": r["session_id"], "user": r["distinct_id"]} for r in session_recordings] == [ + {"session": session_id_two, "user": user} + ] + + @snapshot_clickhouse_queries + def test_event_filter(self): + user = "test_event_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + session_id_one = f"test_event_filter-{str(uuid4())}" + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + user, + self.an_hour_ago, + properties={"$session_id": session_id_one, "$window_id": str(uuid4())}, + ) + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ] + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id_one + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$autocapture", + "type": "events", + "order": 0, + "name": "$autocapture", + } + ] + } + ) + assert session_recordings == [] + + @snapshot_clickhouse_queries + def test_event_filter_has_ttl_applied_too(self): + user = "test_event_filter_has_ttl_applied_too-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + session_id_one = f"test_event_filter_has_ttl_applied_too-{str(uuid4())}" + + # this is artificially incorrect data, the session events are within TTL + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + # but the page view event is outside TTL + self.create_event( + user, + self.an_hour_ago - relativedelta(days=SessionRecordingListFromFilters.SESSION_RECORDINGS_DEFAULT_LIMIT + 1), + properties={"$session_id": session_id_one, "$window_id": str(uuid4())}, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ] + } + ) + assert len(session_recordings) == 0 + + (session_recordings, _, _) = self._filter_recordings_by({}) + # without an event filter the recording is present, showing that the TTL was applied to the events table too + # we want this to limit the amount of event data we query + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id_one + + @snapshot_clickhouse_queries + def test_ttl_days(self): + assert ttl_days(self.team) == 21 + + with self.is_cloud(True): + # Far enough in the future from `days_since_blob_ingestion` but not paid + with freeze_time("2023-09-01T12:00:01Z"): + assert ttl_days(self.team) == 30 + + self.team.organization.available_product_features = [ + {"key": AvailableFeature.RECORDINGS_PLAYLISTS, "name": AvailableFeature.RECORDINGS_PLAYLISTS} + ] + + # Far enough in the future from `days_since_blob_ingestion` but paid + with freeze_time("2023-12-01T12:00:01Z"): + assert ttl_days(self.team) == 90 + + # Not far enough in the future from `days_since_blob_ingestion` + with freeze_time("2023-09-05T12:00:01Z"): + assert ttl_days(self.team) == 35 + + @snapshot_clickhouse_queries + def test_filter_on_session_ids(self): + user = "test_session_ids-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + first_session_id = str(uuid4()) + second_session_id = str(uuid4()) + third_session_id = str(uuid4()) + + produce_replay_summary( + session_id=first_session_id, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(minutes=5)), + distinct_id=user, + first_url="https://example.io/home", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=59000, + ) + + produce_replay_summary( + session_id=second_session_id, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(minutes=1)), + distinct_id=user, + first_url="https://example.io/home", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=61000, + ) + + produce_replay_summary( + session_id=third_session_id, + team_id=self.team.pk, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(minutes=10)), + distinct_id=user, + first_url="https://example.io/home", + click_count=0, + keypress_count=0, + mouse_activity_count=0, + active_milliseconds=0, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "session_ids": [first_session_id], + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == first_session_id + + (session_recordings, _, _) = self._filter_recordings_by( + { + "session_ids": [first_session_id, second_session_id], + } + ) + + assert sorted([s["session_id"] for s in session_recordings]) == sorted( + [ + first_session_id, + second_session_id, + ] + ) + + @snapshot_clickhouse_queries + def test_event_filter_with_active_sessions( + self, + ): + user = "test_basic_query-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_total_is_61 = f"test_basic_query_active_sessions-total-{str(uuid4())}" + session_id_active_is_61 = f"test_basic_query_active_sessions-active-{str(uuid4())}" + + self.create_event( + user, + self.an_hour_ago, + properties={ + "$session_id": session_id_total_is_61, + "$window_id": str(uuid4()), + }, + ) + produce_replay_summary( + session_id=session_id_total_is_61, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=self.an_hour_ago.isoformat().replace("T", " "), + last_timestamp=(self.an_hour_ago + relativedelta(seconds=61)).isoformat().replace("T", " "), + distinct_id=user, + first_url="https://example.io/home", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=59000, + ) + + self.create_event( + user, + self.an_hour_ago, + properties={ + "$session_id": session_id_active_is_61, + "$window_id": str(uuid4()), + }, + ) + produce_replay_summary( + session_id=session_id_active_is_61, + team_id=self.team.pk, + # can CH handle a timestamp with no T + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=59)), + distinct_id=user, + first_url="https://a-different-url.com", + click_count=2, + keypress_count=2, + mouse_activity_count=2, + active_milliseconds=61000, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "having_predicates": '[{"type":"recording","key":"duration","value":60,"operator":"gt"}]', + } + ) + + assert [(s["session_id"], s["duration"], s["active_seconds"]) for s in session_recordings] == [ + (session_id_total_is_61, 61, 59.0) + ] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "having_predicates": '[{"type":"recording","key":"active_seconds","value":60,"operator":"gt"}]', + } + ) + + assert [(s["session_id"], s["duration"], s["active_seconds"]) for s in session_recordings] == [ + (session_id_active_is_61, 59, 61.0) + ] + + @also_test_with_materialized_columns(["$current_url", "$browser"]) + @snapshot_clickhouse_queries + def test_event_filter_with_properties(self): + user = "test_event_filter_with_properties-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + session_id_one = f"test_event_filter_with_properties-{str(uuid4())}" + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + user, + self.an_hour_ago, + properties={ + "$browser": "Chrome", + "$session_id": session_id_one, + "$window_id": str(uuid4()), + }, + ) + self.create_event( + user, + self.an_hour_ago, + event_name="a_different_event", + properties={ + "$browser": "Safari", + "$session_id": session_id_one, + "$window_id": str(uuid4()), + }, + ) + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "properties": [ + { + "key": "$browser", + "value": ["Chrome"], + "operator": "exact", + "type": "event", + } + ], + } + ] + } + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id_one + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "properties": [ + { + "key": "$browser", + "value": ["Firefox"], + "operator": "exact", + "type": "event", + } + ], + } + ] + } + ) + assert session_recordings == [] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "a_different_event", + "type": "events", + "order": 0, + "name": "a_different_event", + "properties": [ + { + "key": "$browser", + "value": ["Chrome"], + "operator": "exact", + "type": "event", + } + ], + } + ] + } + ) + assert len(session_recordings) == 0 + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "a_different_event", + "type": "events", + "order": 0, + "name": "a_different_event", + "properties": [ + { + "key": "$browser", + "value": ["Safari"], + "operator": "exact", + "type": "event", + } + ], + } + ] + } + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id_one + + @snapshot_clickhouse_queries + def test_multiple_event_filters(self): + session_id = f"test_multiple_event_filters-{str(uuid4())}" + user = "test_multiple_event_filters-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + + self.create_event( + user, + self.an_hour_ago, + properties={"$session_id": session_id, "$window_id": "1", "foo": "bar"}, + ) + self.create_event( + user, + self.an_hour_ago, + properties={"$session_id": session_id, "$window_id": "1", "bar": "foo"}, + ) + self.create_event( + user, + self.an_hour_ago, + properties={"$session_id": session_id, "$window_id": "1", "bar": "foo"}, + event_name="new-event", + ) + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + }, + { + "id": "new-event", + "type": "events", + "order": 0, + "name": "new-event", + }, + ] + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + }, + { + "id": "new-event2", + "type": "events", + "order": 0, + "name": "new-event2", + }, + ] + } + ) + assert session_recordings == [] + + # it uses hasAny instead of hasAll because of the OR filter + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + }, + { + "id": "new-event2", + "type": "events", + "order": 0, + "name": "new-event2", + }, + ], + "operand": "OR", + } + ) + assert len(session_recordings) == 1 + + # two events with the same name + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "name": "$pageview", + "properties": [{"key": "foo", "value": ["bar"], "operator": "exact", "type": "event"}], + }, + { + "id": "$pageview", + "type": "events", + "name": "$pageview", + "properties": [{"key": "bar", "value": ["foo"], "operator": "exact", "type": "event"}], + }, + ], + "operand": "AND", + } + ) + assert len(session_recordings) == 1 + + # two events with different names + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "name": "$pageview", + "properties": [{"key": "foo", "value": ["bar"], "operator": "exact", "type": "event"}], + }, + { + "id": "new-event", + "type": "events", + "name": "new-event", + "properties": [{"key": "foo", "value": ["bar"], "operator": "exact", "type": "event"}], + }, + ], + "operand": "AND", + } + ) + assert len(session_recordings) == 0 + + # two events with different names + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "name": "$pageview", + "properties": [{"key": "foo", "value": ["bar"], "operator": "exact", "type": "event"}], + }, + { + "id": "new-event", + "type": "events", + "name": "new-event", + "properties": [{"key": "foo", "value": ["bar"], "operator": "exact", "type": "event"}], + }, + ], + "operand": "OR", + } + ) + assert len(session_recordings) == 1 + + @snapshot_clickhouse_queries + @also_test_with_materialized_columns(["$session_id", "$browser"], person_properties=["email"]) + @freeze_time("2023-01-04") + def test_action_filter(self): + user = "test_action_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + session_id_one = f"test_action_filter-session-one" + window_id = "test_action_filter-window-id" + action_with_properties = self.create_action( + "custom-event", + properties=[ + {"key": "$browser", "value": "Firefox"}, + {"key": "$session_id", "value": session_id_one}, + {"key": "$window_id", "value": window_id}, + ], + ) + action_without_properties = self.create_action( + name="custom-event", + properties=[ + {"key": "$session_id", "value": session_id_one}, + {"key": "$window_id", "value": window_id}, + ], + ) + + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + user, + self.an_hour_ago, + event_name="custom-event", + properties={ + "$browser": "Chrome", + "$session_id": session_id_one, + "$window_id": window_id, + }, + ) + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "actions": [ + { + "id": action_with_properties.id, + "type": "actions", + "order": 1, + "name": "custom-event", + } + ] + } + ) + assert session_recordings == [] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "actions": [ + { + "id": action_without_properties.id, + "type": "actions", + "order": 1, + "name": "custom-event", + } + ] + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id_one + + # Adding properties to an action + (session_recordings, _, _) = self._filter_recordings_by( + { + "actions": [ + { + "id": action_without_properties.id, + "type": "actions", + "order": 1, + "name": "custom-event", + "properties": [ + { + "key": "$browser", + "value": ["Firefox"], + "operator": "exact", + "type": "event", + } + ], + } + ] + } + ) + assert session_recordings == [] + + # Adding matching properties to an action + (session_recordings, _, _) = self._filter_recordings_by( + { + "actions": [ + { + "id": action_without_properties.id, + "type": "actions", + "order": 1, + "name": "custom-event", + "properties": [ + { + "key": "$browser", + "value": ["Chrome"], + "operator": "exact", + "type": "event", + } + ], + } + ] + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id_one + + def test_all_sessions_recording_object_keys_with_entity_filter(self): + user = "test_all_sessions_recording_object_keys_with_entity_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + session_id = f"test_all_sessions_recording_object_keys_with_entity_filter-{str(uuid4())}" + window_id = str(uuid4()) + + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=60)), + team_id=self.team.id, + first_url="https://recieved-out-of-order.com/second", + ) + self.create_event( + user, + self.an_hour_ago, + properties={"$session_id": session_id, "$window_id": window_id}, + ) + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + first_url="https://recieved-out-of-order.com/first", + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ] + } + ) + + assert session_recordings == [ + { + "activity_score": 0, + "session_id": session_id, + "distinct_id": user, + "duration": 60, + "start_time": self.an_hour_ago, + "end_time": self.an_hour_ago + relativedelta(seconds=60), + "active_seconds": 0.0, + "click_count": 0, + "first_url": "https://recieved-out-of-order.com/first", + "inactive_seconds": 60.0, + "keypress_count": 0, + "mouse_activity_count": 0, + "team_id": self.team.id, + "console_log_count": 0, + "console_warn_count": 0, + "console_error_count": 0, + "ongoing": 1, + } + ] + + @snapshot_clickhouse_queries + def test_duration_filter(self): + user = "test_duration_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_one = "session one is 29 seconds long" + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=29)), + team_id=self.team.id, + ) + + session_id_two = "session two is 61 seconds long" + produce_replay_summary( + distinct_id=user, + session_id=session_id_two, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=61)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + {"having_predicates": '[{"type":"recording","key":"duration","value":60,"operator":"gt"}]'} + ) + assert [r["session_id"] for r in session_recordings] == [session_id_two] + + (session_recordings, _, _) = self._filter_recordings_by( + {"having_predicates": '[{"type":"recording","key":"duration","value":60,"operator":"lt"}]'} + ) + assert [r["session_id"] for r in session_recordings] == [session_id_one] + + @snapshot_clickhouse_queries + def test_operand_or_person_filters(self): + user = "test_operand_or_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "test@posthog.com"}) + + second_user = "test_operand_or_filter-second_user" + Person.objects.create(team=self.team, distinct_ids=[second_user], properties={"email": "david@posthog.com"}) + + session_id_one = "session_id_one" + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + session_id_two = "session_id_two" + produce_replay_summary( + distinct_id=second_user, + session_id=session_id_two, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + { + "key": "email", + "value": ["test@posthog.com"], + "operator": "exact", + "type": "person", + }, + { + "key": "email", + "value": ["david@posthog.com"], + "operator": "exact", + "type": "person", + }, + ], + "operand": "AND", + } + ) + assert len(session_recordings) == 0 + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + { + "key": "email", + "value": ["test@posthog.com"], + "operator": "exact", + "type": "person", + }, + { + "key": "email", + "value": ["david@posthog.com"], + "operator": "exact", + "type": "person", + }, + ], + "operand": "OR", + } + ) + assert len(session_recordings) == 2 + assert sorted([r["session_id"] for r in session_recordings]) == sorted([session_id_one, session_id_two]) + + @snapshot_clickhouse_queries + def test_operand_or_event_filters(self): + user = "test_operand_or_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "test@posthog.com"}) + + second_user = "test_operand_or_filter-second_user" + Person.objects.create(team=self.team, distinct_ids=[second_user], properties={"email": "david@posthog.com"}) + + session_id_one = "session_id_one" + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + self.create_event( + user, + self.an_hour_ago + relativedelta(seconds=10), + properties={"$session_id": session_id_one}, + ) + + session_id_two = "session_id_two" + produce_replay_summary( + distinct_id=second_user, + session_id=session_id_two, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + self.create_event( + user, + self.an_hour_ago + relativedelta(seconds=10), + event_name="custom_event", + properties={"$session_id": session_id_two}, + ) + + session_id_three = "session_id_three" + produce_replay_summary( + distinct_id=second_user, + session_id=session_id_three, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + }, + { + "id": "custom_event", + "type": "events", + "order": 0, + "name": "custom_event", + }, + ], + "operand": "AND", + } + ) + assert len(session_recordings) == 0 + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + }, + { + "id": "custom_event", + "type": "events", + "order": 0, + "name": "custom_event", + }, + ], + "operand": "OR", + } + ) + assert len(session_recordings) == 2 + assert sorted([r["session_id"] for r in session_recordings]) == sorted([session_id_two, session_id_one]) + + @snapshot_clickhouse_queries + def test_operand_or_filters(self): + user = "test_operand_or_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_with_both_log_filters = "both_log_filters" + produce_replay_summary( + distinct_id="user", + session_id=session_with_both_log_filters, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_warn_count=1, + log_messages={ + "warn": [ + "random", + ], + }, + ) + session_with_one_log_filter = "one_log_filter" + produce_replay_summary( + distinct_id="user", + session_id=session_with_one_log_filter, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_warn_count=1, + log_messages={ + "warn": [ + "warn", + ], + }, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_with_both_log_filters + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "operand": "OR", + } + ) + assert len(session_recordings) == 2 + + @snapshot_clickhouse_queries + def test_operand_or_mandatory_filters(self): + user = "test_operand_or_filter-user" + person = Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + second_user = "test_operand_or_filter-second_user" + second_person = Person.objects.create(team=self.team, distinct_ids=[second_user], properties={"email": "bla"}) + + session_id_one = "session_id_one" + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + self.create_event( + user, + self.an_hour_ago + relativedelta(seconds=10), + properties={"$session_id": session_id_one}, + ) + + session_id_two = "session_id_two" + produce_replay_summary( + distinct_id=second_user, + session_id=session_id_two, + first_timestamp=self.an_hour_ago, + last_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + # person or event filter -> person matches, event matches -> returns session + (session_recordings, _, _) = self._filter_recordings_by( + { + "person_uuid": str(person.uuid), + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "operand": "OR", + } + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id_one + + # person or event filter -> person does not match, event matches -> does not return session + (session_recordings, _, _) = self._filter_recordings_by( + { + "person_uuid": str(second_person.uuid), + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "operand": "OR", + } + ) + assert len(session_recordings) == 0 + + # session_id or event filter -> person matches, event matches -> returns session + (session_recordings, _, _) = self._filter_recordings_by( + { + "session_ids": [session_id_one], + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "operand": "OR", + } + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id_one + + # session_id or event filter -> person does not match, event matches -> does not return session + (session_recordings, _, _) = self._filter_recordings_by( + { + "session_ids": [session_id_two], + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "operand": "OR", + } + ) + assert len(session_recordings) == 0 + + @snapshot_clickhouse_queries + def test_date_from_filter(self): + user = "test_date_from_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + produce_replay_summary( + distinct_id=user, + session_id="three days before base time", + first_timestamp=(self.an_hour_ago - relativedelta(days=3, seconds=100)), + last_timestamp=(self.an_hour_ago - relativedelta(days=3)), + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=user, + session_id="two days before base time", + first_timestamp=(self.an_hour_ago - relativedelta(days=2, seconds=100)), + last_timestamp=(self.an_hour_ago - relativedelta(days=2)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by({"date_from": self.an_hour_ago.strftime("%Y-%m-%d")}) + assert session_recordings == [] + + (session_recordings, _, _) = self._filter_recordings_by( + {"date_from": (self.an_hour_ago - relativedelta(days=2)).strftime("%Y-%m-%d")} + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == "two days before base time" + + @snapshot_clickhouse_queries + def test_date_from_filter_cannot_search_before_ttl(self): + with freeze_time(self.an_hour_ago): + user = "test_date_from_filter_cannot_search_before_ttl-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + produce_replay_summary( + distinct_id=user, + session_id="storage is past ttl", + first_timestamp=(self.an_hour_ago - relativedelta(days=22)), + # an illegally long session but it started 22 days ago + last_timestamp=(self.an_hour_ago - relativedelta(days=3)), + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=user, + session_id="storage is not past ttl", + first_timestamp=(self.an_hour_ago - relativedelta(days=19)), + last_timestamp=(self.an_hour_ago - relativedelta(days=2)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + {"date_from": (self.an_hour_ago - relativedelta(days=20)).strftime("%Y-%m-%d")} + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == "storage is not past ttl" + + (session_recordings, _, _) = self._filter_recordings_by( + {"date_from": (self.an_hour_ago - relativedelta(days=21)).strftime("%Y-%m-%d")} + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == "storage is not past ttl" + + (session_recordings, _, _) = self._filter_recordings_by( + {"date_from": (self.an_hour_ago - relativedelta(days=22)).strftime("%Y-%m-%d")} + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == "storage is not past ttl" + + @snapshot_clickhouse_queries + def test_date_to_filter(self): + user = "test_date_to_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + produce_replay_summary( + distinct_id=user, + session_id="three days before base time", + first_timestamp=(self.an_hour_ago - relativedelta(days=3, seconds=100)), + last_timestamp=(self.an_hour_ago - relativedelta(days=3)), + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=user, + session_id="two days before base time", + first_timestamp=(self.an_hour_ago - relativedelta(days=2, seconds=100)), + last_timestamp=(self.an_hour_ago - relativedelta(days=2)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + {"date_to": (self.an_hour_ago - relativedelta(days=4)).strftime("%Y-%m-%d")} + ) + assert session_recordings == [] + + (session_recordings, _, _) = self._filter_recordings_by( + {"date_to": (self.an_hour_ago - relativedelta(days=3)).strftime("%Y-%m-%d")} + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == "three days before base time" + + def test_recording_that_spans_time_bounds(self): + user = "test_recording_that_spans_time_bounds-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + day_line = datetime(2021, 11, 5) + session_id = f"session-one-{user}" + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=(day_line - relativedelta(hours=3)), + last_timestamp=(day_line + relativedelta(hours=3)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "date_to": day_line.strftime("%Y-%m-%d"), + "date_from": (day_line - relativedelta(days=10)).strftime("%Y-%m-%d"), + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id + assert session_recordings[0]["duration"] == 6 * 60 * 60 + + @snapshot_clickhouse_queries + def test_person_id_filter(self): + three_user_ids = [str(uuid4()) for _ in range(3)] + session_id_one = f"test_person_id_filter-{str(uuid4())}" + session_id_two = f"test_person_id_filter-{str(uuid4())}" + p = Person.objects.create( + team=self.team, + distinct_ids=[three_user_ids[0], three_user_ids[1]], + properties={"email": "bla"}, + ) + produce_replay_summary( + distinct_id=three_user_ids[0], + session_id=session_id_one, + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=three_user_ids[1], + session_id=session_id_two, + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=three_user_ids[2], + session_id=str(uuid4()), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by({"person_uuid": str(p.uuid)}) + assert sorted([r["session_id"] for r in session_recordings]) == sorted([session_id_two, session_id_one]) + + @snapshot_clickhouse_queries + def test_all_filters_at_once(self): + three_user_ids = [str(uuid4()) for _ in range(3)] + target_session_id = f"test_all_filters_at_once-{str(uuid4())}" + + p = Person.objects.create( + team=self.team, + distinct_ids=[three_user_ids[0], three_user_ids[1]], + properties={"email": "bla"}, + ) + custom_event_action = self.create_action(name="custom-event") + + produce_replay_summary( + distinct_id=three_user_ids[0], + session_id=target_session_id, + first_timestamp=(self.an_hour_ago - relativedelta(days=3)), + team_id=self.team.id, + ) + produce_replay_summary( + # does not match because of user distinct id + distinct_id=three_user_ids[2], + session_id=target_session_id, + first_timestamp=(self.an_hour_ago - relativedelta(days=3)), + team_id=self.team.id, + ) + self.create_event( + three_user_ids[0], + self.an_hour_ago - relativedelta(days=3), + properties={"$session_id": target_session_id}, + ) + self.create_event( + three_user_ids[0], + self.an_hour_ago - relativedelta(days=3), + event_name="custom-event", + properties={"$browser": "Chrome", "$session_id": target_session_id}, + ) + produce_replay_summary( + distinct_id=three_user_ids[1], + session_id=target_session_id, + first_timestamp=(self.an_hour_ago - relativedelta(days=3) + relativedelta(hours=6)), + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=three_user_ids[1], + # does not match because of session id + session_id=str(uuid4()), + first_timestamp=(self.an_hour_ago - relativedelta(days=3) + relativedelta(hours=6)), + team_id=self.team.id, + ) + + flush_persons_and_events() + + (session_recordings, _, _) = self._filter_recordings_by( + { + "person_uuid": str(p.uuid), + "date_to": (self.an_hour_ago + relativedelta(days=3)).strftime("%Y-%m-%d"), + "date_from": (self.an_hour_ago - relativedelta(days=10)).strftime("%Y-%m-%d"), + "session_recording_duration": '{"type":"recording","key":"duration","value":60,"operator":"gt"}', + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "actions": [ + { + "id": custom_event_action.id, + "type": "actions", + "order": 1, + "name": "custom-event", + } + ], + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == target_session_id + + def test_teams_dont_leak_event_filter(self): + user = "test_teams_dont_leak_event_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + another_team = Team.objects.create(organization=self.organization) + + session_id = f"test_teams_dont_leak_event_filter-{str(uuid4())}" + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event(1, self.an_hour_ago + relativedelta(seconds=15), team=another_team) + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ] + } + ) + assert session_recordings == [] + + @snapshot_clickhouse_queries + @also_test_with_materialized_columns(person_properties=["email"]) + def test_filter_with_person_properties_exact(self): + session_id_one, session_id_two = self._two_sessions_two_persons( + "test_filter_with_person_properties_exact", + session_one_person_properties={"email": "bla@gmail.com"}, + session_two_person_properties={"email": "bla2@hotmail.com"}, + ) + + query_results: SessionRecordingQueryResult = self._filter_recordings_by( + { + "properties": [ + { + "key": "email", + "value": ["bla@gmail.com"], + "operator": "exact", + "type": "person", + } + ] + } + ) + + assert [x["session_id"] for x in query_results.results] == [session_id_one] + + @snapshot_clickhouse_queries + @also_test_with_materialized_columns(person_properties=["email"]) + def test_filter_with_person_properties_not_contains(self): + session_id_one, session_id_two = self._two_sessions_two_persons( + "test_filter_with_person_properties_not_contains", + session_one_person_properties={"email": "bla@gmail.com"}, + session_two_person_properties={"email": "bla2@hotmail.com"}, + ) + + query_results: SessionRecordingQueryResult = self._filter_recordings_by( + {"properties": [{"key": "email", "value": "gmail.com", "operator": "not_icontains", "type": "person"}]} + ) + + assert [x["session_id"] for x in query_results.results] == [session_id_two] + + def _two_sessions_two_persons( + self, label: str, session_one_person_properties: dict, session_two_person_properties: dict + ) -> tuple[str, str]: + sessions = [] + + for i in range(2): + user = f"{label}-user-{i}" + session = f"{label}-session-{i}" + sessions.append(session) + + Person.objects.create( + team=self.team, + distinct_ids=[user], + properties=session_one_person_properties if i == 0 else session_two_person_properties, + ) + + produce_replay_summary( + distinct_id=user, + session_id=session, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=user, + session_id=session, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + return sessions[0], sessions[1] + + @snapshot_clickhouse_queries + @also_test_with_materialized_columns(person_properties=["$some_prop"]) + def test_filter_with_cohort_properties(self): + with self.settings(USE_PRECALCULATED_CH_COHORT_PEOPLE=True): + with freeze_time("2021-08-21T20:00:00.000Z"): + user_one = "test_filter_with_cohort_properties-user" + user_two = "test_filter_with_cohort_properties-user2" + session_id_one = f"test_filter_with_cohort_properties-1-{str(uuid4())}" + session_id_two = f"test_filter_with_cohort_properties-2-{str(uuid4())}" + + Person.objects.create(team=self.team, distinct_ids=[user_one], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=[user_two], + properties={"email": "bla2", "$some_prop": "some_val"}, + ) + cohort = Cohort.objects.create( + team=self.team, + name="cohort1", + groups=[ + { + "properties": [ + { + "key": "$some_prop", + "value": "some_val", + "type": "person", + } + ] + } + ], + ) + cohort.calculate_people_ch(pending_version=0) + + produce_replay_summary( + distinct_id=user_one, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + # self.create_event(user_one, self.base_time, team=self.team) + produce_replay_summary( + distinct_id=user_one, + session_id=session_id_one, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=user_two, + session_id=session_id_two, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + # self.create_event(user_two, self.base_time, team=self.team) + produce_replay_summary( + distinct_id=user_two, + session_id=session_id_two, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + { + "key": "id", + "value": cohort.pk, + "operator": "in", + "type": "cohort", + } + ] + } + ) + + assert [x["session_id"] for x in session_recordings] == [session_id_two] + + @snapshot_clickhouse_queries + @also_test_with_materialized_columns(person_properties=["$some_prop"]) + def test_filter_with_static_and_dynamic_cohort_properties(self): + with self.settings(USE_PRECALCULATED_CH_COHORT_PEOPLE=True): + with freeze_time("2021-08-21T20:00:00.000Z"): + user_one = "test_filter_with_cohort_properties-user-in-static-cohort" + user_two = "test_filter_with_cohort_properties-user2-in-dynamic-cohort" + user_three = "test_filter_with_cohort_properties-user3-in-both-cohort" + + session_id_one = ( + f"in-static-cohort-test_filter_with_static_and_dynamic_cohort_properties-1-{str(uuid4())}" + ) + session_id_two = ( + f"in-dynamic-cohort-test_filter_with_static_and_dynamic_cohort_properties-2-{str(uuid4())}" + ) + session_id_three = ( + f"in-both-cohort-test_filter_with_static_and_dynamic_cohort_properties-3-{str(uuid4())}" + ) + + Person.objects.create(team=self.team, distinct_ids=[user_one], properties={"email": "in@static.cohort"}) + Person.objects.create( + team=self.team, + distinct_ids=[user_two], + properties={"email": "in@dynamic.cohort", "$some_prop": "some_val"}, + ) + Person.objects.create( + team=self.team, + distinct_ids=[user_three], + properties={"email": "in@both.cohorts", "$some_prop": "some_val"}, + ) + + dynamic_cohort = Cohort.objects.create( + team=self.team, + name="cohort1", + groups=[ + { + "properties": [ + { + "key": "$some_prop", + "value": "some_val", + "type": "person", + } + ] + } + ], + ) + + static_cohort = Cohort.objects.create(team=self.team, name="a static cohort", groups=[], is_static=True) + static_cohort.insert_users_by_list([user_one, user_three]) + + dynamic_cohort.calculate_people_ch(pending_version=0) + static_cohort.calculate_people_ch(pending_version=0) + + replay_summaries = [ + (user_one, session_id_one), + (user_two, session_id_two), + (user_three, session_id_three), + ] + for distinct_id, session_id in replay_summaries: + produce_replay_summary( + distinct_id=distinct_id, + session_id=session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=distinct_id, + session_id=session_id, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + { + "key": "id", + "value": static_cohort.pk, + "operator": "in", + "type": "cohort", + }, + ] + } + ) + + assert sorted([x["session_id"] for x in session_recordings]) == sorted( + [session_id_one, session_id_three] + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + { + "key": "id", + "value": dynamic_cohort.pk, + "operator": "in", + "type": "cohort", + }, + ] + } + ) + + assert sorted([x["session_id"] for x in session_recordings]) == sorted( + [session_id_two, session_id_three] + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + { + "key": "id", + "value": dynamic_cohort.pk, + "operator": "in", + "type": "cohort", + }, + { + "key": "id", + "value": static_cohort.pk, + "operator": "in", + "type": "cohort", + }, + ] + } + ) + + assert sorted([x["session_id"] for x in session_recordings]) == [session_id_three] + + @snapshot_clickhouse_queries + @also_test_with_materialized_columns(person_properties=["$some_prop"]) + def test_filter_with_events_and_cohorts(self): + with self.settings(USE_PRECALCULATED_CH_COHORT_PEOPLE=True): + with freeze_time("2021-08-21T20:00:00.000Z"): + user_one = "test_filter_with_events_and_cohorts-user" + user_two = "test_filter_with_events_and_cohorts-user2" + session_id_one = f"test_filter_with_events_and_cohorts-1-{str(uuid4())}" + session_id_two = f"test_filter_with_events_and_cohorts-2-{str(uuid4())}" + + Person.objects.create(team=self.team, distinct_ids=[user_one], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=[user_two], + properties={"email": "bla2", "$some_prop": "some_val"}, + ) + cohort = Cohort.objects.create( + team=self.team, + name="cohort1", + groups=[ + { + "properties": [ + { + "key": "$some_prop", + "value": "some_val", + "type": "person", + } + ] + } + ], + ) + cohort.calculate_people_ch(pending_version=0) + + produce_replay_summary( + distinct_id=user_one, + session_id=session_id_one, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + user_one, + self.an_hour_ago, + team=self.team, + event_name="custom_event", + properties={"$session_id": session_id_one}, + ) + produce_replay_summary( + distinct_id=user_one, + session_id=session_id_one, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=user_two, + session_id=session_id_two, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + user_two, + self.an_hour_ago, + team=self.team, + event_name="custom_event", + properties={"$session_id": session_id_two}, + ) + produce_replay_summary( + distinct_id=user_two, + session_id=session_id_two, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # has to be in the cohort and pageview has to be in the events + # test data has one user in the cohort but no pageviews + "properties": [ + { + "key": "id", + "value": cohort.pk, + "operator": "in", + "type": "cohort", + } + ], + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + } + ) + + assert len(session_recordings) == 0 + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + { + "key": "id", + "value": cohort.pk, + "operator": "in", + "type": "cohort", + } + ], + "events": [ + { + "id": "custom_event", + "type": "events", + "order": 0, + "name": "custom_event", + } + ], + } + ) + + assert [x["session_id"] for x in session_recordings] == [session_id_two] + + @snapshot_clickhouse_queries + @also_test_with_materialized_columns(["$current_url"]) + def test_event_filter_with_matching_on_session_id(self): + user_distinct_id = "test_event_filter_with_matching_on_session_id-user" + Person.objects.create(team=self.team, distinct_ids=[user_distinct_id], properties={"email": "bla"}) + session_id = f"test_event_filter_with_matching_on_session_id-1-{str(uuid4())}" + + self.create_event( + user_distinct_id, + self.an_hour_ago, + event_name="$pageview", + properties={"$session_id": session_id}, + ) + self.create_event( + user_distinct_id, + self.an_hour_ago, + event_name="$autocapture", + properties={"$session_id": str(uuid4())}, + ) + + produce_replay_summary( + distinct_id=user_distinct_id, + session_id=session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=user_distinct_id, + session_id=session_id, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ] + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$autocapture", + "type": "events", + "order": 0, + "name": "$autocapture", + } + ] + } + ) + assert session_recordings == [] + + @also_test_with_materialized_columns(event_properties=["$current_url", "$browser"], person_properties=["email"]) + @snapshot_clickhouse_queries + def test_event_filter_with_hogql_properties(self): + user = "test_event_filter_with_hogql_properties-user" + + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id = f"test_event_filter_with_hogql_properties-1-{str(uuid4())}" + self.create_event( + user, + self.an_hour_ago, + properties={ + "$browser": "Chrome", + "$session_id": session_id, + "$window_id": str(uuid4()), + }, + ) + + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "properties": [ + {"key": "properties.$browser == 'Chrome'", "type": "hogql"}, + ], + } + ] + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "properties": [{"key": "properties.$browser == 'Firefox'", "type": "hogql"}], + } + ] + } + ) + + assert session_recordings == [] + + @snapshot_clickhouse_queries + def test_event_filter_with_hogql_person_properties(self): + user = "test_event_filter_with_hogql_properties-user" + + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id = f"test_event_filter_with_hogql_properties-1-{str(uuid4())}" + self.create_event( + user, + self.an_hour_ago, + properties={ + "$browser": "Chrome", + "$session_id": session_id, + "$window_id": str(uuid4()), + }, + ) + + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "properties": [ + { + "key": "person.properties.email == 'bla'", + "type": "hogql", + }, + ], + } + ] + } + ) + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "properties": [ + { + "key": "person.properties.email == 'something else'", + "type": "hogql", + }, + ], + } + ] + } + ) + + assert session_recordings == [] + + @also_test_with_materialized_columns(["$current_url", "$browser"]) + @snapshot_clickhouse_queries + @freeze_time("2021-01-21T20:00:00.000Z") + def test_any_event_filter_with_properties(self): + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + + page_view_session_id = f"pageview-session-{str(uuid4())}" + my_custom_event_session_id = f"my-custom-event-session-{str(uuid4())}" + non_matching__event_session_id = f"non-matching-event-session-{str(uuid4())}" + + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$browser": "Chrome", + "$session_id": page_view_session_id, + "$window_id": "1", + }, + event_name="$pageview", + ) + + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$browser": "Chrome", + "$session_id": my_custom_event_session_id, + "$window_id": "1", + }, + event_name="my-custom-event", + ) + + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$browser": "Safari", + "$session_id": non_matching__event_session_id, + "$window_id": "1", + }, + event_name="my-non-matching-event", + ) + + produce_replay_summary( + distinct_id="user", + session_id=page_view_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id="user", + session_id=my_custom_event_session_id, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + produce_replay_summary( + distinct_id="user", + session_id=non_matching__event_session_id, + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + # an id of null means "match any event" + "id": None, + "type": "events", + "order": 0, + "name": "All events", + "properties": [], + } + ] + } + ) + + assert sorted( + [sr["session_id"] for sr in session_recordings], + ) == [ + my_custom_event_session_id, + non_matching__event_session_id, + page_view_session_id, + ] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + # an id of null means "match any event" + "id": None, + "type": "events", + "order": 0, + "name": "All events", + "properties": [ + { + "key": "$browser", + "value": ["Chrome"], + "operator": "exact", + "type": "event", + } + ], + } + ] + } + ) + + assert sorted( + [sr["session_id"] for sr in session_recordings], + ) == [ + my_custom_event_session_id, + page_view_session_id, + ] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": None, + "type": "events", + "order": 0, + "name": "All events", + "properties": [ + { + "key": "$browser", + "value": ["Firefox"], + "operator": "exact", + "type": "event", + } + ], + } + ] + } + ) + assert session_recordings == [] + + @snapshot_clickhouse_queries + @freeze_time("2021-01-21T20:00:00.000Z") + def test_filter_for_recordings_with_console_logs(self): + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + + with_logs_session_id = f"with-logs-session-{str(uuid4())}" + without_logs_session_id = f"no-logs-session-{str(uuid4())}" + + produce_replay_summary( + distinct_id="user", + session_id=with_logs_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_log_count=4, + log_messages={ + "info": [ + "info", + "info", + "info", + ], + }, + ) + + produce_replay_summary( + distinct_id="user", + session_id=without_logs_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + + # (session_recordings, _, _) = self._filter_recordings_by({"console_logs": ["info"]}) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + + actual = sorted( + [(sr["session_id"], sr["console_log_count"]) for sr in session_recordings], + key=lambda x: x[0], + ) + + assert actual == [ + (with_logs_session_id, 4), + ] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + assert session_recordings == [] + + @snapshot_clickhouse_queries + @freeze_time("2021-01-21T20:00:00.000Z") + def test_filter_for_recordings_with_console_warns(self): + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + + with_logs_session_id = f"with-logs-session-{str(uuid4())}" + without_logs_session_id = f"no-logs-session-{str(uuid4())}" + + produce_replay_summary( + distinct_id="user", + session_id=with_logs_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_warn_count=4, + log_messages={ + "warn": [ + "warn", + "warn", + "warn", + "warn", + ], + }, + ) + produce_replay_summary( + distinct_id="user", + session_id=without_logs_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + + assert sorted( + [(sr["session_id"], sr["console_warn_count"]) for sr in session_recordings], + key=lambda x: x[0], + ) == [ + (with_logs_session_id, 4), + ] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + + assert session_recordings == [] + + @snapshot_clickhouse_queries + @freeze_time("2021-01-21T20:00:00.000Z") + def test_filter_for_recordings_with_console_errors(self): + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + + with_logs_session_id = f"with-logs-session-{str(uuid4())}" + without_logs_session_id = f"no-logs-session-{str(uuid4())}" + + produce_replay_summary( + distinct_id="user", + session_id=with_logs_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_error_count=4, + log_messages={ + "error": [ + "error", + "error", + "error", + "error", + ], + }, + ) + produce_replay_summary( + distinct_id="user", + session_id=without_logs_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["error"], "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + + assert sorted( + [(sr["session_id"], sr["console_error_count"]) for sr in session_recordings], + key=lambda x: x[0], + ) == [ + (with_logs_session_id, 4), + ] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + + assert session_recordings == [] + + @snapshot_clickhouse_queries + @freeze_time("2021-01-21T20:00:00.000Z") + def test_filter_for_recordings_with_mixed_console_counts(self): + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + + with_logs_session_id = f"with-logs-session-{str(uuid4())}" + with_warns_session_id = f"with-warns-session-{str(uuid4())}" + with_errors_session_id = f"with-errors-session-{str(uuid4())}" + with_two_session_id = f"with-two-session-{str(uuid4())}" + + produce_replay_summary( + distinct_id="user", + session_id=with_logs_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_log_count=4, + log_messages={ + "info": [ + "info", + "info", + "info", + "info", + ], + }, + ) + produce_replay_summary( + distinct_id="user", + session_id=with_warns_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_warn_count=4, + log_messages={ + "warn": [ + "warn", + "warn", + "warn", + "warn", + ], + }, + ) + produce_replay_summary( + distinct_id="user", + session_id=with_errors_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_error_count=4, + log_messages={ + "error": [ + "error", + "error", + "error", + "error", + ], + }, + ) + produce_replay_summary( + distinct_id="user", + session_id=with_two_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_error_count=4, + console_log_count=3, + log_messages={ + "error": [ + "error", + "error", + "error", + "error", + ], + "info": [ + "info", + "info", + "info", + ], + }, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + + assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( + [ + with_errors_session_id, + with_two_session_id, + with_warns_session_id, + ] + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + + assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( + [ + with_two_session_id, + with_logs_session_id, + ] + ) + + @snapshot_clickhouse_queries + @freeze_time("2021-01-21T20:00:00.000Z") + def test_filter_for_recordings_by_console_text(self): + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + + with_logs_session_id = "with-logs-session" + with_warns_session_id = "with-warns-session" + with_errors_session_id = "with-errors-session" + with_two_session_id = "with-two-session" + + produce_replay_summary( + distinct_id="user", + session_id=with_logs_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_log_count=4, + log_messages={ + "info": [ + "log message 1", + "log message 2", + "log message 3", + "log message 4", + ] + }, + ) + produce_replay_summary( + distinct_id="user", + session_id=with_warns_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_warn_count=5, + log_messages={ + "warn": [ + "warn message 1", + "warn message 2", + "warn message 3", + "warn message 4", + "warn message 5", + ] + }, + ) + produce_replay_summary( + distinct_id="user", + session_id=with_errors_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_error_count=4, + log_messages={ + "error": [ + "error message 1", + "error message 2", + "error message 3", + "error message 4", + ] + }, + ) + produce_replay_summary( + distinct_id="user", + session_id=with_two_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_error_count=4, + console_log_count=3, + log_messages={ + "error": [ + "error message 1", + "error message 2", + "error message 3", + "error message 4", + ], + "info": ["log message 1", "log message 2", "log message 3"], + }, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # there are 5 warn and 4 error logs, message 4 matches in both + "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 4", "operator": "exact", "type": "log_entry"}]', + "operand": "OR", + } + ) + + assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( + [ + with_errors_session_id, + with_two_session_id, + with_warns_session_id, + ] + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # there are 5 warn and 4 error logs, message 5 matches only matches in warn + "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', + "operand": "AND", + } + ) + + assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( + [ + with_warns_session_id, + ] + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # message 5 does not match log level "info" + "console_log_filters": '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', + "operand": "AND", + } + ) + + assert sorted([sr["session_id"] for sr in session_recordings]) == [] + + @snapshot_clickhouse_queries + def test_filter_for_recordings_by_snapshot_source(self): + user = "test_duration_filter-user" + Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) + + session_id_one = "session one id" + produce_replay_summary( + distinct_id=user, + session_id=session_id_one, + team_id=self.team.id, + snapshot_source="web", + ) + + session_id_two = "session two id" + produce_replay_summary( + distinct_id=user, + session_id=session_id_two, + team_id=self.team.id, + snapshot_source="mobile", + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "having_predicates": '[{"key": "snapshot_source", "value": ["web"], "operator": "exact", "type": "recording"}]' + } + ) + assert [r["session_id"] for r in session_recordings] == [session_id_one] + + (session_recordings, _, _) = self._filter_recordings_by( + { + "having_predicates": '[{"key": "snapshot_source", "value": ["mobile"], "operator": "exact", "type": "recording"}]' + } + ) + assert [r["session_id"] for r in session_recordings] == [session_id_two] + + @also_test_with_materialized_columns( + event_properties=["is_internal_user"], + person_properties=["email"], + verify_no_jsonextract=False, + ) + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_event_filter_with_test_accounts_excluded(self): + self.team.test_account_filters = [ + { + "key": "email", + "value": "@posthog.com", + "operator": "not_icontains", + "type": "person", + }, + { + "key": "is_internal_user", + "value": ["false"], + "operator": "exact", + "type": "event", + }, + {"key": "properties.$browser == 'Chrome'", "type": "hogql"}, + ] + self.team.save() + + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": "1", + "$window_id": "1", + "is_internal_user": "true", + }, + ) + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": True, + } + ) + self.assertEqual(len(session_recordings), 0) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": False, + } + ) + self.assertEqual(len(session_recordings), 1) + + @also_test_with_materialized_columns( + event_properties=["$browser"], + person_properties=["email"], + verify_no_jsonextract=False, + ) + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_event_filter_with_hogql_event_properties_test_accounts_excluded(self): + self.team.test_account_filters = [ + {"key": "properties.$browser == 'Chrome'", "type": "hogql"}, + ] + self.team.save() + + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=["user2"], + properties={"email": "not-the-other-one"}, + ) + + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user", + self.an_hour_ago, + properties={"$session_id": "1", "$window_id": "1", "$browser": "Chrome"}, + ) + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + produce_replay_summary( + distinct_id="user2", + session_id="2", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user2", + self.an_hour_ago, + properties={"$session_id": "2", "$window_id": "1", "$browser": "Firefox"}, + ) + + # there are 2 pageviews + (session_recordings, _, _) = self._filter_recordings_by( + { + # pageview that matches the hogql test_accounts filter + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": False, + } + ) + self.assertEqual(len(session_recordings), 2) + + self.team.test_account_filters = [ + {"key": "person.properties.email == 'bla'", "type": "hogql"}, + ] + self.team.save() + + (session_recordings, _, _) = self._filter_recordings_by( + { + # only 1 pageview that matches the hogql test_accounts filter + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": True, + } + ) + self.assertEqual(len(session_recordings), 1) + + self.team.test_account_filters = [ + {"key": "properties.$browser == 'Chrome'", "type": "hogql"}, + {"key": "person.properties.email == 'bla'", "type": "hogql"}, + ] + self.team.save() + + # one user sessions matches the person + event test_account filter + (session_recordings, _, _) = self._filter_recordings_by( + { + "filter_test_accounts": True, + } + ) + self.assertEqual(len(session_recordings), 1) + + # TRICKY: we had to disable use of materialized columns for part of the query generation + # due to RAM usage issues on the EU cluster + @also_test_with_materialized_columns(event_properties=["is_internal_user"], verify_no_jsonextract=False) + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_top_level_event_property_test_account_filter(self): + """ + This is a regression test. A user with an $ip test account filter + reported the filtering wasn't working. + + The filter wasn't triggering the "should join events check", and so we didn't apply the filter at all + """ + self.team.test_account_filters = [ + { + "key": "is_internal_user", + "value": ["false"], + "operator": "exact", + "type": "event", + }, + ] + self.team.save() + + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=["user2"], + properties={"email": "not-the-other-one"}, + ) + + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": "1", + "$window_id": "1", + "is_internal_user": False, + }, + ) + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + produce_replay_summary( + distinct_id="user2", + session_id="2", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user2", + self.an_hour_ago, + properties={ + "$session_id": "2", + "$window_id": "1", + "is_internal_user": True, + }, + ) + + # there are 2 pageviews + (session_recordings, _, _) = self._filter_recordings_by( + { + # pageview that matches the hogql test_accounts filter + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": False, + } + ) + self.assertEqual(len(session_recordings), 2) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # only 1 pageview that matches the test_accounts filter + "filter_test_accounts": True, + } + ) + self.assertEqual(len(session_recordings), 1) + + # TRICKY: we had to disable use of materialized columns for part of the query generation + # due to RAM usage issues on the EU cluster + @also_test_with_materialized_columns(event_properties=["is_internal_user"], verify_no_jsonextract=True) + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_top_level_event_property_test_account_filter_allowing_denormalized_props(self): + """ + This is a duplicate of the test test_top_level_event_property_test_account_filter + but with denormalized props allowed + """ + + with self.settings(ALLOW_DENORMALIZED_PROPS_IN_LISTING=True): + self.team.test_account_filters = [ + { + "key": "is_internal_user", + "value": ["false"], + "operator": "exact", + "type": "event", + }, + ] + self.team.save() + + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=["user2"], + properties={"email": "not-the-other-one"}, + ) + + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": "1", + "$window_id": "1", + "is_internal_user": False, + }, + ) + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + produce_replay_summary( + distinct_id="user2", + session_id="2", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user2", + self.an_hour_ago, + properties={ + "$session_id": "2", + "$window_id": "1", + "is_internal_user": True, + }, + ) + + # there are 2 pageviews + (session_recordings, _, _) = self._filter_recordings_by( + { + # pageview that matches the hogql test_accounts filter + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": False, + } + ) + self.assertEqual(len(session_recordings), 2) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # only 1 pageview that matches the test_accounts filter + "filter_test_accounts": True, + } + ) + self.assertEqual(len(session_recordings), 1) + + @also_test_with_materialized_columns(event_properties=["is_internal_user"]) + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_top_level_hogql_event_property_test_account_filter(self): + """ + This is a regression test. A user with an $ip test account filter + reported the filtering wasn't working. + + The filter wasn't triggering the "should join events" check, and so we didn't apply the filter at all + """ + self.team.test_account_filters = [ + {"key": "properties.is_internal_user == 'true'", "type": "hogql"}, + ] + self.team.save() + + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=["user2"], + properties={"email": "not-the-other-one"}, + ) + + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": "1", + "$window_id": "1", + "is_internal_user": False, + }, + ) + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + produce_replay_summary( + distinct_id="user2", + session_id="2", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user2", + self.an_hour_ago, + properties={ + "$session_id": "2", + "$window_id": "1", + "is_internal_user": True, + }, + ) + + # there are 2 pageviews + (session_recordings, _, _) = self._filter_recordings_by( + { + # pageview that matches the hogql test_accounts filter + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": False, + } + ) + self.assertEqual(len(session_recordings), 2) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # only 1 pageview that matches the test_accounts filter + "filter_test_accounts": True, + } + ) + self.assertEqual(len(session_recordings), 1) + + @also_test_with_materialized_columns(person_properties=["email"], verify_no_jsonextract=False) + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_top_level_hogql_person_property_test_account_filter(self): + """ + This is a regression test. A user with an $ip test account filter + reported the filtering wasn't working. + + The filter wasn't triggering the "should join events" check, and so we didn't apply the filter at all + """ + self.team.test_account_filters = [ + {"key": "person.properties.email == 'bla'", "type": "hogql"}, + ] + self.team.save() + + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=["user2"], + properties={"email": "not-the-other-one"}, + ) + + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": "1", + "$window_id": "1", + "is_internal_user": False, + }, + ) + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + produce_replay_summary( + distinct_id="user2", + session_id="2", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user2", + self.an_hour_ago, + properties={ + "$session_id": "2", + "$window_id": "1", + "is_internal_user": True, + }, + ) + + # there are 2 pageviews + (session_recordings, _, _) = self._filter_recordings_by( + { + # pageview that matches the hogql test_accounts filter + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": False, + } + ) + self.assertEqual(len(session_recordings), 2) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # only 1 pageview that matches the test_accounts filter + "filter_test_accounts": True, + } + ) + self.assertEqual(len(session_recordings), 1) + + @also_test_with_materialized_columns(person_properties=["email"], verify_no_jsonextract=False) + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_top_level_person_property_test_account_filter(self): + """ + This is a regression test. A user with an $ip test account filter + reported the filtering wasn't working. + + The filter wasn't triggering the "should join events" check, and so we didn't apply the filter at all + """ + self.team.test_account_filters = [{"key": "email", "value": ["bla"], "operator": "exact", "type": "person"}] + self.team.save() + + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=["user2"], + properties={"email": "not-the-other-one"}, + ) + + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": "1", + "$window_id": "1", + "is_internal_user": False, + }, + ) + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + ) + + produce_replay_summary( + distinct_id="user2", + session_id="2", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ) + self.create_event( + "user2", + self.an_hour_ago, + properties={ + "$session_id": "2", + "$window_id": "1", + "is_internal_user": True, + }, + ) + + # there are 2 pageviews + (session_recordings, _, _) = self._filter_recordings_by( + { + # pageview that matches the hogql test_accounts filter + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": False, + } + ) + self.assertEqual(len(session_recordings), 2) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # only 1 pageview that matches the test_accounts filter + "filter_test_accounts": True, + } + ) + self.assertEqual(len(session_recordings), 1) + + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_event_filter_with_two_events_and_multiple_teams(self): + another_team = Team.objects.create(organization=self.organization) + + # two teams, user with the same properties + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + Person.objects.create(team=another_team, distinct_ids=["user"], properties={"email": "bla"}) + + # a recording session with a pageview and a pageleave + self._a_session_with_two_events(self.team, "1") + self._a_session_with_two_events(another_team, "2") + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + }, + { + "id": "$pageleave", + "type": "events", + "order": 0, + "name": "$pageleave", + }, + ], + } + ) + + self.assertEqual([sr["session_id"] for sr in session_recordings], ["1"]) + + def _a_session_with_two_events(self, team: Team, session_id: str) -> None: + produce_replay_summary( + distinct_id="user", + session_id=session_id, + first_timestamp=self.an_hour_ago, + team_id=team.pk, + ) + self.create_event( + "user", + self.an_hour_ago, + team=team, + event_name="$pageview", + properties={"$session_id": session_id, "$window_id": "1"}, + ) + self.create_event( + "user", + self.an_hour_ago, + team=team, + event_name="$pageleave", + properties={"$session_id": session_id, "$window_id": "1"}, + ) + + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_event_filter_with_group_filter(self): + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + session_id = f"test_event_filter_with_group_filter-ONE-{uuid4()}" + different_group_session = f"test_event_filter_with_group_filter-TWO-{uuid4()}" + + produce_replay_summary( + distinct_id="user", + session_id=session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.pk, + ) + produce_replay_summary( + distinct_id="user", + session_id=different_group_session, + first_timestamp=self.an_hour_ago, + team_id=self.team.pk, + ) + + GroupTypeMapping.objects.create( + team=self.team, project_id=self.team.project_id, group_type="project", group_type_index=0 + ) + create_group( + team_id=self.team.pk, + group_type_index=0, + group_key="project:1", + properties={"name": "project one"}, + ) + + GroupTypeMapping.objects.create( + team=self.team, project_id=self.team.project_id, group_type="organization", group_type_index=1 + ) + create_group( + team_id=self.team.pk, + group_type_index=1, + group_key="org:1", + properties={"name": "org one"}, + ) + + self.create_event( + "user", + self.an_hour_ago, + team=self.team, + event_name="$pageview", + properties={ + "$session_id": session_id, + "$window_id": "1", + "$group_1": "org:1", + }, + ) + self.create_event( + "user", + self.an_hour_ago, + team=self.team, + event_name="$pageview", + properties={ + "$session_id": different_group_session, + "$window_id": "1", + "$group_0": "project:1", + }, + ) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "properties": [ + { + "key": "name", + "value": ["org one"], + "operator": "exact", + "type": "group", + "group_type_index": 1, + } + ], + } + ], + } + ) + + self.assertEqual([sr["session_id"] for sr in session_recordings], [session_id]) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + { + "key": "name", + "value": ["org one"], + "operator": "exact", + "type": "group", + "group_type_index": 1, + } + ], + } + ) + self.assertEqual([sr["session_id"] for sr in session_recordings], [session_id]) + + (session_recordings, _, _) = self._filter_recordings_by( + { + "properties": [ + { + "key": "name", + "value": ["org one"], + "operator": "exact", + "type": "group", + "group_type_index": 2, + } + ], + } + ) + self.assertEqual(session_recordings, []) + + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_ordering(self): + session_id_one = f"test_ordering-one" + session_id_two = f"test_ordering-two" + session_id_three = f"test_ordering-three" + + produce_replay_summary( + session_id=session_id_one, + team_id=self.team.id, + mouse_activity_count=50, + first_timestamp=(self.an_hour_ago + relativedelta(seconds=60)), + ) + produce_replay_summary( + session_id=session_id_two, + team_id=self.team.id, + mouse_activity_count=100, + first_timestamp=(self.an_hour_ago), + ) + produce_replay_summary( + session_id=session_id_three, + team_id=self.team.id, + mouse_activity_count=10, + first_timestamp=(self.an_hour_ago + relativedelta(minutes=10)), + ) + + (session_recordings, _, _) = self._filter_recordings_by({"order": "start_time"}) + assert [r["session_id"] for r in session_recordings] == [session_id_three, session_id_one, session_id_two] + + (session_recordings, _, _) = self._filter_recordings_by({"order": "mouse_activity_count"}) + assert [r["session_id"] for r in session_recordings] == [session_id_two, session_id_one, session_id_three] + + @also_test_with_materialized_columns(event_properties=["$host"], verify_no_jsonextract=False) + @freeze_time("2021-01-21T20:00:00.000Z") + @snapshot_clickhouse_queries + def test_top_level_event_host_property_test_account_filter(self): + """ + This is a regression test. See: https://posthoghelp.zendesk.com/agent/tickets/18059 + """ + self.team.test_account_filters = [ + {"key": "$host", "type": "event", "value": "^(localhost|127\\.0\\.0\\.1)($|:)", "operator": "not_regex"}, + ] + self.team.save() + + Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) + Person.objects.create( + team=self.team, + distinct_ids=["user2"], + properties={"email": "not-the-other-one"}, + ) + + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + ensure_analytics_event_in_session=False, + ) + # the session needs to have multiple matching or not matching events + for _ in range(10): + self.create_event( + "user", + self.an_hour_ago, + properties={ + "$session_id": "1", + "$window_id": "1", + "$host": "localhost", + }, + ) + + produce_replay_summary( + distinct_id="user", + session_id="1", + first_timestamp=self.an_hour_ago + relativedelta(seconds=30), + team_id=self.team.id, + click_count=10, + ensure_analytics_event_in_session=False, + ) + + for _ in range(10): + self.create_event( + "user2", + self.an_hour_ago, + properties={ + "$session_id": "2", + "$window_id": "1", + "$host": "example.com", + }, + ) + produce_replay_summary( + distinct_id="user2", + session_id="2", + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + click_count=10, + ensure_analytics_event_in_session=False, + ) + + # there are 2 pageviews + (session_recordings, _, _) = self._filter_recordings_by( + { + # pageview that matches the hogql test_accounts filter + "events": [ + { + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + } + ], + "filter_test_accounts": False, + } + ) + self.assertEqual(len(session_recordings), 2) + + (session_recordings, _, _) = self._filter_recordings_by( + { + # only 1 pageview that matches the test_accounts filter + "filter_test_accounts": True, + } + ) + assert session_recordings == [ + { + "active_seconds": 0.0, + "activity_score": 0.28, + "click_count": 10, # in the bug this value was 10 X number of events in the session + "console_error_count": 0, + "console_log_count": 0, + "console_warn_count": 0, + "distinct_id": "user2", + "duration": 3600, + "end_time": ANY, + "first_url": "https://not-provided-by-test.com", + "inactive_seconds": 3600.0, + "keypress_count": 0, + "mouse_activity_count": 0, + "session_id": "2", + "start_time": ANY, + "team_id": self.team.id, + "ongoing": 1, + } + ] diff --git a/posthog/session_recordings/session_recording_api.py b/posthog/session_recordings/session_recording_api.py index 6a5f211b23851..91c77804061d8 100644 --- a/posthog/session_recordings/session_recording_api.py +++ b/posthog/session_recordings/session_recording_api.py @@ -38,7 +38,7 @@ ClickHouseSustainedRateThrottle, PersonalApiKeyRateThrottle, ) -from posthog.schema import HogQLQueryModifiers, QueryTiming +from posthog.schema import HogQLQueryModifiers, QueryTiming, RecordingsQuery from posthog.session_recordings.models.session_recording import SessionRecording from posthog.session_recordings.models.session_recording_event import ( SessionRecordingViewed, @@ -47,6 +47,7 @@ ReplayFiltersEventsSubQuery, SessionRecordingListFromFilters, ) +from posthog.session_recordings.queries.session_recording_list_from_query import SessionRecordingListFromQuery from posthog.session_recordings.queries.session_recording_properties import ( SessionRecordingProperties, ) @@ -242,10 +243,8 @@ def validate(self, data): return data -def list_recordings_response( - filter: SessionRecordingsFilter, request: request.Request, serializer_context: dict[str, Any] -) -> Response: - (recordings, timings) = list_recordings(filter, request, context=serializer_context) +def list_recordings_response(listing_result: tuple[dict, dict]) -> Response: + (recordings, timings) = listing_result response = Response(recordings) response.headers["Server-Timing"] = ", ".join( f"{key};dur={round(duration, ndigits=2)}" for key, duration in timings.items() @@ -296,6 +295,26 @@ class SnapshotsSustainedRateThrottle(PersonalApiKeyRateThrottle): rate = "600/hour" +def query_as_params_to_dict(params_dict: dict) -> dict: + """ + before (if ever) we convert this to a query runner that takes a post + we need to convert to a valid dict from the data that arrived in query params + """ + converted = { + **params_dict, + "console_log_filters": json.loads(params_dict.get("console_log_filters", "[]")), + "events": json.loads(params_dict.get("events", "[]")), + "actions": json.loads(params_dict.get("actions", "[]")), + "having_predicates": json.loads(params_dict.get("having_predicates", "{}")), + "properties": json.loads(params_dict.get("properties", "[]")), + } + if "user_modified_filters" in params_dict: + converted["user_modified_filters"] = json.loads(params_dict["user_modified_filters"]) + + converted.pop("as_query", None) + return converted + + # NOTE: Could we put the sharing stuff in the shared mixin :thinking: class SessionRecordingViewSet(TeamAndOrgViewSetMixin, viewsets.GenericViewSet, UpdateModelMixin): scope_object = "session_recording" @@ -324,11 +343,17 @@ def safely_get_object(self, queryset) -> SessionRecording: def list(self, request: request.Request, *args: Any, **kwargs: Any) -> Response: use_query_type = request.GET.get("as_query", False) if use_query_type: - raise ValueError("as_query is not supported for session recordings") + data_dict = query_as_params_to_dict(request.GET.dict()) + query = RecordingsQuery.model_validate(data_dict) + # a little duplication for now + self._maybe_report_recording_list_filters_changed(request, team=self.team) + return list_recordings_response( + list_recordings_from_query(query, request, context=self.get_serializer_context()) + ) else: filter = SessionRecordingsFilter(request=request, team=self.team) self._maybe_report_recording_list_filters_changed(request, team=self.team) - return list_recordings_response(filter, request, self.get_serializer_context()) + return list_recordings_response(list_recordings(filter, request, context=self.get_serializer_context())) @extend_schema( exclude=True, @@ -859,6 +884,107 @@ def _send_realtime_snapshots_to_client( raise exceptions.ValidationError(f"Invalid version: {version}") +# TODO i guess this becomes the query runner for our _internal_ use of RecordingsQuery +def list_recordings_from_query( + query: RecordingsQuery, request: request.Request, context: dict[str, Any] +) -> tuple[dict, dict]: + """ + As we can store recordings in S3 or in Clickhouse we need to do a few things here + + A. If filter.session_ids is specified: + 1. We first try to load them directly from Postgres if they have been persisted to S3 (they might have fell out of CH) + 2. Any that couldn't be found are then loaded from Clickhouse + B. Otherwise we just load all values from Clickhouse + 2. Once loaded we convert them to SessionRecording objects in case we have any other persisted data + """ + + all_session_ids = query.session_ids + + recordings: list[SessionRecording] = [] + more_recordings_available = False + team = context["get_team"]() + hogql_timings: list[QueryTiming] | None = None + + timer = ServerTimingsGathered() + + if all_session_ids: + with timer("load_persisted_recordings"): + # If we specify the session ids (like from pinned recordings) we can optimise by only going to Postgres + sorted_session_ids = sorted(all_session_ids) + + persisted_recordings_queryset = SessionRecording.objects.filter( + team=team, session_id__in=sorted_session_ids + ).exclude(object_storage_path=None) + + persisted_recordings = persisted_recordings_queryset.all() + + recordings = recordings + list(persisted_recordings) + + remaining_session_ids = list(set(all_session_ids) - {x.session_id for x in persisted_recordings}) + query.session_ids = remaining_session_ids + + if (all_session_ids and query.session_ids) or not all_session_ids: + distinct_id = str(cast(User, request.user).distinct_id) + modifiers = safely_read_modifiers_overrides(distinct_id, team) + + with timer("load_recordings_from_hogql"): + (ch_session_recordings, more_recordings_available, hogql_timings) = SessionRecordingListFromQuery( + query=query, team=team, hogql_query_modifiers=modifiers + ).run() + + with timer("build_recordings"): + recordings_from_clickhouse = SessionRecording.get_or_build_from_clickhouse(team, ch_session_recordings) + recordings = recordings + recordings_from_clickhouse + + recordings = [x for x in recordings if not x.deleted] + + # If we have specified session_ids we need to sort them by the order they were specified + if all_session_ids: + recordings = sorted( + recordings, + key=lambda x: cast(list[str], all_session_ids).index(x.session_id), + ) + + if not request.user.is_authenticated: # for mypy + raise exceptions.NotAuthenticated() + + # Update the viewed status for all loaded recordings + with timer("load_viewed_recordings"): + viewed_session_recordings = set( + SessionRecordingViewed.objects.filter(team=team, user=request.user).values_list("session_id", flat=True) + ) + + with timer("load_persons"): + # Get the related persons for all the recordings + distinct_ids = sorted([x.distinct_id for x in recordings]) + person_distinct_ids = PersonDistinctId.objects.filter(distinct_id__in=distinct_ids, team=team).select_related( + "person" + ) + + with timer("process_persons"): + distinct_id_to_person = {} + for person_distinct_id in person_distinct_ids: + person_distinct_id.person._distinct_ids = [ + person_distinct_id.distinct_id + ] # Stop the person from loading all distinct ids + distinct_id_to_person[person_distinct_id.distinct_id] = person_distinct_id.person + + for recording in recordings: + recording.viewed = recording.session_id in viewed_session_recordings + person = distinct_id_to_person.get(recording.distinct_id) + if person: + recording.person = person + + session_recording_serializer = SessionRecordingSerializer(recordings, context=context, many=True) + results = session_recording_serializer.data + + all_timings = _generate_timings(hogql_timings, timer) + return ( + {"results": results, "has_next": more_recordings_available, "version": 4}, + all_timings, + ) + + def list_recordings( filter: SessionRecordingsFilter, request: request.Request, context: dict[str, Any] ) -> tuple[dict, dict]: diff --git a/posthog/session_recordings/test/test_session_recordings.py b/posthog/session_recordings/test/test_session_recordings.py index e1be77900953d..cb09beccd6537 100644 --- a/posthog/session_recordings/test/test_session_recordings.py +++ b/posthog/session_recordings/test/test_session_recordings.py @@ -169,15 +169,17 @@ def test_can_list_recordings_even_when_the_person_has_multiple_distinct_ids(self assert results_[0]["distinct_id"] == "user2" assert results_[1]["distinct_id"] in twelve_distinct_ids + @parameterized.expand([[True], [False]]) @patch("posthoganalytics.capture") @patch("posthog.session_recordings.session_recording_api.SessionRecordingListFromFilters") - def test_console_log_filters_are_correctly_passed_to_listing(self, mock_summary_lister, mock_capture): + def test_console_log_filters_are_correctly_passed_to_listing(self, as_query, mock_summary_lister, mock_capture): mock_summary_lister.return_value.run.return_value = ([], False) params_string = urlencode( { "console_log_filters": '[{"key": "console_log_level", "value": ["warn", "error"], "operator": "exact", "type": "recording"}]', "user_modified_filters": '{"my_filter": "something"}', + "as_query": as_query, } ) self.client.get(f"/api/projects/{self.team.id}/session_recordings?{params_string}") From 647faa91b09f7547364c934c7b3220d3020aeefc Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 14:11:01 +0000 Subject: [PATCH 08/17] like this? --- posthog/hogql/printer.py | 6 +- .../session_recording_list_from_query.py | 122 ++++++----- ...est_session_recording_list_from_query.ambr | 205 ++++++++++++++++++ .../test_session_recording_list_from_query.py | 7 +- .../session_recording_api.py | 15 +- .../test/test_session_recordings.py | 47 +++- 6 files changed, 327 insertions(+), 75 deletions(-) create mode 100644 posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr diff --git a/posthog/hogql/printer.py b/posthog/hogql/printer.py index da81bdd32d37f..9e031a53f3481 100644 --- a/posthog/hogql/printer.py +++ b/posthog/hogql/printer.py @@ -617,7 +617,11 @@ def visit_lambda(self, node: ast.Lambda): return f"({', '.join(identifiers)}) -> {self.visit(node.expr)}" def visit_order_expr(self, node: ast.OrderExpr): - return f"{self.visit(node.expr)} {node.order}" + try: + return f"{self.visit(node.expr)} {node.order}" + except: + breakpoint() + return "" def __get_optimized_property_group_compare_operation(self, node: ast.CompareOperation) -> str | None: """ diff --git a/posthog/session_recordings/queries/session_recording_list_from_query.py b/posthog/session_recordings/queries/session_recording_list_from_query.py index 340e11422a927..703dfd11557e1 100644 --- a/posthog/session_recordings/queries/session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/session_recording_list_from_query.py @@ -7,7 +7,7 @@ from posthog.hogql import ast from posthog.hogql.ast import CompareOperation from posthog.hogql.parser import parse_select -from posthog.hogql.property import entity_to_expr, property_to_expr +from posthog.hogql.property import property_to_expr from posthog.hogql.query import execute_hogql_query from posthog.hogql_queries.insights.paginators import HogQLHasMorePaginator from posthog.models import Team, Property @@ -16,7 +16,6 @@ from posthog.models.property import PropertyGroup from posthog.schema import QueryTiming, HogQLQueryModifiers, PersonsOnEventsMode, RecordingsQuery from posthog.session_recordings.queries.session_replay_events import ttl_days -from posthog.constants import TREND_FILTER_TYPE_ACTIONS import structlog @@ -170,12 +169,12 @@ def get_query(self): ), "order_by": self._order_by_clause(), "where_predicates": self._where_predicates(), - "having_predicates": self._having_predicates(), + "having_predicates": self._having_predicates() or ast.Constant(value=True), }, ) def _order_by_clause(self) -> ast.Field: - return ast.Field(chain=[self._query.order]) + return ast.Field(chain=[self._query.order or "start_time"]) def _where_predicates(self) -> Union[ast.And, ast.Or]: exprs: list[ast.Expr] = [ @@ -253,12 +252,12 @@ def _where_predicates(self) -> Union[ast.And, ast.Or]: ) ) - remaining_properties = self._strip_person_and_event_and_cohort_properties(self._query.property_groups) + remaining_properties = self._strip_person_and_event_and_cohort_properties(self._query.properties) if remaining_properties: posthoganalytics.capture_exception(UnexpectedQueryProperties(remaining_properties)) optional_exprs.append(property_to_expr(remaining_properties, team=self._team, scope="replay")) - if self._query.console_log_filters and self._query.console_log_filters.values: + if self._query.console_log_filters: console_logs_subquery = ast.SelectQuery( select=[ast.Field(chain=["log_source_id"])], select_from=ast.JoinExpr(table=ast.Field(chain=["console_logs_log_entries"])), @@ -291,10 +290,20 @@ def _where_predicates(self) -> Union[ast.And, ast.Or]: return ast.And(exprs=exprs) - def _having_predicates(self) -> ast.Expr: - return property_to_expr(self._query.having_predicates, team=self._team, scope="replay") + def _having_predicates(self) -> ast.Expr | None: + return ( + property_to_expr(self._query.having_predicates, team=self._team, scope="replay") + if self._query.having_predicates + else None + ) - def _strip_person_and_event_and_cohort_properties(self, property_group: PropertyGroup) -> PropertyGroup | None: + def _strip_person_and_event_and_cohort_properties( + self, property_group: PropertyGroup | None + ) -> PropertyGroup | None: + if not property_group: + return None + + breakpoint() property_groups_to_keep = [ g for g in property_group.flat @@ -345,7 +354,7 @@ def get_query(self) -> ast.SelectQuery | ast.SelectSetQuery | None: @cached_property def person_properties(self) -> PropertyGroup | None: - person_property_groups = [g for g in self._query.property_groups.flat if is_person_property(g)] + person_property_groups = [g for g in (self._query.properties or []) if is_person_property(g)] return ( PropertyGroup( type=self._query.property_operand, @@ -394,7 +403,7 @@ def get_query(self) -> ast.SelectQuery | ast.SelectSetQuery | None: @cached_property def cohort_properties(self) -> PropertyGroup | None: - cohort_property_groups = [g for g in self._query.property_groups.flat if is_cohort_property(g)] + cohort_property_groups = [g for g in (self._query.properties or []) if is_cohort_property(g)] return ( PropertyGroup( type=self._query.property_operand, @@ -495,28 +504,28 @@ def __init__( self._query = filter self._hogql_query_modifiers = hogql_query_modifiers - @cached_property - def _event_predicates(self): - event_exprs: list[ast.Expr] = [] - event_names: set[int | str] = set() - - for entity in self._query.entities: - if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = entity.get_action() - event_names.update([ae for ae in action.get_step_events() if ae and ae not in event_names]) - else: - if entity.id and entity.id not in event_names: - event_names.add(entity.id) - - # TODO: we're not passing the "right" type in here - should we change the signature or do something else? - entity_exprs = [entity_to_expr(entity=entity)] # type: ignore - - if entity.property_groups: - entity_exprs.append(property_to_expr(entity.property_groups, team=self._team, scope="replay_entity")) - - event_exprs.append(ast.And(exprs=entity_exprs)) - - return event_exprs, list(event_names) + # @cached_property + # def _event_predicates(self): + # event_exprs: list[ast.Expr] = [] + # event_names: set[int | str] = set() + # + # for entity in self._query.entities: + # if entity.type == TREND_FILTER_TYPE_ACTIONS: + # action = entity.get_action() + # event_names.update([ae for ae in action.get_step_events() if ae and ae not in event_names]) + # else: + # if entity.id and entity.id not in event_names: + # event_names.add(entity.id) + # + # # TODO: we're not passing the "right" type in here - should we change the signature or do something else? + # entity_exprs = [entity_to_expr(entity=entity)] # type: ignore + # + # if entity.property_groups: + # entity_exprs.append(property_to_expr(entity.property_groups, team=self._team, scope="replay_entity")) + # + # event_exprs.append(ast.And(exprs=entity_exprs)) + # + # return event_exprs, list(event_names) def _select_from_events(self, select_expr: ast.Expr) -> ast.SelectQuery: return ast.SelectQuery( @@ -525,13 +534,14 @@ def _select_from_events(self, select_expr: ast.Expr) -> ast.SelectQuery: table=ast.Field(chain=["events"]), ), where=self._where_predicates(), - having=self._having_predicates(), + # having=self._having_predicates(), group_by=[ast.Field(chain=["$session_id"])], ) def get_query_for_session_id_matching(self) -> ast.SelectQuery | ast.SelectSetQuery | None: use_poe = poe_is_active(self._team) and self.person_properties - if self._query.entities or self.event_properties or self.group_properties or use_poe: + # todo entities were in filter what's the equivalent? + if self.event_properties or self.group_properties or use_poe: return self._select_from_events(ast.Alias(alias="session_id", expr=ast.Field(chain=["$session_id"]))) else: return None @@ -597,10 +607,10 @@ def _where_predicates(self) -> ast.Expr: ) ) - (event_where_exprs, _) = self._event_predicates - if event_where_exprs: - # we OR all events in the where and use hasAll / hasAny in the HAVING clause - exprs.append(ast.Or(exprs=event_where_exprs)) + # (event_where_exprs, _) = self._event_predicates + # if event_where_exprs: + # # we OR all events in the where and use hasAll / hasAny in the HAVING clause + # exprs.append(ast.Or(exprs=event_where_exprs)) if self.event_properties: exprs.append(property_to_expr(self.event_properties, team=self._team, scope="replay")) @@ -622,32 +632,32 @@ def _where_predicates(self) -> ast.Expr: return ast.And(exprs=exprs) - def _having_predicates(self) -> ast.Expr: - (_, event_names) = self._event_predicates - - if event_names: - return ast.Call( - name="hasAll" if self._query._operand == "AND" else "hasAny", - args=[ - ast.Call(name="groupUniqArray", args=[ast.Field(chain=["event"])]), - # KLUDGE: sorting only so that snapshot tests are consistent - ast.Constant(value=sorted(event_names)), - ], - ) - - return ast.Constant(value=True) + # def _having_predicates(self) -> ast.Expr: + # (_, event_names) = self._event_predicates + # + # if event_names: + # return ast.Call( + # name="hasAll" if self._query._operand == "AND" else "hasAny", + # args=[ + # ast.Call(name="groupUniqArray", args=[ast.Field(chain=["event"])]), + # # KLUDGE: sorting only so that snapshot tests are consistent + # ast.Constant(value=sorted(event_names)), + # ], + # ) + # + # return ast.Constant(value=True) @cached_property def event_properties(self): - return [g for g in self._query.property_groups.flat if is_event_property(g)] + return [g for g in (self._query.properties or []) if is_event_property(g)] @cached_property def group_properties(self): - return [g for g in self._query.property_groups.flat if is_group_property(g)] + return [g for g in (self._query.properties or []) if is_group_property(g)] @cached_property def person_properties(self) -> PropertyGroup | None: - person_property_groups = [g for g in self._query.property_groups.flat if is_person_property(g)] + person_property_groups = [g for g in (self._query.properties or []) if is_person_property(g)] return ( PropertyGroup( type=self._query.property_operand, diff --git a/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr new file mode 100644 index 0000000000000..5d5e9941b8024 --- /dev/null +++ b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr @@ -0,0 +1,205 @@ +# serializer version: 1 +# name: TestSessionRecordingsListFromQuery.test_basic_query + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_basic_query_active_sessions + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING ifNull(greater(duration, 60.0), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_basic_query_active_sessions.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING ifNull(greater(active_seconds, '60'), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_basic_query_active_sessions.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING ifNull(greater(inactive_seconds, '60'), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_ordering + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_ordering.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY mouse_activity_count DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py index cec778e5cca82..ab2795dd600e2 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py @@ -22,6 +22,7 @@ from posthog.session_recordings.queries.test.session_replay_sql import ( produce_replay_summary, ) +from posthog.session_recordings.session_recording_api import query_as_params_to_dict from posthog.session_recordings.sql.session_replay_event_sql import ( TRUNCATE_SESSION_REPLAY_EVENTS_TABLE_SQL, ) @@ -79,8 +80,8 @@ def create_event( properties=properties, ) - def _filter_recordings_by(self, recordings_filter: dict) -> SessionRecordingQueryResult: - the_query = RecordingsQuery(team=self.team, data=recordings_filter) + def _filter_recordings_by(self, recordings_filter: dict | None = None) -> SessionRecordingQueryResult: + the_query = RecordingsQuery.model_validate(query_as_params_to_dict(recordings_filter or {})) session_recording_list_instance = SessionRecordingListFromQuery( query=the_query, team=self.team, hogql_query_modifiers=None ) @@ -140,7 +141,7 @@ def test_basic_query(self): active_milliseconds=1980 * 1000 * 0.4, # 40% of the total expected duration ) - session_recordings, more_recordings_available, _ = self._filter_recordings_by({"no_filter": None}) + session_recordings, more_recordings_available, _ = self._filter_recordings_by() assert session_recordings == [ { diff --git a/posthog/session_recordings/session_recording_api.py b/posthog/session_recordings/session_recording_api.py index 91c77804061d8..67feca8099ce0 100644 --- a/posthog/session_recordings/session_recording_api.py +++ b/posthog/session_recordings/session_recording_api.py @@ -300,16 +300,9 @@ def query_as_params_to_dict(params_dict: dict) -> dict: before (if ever) we convert this to a query runner that takes a post we need to convert to a valid dict from the data that arrived in query params """ - converted = { - **params_dict, - "console_log_filters": json.loads(params_dict.get("console_log_filters", "[]")), - "events": json.loads(params_dict.get("events", "[]")), - "actions": json.loads(params_dict.get("actions", "[]")), - "having_predicates": json.loads(params_dict.get("having_predicates", "{}")), - "properties": json.loads(params_dict.get("properties", "[]")), - } - if "user_modified_filters" in params_dict: - converted["user_modified_filters"] = json.loads(params_dict["user_modified_filters"]) + converted = {} + for key in params_dict: + converted[key] = json.loads(params_dict[key]) converted.pop("as_query", None) return converted @@ -341,7 +334,7 @@ def safely_get_object(self, queryset) -> SessionRecording: return recording def list(self, request: request.Request, *args: Any, **kwargs: Any) -> Response: - use_query_type = request.GET.get("as_query", False) + use_query_type = (request.GET.get("as_query", "False")).lower() == "true" if use_query_type: data_dict = query_as_params_to_dict(request.GET.dict()) query = RecordingsQuery.model_validate(data_dict) diff --git a/posthog/session_recordings/test/test_session_recordings.py b/posthog/session_recordings/test/test_session_recordings.py index cb09beccd6537..aaf9333f8faeb 100644 --- a/posthog/session_recordings/test/test_session_recordings.py +++ b/posthog/session_recordings/test/test_session_recordings.py @@ -19,6 +19,7 @@ from posthog.models.filters.session_recordings_filter import SessionRecordingsFilter from posthog.models.property import Property from posthog.models.team import Team +from posthog.schema import RecordingsQuery, LogEntryPropertyFilter from posthog.session_recordings.models.session_recording_event import ( SessionRecordingViewed, ) @@ -169,22 +170,26 @@ def test_can_list_recordings_even_when_the_person_has_multiple_distinct_ids(self assert results_[0]["distinct_id"] == "user2" assert results_[1]["distinct_id"] in twelve_distinct_ids - @parameterized.expand([[True], [False]]) @patch("posthoganalytics.capture") @patch("posthog.session_recordings.session_recording_api.SessionRecordingListFromFilters") - def test_console_log_filters_are_correctly_passed_to_listing(self, as_query, mock_summary_lister, mock_capture): + @patch("posthog.session_recordings.session_recording_api.list_recordings_from_query") + def test_console_log_filters_are_correctly_passed_to_listing_when_filters_are_used( + self, mock_query_lister, mock_summary_lister, mock_capture + ): mock_summary_lister.return_value.run.return_value = ([], False) + mock_query_lister.return_value.run.return_value = ([], False) params_string = urlencode( { - "console_log_filters": '[{"key": "console_log_level", "value": ["warn", "error"], "operator": "exact", "type": "recording"}]', + "console_log_filters": '[{"key": "console_log_level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}]', "user_modified_filters": '{"my_filter": "something"}', - "as_query": as_query, + "as_query": False, } ) self.client.get(f"/api/projects/{self.team.id}/session_recordings?{params_string}") assert len(mock_summary_lister.call_args_list) == 1 + assert len(mock_query_lister.call_args_list) == 0 filter_passed_to_mock: SessionRecordingsFilter = mock_summary_lister.call_args_list[0].kwargs["filter"] console_filter = cast(Property, filter_passed_to_mock.console_log_filters.values[0]) assert console_filter.value == ["warn", "error"] @@ -199,6 +204,40 @@ def test_console_log_filters_are_correctly_passed_to_listing(self, as_query, moc groups=ANY, ) + @patch("posthoganalytics.capture") + @patch("posthog.session_recordings.session_recording_api.SessionRecordingListFromFilters") + @patch("posthog.session_recordings.session_recording_api.list_recordings_from_query") + def test_console_log_filters_are_correctly_passed_to_listing_when_query_is_used( + self, mock_query_lister, mock_summary_lister, mock_capture + ): + mock_summary_lister.return_value.run.return_value = ([], False) + mock_query_lister.return_value = ([], False) + + params_string = urlencode( + { + "console_log_filters": '[{"key": "console_log_level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}]', + "user_modified_filters": '{"my_filter": "something"}', + "as_query": True, + } + ) + self.client.get(f"/api/projects/{self.team.id}/session_recordings?{params_string}") + + assert len(mock_summary_lister.call_args_list) == 0 + assert len(mock_query_lister.call_args_list) == 1 + query_passed_to_mock: RecordingsQuery = mock_query_lister.call_args_list[0][0][0] + console_filter = cast(LogEntryPropertyFilter, query_passed_to_mock.console_log_filters[0]) + assert console_filter.value == ["warn", "error"] + assert mock_capture.call_args_list[0] == call( + self.user.distinct_id, + "recording list filters changed", + properties={ + "$current_url": ANY, + "$session_id": ANY, + "partial_filter_chosen_my_filter": "something", + }, + groups=ANY, + ) + @snapshot_postgres_queries def test_listing_recordings_is_not_nplus1_for_persons(self): with freeze_time("2022-06-03T12:00:00.000Z"): From 1454b13351a72deefd7d1ea83e325deb89f4527f Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 15:14:22 +0100 Subject: [PATCH 09/17] Discard changes to posthog/hogql/printer.py --- posthog/hogql/printer.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/posthog/hogql/printer.py b/posthog/hogql/printer.py index 9e031a53f3481..da81bdd32d37f 100644 --- a/posthog/hogql/printer.py +++ b/posthog/hogql/printer.py @@ -617,11 +617,7 @@ def visit_lambda(self, node: ast.Lambda): return f"({', '.join(identifiers)}) -> {self.visit(node.expr)}" def visit_order_expr(self, node: ast.OrderExpr): - try: - return f"{self.visit(node.expr)} {node.order}" - except: - breakpoint() - return "" + return f"{self.visit(node.expr)} {node.order}" def __get_optimized_property_group_compare_operation(self, node: ast.CompareOperation) -> str | None: """ From 00794a36f582f316cb7018252ba7ab0d204b6dc5 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 14:25:01 +0000 Subject: [PATCH 10/17] ee tests --- .../test_session_recording_list_from_query.py | 347 ++++++++++++++++++ .../session_recording_playlist.py | 24 +- 2 files changed, 366 insertions(+), 5 deletions(-) create mode 100644 ee/session_recordings/queries/test/test_session_recording_list_from_query.py diff --git a/ee/session_recordings/queries/test/test_session_recording_list_from_query.py b/ee/session_recordings/queries/test/test_session_recording_list_from_query.py new file mode 100644 index 0000000000000..94d54baaf52a2 --- /dev/null +++ b/ee/session_recordings/queries/test/test_session_recording_list_from_query.py @@ -0,0 +1,347 @@ +import re +from itertools import product +from uuid import uuid4 + +from dateutil.relativedelta import relativedelta +from django.utils.timezone import now +from freezegun import freeze_time +from parameterized import parameterized + +from ee.clickhouse.materialized_columns.columns import materialize +from posthog.clickhouse.client import sync_execute +from posthog.hogql.ast import CompareOperation, And, SelectQuery +from posthog.hogql.base import Expr +from posthog.hogql.context import HogQLContext +from posthog.hogql.printer import print_ast +from posthog.models import Person +from posthog.schema import PersonsOnEventsMode, RecordingsQuery +from posthog.session_recordings.queries.session_recording_list_from_query import SessionRecordingListFromQuery +from posthog.session_recordings.queries.test.session_replay_sql import produce_replay_summary +from posthog.session_recordings.sql.session_replay_event_sql import TRUNCATE_SESSION_REPLAY_EVENTS_TABLE_SQL +from posthog.test.base import ( + APIBaseTest, + ClickhouseTestMixin, + QueryMatchingTest, + snapshot_clickhouse_queries, + _create_event, +) + + +# The HogQL pair of TestClickhouseSessionRecordingsListFromSessionReplay can be renamed when delete the old one +@freeze_time("2021-01-01T13:46:23") +class TestClickhouseSessionRecordingsListFromQuery(ClickhouseTestMixin, APIBaseTest, QueryMatchingTest): + def _print_query(self, query: SelectQuery) -> str: + return print_ast( + query, + HogQLContext(team_id=self.team.pk, enable_select_queries=True), + "clickhouse", + pretty=True, + ) + + def tearDown(self) -> None: + sync_execute(TRUNCATE_SESSION_REPLAY_EVENTS_TABLE_SQL()) + + @property + def base_time(self): + return (now() - relativedelta(hours=1)).replace(microsecond=0, second=0) + + def create_event( + self, + distinct_id, + timestamp, + team=None, + event_name="$pageview", + properties=None, + ): + if team is None: + team = self.team + if properties is None: + properties = {"$os": "Windows 95", "$current_url": "aloha.com/2"} + return _create_event( + team=team, + event=event_name, + timestamp=timestamp, + distinct_id=distinct_id, + properties=properties, + ) + + @parameterized.expand( + [ + [ + "test_poe_v1_still_falls_back_to_person_subquery", + True, + False, + False, + PersonsOnEventsMode.PERSON_ID_NO_OVERRIDE_PROPERTIES_ON_EVENTS, + ], + [ + "test_poe_being_unavailable_we_fall_back_to_person_id_overrides", + False, + False, + False, + PersonsOnEventsMode.PERSON_ID_OVERRIDE_PROPERTIES_JOINED, + ], + [ + "test_poe_being_unavailable_we_fall_back_to_person_subquery_but_still_use_mat_props", + False, + False, + False, + PersonsOnEventsMode.PERSON_ID_OVERRIDE_PROPERTIES_JOINED, + ], + [ + "test_allow_denormalised_props_fix_does_not_stop_all_poe_processing", + False, + True, + False, + PersonsOnEventsMode.PERSON_ID_OVERRIDE_PROPERTIES_ON_EVENTS, + ], + [ + "test_poe_v2_available_person_properties_are_used_in_replay_listing", + False, + True, + True, + PersonsOnEventsMode.PERSON_ID_OVERRIDE_PROPERTIES_ON_EVENTS, + ], + ] + ) + def test_effect_of_poe_settings_on_query_generated( + self, + _name: str, + poe_v1: bool, + poe_v2: bool, + allow_denormalized_props: bool, + expected_poe_mode: PersonsOnEventsMode, + ) -> None: + with self.settings( + PERSON_ON_EVENTS_OVERRIDE=poe_v1, + PERSON_ON_EVENTS_V2_OVERRIDE=poe_v2, + ALLOW_DENORMALIZED_PROPS_IN_LISTING=allow_denormalized_props, + ): + assert self.team.person_on_events_mode == expected_poe_mode + materialize("events", "rgInternal", table_column="person_properties") + + query = RecordingsQuery.model_validate( + { + "properties": [ + { + "key": "rgInternal", + "value": ["false"], + "operator": "exact", + "type": "person", + } + ] + }, + ) + session_recording_list_instance = SessionRecordingListFromQuery( + query=query, team=self.team, hogql_query_modifiers=None + ) + + hogql_parsed_select = session_recording_list_instance.get_query() + printed_query = self._print_query(hogql_parsed_select) + + person_filtering_expr = self._matching_person_filter_expr_from(hogql_parsed_select) + + self._assert_is_events_person_filter(person_filtering_expr) + + if poe_v1 or poe_v2: + # Property used directly from event (from materialized column) + assert "ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null')" in printed_query + else: + # We get the person property value from the persons JOIN + assert re.search( + r"argMax\(replaceRegexpAll\(nullIf\(nullIf\(JSONExtractRaw\(person\.properties, %\(hogql_val_\d+\)s\), ''\), 'null'\), '^\"|\"\$', ''\), person\.version\) AS properties___rgInternal", + printed_query, + ) + # Then we actually filter on that property value + assert re.search( + r"ifNull\(equals\(events__person\.properties___rgInternal, %\(hogql_val_\d+\)s\), 0\)", + printed_query, + ) + self.assertQueryMatchesSnapshot(printed_query) + + def _assert_is_pdi_filter(self, person_filtering_expr: list[Expr]) -> None: + assert person_filtering_expr[0].right.select_from.table.chain == ["person_distinct_ids"] + assert person_filtering_expr[0].right.where.left.chain == ["person", "properties", "rgInternal"] + + def _assert_is_events_person_filter(self, person_filtering_expr: list[Expr]) -> None: + assert person_filtering_expr[0].right.select_from.table.chain == ["events"] + event_person_condition = [ + x + for x in person_filtering_expr[0].right.where.exprs + if isinstance(x, CompareOperation) and x.left.chain == ["person", "properties", "rgInternal"] + ] + assert len(event_person_condition) == 1 + + def _matching_person_filter_expr_from(self, hogql_parsed_select: SelectQuery) -> list[Expr]: + where_conditions: list[Expr] = hogql_parsed_select.where.exprs + ands = [x for x in where_conditions if isinstance(x, And)] + assert len(ands) == 1 + and_comparisons = [x for x in ands[0].exprs if isinstance(x, CompareOperation)] + assert len(and_comparisons) == 1 + assert isinstance(and_comparisons[0].right, SelectQuery) + return and_comparisons + + settings_combinations = [ + ["poe v2 and materialized columns allowed", False, True, True], + ["poe v2 and materialized columns off", False, True, False], + ["poe off and materialized columns allowed", False, False, True], + ["poe off and materialized columns not allowed", False, False, False], + ["poe v1 and materialized columns allowed", True, False, True], + ["poe v1 and not materialized columns not allowed", True, False, False], + ] + + # Options for "materialize person columns" + materialization_options = [ + [" with materialization", True], + [" without materialization", False], + ] + + # Expand the parameter list to the product of all combinations with "materialize person columns" + # e.g. [a, b] x [c, d] = [a, c], [a, d], [b, c], [b, d] + test_case_combinations = [ + [f"{name}{mat_option}", poe_v1, poe, mat_columns, mat_person] + for (name, poe_v1, poe, mat_columns), (mat_option, mat_person) in product( + settings_combinations, materialization_options + ) + ] + + @parameterized.expand(test_case_combinations) + @snapshot_clickhouse_queries + def test_event_filter_with_person_properties_materialized( + self, + _name: str, + poe1_enabled: bool, + poe2_enabled: bool, + allow_denormalised_props: bool, + materialize_person_props: bool, + ) -> None: + # KLUDGE: I couldn't figure out how to use @also_test_with_materialized_columns(person_properties=["email"]) + # KLUDGE: and the parameterized.expand decorator at the same time, so we generate test case combos + # KLUDGE: for materialization on and off to test both sides the way the decorator would have + if materialize_person_props: + materialize("events", "email", table_column="person_properties") + materialize("person", "email") + + with self.settings( + PERSON_ON_EVENTS_OVERRIDE=poe1_enabled, + PERSON_ON_EVENTS_V2_OVERRIDE=poe2_enabled, + ALLOW_DENORMALIZED_PROPS_IN_LISTING=allow_denormalised_props, + ): + user_one = "test_event_filter_with_person_properties-user" + user_two = "test_event_filter_with_person_properties-user2" + session_id_one = f"test_event_filter_with_person_properties-1-{str(uuid4())}" + session_id_two = f"test_event_filter_with_person_properties-2-{str(uuid4())}" + + Person.objects.create(team=self.team, distinct_ids=[user_one], properties={"email": "bla"}) + Person.objects.create(team=self.team, distinct_ids=[user_two], properties={"email": "bla2"}) + + self._add_replay_with_pageview(session_id_one, user_one) + produce_replay_summary( + distinct_id=user_one, + session_id=session_id_one, + first_timestamp=(self.base_time + relativedelta(seconds=30)), + team_id=self.team.id, + ) + self._add_replay_with_pageview(session_id_two, user_two) + produce_replay_summary( + distinct_id=user_two, + session_id=session_id_two, + first_timestamp=(self.base_time + relativedelta(seconds=30)), + team_id=self.team.id, + ) + + match_everyone_filter = RecordingsQuery.model_validate( + {"properties": []}, + ) + + session_recording_list_instance = SessionRecordingListFromQuery( + query=match_everyone_filter, team=self.team, hogql_query_modifiers=None + ) + (session_recordings, _, _) = session_recording_list_instance.run() + + assert sorted([x["session_id"] for x in session_recordings]) == sorted([session_id_one, session_id_two]) + + match_bla_filter = RecordingsQuery.model_validate( + { + "properties": [ + { + "key": "email", + "value": ["bla"], + "operator": "exact", + "type": "person", + } + ] + }, + ) + + session_recording_list_instance = SessionRecordingListFromQuery( + query=match_bla_filter, team=self.team, hogql_query_modifiers=None + ) + (session_recordings, _, _) = session_recording_list_instance.run() + + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_id_one + + def _add_replay_with_pageview(self, session_id: str, user: str) -> None: + self.create_event( + user, + self.base_time, + properties={"$session_id": session_id, "$window_id": str(uuid4())}, + ) + produce_replay_summary( + distinct_id=user, + session_id=session_id, + first_timestamp=self.base_time, + team_id=self.team.id, + ) + + @parameterized.expand(test_case_combinations) + @snapshot_clickhouse_queries + def test_person_id_filter( + self, + _name: str, + poe2_enabled: bool, + poe1_enabled: bool, + allow_denormalised_props: bool, + materialize_person_props: bool, + ) -> None: + # KLUDGE: I couldn't figure out how to use @also_test_with_materialized_columns(person_properties=["email"]) + # KLUDGE: and the parameterized.expand decorator at the same time, so we generate test case combos + # KLUDGE: for materialization on and off to test both sides the way the decorator would have + if materialize_person_props: + # it shouldn't matter to this test whether any column is materialized + # but let's keep the tests in this file similar so we flush out any unexpected interactions + materialize("events", "email", table_column="person_properties") + materialize("person", "email") + + with self.settings( + PERSON_ON_EVENTS_OVERRIDE=poe1_enabled, + PERSON_ON_EVENTS_V2_OVERRIDE=poe2_enabled, + ALLOW_DENORMALIZED_PROPS_IN_LISTING=allow_denormalised_props, + ): + three_user_ids = ["person-1-distinct-1", "person-1-distinct-2", "person-2"] + session_id_one = f"test_person_id_filter-session-one" + session_id_two = f"test_person_id_filter-session-two" + session_id_three = f"test_person_id_filter-session-three" + + p = Person.objects.create( + team=self.team, + distinct_ids=[three_user_ids[0], three_user_ids[1]], + properties={"email": "bla"}, + ) + Person.objects.create( + team=self.team, + distinct_ids=[three_user_ids[2]], + properties={"email": "bla2"}, + ) + + self._add_replay_with_pageview(session_id_one, three_user_ids[0]) + self._add_replay_with_pageview(session_id_two, three_user_ids[1]) + self._add_replay_with_pageview(session_id_three, three_user_ids[2]) + + query = RecordingsQuery.model_validate({"person_uuid": str(p.uuid)}) + session_recording_list_instance = SessionRecordingListFromQuery( + query=query, team=self.team, hogql_query_modifiers=None + ) + (session_recordings, _, _) = session_recording_list_instance.run() + assert sorted([r["session_id"] for r in session_recordings]) == sorted([session_id_two, session_id_one]) diff --git a/ee/session_recordings/session_recording_playlist.py b/ee/session_recordings/session_recording_playlist.py index 1a7d6e68250fb..a3dc50c1228f5 100644 --- a/ee/session_recordings/session_recording_playlist.py +++ b/ee/session_recordings/session_recording_playlist.py @@ -34,7 +34,13 @@ ClickHouseBurstRateThrottle, ClickHouseSustainedRateThrottle, ) -from posthog.session_recordings.session_recording_api import list_recordings_response, list_recordings +from posthog.schema import RecordingsQuery +from posthog.session_recordings.session_recording_api import ( + list_recordings_response, + list_recordings, + query_as_params_to_dict, + list_recordings_from_query, +) from posthog.utils import relative_date_parse logger = structlog.get_logger(__name__) @@ -224,11 +230,19 @@ def recordings(self, request: request.Request, *args: Any, **kwargs: Any) -> res .values_list("recording_id", flat=True) ) - filter = SessionRecordingsFilter(request=request, team=self.team) - filter = filter.shallow_clone({SESSION_RECORDINGS_FILTER_IDS: json.dumps(playlist_items)}) + use_query_type = (request.GET.get("as_query", "False")).lower() == "true" - # TODO this needs converting to Query as well - return list_recordings_response(list_recordings(filter, request, context=self.get_serializer_context())) + if use_query_type: + data_dict = query_as_params_to_dict(request.GET.dict()) + query = RecordingsQuery.model_validate(data_dict) + query.session_ids = playlist_items + return list_recordings_response( + list_recordings_from_query(query, request, context=self.get_serializer_context()) + ) + else: + filter = SessionRecordingsFilter(request=request, team=self.team) + filter = filter.shallow_clone({SESSION_RECORDINGS_FILTER_IDS: json.dumps(playlist_items)}) + return list_recordings_response(list_recordings(filter, request, context=self.get_serializer_context())) # As of now, you can only "update" a session recording by adding or removing a recording from a static playlist @action( From 438c07503ba30a8d0d589b1f2625c88f79bf429d Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 21:14:19 +0000 Subject: [PATCH 11/17] a little more --- .../session_recording_list_from_query.py | 225 +- ...est_session_recording_list_from_query.ambr | 3892 ++++++++++++++++- .../test_session_recording_list_from_query.py | 45 +- .../session_recording_api.py | 6 +- 4 files changed, 4077 insertions(+), 91 deletions(-) diff --git a/posthog/session_recordings/queries/session_recording_list_from_query.py b/posthog/session_recordings/queries/session_recording_list_from_query.py index 703dfd11557e1..93f48568c319c 100644 --- a/posthog/session_recordings/queries/session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/session_recording_list_from_query.py @@ -4,17 +4,28 @@ import posthoganalytics +from posthog.constants import PropertyOperatorType from posthog.hogql import ast from posthog.hogql.ast import CompareOperation from posthog.hogql.parser import parse_select -from posthog.hogql.property import property_to_expr +from posthog.hogql.property import property_to_expr, action_to_expr from posthog.hogql.query import execute_hogql_query from posthog.hogql_queries.insights.paginators import HogQLHasMorePaginator -from posthog.models import Team, Property -from posthog.models.filters.session_recordings_filter import SessionRecordingsFilter +from posthog.hogql_queries.legacy_compatibility.filter_to_query import MathAvailability, legacy_entity_to_node +from posthog.hogql_queries.utils.query_date_range import QueryDateRange +from posthog.models import Team, Property, Entity, Action from posthog.models.filters.mixins.utils import cached_property from posthog.models.property import PropertyGroup -from posthog.schema import QueryTiming, HogQLQueryModifiers, PersonsOnEventsMode, RecordingsQuery +from posthog.schema import ( + QueryTiming, + HogQLQueryModifiers, + PersonsOnEventsMode, + RecordingsQuery, + DateRange, + NodeKind, + EventsNode, + ActionsNode, +) from posthog.session_recordings.queries.session_replay_events import ttl_days import structlog @@ -123,12 +134,21 @@ def __init__( **_, ): self._team = team + self._query = query + if self._query.filter_test_accounts: + self._query.properties = self._query.properties or [] + self._query.properties += self._test_account_filters + self._paginator = HogQLHasMorePaginator( limit=query.limit or self.SESSION_RECORDINGS_DEFAULT_LIMIT, offset=query.offset or 0 ) self._hogql_query_modifiers = hogql_query_modifiers + @cached_property + def _test_account_filters(self) -> list[Property]: + return [Property(**p) for p in self._team.test_account_filters] + @property def ttl_days(self): return ttl_days(self._team) @@ -176,6 +196,15 @@ def get_query(self): def _order_by_clause(self) -> ast.Field: return ast.Field(chain=[self._query.order or "start_time"]) + @cached_property + def query_date_range(self): + return QueryDateRange( + date_range=DateRange(date_from=self._query.date_from, date_to=self._query.date_to, explicitDate=True), + team=self._team, + interval=None, + now=datetime.now(), + ) + def _where_predicates(self) -> Union[ast.And, ast.Or]: exprs: list[ast.Expr] = [ ast.CompareOperation( @@ -204,15 +233,16 @@ def _where_predicates(self) -> Union[ast.And, ast.Or]: ast.CompareOperation( op=ast.CompareOperationOp.GtEq, left=ast.Field(chain=["s", "min_first_timestamp"]), - right=ast.Constant(value=self._query.date_from), + right=ast.Constant(value=self.query_date_range.date_from()), ) ) + if self._query.date_to: exprs.append( ast.CompareOperation( op=ast.CompareOperationOp.LtEq, left=ast.Field(chain=["s", "min_first_timestamp"]), - right=ast.Constant(value=self._query.date_to), + right=ast.Constant(value=self.query_date_range.date_to()), ) ) @@ -263,7 +293,7 @@ def _where_predicates(self) -> Union[ast.And, ast.Or]: select_from=ast.JoinExpr(table=ast.Field(chain=["console_logs_log_entries"])), where=ast.And( exprs=[ - self._query.ast_operand( + self.ast_operand( exprs=[ property_to_expr(self._query.console_log_filters, team=self._team), ] @@ -277,6 +307,9 @@ def _where_predicates(self) -> Union[ast.And, ast.Or]: ), ) + if self.ast_operand == ast.Or: + breakpoint() + optional_exprs.append( ast.CompareOperation( op=ast.CompareOperationOp.In, @@ -286,7 +319,7 @@ def _where_predicates(self) -> Union[ast.And, ast.Or]: ) if optional_exprs: - exprs.append(self._query.ast_operand(exprs=optional_exprs)) + exprs.append(self.ast_operand(exprs=optional_exprs)) return ast.And(exprs=exprs) @@ -297,16 +330,23 @@ def _having_predicates(self) -> ast.Expr | None: else None ) + @cached_property + def property_operand(self): + return PropertyOperatorType.AND if self._query.operand == "AND" else PropertyOperatorType.OR + + @cached_property + def ast_operand(self) -> type[Union[ast.And, ast.Or]]: + return ast.And if self.property_operand == "AND" else ast.Or + def _strip_person_and_event_and_cohort_properties( - self, property_group: PropertyGroup | None + self, properties: list[Property] | list[PropertyGroup] | None ) -> PropertyGroup | None: - if not property_group: + if not properties: return None - breakpoint() property_groups_to_keep = [ g - for g in property_group.flat + for g in properties if not is_event_property(g) and not is_person_property(g) and not is_group_property(g) @@ -315,7 +355,7 @@ def _strip_person_and_event_and_cohort_properties( return ( PropertyGroup( - type=self._query.property_operand, + type=self.property_operand, values=property_groups_to_keep, ) if property_groups_to_keep @@ -352,12 +392,16 @@ def get_query(self) -> ast.SelectQuery | ast.SelectSetQuery | None: else: return None + @cached_property + def property_operand(self): + return PropertyOperatorType.AND if self._query.operand == "AND" else PropertyOperatorType.OR + @cached_property def person_properties(self) -> PropertyGroup | None: person_property_groups = [g for g in (self._query.properties or []) if is_person_property(g)] return ( PropertyGroup( - type=self._query.property_operand, + type=self.property_operand, values=person_property_groups, ) if person_property_groups @@ -401,12 +445,16 @@ def get_query(self) -> ast.SelectQuery | ast.SelectSetQuery | None: return None + @cached_property + def property_operand(self): + return PropertyOperatorType.AND if self._query.operand == "AND" else PropertyOperatorType.OR + @cached_property def cohort_properties(self) -> PropertyGroup | None: cohort_property_groups = [g for g in (self._query.properties or []) if is_cohort_property(g)] return ( PropertyGroup( - type=self._query.property_operand, + type=self.property_operand, values=cohort_property_groups, ) if cohort_property_groups @@ -442,6 +490,15 @@ def get_operation(self) -> CompareOperation | None: right=q, ) + @cached_property + def query_date_range(self): + return QueryDateRange( + date_range=DateRange(date_from=self._query.date_from, date_to=self._query.date_to, explicitDate=True), + team=self._team, + interval=None, + now=datetime.now(), + ) + def get_query(self) -> ast.SelectQuery | ast.SelectSetQuery | None: if not self._query.person_uuid: return None @@ -467,8 +524,8 @@ def get_query(self) -> ast.SelectQuery | ast.SelectSetQuery | None: { "person_id": ast.Constant(value=self._query.person_uuid), "ttl_days": ast.Constant(value=self._ttl_days), - "date_from": ast.Constant(value=self._query.date_from), - "date_to": ast.Constant(value=self._query.date_to), + "date_from": ast.Constant(value=self.query_date_range.date_from()), + "date_to": ast.Constant(value=self.query_date_range.date_to()), "now": ast.Constant(value=now), "ttl_date": ast.Constant(value=now - timedelta(days=self._ttl_days)), }, @@ -490,6 +547,15 @@ class ReplayFiltersEventsSubQuery: _team: Team _query: RecordingsQuery + @cached_property + def query_date_range(self): + return QueryDateRange( + date_range=DateRange(date_from=self._query.date_from, date_to=self._query.date_to, explicitDate=True), + team=self._team, + interval=None, + now=datetime.now(), + ) + @property def ttl_days(self): return ttl_days(self._team) @@ -497,35 +563,47 @@ def ttl_days(self): def __init__( self, team: Team, - filter: SessionRecordingsFilter, + query: RecordingsQuery, hogql_query_modifiers: Optional[HogQLQueryModifiers] = None, ): self._team = team - self._query = filter + self._query = query self._hogql_query_modifiers = hogql_query_modifiers - # @cached_property - # def _event_predicates(self): - # event_exprs: list[ast.Expr] = [] - # event_names: set[int | str] = set() - # - # for entity in self._query.entities: - # if entity.type == TREND_FILTER_TYPE_ACTIONS: - # action = entity.get_action() - # event_names.update([ae for ae in action.get_step_events() if ae and ae not in event_names]) - # else: - # if entity.id and entity.id not in event_names: - # event_names.add(entity.id) - # - # # TODO: we're not passing the "right" type in here - should we change the signature or do something else? - # entity_exprs = [entity_to_expr(entity=entity)] # type: ignore - # - # if entity.property_groups: - # entity_exprs.append(property_to_expr(entity.property_groups, team=self._team, scope="replay_entity")) - # - # event_exprs.append(ast.And(exprs=entity_exprs)) - # - # return event_exprs, list(event_names) + def _entity_to_expr(self, entity: EventsNode | ActionsNode) -> ast.Expr: + if entity.kind == NodeKind.ACTIONS_NODE and entity.id is not None: + action = Action.objects.get(pk=entity.id) + return action_to_expr(action) + if entity.name is None: + return ast.Constant(value=True) + + return ast.CompareOperation( + op=ast.CompareOperationOp.Eq, + left=ast.Field(chain=["events", "event"]), + right=ast.Constant(value=entity.name), + ) + + @cached_property + def _event_predicates(self): + event_exprs: list[ast.Expr] = [] + event_names: set[int | str] = set() + + for entity in self.event_entities: + if entity.kind == NodeKind.ACTIONS_NODE: + action = entity.get_action() + event_names.update([ae for ae in action.get_step_events() if ae and ae not in event_names]) + else: + if entity.event and entity.event not in event_names: + event_names.add(entity.event) + + entity_exprs = [self._entity_to_expr(entity=entity)] + + if entity.properties: + entity_exprs.append(property_to_expr(entity.properties, team=self._team, scope="replay_entity")) + + event_exprs.append(ast.And(exprs=entity_exprs)) + + return event_exprs, list(event_names) def _select_from_events(self, select_expr: ast.Expr) -> ast.SelectQuery: return ast.SelectQuery( @@ -540,8 +618,8 @@ def _select_from_events(self, select_expr: ast.Expr) -> ast.SelectQuery: def get_query_for_session_id_matching(self) -> ast.SelectQuery | ast.SelectSetQuery | None: use_poe = poe_is_active(self._team) and self.person_properties - # todo entities were in filter what's the equivalent? - if self.event_properties or self.group_properties or use_poe: + + if self.entities or self.event_properties or self.group_properties or use_poe: return self._select_from_events(ast.Alias(alias="session_id", expr=ast.Field(chain=["$session_id"]))) else: return None @@ -593,7 +671,7 @@ def _where_predicates(self) -> ast.Expr: ast.CompareOperation( op=ast.CompareOperationOp.GtEq, left=ast.Field(chain=["timestamp"]), - right=ast.Constant(value=self._query.date_from - timedelta(minutes=2)), + right=ast.Constant(value=self.query_date_range.date_from() - timedelta(minutes=2)), ) ) @@ -603,14 +681,14 @@ def _where_predicates(self) -> ast.Expr: ast.CompareOperation( op=ast.CompareOperationOp.LtEq, left=ast.Field(chain=["timestamp"]), - right=ast.Constant(value=self._query.date_to), + right=ast.Constant(value=self.query_date_range.date_to()), ) ) - # (event_where_exprs, _) = self._event_predicates - # if event_where_exprs: - # # we OR all events in the where and use hasAll / hasAny in the HAVING clause - # exprs.append(ast.Or(exprs=event_where_exprs)) + (event_where_exprs, _) = self._event_predicates + if event_where_exprs: + # we OR all events in the where and use hasAll / hasAny in the HAVING clause + exprs.append(ast.Or(exprs=event_where_exprs)) if self.event_properties: exprs.append(property_to_expr(self.event_properties, team=self._team, scope="replay")) @@ -632,20 +710,35 @@ def _where_predicates(self) -> ast.Expr: return ast.And(exprs=exprs) - # def _having_predicates(self) -> ast.Expr: - # (_, event_names) = self._event_predicates - # - # if event_names: - # return ast.Call( - # name="hasAll" if self._query._operand == "AND" else "hasAny", - # args=[ - # ast.Call(name="groupUniqArray", args=[ast.Field(chain=["event"])]), - # # KLUDGE: sorting only so that snapshot tests are consistent - # ast.Constant(value=sorted(event_names)), - # ], - # ) - # - # return ast.Constant(value=True) + def _having_predicates(self) -> ast.Expr: + (_, event_names) = self._event_predicates + + if event_names: + return ast.Call( + name="hasAll" if self._query._operand == "AND" else "hasAny", + args=[ + ast.Call(name="groupUniqArray", args=[ast.Field(chain=["event"])]), + # KLUDGE: sorting only so that snapshot tests are consistent + ast.Constant(value=sorted(event_names)), + ], + ) + + return ast.Constant(value=True) + + @cached_property + def action_entities(self): + # TODO what do we send to the API instead to avoid needing to do this + return [legacy_entity_to_node(Entity(e), True, MathAvailability.Unavailable) for e in self._query.actions or []] + + @cached_property + def event_entities(self): + # TODO what do we send to the API instead to avoid needing to do this + # TODO is this overkill since it feels like we only need a few things off the entity + return [legacy_entity_to_node(Entity(e), True, MathAvailability.Unavailable) for e in self._query.events or []] + + @cached_property + def entities(self): + return self.action_entities + self.event_entities @cached_property def event_properties(self): @@ -655,12 +748,16 @@ def event_properties(self): def group_properties(self): return [g for g in (self._query.properties or []) if is_group_property(g)] + @cached_property + def property_operand(self): + return PropertyOperatorType.AND if self._query.operand == "AND" else PropertyOperatorType.OR + @cached_property def person_properties(self) -> PropertyGroup | None: person_property_groups = [g for g in (self._query.properties or []) if is_person_property(g)] return ( PropertyGroup( - type=self._query.property_operand, + type=self.property_operand, values=person_property_groups, ) if person_property_groups diff --git a/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr index 5d5e9941b8024..c8f1c29c0c597 100644 --- a/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr +++ b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr @@ -135,6 +135,2847 @@ max_bytes_before_external_group_by=0 ''' # --- +# name: TestSessionRecordingsListFromQuery.test_basic_query_with_ordering + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY active_seconds DESC + LIMIT 4 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_basic_query_with_ordering.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY console_error_count DESC + LIMIT 4 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_basic_query_with_ordering.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 4 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_basic_query_with_paging + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 2 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_basic_query_with_paging.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 2 + OFFSET 1 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_basic_query_with_paging.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 2 + OFFSET 2 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_date_from_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_date_from_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-30 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_date_from_filter_cannot_search_before_ttl + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 12:41:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 12:46:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-12 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_date_from_filter_cannot_search_before_ttl.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 12:41:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 12:46:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_date_from_filter_cannot_search_before_ttl.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 12:41:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 12:46:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-10 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_duration_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING ifNull(greater(duration, 60.0), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_duration_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING ifNull(less(duration, 60.0), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$autocapture')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_has_ttl_applied_too + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_has_ttl_applied_too.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_active_sessions + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING ifNull(greater(duration, 60.0), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_active_sessions.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING ifNull(greater(active_seconds, 60.0), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_group_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT JOIN + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(groups.group_properties, 'name'), ''), 'null'), '^"|"$', ''), toTimeZone(groups._timestamp, 'UTC')) AS properties___name, groups.group_type_index AS index, groups.group_key AS key + FROM groups + WHERE and(equals(groups.team_id, 99999), equals(index, 1)) + GROUP BY groups.group_type_index, groups.group_key) AS events__group_1 ON equals(events.`$group_1`, events__group_1.key) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(events__group_1.properties___name, 'org one'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_group_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT JOIN + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(groups.group_properties, 'name'), ''), 'null'), '^"|"$', ''), toTimeZone(groups._timestamp, 'UTC')) AS properties___name, groups.group_type_index AS index, groups.group_key AS key + FROM groups + WHERE and(equals(groups.team_id, 99999), equals(index, 1)) + GROUP BY groups.group_type_index, groups.group_key) AS events__group_1 ON equals(events.`$group_1`, events__group_1.key) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__group_1.properties___name, 'org one'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_group_filter.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT JOIN + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(groups.group_properties, 'name'), ''), 'null'), '^"|"$', ''), toTimeZone(groups._timestamp, 'UTC')) AS properties___name, groups.group_type_index AS index, groups.group_key AS key + FROM groups + WHERE and(equals(groups.team_id, 99999), equals(index, 2)) + GROUP BY groups.group_type_index, groups.group_key) AS events__group_2 ON equals(events.`$group_2`, events__group_2.key) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__group_2.properties___name, 'org one'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_event_properties_test_accounts_excluded + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_event_properties_test_accounts_excluded.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_event_properties_test_accounts_excluded.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_event_properties_test_accounts_excluded_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_event_properties_test_accounts_excluded_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_event_properties_test_accounts_excluded_materialized.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_person_properties + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'bla'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_person_properties.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'something else'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_properties + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_properties.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_properties_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_hogql_properties_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Firefox'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_matching_on_session_id + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_matching_on_session_id.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$autocapture')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_properties + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_properties.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_properties.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_properties.3 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Safari'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_properties_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_properties_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Firefox'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_properties_materialized.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_properties_materialized.3 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Safari'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_test_accounts_excluded + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_test_accounts_excluded.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_test_accounts_excluded_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), and(ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_test_accounts_excluded_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_event_filter_with_two_events_and_multiple_teams + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_by_snapshot_source + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING ifNull(equals(argMinMerge(s.snapshot_source), 'web'), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_by_snapshot_source.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING ifNull(equals(argMinMerge(s.snapshot_source), 'mobile'), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_with_console_errors + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'error'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_with_console_errors.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_with_console_logs + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_with_console_logs.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_with_console_warns + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_with_console_warns.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_with_mixed_console_counts + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(or(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.level, 'error'), 0)), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_with_mixed_console_counts.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_on_session_ids + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), + ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), + in(s.session_id, + ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001' /* ... */]) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_on_session_ids.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), + ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), + in(s.session_id, + ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001' /* ... */]) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_cohort_properties + ''' + + SELECT count(DISTINCT person_id) + FROM cohortpeople + WHERE team_id = 99999 + AND cohort_id = 99999 + AND version = NULL + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_cohort_properties.1 + ''' + /* cohort_calculation: */ + SELECT count(DISTINCT person_id) + FROM cohortpeople + WHERE team_id = 99999 + AND cohort_id = 99999 + AND version = 0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_cohort_properties.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-08-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), 0), in(s.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), in(person_distinct_id2.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), 1, in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) + GROUP BY person_distinct_id2.distinct_id + HAVING and(ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0), in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_person_properties_exact + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla@gmail.com'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_person_properties_not_contains + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(notILike(events__person.properties___email, '%gmail.com%'), 1)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, ['session_id_one']), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), in(events.`$session_id`, ['session_id_one'])) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters.3 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, ['session_id_two']), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), in(events.`$session_id`, ['session_id_two'])) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_person_filters + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(ifNull(equals(events__person.properties___email, 'test@posthog.com'), 0), ifNull(equals(events__person.properties___email, 'david@posthog.com'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_person_filters.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), or(ifNull(equals(events__person.properties___email, 'test@posthog.com'), 0), ifNull(equals(events__person.properties___email, 'david@posthog.com'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- # name: TestSessionRecordingsListFromQuery.test_ordering ''' SELECT s.session_id AS session_id, @@ -155,7 +2996,726 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_ordering.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY mouse_activity_count DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_person_id_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_sessions_with_current_data + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_host_property_test_account_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_host_property_test_account_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(not(match(toString(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$host'), ''), 'null'), '^"|"$', '')), '^(localhost|127\\.0\\.0\\.1)($|:)')), 1)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_host_property_test_account_filter_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_host_property_test_account_filter_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(not(match(toString(nullIf(nullIf(events.`mat_$host`, ''), 'null')), '^(localhost|127\\.0\\.0\\.1)($|:)')), 1)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_property_test_account_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_property_test_account_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'false'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_property_test_account_filter_allowing_denormalized_props + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_property_test_account_filter_allowing_denormalized_props.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'false'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_property_test_account_filter_allowing_denormalized_props_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_property_test_account_filter_allowing_denormalized_props_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_property_test_account_filter_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_event_property_test_account_filter_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_hogql_event_property_test_account_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_hogql_event_property_test_account_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'true'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_hogql_event_property_test_account_filter_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_hogql_event_property_test_account_filter_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'true'), 0)) + GROUP BY events.`$session_id`))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -169,7 +3729,189 @@ max_bytes_before_external_group_by=0 ''' # --- -# name: TestSessionRecordingsListFromQuery.test_ordering.1 +# name: TestSessionRecordingsListFromQuery.test_top_level_hogql_person_property_test_account_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_hogql_person_property_test_account_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_hogql_person_property_test_account_filter_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_hogql_person_property_test_account_filter_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_person_property_test_account_filter ''' SELECT s.session_id AS session_id, any(s.team_id), @@ -192,7 +3934,151 @@ WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 - ORDER BY mouse_activity_count DESC + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_person_property_test_account_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_person_property_test_account_filter_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_top_level_person_property_test_account_filter_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC LIMIT 51 OFFSET 0 SETTINGS readonly=2, max_execution_time=60, diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py index ab2795dd600e2..f4aa82d2f88ee 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py @@ -370,9 +370,7 @@ def test_basic_query_with_paging(self): active_milliseconds=1980 * 1000 * 0.4, # 40% of the total expected duration ) - (session_recordings, more_recordings_available, _) = self._filter_recordings_by( - {"no_filter": None, "limit": 1, "offset": 0} - ) + (session_recordings, more_recordings_available, _) = self._filter_recordings_by({"limit": 1, "offset": 0}) assert session_recordings == [ { @@ -398,9 +396,7 @@ def test_basic_query_with_paging(self): assert more_recordings_available is True - (session_recordings, more_recordings_available, _) = self._filter_recordings_by( - {"no_filter": None, "limit": 1, "offset": 1} - ) + (session_recordings, more_recordings_available, _) = self._filter_recordings_by({"limit": 1, "offset": 1}) assert session_recordings == [ { @@ -426,9 +422,7 @@ def test_basic_query_with_paging(self): assert more_recordings_available is False - (session_recordings, more_recordings_available, _) = self._filter_recordings_by( - {"no_filter": None, "limit": 1, "offset": 2} - ) + (session_recordings, more_recordings_available, _) = self._filter_recordings_by({"limit": 1, "offset": 2}) assert session_recordings == [] @@ -477,23 +471,17 @@ def test_basic_query_with_ordering(self): active_milliseconds=1000, # most activity, but the least errors ) - (session_recordings) = self._filter_recordings_by( - {"no_filter": None, "limit": 3, "offset": 0, "order": "active_seconds"} - ) + (session_recordings) = self._filter_recordings_by({"limit": 3, "offset": 0, "order": "active_seconds"}) ordered_by_activity = [(r["session_id"], r["active_seconds"]) for r in session_recordings.results] assert ordered_by_activity == [(session_id_two, 1.0), (session_id_one, 0.002)] - (session_recordings) = self._filter_recordings_by( - {"no_filter": None, "limit": 3, "offset": 0, "order": "console_error_count"} - ) + (session_recordings) = self._filter_recordings_by({"limit": 3, "offset": 0, "order": "console_error_count"}) ordered_by_errors = [(r["session_id"], r["console_error_count"]) for r in session_recordings.results] assert ordered_by_errors == [(session_id_one, 1012), (session_id_two, 430)] - (session_recordings) = self._filter_recordings_by( - {"no_filter": None, "limit": 3, "offset": 0, "order": "start_time"} - ) + (session_recordings) = self._filter_recordings_by({"limit": 3, "offset": 0, "order": "start_time"}) ordered_by_default = [(r["session_id"], r["start_time"]) for r in session_recordings.results] assert ordered_by_default == [(session_id_one, session_one_start), (session_id_two, session_two_start)] @@ -602,7 +590,7 @@ def test_first_url_selection(self): first_url="https://on-second-received-event-but-actually-first.com", ) - session_recordings, more_recordings_available, _ = self._filter_recordings_by({"no_filter": None}) + session_recordings, more_recordings_available, _ = self._filter_recordings_by() assert sorted( [{"session_id": r["session_id"], "first_url": r["first_url"]} for r in session_recordings], @@ -663,7 +651,7 @@ def test_recordings_dont_leak_data_between_teams(self): active_milliseconds=20 * 1000 * 0.5, # 50% of the total expected duration ) - (session_recordings, _, _) = self._filter_recordings_by({"no_filter": None}) + (session_recordings, _, _) = self._filter_recordings_by() assert [{"session": r["session_id"], "user": r["distinct_id"]} for r in session_recordings] == [ {"session": session_id_two, "user": user} @@ -3740,6 +3728,17 @@ def test_top_level_person_property_test_account_filter(self): first_timestamp=self.an_hour_ago, team_id=self.team.id, ) + self.create_event( + "user", + self.an_hour_ago, + properties={ + "event": "something that won't match", + "$session_id": "1", + "$window_id": "1", + "is_internal_user": False, + }, + ) + self.create_event( "user", self.an_hour_ago, @@ -3938,7 +3937,7 @@ def test_event_filter_with_group_filter(self): } ) - self.assertEqual([sr["session_id"] for sr in session_recordings], [session_id]) + assert [sr["session_id"] for sr in session_recordings] == [session_id] (session_recordings, _, _) = self._filter_recordings_by( { @@ -3953,7 +3952,7 @@ def test_event_filter_with_group_filter(self): ], } ) - self.assertEqual([sr["session_id"] for sr in session_recordings], [session_id]) + assert [sr["session_id"] for sr in session_recordings] == [session_id] (session_recordings, _, _) = self._filter_recordings_by( { @@ -3968,7 +3967,7 @@ def test_event_filter_with_group_filter(self): ], } ) - self.assertEqual(session_recordings, []) + assert session_recordings == [] @freeze_time("2021-01-21T20:00:00.000Z") @snapshot_clickhouse_queries diff --git a/posthog/session_recordings/session_recording_api.py b/posthog/session_recordings/session_recording_api.py index 67feca8099ce0..40ea55e7f45ec 100644 --- a/posthog/session_recordings/session_recording_api.py +++ b/posthog/session_recordings/session_recording_api.py @@ -4,6 +4,7 @@ from collections.abc import Generator from contextlib import contextmanager from datetime import UTC, datetime, timedelta +from json import JSONDecodeError from typing import Any, Optional, cast import posthoganalytics @@ -302,7 +303,10 @@ def query_as_params_to_dict(params_dict: dict) -> dict: """ converted = {} for key in params_dict: - converted[key] = json.loads(params_dict[key]) + try: + converted[key] = json.loads(params_dict[key]) if isinstance(params_dict[key], str) else params_dict[key] + except JSONDecodeError: + converted[key] = params_dict[key] converted.pop("as_query", None) return converted From 29de030c7cd6273041ba026ab1c28672b6253922 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 22:35:27 +0000 Subject: [PATCH 12/17] a little more --- .../session_recording_list_from_query.py | 12 +- ...est_session_recording_list_from_query.ambr | 495 ++++++++++++++++++ .../test_session_recording_list_from_query.py | 4 +- 3 files changed, 502 insertions(+), 9 deletions(-) diff --git a/posthog/session_recordings/queries/session_recording_list_from_query.py b/posthog/session_recordings/queries/session_recording_list_from_query.py index 93f48568c319c..565e804f85ef5 100644 --- a/posthog/session_recordings/queries/session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/session_recording_list_from_query.py @@ -199,7 +199,7 @@ def _order_by_clause(self) -> ast.Field: @cached_property def query_date_range(self): return QueryDateRange( - date_range=DateRange(date_from=self._query.date_from, date_to=self._query.date_to, explicitDate=True), + date_range=DateRange(date_from=self._query.date_from, date_to=self._query.date_to), team=self._team, interval=None, now=datetime.now(), @@ -307,9 +307,6 @@ def _where_predicates(self) -> Union[ast.And, ast.Or]: ), ) - if self.ast_operand == ast.Or: - breakpoint() - optional_exprs.append( ast.CompareOperation( op=ast.CompareOperationOp.In, @@ -574,7 +571,8 @@ def _entity_to_expr(self, entity: EventsNode | ActionsNode) -> ast.Expr: if entity.kind == NodeKind.ACTIONS_NODE and entity.id is not None: action = Action.objects.get(pk=entity.id) return action_to_expr(action) - if entity.name is None: + + if entity.event is None: return ast.Constant(value=True) return ast.CompareOperation( @@ -588,9 +586,9 @@ def _event_predicates(self): event_exprs: list[ast.Expr] = [] event_names: set[int | str] = set() - for entity in self.event_entities: + for entity in self.entities: if entity.kind == NodeKind.ACTIONS_NODE: - action = entity.get_action() + action = Action.objects.get(pk=int(entity.id), team__project_id=self._team.project_id) event_names.update([ae for ae in action.get_step_events() if ae and ae not in event_names]) else: if entity.event and entity.event not in event_names: diff --git a/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr index c8f1c29c0c597..5beda081f2018 100644 --- a/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr +++ b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr @@ -1,4 +1,431 @@ # serializer version: 1 +# name: TestSessionRecordingsListFromQuery.test_action_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2023-01-03 23:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'custom-event'), and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0), ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0)))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_action_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2023-01-03 23:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'custom-event'), and(ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0)))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_action_filter.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2023-01-03 23:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(and(equals(events.event, 'custom-event'), and(ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0))), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_action_filter.3 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2023-01-03 23:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(and(equals(events.event, 'custom-event'), and(ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0))), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_all_filters_at_once + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-22 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-04 00:00:00.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-22 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-04 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-21 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-04 00:00:00.000000', 6, 'UTC')), or(equals(events.event, 'custom-event'), equals(events.event, '$pageview'))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING ifNull(greater(duration, 60.0), 0) + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_any_event_filter_with_properties + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), 1) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_any_event_filter_with_properties.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(1, ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_any_event_filter_with_properties.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(1, ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_any_event_filter_with_properties_materialized + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), 1) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_any_event_filter_with_properties_materialized.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(1, ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_any_event_filter_with_properties_materialized.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(1, ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Firefox'), 0))) + GROUP BY events.`$session_id`))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- # name: TestSessionRecordingsListFromQuery.test_basic_query ''' SELECT s.session_id AS session_id, @@ -509,6 +936,74 @@ max_bytes_before_external_group_by=0 ''' # --- +# name: TestSessionRecordingsListFromQuery.test_date_to_filter + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-28 23:59:59.999999', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_date_to_filter.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 23:59:59.999999', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- # name: TestSessionRecordingsListFromQuery.test_duration_filter ''' SELECT s.session_id AS session_id, diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py index f4aa82d2f88ee..9a64ccddc2930 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py @@ -1851,7 +1851,7 @@ def test_date_to_filter(self): ) assert len(session_recordings) == 1 - assert session_recordings[0]["session_id"] == "three days before base time" + assert [s["session_id"] for s in session_recordings] == ["three days before base time"] def test_recording_that_spans_time_bounds(self): user = "test_recording_that_spans_time_bounds-user" @@ -1963,7 +1963,7 @@ def test_all_filters_at_once(self): "person_uuid": str(p.uuid), "date_to": (self.an_hour_ago + relativedelta(days=3)).strftime("%Y-%m-%d"), "date_from": (self.an_hour_ago - relativedelta(days=10)).strftime("%Y-%m-%d"), - "session_recording_duration": '{"type":"recording","key":"duration","value":60,"operator":"gt"}', + "having_predicates": '[{"type":"recording","key":"duration","value":60,"operator":"gt"}]', "events": [ { "id": "$pageview", From 54a0650df10e679668c9fd405d4c3033acb4d33a Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sat, 23 Nov 2024 23:47:18 +0000 Subject: [PATCH 13/17] almost --- frontend/src/queries/schema.json | 7 +- frontend/src/queries/schema.ts | 9 + posthog/schema.py | 6 +- .../session_recording_list_from_query.py | 19 +- ...est_session_recording_list_from_query.ambr | 2317 +++++++++++------ ...est_session_recording_list_from_filters.py | 2 +- .../test_session_recording_list_from_query.py | 4 +- 7 files changed, 1610 insertions(+), 754 deletions(-) diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index 16a677b75b31f..c40767048e1b0 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -11344,6 +11344,7 @@ "type": "array" }, "date_from": { + "default": "-3d", "type": ["string", "null"] }, "date_to": { @@ -11379,10 +11380,12 @@ "type": "integer" }, "operand": { - "$ref": "#/definitions/FilterLogicalOperator" + "$ref": "#/definitions/FilterLogicalOperator", + "default": "AND" }, "order": { - "$ref": "#/definitions/RecordingOrder" + "$ref": "#/definitions/RecordingOrder", + "default": "start_time" }, "person_uuid": { "type": "string" diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts index df891080cd395..34c4138f83535 100644 --- a/frontend/src/queries/schema.ts +++ b/frontend/src/queries/schema.ts @@ -328,6 +328,9 @@ export type RecordingOrder = export interface RecordingsQuery extends DataNode { kind: NodeKind.RecordingsQuery + /** + * @default "-3d" + * */ date_from?: string | null date_to?: string | null events?: FilterType['events'] @@ -336,9 +339,15 @@ export interface RecordingsQuery extends DataNode { console_log_filters?: LogEntryPropertyFilter[] having_predicates?: AnyPropertyFilter[] // duration and snapshot_source filters filter_test_accounts?: boolean + /** + * @default "AND" + * */ operand?: FilterLogicalOperator session_ids?: string[] person_uuid?: string + /** + * @default "start_time" + * */ order?: RecordingOrder limit?: integer offset?: integer diff --git a/posthog/schema.py b/posthog/schema.py index 255c60fbd8a0f..8b2542a414101 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -5839,7 +5839,7 @@ class RecordingsQuery(BaseModel): ) actions: Optional[list[dict[str, Any]]] = None console_log_filters: Optional[list[LogEntryPropertyFilter]] = None - date_from: Optional[str] = None + date_from: Optional[str] = "-3d" date_to: Optional[str] = None events: Optional[list[dict[str, Any]]] = None filter_test_accounts: Optional[bool] = None @@ -5868,8 +5868,8 @@ class RecordingsQuery(BaseModel): default=None, description="Modifiers used when performing the query" ) offset: Optional[int] = None - operand: Optional[FilterLogicalOperator] = None - order: Optional[RecordingOrder] = None + operand: Optional[FilterLogicalOperator] = FilterLogicalOperator.AND_ + order: Optional[RecordingOrder] = RecordingOrder.START_TIME person_uuid: Optional[str] = None properties: Optional[ list[ diff --git a/posthog/session_recordings/queries/session_recording_list_from_query.py b/posthog/session_recordings/queries/session_recording_list_from_query.py index 565e804f85ef5..1fed3e7bbb699 100644 --- a/posthog/session_recordings/queries/session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/session_recording_list_from_query.py @@ -194,7 +194,7 @@ def get_query(self): ) def _order_by_clause(self) -> ast.Field: - return ast.Field(chain=[self._query.order or "start_time"]) + return ast.Field(chain=[self._query.order]) @cached_property def query_date_range(self): @@ -291,18 +291,9 @@ def _where_predicates(self) -> Union[ast.And, ast.Or]: console_logs_subquery = ast.SelectQuery( select=[ast.Field(chain=["log_source_id"])], select_from=ast.JoinExpr(table=ast.Field(chain=["console_logs_log_entries"])), - where=ast.And( + where=self.ast_operand( exprs=[ - self.ast_operand( - exprs=[ - property_to_expr(self._query.console_log_filters, team=self._team), - ] - ), - ast.CompareOperation( - op=ast.CompareOperationOp.Eq, - left=ast.Field(chain=["log_source"]), - right=ast.Constant(value="session_replay"), - ), + property_to_expr(self._query.console_log_filters, team=self._team), ] ), ) @@ -610,7 +601,7 @@ def _select_from_events(self, select_expr: ast.Expr) -> ast.SelectQuery: table=ast.Field(chain=["events"]), ), where=self._where_predicates(), - # having=self._having_predicates(), + having=self._having_predicates(), group_by=[ast.Field(chain=["$session_id"])], ) @@ -713,7 +704,7 @@ def _having_predicates(self) -> ast.Expr: if event_names: return ast.Call( - name="hasAll" if self._query._operand == "AND" else "hasAny", + name="hasAll" if self.property_operand == PropertyOperatorType.AND else "hasAny", args=[ ast.Call(name="groupUniqArray", args=[ast.Field(chain=["event"])]), # KLUDGE: sorting only so that snapshot tests are consistent diff --git a/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr index 5beda081f2018..1c0954bd76a4b 100644 --- a/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr +++ b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr @@ -19,11 +19,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2023-01-03 23:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'custom-event'), and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0), ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0)))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2023-01-01 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-31 23:58:00.000000', 6, 'UTC')), and(equals(events.event, 'custom-event'), and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0), ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0)))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['custom-event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -57,11 +58,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2023-01-03 23:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'custom-event'), and(ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0)))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2023-01-01 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-31 23:58:00.000000', 6, 'UTC')), and(equals(events.event, 'custom-event'), and(ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0)))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['custom-event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -95,11 +97,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2023-01-03 23:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(and(equals(events.event, 'custom-event'), and(ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0))), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2023-01-01 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-31 23:58:00.000000', 6, 'UTC')), and(and(equals(events.event, 'custom-event'), and(ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0))), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['custom-event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -133,11 +136,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2023-01-03 23:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(and(equals(events.event, 'custom-event'), and(ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0))), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2023-01-01 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-14 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-12-31 23:58:00.000000', 6, 'UTC')), and(and(equals(events.event, 'custom-event'), and(ifNull(equals(nullIf(nullIf(events.`$session_id`, ''), 'null'), 'test_action_filter-session-one'), 0), ifNull(equals(nullIf(nullIf(events.`$window_id`, ''), 'null'), 'test_action_filter-window-id'), 0))), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['custom-event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -180,11 +184,12 @@ WHERE equals(person_distinct_id_overrides.team_id, 99999) GROUP BY person_distinct_id_overrides.distinct_id HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-22 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-04 00:00:00.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-22 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-04 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-22 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-04 00:00:00.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-22 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-04 23:59:59.999999', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id FROM events WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-21 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-04 00:00:00.000000', 6, 'UTC')), or(equals(events.event, 'custom-event'), equals(events.event, '$pageview'))) - GROUP BY events.`$session_id`))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview', 'custom-event'])))) GROUP BY s.session_id HAVING ifNull(greater(duration, 60.0), 0) ORDER BY start_time DESC @@ -218,11 +223,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), 1) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), 1) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -256,11 +262,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(1, ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), and(1, ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -294,11 +301,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(1, ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), and(1, ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -332,11 +340,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), 1) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), 1) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -370,11 +379,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(1, ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), and(1, ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -408,11 +418,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(1, ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Firefox'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), and(1, ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Firefox'), 0))) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -446,7 +457,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -480,7 +491,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING ifNull(greater(duration, 60.0), 0) ORDER BY start_time DESC @@ -514,7 +525,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING ifNull(greater(active_seconds, '60'), 0) ORDER BY start_time DESC @@ -548,7 +559,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING ifNull(greater(inactive_seconds, '60'), 0) ORDER BY start_time DESC @@ -582,7 +593,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY active_seconds DESC @@ -616,7 +627,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY console_error_count DESC @@ -650,7 +661,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -684,7 +695,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -718,7 +729,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -752,7 +763,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -956,7 +967,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-28 23:59:59.999999', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-28 23:59:59.999999', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -990,7 +1001,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 23:59:59.999999', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 23:59:59.999999', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1024,7 +1035,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING ifNull(greater(duration, 60.0), 0) ORDER BY start_time DESC @@ -1058,7 +1069,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING ifNull(less(duration, 60.0), 0) ORDER BY start_time DESC @@ -1092,11 +1103,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1130,11 +1142,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$autocapture')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$autocapture')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$autocapture'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1168,11 +1181,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1206,7 +1220,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1240,7 +1254,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING ifNull(greater(duration, 60.0), 0) ORDER BY start_time DESC @@ -1274,7 +1293,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING ifNull(greater(active_seconds, 60.0), 0) ORDER BY start_time DESC @@ -1308,16 +1332,17 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT JOIN - (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(groups.group_properties, 'name'), ''), 'null'), '^"|"$', ''), toTimeZone(groups._timestamp, 'UTC')) AS properties___name, groups.group_type_index AS index, groups.group_key AS key - FROM groups - WHERE and(equals(groups.team_id, 99999), equals(index, 1)) - GROUP BY groups.group_type_index, groups.group_key) AS events__group_1 ON equals(events.`$group_1`, events__group_1.key) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(events__group_1.properties___name, 'org one'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT JOIN + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(groups.group_properties, 'name'), ''), 'null'), '^"|"$', ''), toTimeZone(groups._timestamp, 'UTC')) AS properties___name, groups.group_type_index AS index, groups.group_key AS key + FROM groups + WHERE and(equals(groups.team_id, 99999), equals(index, 1)) + GROUP BY groups.group_type_index, groups.group_key) AS events__group_1 ON equals(events.`$group_1`, events__group_1.key) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(events__group_1.properties___name, 'org one'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1351,16 +1376,17 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT JOIN - (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(groups.group_properties, 'name'), ''), 'null'), '^"|"$', ''), toTimeZone(groups._timestamp, 'UTC')) AS properties___name, groups.group_type_index AS index, groups.group_key AS key - FROM groups - WHERE and(equals(groups.team_id, 99999), equals(index, 1)) - GROUP BY groups.group_type_index, groups.group_key) AS events__group_1 ON equals(events.`$group_1`, events__group_1.key) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__group_1.properties___name, 'org one'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT JOIN + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(groups.group_properties, 'name'), ''), 'null'), '^"|"$', ''), toTimeZone(groups._timestamp, 'UTC')) AS properties___name, groups.group_type_index AS index, groups.group_key AS key + FROM groups + WHERE and(equals(groups.team_id, 99999), equals(index, 1)) + GROUP BY groups.group_type_index, groups.group_key) AS events__group_1 ON equals(events.`$group_1`, events__group_1.key) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__group_1.properties___name, 'org one'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1394,16 +1420,17 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT JOIN - (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(groups.group_properties, 'name'), ''), 'null'), '^"|"$', ''), toTimeZone(groups._timestamp, 'UTC')) AS properties___name, groups.group_type_index AS index, groups.group_key AS key - FROM groups - WHERE and(equals(groups.team_id, 99999), equals(index, 2)) - GROUP BY groups.group_type_index, groups.group_key) AS events__group_2 ON equals(events.`$group_2`, events__group_2.key) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__group_2.properties___name, 'org one'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT JOIN + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(groups.group_properties, 'name'), ''), 'null'), '^"|"$', ''), toTimeZone(groups._timestamp, 'UTC')) AS properties___name, groups.group_type_index AS index, groups.group_key AS key + FROM groups + WHERE and(equals(groups.team_id, 99999), equals(index, 2)) + GROUP BY groups.group_type_index, groups.group_key) AS events__group_2 ON equals(events.`$group_2`, events__group_2.key) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__group_2.properties___name, 'org one'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1437,11 +1464,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1475,26 +1503,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'bla'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1528,26 +1557,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1581,11 +1611,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1619,26 +1650,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'bla'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1672,26 +1704,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1725,26 +1758,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'bla'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'bla'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1778,26 +1812,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'something else'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'something else'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1831,11 +1866,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1869,11 +1905,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1907,11 +1944,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1945,11 +1983,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Firefox'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Firefox'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1983,11 +2022,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2021,11 +2061,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$autocapture')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$autocapture')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$autocapture'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2059,11 +2100,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2097,11 +2139,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Firefox'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2135,11 +2178,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['a_different_event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2173,11 +2217,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Safari'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Safari'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['a_different_event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2211,11 +2256,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2249,11 +2295,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Firefox'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, '$pageview'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Firefox'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2287,11 +2334,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['a_different_event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2325,11 +2373,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Safari'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(equals(events.event, 'a_different_event'), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Safari'), 0))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['a_different_event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2363,26 +2412,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview'), and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1)) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2416,11 +2466,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2454,26 +2505,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), and(ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview'), and(ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1)) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2507,11 +2559,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2545,7 +2598,132 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), or(equals(events.event, '$pageview'), equals(events.event, '$pageleave'))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageleave', '$pageview'])))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_by_console_text + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.message AS message + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(or(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.level, 'error'), 0)), ifNull(ilike(console_logs_log_entries.message, '%message 4%'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_by_console_text.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.message AS message + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(or(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.level, 'error'), 0)), ifNull(ilike(console_logs_log_entries.message, '%message 5%'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_for_recordings_by_console_text.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.message AS message + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(ilike(console_logs_log_entries.message, '%message 5%'), 0))))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2579,7 +2757,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING ifNull(equals(argMinMerge(s.snapshot_source), 'web'), 0) ORDER BY start_time DESC @@ -2613,7 +2791,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING ifNull(equals(argMinMerge(s.snapshot_source), 'mobile'), 0) ORDER BY start_time DESC @@ -2647,13 +2825,13 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT console_logs_log_entries.log_source_id AS log_source_id - FROM - (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source - FROM log_entries - WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries - WHERE and(ifNull(equals(console_logs_log_entries.level, 'error'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE ifNull(equals(console_logs_log_entries.level, 'error'), 0)))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2687,13 +2865,13 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT console_logs_log_entries.log_source_id AS log_source_id - FROM - (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source - FROM log_entries - WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries - WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE ifNull(equals(console_logs_log_entries.level, 'info'), 0)))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2727,13 +2905,13 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT console_logs_log_entries.log_source_id AS log_source_id - FROM - (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source - FROM log_entries - WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries - WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE ifNull(equals(console_logs_log_entries.level, 'info'), 0)))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2767,13 +2945,13 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT console_logs_log_entries.log_source_id AS log_source_id - FROM - (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source - FROM log_entries - WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries - WHERE and(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE ifNull(equals(console_logs_log_entries.level, 'warn'), 0)))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2807,13 +2985,13 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT console_logs_log_entries.log_source_id AS log_source_id - FROM - (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source - FROM log_entries - WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries - WHERE and(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE ifNull(equals(console_logs_log_entries.level, 'warn'), 0)))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2847,13 +3025,13 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT console_logs_log_entries.log_source_id AS log_source_id - FROM - (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source - FROM log_entries - WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries - WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE ifNull(equals(console_logs_log_entries.level, 'info'), 0)))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2887,13 +3065,13 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT console_logs_log_entries.log_source_id AS log_source_id - FROM - (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source - FROM log_entries - WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries - WHERE and(or(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.level, 'error'), 0)), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE or(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.level, 'error'), 0))))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2927,13 +3105,13 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT console_logs_log_entries.log_source_id AS log_source_id - FROM - (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.log_source AS log_source - FROM log_entries - WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries - WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(equals(console_logs_log_entries.log_source, 'session_replay'), 0))))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE ifNull(equals(console_logs_log_entries.level, 'info'), 0)))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -2970,7 +3148,8 @@ WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001' /* ... */]) + ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001' /* ... */], + ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3007,7 +3186,8 @@ WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001' /* ... */]) + ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001' /* ... */], + ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3061,21 +3241,21 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-08-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), 0), in(s.distinct_id, - (SELECT person_distinct_id2.distinct_id AS distinct_id - FROM person_distinct_id2 - WHERE and(equals(person_distinct_id2.team_id, 99999), in(person_distinct_id2.distinct_id, - (SELECT person_distinct_id2.distinct_id AS distinct_id - FROM person_distinct_id2 - WHERE and(equals(person_distinct_id2.team_id, 99999), 1, in(person_distinct_id2.person_id, - (SELECT cohortpeople.person_id AS person_id - FROM cohortpeople - WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) - GROUP BY person_distinct_id2.distinct_id - HAVING and(ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0), in(person_distinct_id2.person_id, - (SELECT cohortpeople.person_id AS person_id - FROM cohortpeople - WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-08-18 00:00:00.000000', 6, 'UTC')), 0), in(s.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), in(person_distinct_id2.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), 1, in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) + GROUP BY person_distinct_id2.distinct_id + HAVING and(ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0), in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3089,7 +3269,27 @@ max_bytes_before_external_group_by=0 ''' # --- -# name: TestSessionRecordingsListFromQuery.test_filter_with_person_properties_exact +# name: TestSessionRecordingsListFromQuery.test_filter_with_events_and_cohorts + ''' + + SELECT count(DISTINCT person_id) + FROM cohortpeople + WHERE team_id = 99999 + AND cohort_id = 99999 + AND version = NULL + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_events_and_cohorts.1 + ''' + /* cohort_calculation: */ + SELECT count(DISTINCT person_id) + FROM cohortpeople + WHERE team_id = 99999 + AND cohort_id = 99999 + AND version = 0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_events_and_cohorts.2 ''' SELECT s.session_id AS session_id, any(s.team_id), @@ -3106,29 +3306,29 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-08-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla@gmail.com'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-08-18 00:00:00.000000', 6, 'UTC')), 0), and(in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-08-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview']))), in(s.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), in(person_distinct_id2.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), 1, in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) + GROUP BY person_distinct_id2.distinct_id + HAVING and(ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0), in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0))))))))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3142,7 +3342,7 @@ max_bytes_before_external_group_by=0 ''' # --- -# name: TestSessionRecordingsListFromQuery.test_filter_with_person_properties_not_contains +# name: TestSessionRecordingsListFromQuery.test_filter_with_events_and_cohorts.3 ''' SELECT s.session_id AS session_id, any(s.team_id), @@ -3159,29 +3359,29 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-08-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(notILike(events__person.properties___email, '%gmail.com%'), 1)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-08-18 00:00:00.000000', 6, 'UTC')), 0), and(in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-08-17 23:58:00.000000', 6, 'UTC')), equals(events.event, 'custom_event')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['custom_event']))), in(s.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), in(person_distinct_id2.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), 1, in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) + GROUP BY person_distinct_id2.distinct_id + HAVING and(ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0), in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0))))))))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3195,7 +3395,7 @@ max_bytes_before_external_group_by=0 ''' # --- -# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters +# name: TestSessionRecordingsListFromQuery.test_filter_with_person_properties_exact ''' SELECT s.session_id AS session_id, any(s.team_id), @@ -3215,20 +3415,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT DISTINCT events.`$session_id` AS `$session_id` - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla@gmail.com'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3242,7 +3449,416 @@ max_bytes_before_external_group_by=0 ''' # --- -# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters.1 +# name: TestSessionRecordingsListFromQuery.test_filter_with_person_properties_not_contains + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(notILike(events__person.properties___email, '%gmail.com%'), 1)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_static_and_dynamic_cohort_properties + ''' + + SELECT count(DISTINCT person_id) + FROM person_static_cohort + WHERE team_id = 99999 + AND cohort_id = 99999 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_static_and_dynamic_cohort_properties.1 + ''' + + SELECT count(DISTINCT person_id) + FROM cohortpeople + WHERE team_id = 99999 + AND cohort_id = 99999 + AND version = NULL + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_static_and_dynamic_cohort_properties.2 + ''' + /* cohort_calculation: */ + SELECT count(DISTINCT person_id) + FROM cohortpeople + WHERE team_id = 99999 + AND cohort_id = 99999 + AND version = 0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_static_and_dynamic_cohort_properties.3 + ''' + + SELECT count(DISTINCT person_id) + FROM cohortpeople + WHERE team_id = 99999 + AND cohort_id = 99999 + AND version = NULL + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_static_and_dynamic_cohort_properties.4 + ''' + /* cohort_calculation: */ + SELECT count(DISTINCT person_id) + FROM cohortpeople + WHERE team_id = 99999 + AND cohort_id = 99999 + AND version = 0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_static_and_dynamic_cohort_properties.5 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-08-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-08-18 00:00:00.000000', 6, 'UTC')), 0), in(s.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), in(person_distinct_id2.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), 1, in(person_distinct_id2.person_id, + (SELECT person_static_cohort.person_id AS person_id + FROM person_static_cohort + WHERE and(equals(person_static_cohort.team_id, 99999), equals(person_static_cohort.cohort_id, 99999)))))))) + GROUP BY person_distinct_id2.distinct_id + HAVING and(ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0), in(person_distinct_id2.person_id, + (SELECT person_static_cohort.person_id AS person_id + FROM person_static_cohort + WHERE and(equals(person_static_cohort.team_id, 99999), equals(person_static_cohort.cohort_id, 99999)))))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_static_and_dynamic_cohort_properties.6 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-08-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-08-18 00:00:00.000000', 6, 'UTC')), 0), in(s.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), in(person_distinct_id2.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), 1, in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) + GROUP BY person_distinct_id2.distinct_id + HAVING and(ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0), in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_filter_with_static_and_dynamic_cohort_properties.7 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-08-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-07-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-08-18 00:00:00.000000', 6, 'UTC')), 0), in(s.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), in(person_distinct_id2.distinct_id, + (SELECT person_distinct_id2.distinct_id AS distinct_id + FROM person_distinct_id2 + WHERE and(equals(person_distinct_id2.team_id, 99999), 1, and(in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))), in(person_distinct_id2.person_id, + (SELECT person_static_cohort.person_id AS person_id + FROM person_static_cohort + WHERE and(equals(person_static_cohort.team_id, 99999), equals(person_static_cohort.cohort_id, 99999))))))))) + GROUP BY person_distinct_id2.distinct_id + HAVING and(ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0), and(in(person_distinct_id2.person_id, + (SELECT cohortpeople.person_id AS person_id + FROM cohortpeople + WHERE and(equals(cohortpeople.team_id, 99999), equals(cohortpeople.cohort_id, 99999), equals(cohortpeople.version, 0)))), in(person_distinct_id2.person_id, + (SELECT person_static_cohort.person_id AS person_id + FROM person_static_cohort + WHERE and(equals(person_static_cohort.team_id, 99999), equals(person_static_cohort.cohort_id, 99999))))))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_multiple_event_filters + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), or(equals(events.event, '$pageview'), equals(events.event, 'new-event'))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview', 'new-event'])))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_multiple_event_filters.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), or(equals(events.event, '$pageview'), equals(events.event, 'new-event2'))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview', 'new-event2'])))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_multiple_event_filters.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), or(equals(events.event, '$pageview'), equals(events.event, 'new-event2'))) + GROUP BY events.`$session_id` + HAVING hasAny(groupUniqArray(events.event), ['$pageview', 'new-event2'])))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_multiple_event_filters.3 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), or(and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0)), and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'bar'), ''), 'null'), '^"|"$', ''), 'foo'), 0)))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_multiple_event_filters.4 ''' SELECT s.session_id AS session_id, any(s.team_id), @@ -3262,20 +3878,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT DISTINCT events.`$session_id` AS `$session_id` - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), or(and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0)), and(equals(events.event, 'new-event'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0)))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview', 'new-event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3289,7 +3897,7 @@ max_bytes_before_external_group_by=0 ''' # --- -# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters.2 +# name: TestSessionRecordingsListFromQuery.test_multiple_event_filters.5 ''' SELECT s.session_id AS session_id, any(s.team_id), @@ -3309,11 +3917,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, ['session_id_one']), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), in(events.`$session_id`, ['session_id_one'])) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), or(and(equals(events.event, '$pageview'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0)), and(equals(events.event, 'new-event'), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0)))) + GROUP BY events.`$session_id` + HAVING hasAny(groupUniqArray(events.event), ['$pageview', 'new-event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3327,7 +3936,7 @@ max_bytes_before_external_group_by=0 ''' # --- -# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters.3 +# name: TestSessionRecordingsListFromQuery.test_operand_or_event_filters ''' SELECT s.session_id AS session_id, any(s.team_id), @@ -3347,11 +3956,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, ['session_id_two']), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview'), in(events.`$session_id`, ['session_id_two'])) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), or(equals(events.event, '$pageview'), equals(events.event, 'custom_event'))) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview', 'custom_event'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3365,7 +3975,46 @@ max_bytes_before_external_group_by=0 ''' # --- -# name: TestSessionRecordingsListFromQuery.test_operand_or_person_filters +# name: TestSessionRecordingsListFromQuery.test_operand_or_event_filters.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), or(equals(events.event, '$pageview'), equals(events.event, 'custom_event'))) + GROUP BY events.`$session_id` + HAVING hasAny(groupUniqArray(events.event), ['$pageview', 'custom_event'])))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters ''' SELECT s.session_id AS session_id, any(s.team_id), @@ -3386,7 +4035,7 @@ round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id + (SELECT DISTINCT events.`$session_id` AS `$session_id` FROM events LEFT OUTER JOIN (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id @@ -3394,17 +4043,12 @@ WHERE equals(person_distinct_id_overrides.team_id, 99999) GROUP BY person_distinct_id_overrides.distinct_id HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), and(ifNull(equals(events__person.properties___email, 'test@posthog.com'), 0), ifNull(equals(events__person.properties___email, 'david@posthog.com'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAny(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3418,7 +4062,7 @@ max_bytes_before_external_group_by=0 ''' # --- -# name: TestSessionRecordingsListFromQuery.test_operand_or_person_filters.1 +# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters.1 ''' SELECT s.session_id AS session_id, any(s.team_id), @@ -3439,7 +4083,7 @@ round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id + (SELECT DISTINCT events.`$session_id` AS `$session_id` FROM events LEFT OUTER JOIN (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id @@ -3447,17 +4091,198 @@ WHERE equals(person_distinct_id_overrides.team_id, 99999) GROUP BY person_distinct_id_overrides.distinct_id HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), or(ifNull(equals(events__person.properties___email, 'test@posthog.com'), 0), ifNull(equals(events__person.properties___email, 'david@posthog.com'), 0))) - GROUP BY events.`$session_id`))) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAny(groupUniqArray(events.event), ['$pageview'])))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, ['session_id_one']), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview'), in(events.`$session_id`, ['session_id_one'])) + GROUP BY events.`$session_id` + HAVING hasAny(groupUniqArray(events.event), ['$pageview'])))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters.3 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, ['session_id_two']), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview'), in(events.`$session_id`, ['session_id_two'])) + GROUP BY events.`$session_id` + HAVING hasAny(groupUniqArray(events.event), ['$pageview'])))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_person_filters + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), and(ifNull(equals(events__person.properties___email, 'test@posthog.com'), 0), ifNull(equals(events__person.properties___email, 'david@posthog.com'), 0))) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_person_filters.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), or(ifNull(equals(events__person.properties___email, 'test@posthog.com'), 0), ifNull(equals(events__person.properties___email, 'david@posthog.com'), 0))) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3491,7 +4316,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3525,7 +4350,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY mouse_activity_count DESC @@ -3568,7 +4393,7 @@ WHERE equals(person_distinct_id_overrides.team_id, 99999) GROUP BY person_distinct_id_overrides.distinct_id HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`))))) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3602,7 +4427,7 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3636,11 +4461,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3674,11 +4500,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(not(match(toString(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$host'), ''), 'null'), '^"|"$', '')), '^(localhost|127\\.0\\.0\\.1)($|:)')), 1)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(not(match(toString(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$host'), ''), 'null'), '^"|"$', '')), '^(localhost|127\\.0\\.0\\.1)($|:)')), 1)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3712,11 +4539,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3750,11 +4578,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(not(match(toString(nullIf(nullIf(events.`mat_$host`, ''), 'null')), '^(localhost|127\\.0\\.0\\.1)($|:)')), 1)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(not(match(toString(nullIf(nullIf(events.`mat_$host`, ''), 'null')), '^(localhost|127\\.0\\.0\\.1)($|:)')), 1)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3788,11 +4617,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3826,11 +4656,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'false'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'false'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3864,11 +4695,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3902,11 +4734,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'false'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'false'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3940,11 +4773,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -3978,11 +4812,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4016,11 +4851,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4054,11 +4890,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4092,11 +4929,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4130,11 +4968,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'true'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'is_internal_user'), ''), 'null'), '^"|"$', ''), 'true'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4168,11 +5007,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4206,11 +5046,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'true'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'true'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4244,11 +5085,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4282,26 +5124,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4335,11 +5178,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4373,26 +5217,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4426,7 +5271,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4460,26 +5310,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, 'email'), ''), 'null'), '^"|"$', '') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4513,11 +5364,12 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), equals(events.event, '$pageview')) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), equals(events.event, '$pageview')) + GROUP BY events.`$session_id` + HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -4551,26 +5403,27 @@ ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-21 19:55:00.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), in(s.session_id, - (SELECT events.`$session_id` AS session_id - FROM events - LEFT OUTER JOIN - (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id - FROM person_distinct_id_overrides - WHERE equals(person_distinct_id_overrides.team_id, 99999) - GROUP BY person_distinct_id_overrides.distinct_id - HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) - LEFT JOIN - (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email - FROM person - WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), - (SELECT person.id AS id, max(person.version) AS version - FROM person - WHERE equals(person.team_id, 99999) - GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) - GROUP BY events.`$session_id`))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-18 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-31 20:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-17 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py index 3633092a40e25..5fa06103c1d20 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py @@ -3105,7 +3105,7 @@ def test_filter_for_recordings_by_console_text(self): (session_recordings, _, _) = self._filter_recordings_by( { # there are 5 warn and 4 error logs, message 4 matches in both - "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 4", "operator": "exact", "type": "log_entry"}]', + "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 4", "operator": "icontains", "type": "log_entry"}]', "operand": "OR", } ) diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py index 9a64ccddc2930..583ca94c2ad48 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py @@ -2387,7 +2387,7 @@ def test_filter_with_events_and_cohorts(self): } ) - assert len(session_recordings) == 0 + assert [s["session_id"] for s in session_recordings] == [] (session_recordings, _, _) = self._filter_recordings_by( { @@ -3095,7 +3095,7 @@ def test_filter_for_recordings_by_console_text(self): (session_recordings, _, _) = self._filter_recordings_by( { # there are 5 warn and 4 error logs, message 4 matches in both - "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 4", "operator": "exact", "type": "log_entry"}]', + "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 4", "operator": "icontains", "type": "log_entry"}]', "operand": "OR", } ) From 57b6cda276533a28d80d03969019d77d7dcb78d3 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2024 00:03:23 +0000 Subject: [PATCH 14/17] Update query snapshots --- ...est_session_recording_list_from_query.ambr | 1613 +++++++++++++++++ 1 file changed, 1613 insertions(+) create mode 100644 ee/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr diff --git a/ee/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr b/ee/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr new file mode 100644 index 0000000000000..45958980190d0 --- /dev/null +++ b/ee/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr @@ -0,0 +1,1613 @@ +# serializer version: 1 +# name: TestClickhouseSessionRecordingsListFromQuery.test_effect_of_poe_settings_on_query_generated_0_test_poe_v1_still_falls_back_to_person_subquery + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, %(hogql_val_0)s)) AS start_time, + max(toTimeZone(s.max_last_timestamp, %(hogql_val_1)s)) AS end_time, + dateDiff(%(hogql_val_2)s, start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_7)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_8)s), now64(6, %(hogql_val_9)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null'), %(hogql_val_11)s), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 50000 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_effect_of_poe_settings_on_query_generated_1_test_poe_being_unavailable_we_fall_back_to_person_id_overrides + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, %(hogql_val_0)s)) AS start_time, + max(toTimeZone(s.max_last_timestamp, %(hogql_val_1)s)) AS end_time, + dateDiff(%(hogql_val_2)s, start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, %(hogql_val_7)s), ''), 'null'), '^"|"$', ''), person.version) AS properties___rgInternal, person.id AS id + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, %(hogql_val_8)s), person.version), plus(now64(6, %(hogql_val_9)s), toIntervalDay(1))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), now64(6, %(hogql_val_12)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_13)s), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_14)s), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 50000 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_effect_of_poe_settings_on_query_generated_2_test_poe_being_unavailable_we_fall_back_to_person_subquery_but_still_use_mat_props + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, %(hogql_val_0)s)) AS start_time, + max(toTimeZone(s.max_last_timestamp, %(hogql_val_1)s)) AS end_time, + dateDiff(%(hogql_val_2)s, start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, %(hogql_val_7)s), ''), 'null'), '^"|"$', ''), person.version) AS properties___rgInternal, person.id AS id + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, %(hogql_val_8)s), person.version), plus(now64(6, %(hogql_val_9)s), toIntervalDay(1))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), now64(6, %(hogql_val_12)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_13)s), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_14)s), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 50000 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_effect_of_poe_settings_on_query_generated_3_test_allow_denormalised_props_fix_does_not_stop_all_poe_processing + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, %(hogql_val_0)s)) AS start_time, + max(toTimeZone(s.max_last_timestamp, %(hogql_val_1)s)) AS end_time, + dateDiff(%(hogql_val_2)s, start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_7)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_8)s), now64(6, %(hogql_val_9)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null'), %(hogql_val_11)s), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 50000 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_effect_of_poe_settings_on_query_generated_4_test_poe_v2_available_person_properties_are_used_in_replay_listing + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, %(hogql_val_0)s)) AS start_time, + max(toTimeZone(s.max_last_timestamp, %(hogql_val_1)s)) AS end_time, + dateDiff(%(hogql_val_2)s, start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_7)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_8)s), now64(6, %(hogql_val_9)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null'), %(hogql_val_11)s), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 50000 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_00_poe_v2_and_materialized_columns_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_00_poe_v2_and_materialized_columns_allowed_with_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_email, ''), 'null'), 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_01_poe_v2_and_materialized_columns_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_01_poe_v2_and_materialized_columns_allowed_without_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_email, ''), 'null'), 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_02_poe_v2_and_materialized_columns_off_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_02_poe_v2_and_materialized_columns_off_with_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_email, ''), 'null'), 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_03_poe_v2_and_materialized_columns_off_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_03_poe_v2_and_materialized_columns_off_without_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_email, ''), 'null'), 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_04_poe_off_and_materialized_columns_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_04_poe_off_and_materialized_columns_allowed_with_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_05_poe_off_and_materialized_columns_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_05_poe_off_and_materialized_columns_allowed_without_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_06_poe_off_and_materialized_columns_not_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_06_poe_off_and_materialized_columns_not_allowed_with_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_07_poe_off_and_materialized_columns_not_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_07_poe_off_and_materialized_columns_not_allowed_without_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + LEFT JOIN + (SELECT person.id AS id, nullIf(nullIf(person.pmat_email, ''), 'null') AS properties___email + FROM person + WHERE and(equals(person.team_id, 99999), ifNull(in(tuple(person.id, person.version), + (SELECT person.id AS id, max(person.version) AS version + FROM person + WHERE equals(person.team_id, 99999) + GROUP BY person.id + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, 'UTC'), person.version), plus(now64(6, 'UTC'), toIntervalDay(1))), 0)))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___email, 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_08_poe_v1_and_materialized_columns_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_08_poe_v1_and_materialized_columns_allowed_with_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_email, ''), 'null'), 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_09_poe_v1_and_materialized_columns_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_09_poe_v1_and_materialized_columns_allowed_without_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_email, ''), 'null'), 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_10_poe_v1_and_not_materialized_columns_not_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_10_poe_v1_and_not_materialized_columns_not_allowed_with_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_email, ''), 'null'), 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_11_poe_v1_and_not_materialized_columns_not_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_event_filter_with_person_properties_materialized_11_poe_v1_and_not_materialized_columns_not_allowed_without_materialization.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT events.`$session_id` AS session_id + FROM events + WHERE and(equals(events.team_id, 99999), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_email, ''), 'null'), 'bla'), 0)) + GROUP BY events.`$session_id` + HAVING 1))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_00_poe_v2_and_materialized_columns_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.person_id, '00000000-0000-0000-0000-000000000000'), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_01_poe_v2_and_materialized_columns_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.person_id, '00000000-0000-0000-0000-000000000000'), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_02_poe_v2_and_materialized_columns_off_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.person_id, '00000000-0000-0000-0000-000000000000'), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_03_poe_v2_and_materialized_columns_off_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.person_id, '00000000-0000-0000-0000-000000000000'), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_04_poe_off_and_materialized_columns_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_05_poe_off_and_materialized_columns_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_06_poe_off_and_materialized_columns_not_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_07_poe_off_and_materialized_columns_not_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_08_poe_v1_and_materialized_columns_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_09_poe_v1_and_materialized_columns_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_10_poe_v1_and_not_materialized_columns_not_allowed_with_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestClickhouseSessionRecordingsListFromQuery.test_person_id_filter_11_poe_v1_and_not_materialized_columns_not_allowed_without_materialization + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT DISTINCT events.`$session_id` AS `$session_id` + FROM events + LEFT OUTER JOIN + (SELECT argMax(person_distinct_id_overrides.person_id, person_distinct_id_overrides.version) AS person_id, person_distinct_id_overrides.distinct_id AS distinct_id + FROM person_distinct_id_overrides + WHERE equals(person_distinct_id_overrides.team_id, 99999) + GROUP BY person_distinct_id_overrides.distinct_id + HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) + WHERE and(equals(events.team_id, 99999), ifNull(equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), '00000000-0000-0000-0000-000000000000'), 0), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0)) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- From 1a77a199a994ec6610048610b584d33db7846b89 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Sun, 24 Nov 2024 10:35:30 +0000 Subject: [PATCH 15/17] test should not be passing --- .../session_recording_list_from_filters.py | 13 +- ...est_session_recording_list_from_query.ambr | 200 ++++++++++++++++++ ...est_session_recording_list_from_filters.py | 19 +- .../test_session_recording_list_from_query.py | 56 ++++- 4 files changed, 263 insertions(+), 25 deletions(-) diff --git a/posthog/session_recordings/queries/session_recording_list_from_filters.py b/posthog/session_recordings/queries/session_recording_list_from_filters.py index 46fb0664c56b7..e509616aa6dd0 100644 --- a/posthog/session_recordings/queries/session_recording_list_from_filters.py +++ b/posthog/session_recordings/queries/session_recording_list_from_filters.py @@ -262,18 +262,9 @@ def _where_predicates(self) -> Union[ast.And, ast.Or]: console_logs_subquery = ast.SelectQuery( select=[ast.Field(chain=["log_source_id"])], select_from=ast.JoinExpr(table=ast.Field(chain=["console_logs_log_entries"])), - where=ast.And( + where=self._filter.ast_operand( exprs=[ - self._filter.ast_operand( - exprs=[ - property_to_expr(self._filter.console_log_filters, team=self._team), - ] - ), - ast.CompareOperation( - op=ast.CompareOperationOp.Eq, - left=ast.Field(chain=["log_source"]), - right=ast.Constant(value="session_replay"), - ), + property_to_expr(self._filter.console_log_filters, team=self._team), ] ), ) diff --git a/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr index 1c0954bd76a4b..66e9fc5dc3a8b 100644 --- a/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr +++ b/posthog/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_query.ambr @@ -4014,6 +4014,206 @@ max_bytes_before_external_group_by=0 ''' # --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_filters + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.message AS message + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.message, 'random'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_filters.1 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.message AS message + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'info'), 0), ifNull(equals(console_logs_log_entries.message, 'random'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_filters.2 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE ifNull(equals(console_logs_log_entries.level, 'warn'), 0)))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_filters.3 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.message AS message + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE ifNull(equals(console_logs_log_entries.message, 'random'), 0)))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- +# name: TestSessionRecordingsListFromQuery.test_operand_or_filters.4 + ''' + SELECT s.session_id AS session_id, + any(s.team_id), + any(s.distinct_id), + min(toTimeZone(s.min_first_timestamp, 'UTC')) AS start_time, + max(toTimeZone(s.max_last_timestamp, 'UTC')) AS end_time, + dateDiff('SECOND', start_time, end_time) AS duration, + argMinMerge(s.first_url) AS first_url, + sum(s.click_count) AS click_count, + sum(s.keypress_count) AS keypress_count, + sum(s.mouse_activity_count) AS mouse_activity_count, + divide(sum(s.active_milliseconds), 1000) AS active_seconds, + minus(duration, active_seconds) AS inactive_seconds, + sum(s.console_log_count) AS console_log_count, + sum(s.console_warn_count) AS console_warn_count, + sum(s.console_error_count) AS console_error_count, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-29 00:00:00.000000', 6, 'UTC')), 0), in(s.session_id, + (SELECT console_logs_log_entries.log_source_id AS log_source_id + FROM + (SELECT log_entries.log_source_id AS log_source_id, log_entries.level AS level, log_entries.message AS message + FROM log_entries + WHERE and(equals(log_entries.team_id, 99999), equals(log_entries.log_source, 'session_replay'))) AS console_logs_log_entries + WHERE and(ifNull(equals(console_logs_log_entries.level, 'warn'), 0), ifNull(equals(console_logs_log_entries.message, 'random'), 0))))) + GROUP BY s.session_id + HAVING 1 + ORDER BY start_time DESC + LIMIT 51 + OFFSET 0 SETTINGS readonly=2, + max_execution_time=60, + allow_experimental_object_type=1, + format_csv_allow_double_quotes=0, + max_ast_elements=4000000, + max_expanded_ast_elements=4000000, + max_bytes_before_external_group_by=0 + ''' +# --- # name: TestSessionRecordingsListFromQuery.test_operand_or_mandatory_filters ''' SELECT s.session_id AS session_id, diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py index 5fa06103c1d20..94187ec9fc1b0 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py @@ -3037,6 +3037,7 @@ def test_filter_for_recordings_by_console_text(self): with_warns_session_id = "with-warns-session" with_errors_session_id = "with-errors-session" with_two_session_id = "with-two-session" + with_no_matches_session_id = "with-no-matches-session" produce_replay_summary( distinct_id="user", @@ -3096,11 +3097,21 @@ def test_filter_for_recordings_by_console_text(self): "error message 1", "error message 2", "error message 3", - "error message 4", ], "info": ["log message 1", "log message 2", "log message 3"], }, ) + produce_replay_summary( + distinct_id="user", + session_id=with_no_matches_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_error_count=4, + console_log_count=3, + log_messages={ + "info": ["log message 1", "log message 2", "log message 3"], + }, + ) (session_recordings, _, _) = self._filter_recordings_by( { @@ -3111,11 +3122,7 @@ def test_filter_for_recordings_by_console_text(self): ) assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( - [ - with_errors_session_id, - with_two_session_id, - with_warns_session_id, - ] + [with_errors_session_id, with_two_session_id, with_warns_session_id, with_logs_session_id] ) (session_recordings, _, _) = self._filter_recordings_by( diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py index 583ca94c2ad48..0770b66554b56 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py @@ -1614,9 +1614,9 @@ def test_operand_or_filters(self): session_id=session_with_both_log_filters, first_timestamp=self.an_hour_ago, team_id=self.team.id, - console_warn_count=1, + console_log_count=1, log_messages={ - "warn": [ + "info": [ "random", ], }, @@ -1635,12 +1635,42 @@ def test_operand_or_filters(self): }, ) + # neither has WARN and message random (session_recordings, _, _) = self._filter_recordings_by( { "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', "operand": "AND", } ) + assert len(session_recordings) == 0 + + # AND only matches one recording + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_with_both_log_filters + + # only one is warn level + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) + assert len(session_recordings) == 1 + assert session_recordings[0]["session_id"] == session_with_one_log_filter + + # only one has message RANDOM + (session_recordings, _, _) = self._filter_recordings_by( + { + "console_log_filters": '[{"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "operand": "AND", + } + ) assert len(session_recordings) == 1 assert session_recordings[0]["session_id"] == session_with_both_log_filters @@ -1650,7 +1680,8 @@ def test_operand_or_filters(self): "operand": "OR", } ) - assert len(session_recordings) == 2 + # TODO this should match both but I want to compare the generated SQL + assert len(session_recordings) == 0 @snapshot_clickhouse_queries def test_operand_or_mandatory_filters(self): @@ -3027,6 +3058,7 @@ def test_filter_for_recordings_by_console_text(self): with_warns_session_id = "with-warns-session" with_errors_session_id = "with-errors-session" with_two_session_id = "with-two-session" + with_no_matches_session_id = "with-no-matches-session" produce_replay_summary( distinct_id="user", @@ -3092,6 +3124,18 @@ def test_filter_for_recordings_by_console_text(self): }, ) + produce_replay_summary( + distinct_id="user", + session_id=with_no_matches_session_id, + first_timestamp=self.an_hour_ago, + team_id=self.team.id, + console_error_count=4, + console_log_count=3, + log_messages={ + "info": ["log message 1", "log message 2", "log message 3"], + }, + ) + (session_recordings, _, _) = self._filter_recordings_by( { # there are 5 warn and 4 error logs, message 4 matches in both @@ -3101,11 +3145,7 @@ def test_filter_for_recordings_by_console_text(self): ) assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( - [ - with_errors_session_id, - with_two_session_id, - with_warns_session_id, - ] + [with_errors_session_id, with_two_session_id, with_warns_session_id, with_logs_session_id] ) (session_recordings, _, _) = self._filter_recordings_by( From efcfad2c527acf12c7842df6859983c07c5a7ff2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2024 10:56:02 +0000 Subject: [PATCH 16/17] Update UI snapshots for `chromium` (2) --- ...nents-activitylog--team-activity--dark.png | Bin 55445 -> 55899 bytes ...ents-activitylog--team-activity--light.png | Bin 55006 -> 55164 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/frontend/__snapshots__/components-activitylog--team-activity--dark.png b/frontend/__snapshots__/components-activitylog--team-activity--dark.png index d7bc111b04eb0bf500e803e2c68d7cff9375c3e8..69ffefc47369b08110af4c7d52a702ff3b1b3911 100644 GIT binary patch literal 55899 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 literal 55445 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%0D(1Um-cZ|Qc|KVtv%ZHmlGfF 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 Date: Sun, 24 Nov 2024 11:14:13 +0000 Subject: [PATCH 17/17] fiddling --- .../session_recording_list_from_query.py | 30 ++- ...est_session_recording_list_from_filters.py | 167 ++++++++------- .../test_session_recording_list_from_query.py | 196 +++++++++--------- 3 files changed, 203 insertions(+), 190 deletions(-) diff --git a/posthog/session_recordings/queries/session_recording_list_from_query.py b/posthog/session_recordings/queries/session_recording_list_from_query.py index 1fed3e7bbb699..2a5f72ba4f2e6 100644 --- a/posthog/session_recordings/queries/session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/session_recording_list_from_query.py @@ -11,6 +11,7 @@ from posthog.hogql.property import property_to_expr, action_to_expr from posthog.hogql.query import execute_hogql_query from posthog.hogql_queries.insights.paginators import HogQLHasMorePaginator +from posthog.hogql_queries.legacy_compatibility.clean_properties import clean_global_properties from posthog.hogql_queries.legacy_compatibility.filter_to_query import MathAvailability, legacy_entity_to_node from posthog.hogql_queries.utils.query_date_range import QueryDateRange from posthog.models import Team, Property, Entity, Action @@ -30,22 +31,24 @@ import structlog +from posthog.types import AnyPropertyFilter + logger = structlog.get_logger(__name__) -def is_event_property(p: Property) -> bool: +def is_event_property(p: AnyPropertyFilter) -> bool: return p.type == "event" or (p.type == "hogql" and bool(re.search(r"(? bool: +def is_person_property(p: AnyPropertyFilter) -> bool: return p.type == "person" or (p.type == "hogql" and "person.properties" in p.key) -def is_group_property(p: Property) -> bool: +def is_group_property(p: AnyPropertyFilter) -> bool: return p.type == "group" -def is_cohort_property(p: Property) -> bool: +def is_cohort_property(p: AnyPropertyFilter) -> bool: return "cohort" in p.type @@ -147,7 +150,7 @@ def __init__( @cached_property def _test_account_filters(self) -> list[Property]: - return [Property(**p) for p in self._team.test_account_filters] + return [clean_global_properties(p) for p in self._team.test_account_filters] @property def ttl_days(self): @@ -194,7 +197,7 @@ def get_query(self): ) def _order_by_clause(self) -> ast.Field: - return ast.Field(chain=[self._query.order]) + return ast.Field(chain=[self._query.order.value]) @cached_property def query_date_range(self): @@ -327,12 +330,12 @@ def ast_operand(self) -> type[Union[ast.And, ast.Or]]: return ast.And if self.property_operand == "AND" else ast.Or def _strip_person_and_event_and_cohort_properties( - self, properties: list[Property] | list[PropertyGroup] | None - ) -> PropertyGroup | None: + self, properties: list[AnyPropertyFilter] | None + ) -> list[AnyPropertyFilter] | None: if not properties: return None - property_groups_to_keep = [ + properties_to_keep = [ g for g in properties if not is_event_property(g) @@ -341,14 +344,7 @@ def _strip_person_and_event_and_cohort_properties( and not is_cohort_property(g) ] - return ( - PropertyGroup( - type=self.property_operand, - values=property_groups_to_keep, - ) - if property_groups_to_keep - else None - ) + return properties_to_keep def poe_is_active(team: Team) -> bool: diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py index 94187ec9fc1b0..61f95283e963a 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_filters.py @@ -1,10 +1,13 @@ from datetime import datetime +from typing import Literal from unittest.mock import ANY from uuid import uuid4 from dateutil.relativedelta import relativedelta from django.utils.timezone import now from freezegun import freeze_time +from parameterized import parameterized + from posthog.clickhouse.client import sync_execute from posthog.clickhouse.log_entries import TRUNCATE_LOG_ENTRIES_TABLE_SQL from posthog.constants import AvailableFeature @@ -1613,8 +1616,53 @@ def test_operand_or_event_filters(self): assert len(session_recordings) == 2 assert sorted([r["session_id"] for r in session_recordings]) == sorted([session_id_two, session_id_one]) + @parameterized.expand( + [ + # Case 1: Neither has WARN and message "random" + ( + '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "AND", + 0, + [], + ), + # Case 2: AND only matches one recording + ( + '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "AND", + 1, + ["both_log_filters"], + ), + # Case 3: Only one is WARN level + ( + '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}]', + "AND", + 1, + ["one_log_filter"], + ), + # Case 4: Only one has message "random" + ( + '[{"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "AND", + 1, + ["both_log_filters"], + ), + # Case 5: OR matches both (update assertion if TODO behavior changes) + ( + '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "OR", + 0, # Adjust this to match the correct behavior once implemented + [], + ), + ] + ) @snapshot_clickhouse_queries - def test_operand_or_filters(self): + def test_operand_or_filters( + self, + console_log_filters: str, + operand: Literal["AND", "OR"], + expected_count: int, + expected_session_ids: list[str], + ) -> None: user = "test_operand_or_filter-user" Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) @@ -1624,13 +1672,10 @@ def test_operand_or_filters(self): session_id=session_with_both_log_filters, first_timestamp=self.an_hour_ago, team_id=self.team.id, - console_warn_count=1, - log_messages={ - "warn": [ - "random", - ], - }, + console_log_count=1, + log_messages={"info": ["random"]}, ) + session_with_one_log_filter = "one_log_filter" produce_replay_summary( distinct_id="user", @@ -1638,29 +1683,15 @@ def test_operand_or_filters(self): first_timestamp=self.an_hour_ago, team_id=self.team.id, console_warn_count=1, - log_messages={ - "warn": [ - "warn", - ], - }, + log_messages={"warn": ["warn"]}, ) - (session_recordings, _, _) = self._filter_recordings_by( - { - "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', - "operand": "AND", - } + session_recordings, _, _ = self._filter_recordings_by( + {"console_log_filters": console_log_filters, "operand": operand} ) - assert len(session_recordings) == 1 - assert session_recordings[0]["session_id"] == session_with_both_log_filters - (session_recordings, _, _) = self._filter_recordings_by( - { - "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', - "operand": "OR", - } - ) - assert len(session_recordings) == 2 + assert len(session_recordings) == expected_count + assert sorted([rec["session_id"] for rec in session_recordings]) == sorted(expected_session_ids) @snapshot_clickhouse_queries def test_operand_or_mandatory_filters(self): @@ -3030,18 +3061,40 @@ def test_filter_for_recordings_with_mixed_console_counts(self): @snapshot_clickhouse_queries @freeze_time("2021-01-21T20:00:00.000Z") - def test_filter_for_recordings_by_console_text(self): + @parameterized.expand( + [ + # Case 1: OR operand, message 4 matches in warn and error + ( + '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 4", "operator": "icontains", "type": "log_entry"}]', + "OR", + ["with-errors-session", "with-two-session", "with-warns-session", "with-logs-session"], + ), + # Case 2: AND operand, message 5 matches only in warn + ( + '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', + "AND", + ["with-warns-session"], + ), + # Case 3: AND operand, message 5 does not match log level "info" + ( + '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', + "AND", + [], + ), + ] + ) + def test_filter_for_recordings_by_console_text( + self, + console_log_filters: str, + operand: Literal["AND", "OR"], + expected_session_ids: list[str], + ) -> None: Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) - with_logs_session_id = "with-logs-session" - with_warns_session_id = "with-warns-session" - with_errors_session_id = "with-errors-session" - with_two_session_id = "with-two-session" - with_no_matches_session_id = "with-no-matches-session" - + # Create sessions produce_replay_summary( distinct_id="user", - session_id=with_logs_session_id, + session_id="with-logs-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_log_count=4, @@ -3056,7 +3109,7 @@ def test_filter_for_recordings_by_console_text(self): ) produce_replay_summary( distinct_id="user", - session_id=with_warns_session_id, + session_id="with-warns-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_warn_count=5, @@ -3072,7 +3125,7 @@ def test_filter_for_recordings_by_console_text(self): ) produce_replay_summary( distinct_id="user", - session_id=with_errors_session_id, + session_id="with-errors-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_error_count=4, @@ -3087,7 +3140,7 @@ def test_filter_for_recordings_by_console_text(self): ) produce_replay_summary( distinct_id="user", - session_id=with_two_session_id, + session_id="with-two-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_error_count=4, @@ -3097,13 +3150,14 @@ def test_filter_for_recordings_by_console_text(self): "error message 1", "error message 2", "error message 3", + "error message 4", ], "info": ["log message 1", "log message 2", "log message 3"], }, ) produce_replay_summary( distinct_id="user", - session_id=with_no_matches_session_id, + session_id="with-no-matches-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_error_count=4, @@ -3113,41 +3167,12 @@ def test_filter_for_recordings_by_console_text(self): }, ) - (session_recordings, _, _) = self._filter_recordings_by( - { - # there are 5 warn and 4 error logs, message 4 matches in both - "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 4", "operator": "icontains", "type": "log_entry"}]', - "operand": "OR", - } - ) - - assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( - [with_errors_session_id, with_two_session_id, with_warns_session_id, with_logs_session_id] - ) - - (session_recordings, _, _) = self._filter_recordings_by( - { - # there are 5 warn and 4 error logs, message 5 matches only matches in warn - "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', - "operand": "AND", - } - ) - - assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( - [ - with_warns_session_id, - ] - ) - - (session_recordings, _, _) = self._filter_recordings_by( - { - # message 5 does not match log level "info" - "console_log_filters": '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', - "operand": "AND", - } + # Perform the filtering and validate results + session_recordings, _, _ = self._filter_recordings_by( + {"console_log_filters": console_log_filters, "operand": operand} ) - assert sorted([sr["session_id"] for sr in session_recordings]) == [] + assert sorted([sr["session_id"] for sr in session_recordings]) == sorted(expected_session_ids) @snapshot_clickhouse_queries def test_filter_for_recordings_by_snapshot_source(self): diff --git a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py index 0770b66554b56..fa63d032fe07b 100644 --- a/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py +++ b/posthog/session_recordings/queries/test/test_session_recording_list_from_query.py @@ -1,10 +1,13 @@ from datetime import datetime +from typing import Literal from unittest.mock import ANY from uuid import uuid4 from dateutil.relativedelta import relativedelta from django.utils.timezone import now from freezegun import freeze_time +from parameterized import parameterized + from posthog.clickhouse.client import sync_execute from posthog.clickhouse.log_entries import TRUNCATE_LOG_ENTRIES_TABLE_SQL from posthog.constants import AvailableFeature @@ -1603,8 +1606,53 @@ def test_operand_or_event_filters(self): assert len(session_recordings) == 2 assert sorted([r["session_id"] for r in session_recordings]) == sorted([session_id_two, session_id_one]) + @parameterized.expand( + [ + # Case 1: Neither has WARN and message "random" + ( + '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "AND", + 0, + [], + ), + # Case 2: AND only matches one recording + ( + '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "AND", + 1, + ["both_log_filters"], + ), + # Case 3: Only one is WARN level + ( + '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}]', + "AND", + 1, + ["one_log_filter"], + ), + # Case 4: Only one has message "random" + ( + '[{"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "AND", + 1, + ["both_log_filters"], + ), + # Case 5: OR matches both (update assertion if TODO behavior changes) + ( + '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', + "OR", + 0, # Adjust this to match the correct behavior once implemented + [], + ), + ] + ) @snapshot_clickhouse_queries - def test_operand_or_filters(self): + def test_operand_or_filters( + self, + console_log_filters: str, + operand: Literal["AND", "OR"], + expected_count: int, + expected_session_ids: list[str], + ) -> None: user = "test_operand_or_filter-user" Person.objects.create(team=self.team, distinct_ids=[user], properties={"email": "bla"}) @@ -1615,12 +1663,9 @@ def test_operand_or_filters(self): first_timestamp=self.an_hour_ago, team_id=self.team.id, console_log_count=1, - log_messages={ - "info": [ - "random", - ], - }, + log_messages={"info": ["random"]}, ) + session_with_one_log_filter = "one_log_filter" produce_replay_summary( distinct_id="user", @@ -1628,60 +1673,15 @@ def test_operand_or_filters(self): first_timestamp=self.an_hour_ago, team_id=self.team.id, console_warn_count=1, - log_messages={ - "warn": [ - "warn", - ], - }, + log_messages={"warn": ["warn"]}, ) - # neither has WARN and message random - (session_recordings, _, _) = self._filter_recordings_by( - { - "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', - "operand": "AND", - } + session_recordings, _, _ = self._filter_recordings_by( + {"console_log_filters": console_log_filters, "operand": operand} ) - assert len(session_recordings) == 0 - # AND only matches one recording - (session_recordings, _, _) = self._filter_recordings_by( - { - "console_log_filters": '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', - "operand": "AND", - } - ) - assert len(session_recordings) == 1 - assert session_recordings[0]["session_id"] == session_with_both_log_filters - - # only one is warn level - (session_recordings, _, _) = self._filter_recordings_by( - { - "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}]', - "operand": "AND", - } - ) - assert len(session_recordings) == 1 - assert session_recordings[0]["session_id"] == session_with_one_log_filter - - # only one has message RANDOM - (session_recordings, _, _) = self._filter_recordings_by( - { - "console_log_filters": '[{"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', - "operand": "AND", - } - ) - assert len(session_recordings) == 1 - assert session_recordings[0]["session_id"] == session_with_both_log_filters - - (session_recordings, _, _) = self._filter_recordings_by( - { - "console_log_filters": '[{"key": "level", "value": ["warn"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "random", "operator": "exact", "type": "log_entry"}]', - "operand": "OR", - } - ) - # TODO this should match both but I want to compare the generated SQL - assert len(session_recordings) == 0 + assert len(session_recordings) == expected_count + assert sorted([rec["session_id"] for rec in session_recordings]) == sorted(expected_session_ids) @snapshot_clickhouse_queries def test_operand_or_mandatory_filters(self): @@ -3051,18 +3051,40 @@ def test_filter_for_recordings_with_mixed_console_counts(self): @snapshot_clickhouse_queries @freeze_time("2021-01-21T20:00:00.000Z") - def test_filter_for_recordings_by_console_text(self): + @parameterized.expand( + [ + # Case 1: OR operand, message 4 matches in warn and error + ( + '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 4", "operator": "icontains", "type": "log_entry"}]', + "OR", + ["with-errors-session", "with-two-session", "with-warns-session", "with-logs-session"], + ), + # Case 2: AND operand, message 5 matches only in warn + ( + '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', + "AND", + ["with-warns-session"], + ), + # Case 3: AND operand, message 5 does not match log level "info" + ( + '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', + "AND", + [], + ), + ] + ) + def test_filter_for_recordings_by_console_text( + self, + console_log_filters: str, + operand: Literal["AND", "OR"], + expected_session_ids: list[str], + ) -> None: Person.objects.create(team=self.team, distinct_ids=["user"], properties={"email": "bla"}) - with_logs_session_id = "with-logs-session" - with_warns_session_id = "with-warns-session" - with_errors_session_id = "with-errors-session" - with_two_session_id = "with-two-session" - with_no_matches_session_id = "with-no-matches-session" - + # Create sessions produce_replay_summary( distinct_id="user", - session_id=with_logs_session_id, + session_id="with-logs-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_log_count=4, @@ -3077,7 +3099,7 @@ def test_filter_for_recordings_by_console_text(self): ) produce_replay_summary( distinct_id="user", - session_id=with_warns_session_id, + session_id="with-warns-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_warn_count=5, @@ -3093,7 +3115,7 @@ def test_filter_for_recordings_by_console_text(self): ) produce_replay_summary( distinct_id="user", - session_id=with_errors_session_id, + session_id="with-errors-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_error_count=4, @@ -3108,7 +3130,7 @@ def test_filter_for_recordings_by_console_text(self): ) produce_replay_summary( distinct_id="user", - session_id=with_two_session_id, + session_id="with-two-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_error_count=4, @@ -3123,10 +3145,9 @@ def test_filter_for_recordings_by_console_text(self): "info": ["log message 1", "log message 2", "log message 3"], }, ) - produce_replay_summary( distinct_id="user", - session_id=with_no_matches_session_id, + session_id="with-no-matches-session", first_timestamp=self.an_hour_ago, team_id=self.team.id, console_error_count=4, @@ -3136,41 +3157,12 @@ def test_filter_for_recordings_by_console_text(self): }, ) - (session_recordings, _, _) = self._filter_recordings_by( - { - # there are 5 warn and 4 error logs, message 4 matches in both - "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 4", "operator": "icontains", "type": "log_entry"}]', - "operand": "OR", - } - ) - - assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( - [with_errors_session_id, with_two_session_id, with_warns_session_id, with_logs_session_id] - ) - - (session_recordings, _, _) = self._filter_recordings_by( - { - # there are 5 warn and 4 error logs, message 5 matches only matches in warn - "console_log_filters": '[{"key": "level", "value": ["warn", "error"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', - "operand": "AND", - } - ) - - assert sorted([sr["session_id"] for sr in session_recordings]) == sorted( - [ - with_warns_session_id, - ] - ) - - (session_recordings, _, _) = self._filter_recordings_by( - { - # message 5 does not match log level "info" - "console_log_filters": '[{"key": "level", "value": ["info"], "operator": "exact", "type": "log_entry"}, {"key": "message", "value": "message 5", "operator": "icontains", "type": "log_entry"}]', - "operand": "AND", - } + # Perform the filtering and validate results + session_recordings, _, _ = self._filter_recordings_by( + {"console_log_filters": console_log_filters, "operand": operand} ) - assert sorted([sr["session_id"] for sr in session_recordings]) == [] + assert sorted([sr["session_id"] for sr in session_recordings]) == sorted(expected_session_ids) @snapshot_clickhouse_queries def test_filter_for_recordings_by_snapshot_source(self):