Skip to content

Commit

Permalink
and search
Browse files Browse the repository at this point in the history
  • Loading branch information
pauldambra committed Sep 22, 2023
1 parent 5c11ba6 commit f6d5495
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from '~/types'
import { DateFilter } from 'lib/components/DateFilter/DateFilter'
import { DurationFilter } from './DurationFilter'
import { LemonButtonWithDropdown, LemonCheckbox } from '@posthog/lemon-ui'
import { LemonButtonWithDropdown, LemonCheckbox, LemonInput } from '@posthog/lemon-ui'
import { TestAccountFilter } from 'scenes/insights/filters/TestAccountFilter'
import { teamLogic } from 'scenes/teamLogic'
import { useValues } from 'kea'
Expand Down Expand Up @@ -131,6 +131,17 @@ export const AdvancedSessionRecordingsFilters = ({
<LemonLabel info="Show recordings that have captured console log messages">
Filter by console logs
</LemonLabel>
<LemonInput
size={'small'}
fullWidth={true}
placeholder={'containing text'}
value={filters.console_search_query}
onChange={(s) => {
setFilters({
console_search_query: s,
})
}}
/>
<ConsoleFilters
filters={filters}
setConsoleFilters={(x) =>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ export interface RecordingFilters {
properties?: AnyPropertyFilter[]
session_recording_duration?: RecordingDurationFilter
duration_type_filter?: DurationType
console_search_query?: string
console_logs?: FilterableLogLevel[]
filter_test_accounts?: boolean
}
Expand Down
4 changes: 4 additions & 0 deletions posthog/models/filters/mixins/session_recordings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ def person_uuid(self) -> Optional[str]:


class SessionRecordingsMixin(BaseParamMixin):
@cached_property
def console_search_query(self) -> str | None:
return self._data.get("console_search_query", None)

@cached_property
def console_logs_filter(self) -> List[Literal["error", "warn", "log"]]:
user_value = self._data.get("console_logs", None) or []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ def _get_recording_start_time_clause(recording_filters: SessionRecordingsFilter)
return start_time_clause, start_time_params


def _get_filter_by_log_text_session_ids_clause(
team: Team, recording_filters: SessionRecordingsFilter, column_name="session_id"
) -> Tuple[str, Dict[str, Any]]:
if not recording_filters.console_search_query:
return "", {}

log_query = LogQuery(team=team, filter=recording_filters).get_query()
matching_session_ids = sync_execute(log_query[0], log_query[1])

# we return this _even_ if there are no matching ids since if there are no matching ids
# then no sessions can match...
return f'AND "{column_name}" in %(session_ids)s', {
"session_ids": (recording_filters.session_ids or []) + matching_session_ids
}


def _get_filter_by_provided_session_ids_clause(
recording_filters: SessionRecordingsFilter, column_name="session_id"
) -> Tuple[str, Dict[str, Any]]:
Expand All @@ -71,6 +87,72 @@ def ttl_days(team: Team) -> int:
return ttl_days


class LogQuery:
_filter: SessionRecordingsFilter
_team_id: int
_team: Team

def __init__(
self,
team: Team,
filter: SessionRecordingsFilter,
):
self._filter = filter
self._team = team
self._team_id = team.pk

_rawQuery = """
SELECT distinct log_source_id as session_id
FROM log_entries
PREWHERE team_id = %(team_id)s
AND timestamp >= %(clamped_to_storage_ttl)s
AND timestamp <= now()
{events_timestamp_clause}
WHERE 1=1 {console_log_clause}
AND message ilike %(console_search_query)s
"""

@property
def ttl_days(self):
return ttl_days(self._team)

# We want to select events beyond the range of the recording to handle the case where
# a recording spans the time boundaries
# TODO This is just copied from below
@cached_property
def _get_events_timestamp_clause(self) -> Tuple[str, Dict[str, Any]]:
timestamp_clause = ""
timestamp_params = {}
if self._filter.date_from:
timestamp_clause += "\nAND timestamp >= %(event_start_time)s"
timestamp_params["event_start_time"] = self._filter.date_from - timedelta(hours=12)
if self._filter.date_to:
timestamp_clause += "\nAND timestamp <= %(event_end_time)s"
timestamp_params["event_end_time"] = self._filter.date_to + timedelta(hours=12)
return timestamp_clause, timestamp_params

@staticmethod
def _get_console_log_clause(console_logs_filter: List[Literal["error", "warn", "log"]]) -> str:
filters = [f"console_{log}_count > 0" for log in console_logs_filter]
return f"AND ({' OR '.join(filters)})" if filters else ""

def get_query(self):
# TODO if no self._filter.console_search_query then return "", {} or something?

events_timestamp_clause, events_timestamp_params = self._get_events_timestamp_clause
console_log_clause = self._get_console_log_clause(self._filter.console_logs_filter)
console_search_query = f"%{self._filter.console_search_query}%"
return self._rawQuery.format(
events_timestamp_clause=events_timestamp_clause,
console_log_clause=console_log_clause,
), {
"team_id": self._team_id,
"clamped_to_storage_ttl": (datetime.now() - timedelta(days=self.ttl_days)),
"console_search_query": console_search_query,
**events_timestamp_params,
}


class PersonsQuery(EventQuery):
_filter: SessionRecordingsFilter

Expand Down Expand Up @@ -301,7 +383,7 @@ def get_query(self, select_event_ids: bool = False) -> Tuple[str, Dict[str, Any]

_, recording_start_time_params = _get_recording_start_time_clause(self._filter)
provided_session_ids_clause, provided_session_ids_params = _get_filter_by_provided_session_ids_clause(
recording_filters=self._filter, column_name="$session_id"
team=self._team, recording_filters=self._filter, column_name="$session_id"
)

event_filters = self.build_event_filters
Expand Down Expand Up @@ -436,6 +518,7 @@ def ttl_days(self):
{persons_sub_query}
{events_sub_query}
{provided_session_ids_clause}
{log_matching_session_ids_clause}
GROUP BY session_id
HAVING 1=1 {duration_clause} {console_log_clause}
ORDER BY start_time DESC
Expand Down Expand Up @@ -501,6 +584,11 @@ def get_query(self) -> Tuple[str, Dict[str, Any]]:
provided_session_ids_clause, provided_session_ids_params = _get_filter_by_provided_session_ids_clause(
recording_filters=self._filter
)

log_matching_session_ids_clause, log_matching_session_ids_params = _get_filter_by_log_text_session_ids_clause(
team=self._team, recording_filters=self._filter
)

duration_clause, duration_params = self.duration_clause(self._filter.duration_type_filter)
console_log_clause = self._get_console_log_clause(self._filter.console_logs_filter)

Expand All @@ -524,6 +612,7 @@ def get_query(self) -> Tuple[str, Dict[str, Any]]:
console_log_clause=console_log_clause,
persons_sub_query=persons_select,
events_sub_query=events_select,
log_matching_session_ids_clause=log_matching_session_ids_clause,
),
{
**base_params,
Expand All @@ -532,6 +621,7 @@ def get_query(self) -> Tuple[str, Dict[str, Any]]:
**duration_params,
**provided_session_ids_params,
**persons_select_params,
**log_matching_session_ids_params,
},
)

Expand Down

0 comments on commit f6d5495

Please sign in to comment.