feat(api): search sessions support AND|THEN|OR between events
feat(api): search sessions support single-operator-multiple-values
This commit is contained in:
parent
c0183da5ff
commit
3db36ea688
4 changed files with 192 additions and 85 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
from chalicelib.utils import pg_client, helper
|
import schemas
|
||||||
from chalicelib.core import sessions_metas, metadata
|
|
||||||
from chalicelib.core import issues
|
from chalicelib.core import issues
|
||||||
|
from chalicelib.core import sessions_metas, metadata
|
||||||
|
from chalicelib.utils import pg_client, helper
|
||||||
from chalicelib.utils.TimeUTC import TimeUTC
|
from chalicelib.utils.TimeUTC import TimeUTC
|
||||||
from chalicelib.utils.event_filter_definition import SupportedFilter, Event
|
from chalicelib.utils.event_filter_definition import SupportedFilter, Event
|
||||||
|
|
||||||
|
|
@ -235,23 +236,23 @@ def __generic_autocomplete(event: Event):
|
||||||
|
|
||||||
|
|
||||||
class event_type:
|
class event_type:
|
||||||
CLICK = Event(ui_type="CLICK", table="events.clicks", column="label")
|
CLICK = Event(ui_type=schemas.EventType.click, table="events.clicks", column="label")
|
||||||
INPUT = Event(ui_type="INPUT", table="events.inputs", column="label")
|
INPUT = Event(ui_type=schemas.EventType.input, table="events.inputs", column="label")
|
||||||
LOCATION = Event(ui_type="LOCATION", table="events.pages", column="base_path")
|
LOCATION = Event(ui_type=schemas.EventType.location, table="events.pages", column="base_path")
|
||||||
CUSTOM = Event(ui_type="CUSTOM", table="events_common.customs", column="name")
|
CUSTOM = Event(ui_type=schemas.EventType.custom, table="events_common.customs", column="name")
|
||||||
REQUEST = Event(ui_type="REQUEST", table="events_common.requests", column="url")
|
REQUEST = Event(ui_type=schemas.EventType.request, table="events_common.requests", column="url")
|
||||||
GRAPHQL = Event(ui_type="GRAPHQL", table="events.graphql", column="name")
|
GRAPHQL = Event(ui_type=schemas.EventType.graphql, table="events.graphql", column="name")
|
||||||
STATEACTION = Event(ui_type="STATEACTION", table="events.state_actions", column="name")
|
STATEACTION = Event(ui_type=schemas.EventType.state_action, table="events.state_actions", column="name")
|
||||||
ERROR = Event(ui_type="ERROR", table="events.errors",
|
ERROR = Event(ui_type=schemas.EventType.error, table="events.errors",
|
||||||
column=None) # column=None because errors are searched by name or message
|
column=None) # column=None because errors are searched by name or message
|
||||||
METADATA = Event(ui_type="METADATA", table="public.sessions", column=None)
|
METADATA = Event(ui_type=schemas.EventType.metadata, table="public.sessions", column=None)
|
||||||
# IOS
|
# IOS
|
||||||
CLICK_IOS = Event(ui_type="CLICK_IOS", table="events_ios.clicks", column="label")
|
CLICK_IOS = Event(ui_type=schemas.EventType.click_ios, table="events_ios.clicks", column="label")
|
||||||
INPUT_IOS = Event(ui_type="INPUT_IOS", table="events_ios.inputs", column="label")
|
INPUT_IOS = Event(ui_type=schemas.EventType.input_ios, table="events_ios.inputs", column="label")
|
||||||
VIEW_IOS = Event(ui_type="VIEW_IOS", table="events_ios.views", column="name")
|
VIEW_IOS = Event(ui_type=schemas.EventType.view_ios, table="events_ios.views", column="name")
|
||||||
CUSTOM_IOS = Event(ui_type="CUSTOM_IOS", table="events_common.customs", column="name")
|
CUSTOM_IOS = Event(ui_type=schemas.EventType.custom_ios, table="events_common.customs", column="name")
|
||||||
REQUEST_IOS = Event(ui_type="REQUEST_IOS", table="events_common.requests", column="url")
|
REQUEST_IOS = Event(ui_type=schemas.EventType.request_ios, table="events_common.requests", column="url")
|
||||||
ERROR_IOS = Event(ui_type="ERROR_IOS", table="events_ios.crashes",
|
ERROR_IOS = Event(ui_type=schemas.EventType.error_ios, table="events_ios.crashes",
|
||||||
column=None) # column=None because errors are searched by name or message
|
column=None) # column=None because errors are searched by name or message
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,6 @@ def __is_multivalue(op: schemas.SearchEventOperator):
|
||||||
|
|
||||||
|
|
||||||
def __get_sql_operator(op: schemas.SearchEventOperator):
|
def __get_sql_operator(op: schemas.SearchEventOperator):
|
||||||
op = op.lower()
|
|
||||||
return {
|
return {
|
||||||
schemas.SearchEventOperator._is: "=",
|
schemas.SearchEventOperator._is: "=",
|
||||||
schemas.SearchEventOperator._is_any: "IN",
|
schemas.SearchEventOperator._is_any: "IN",
|
||||||
|
|
@ -145,6 +144,22 @@ def __get_sql_value_multiple(values):
|
||||||
return tuple(values) if isinstance(values, list) else (values,)
|
return tuple(values) if isinstance(values, list) else (values,)
|
||||||
|
|
||||||
|
|
||||||
|
def __multiple_conditions(condition, values, value_key="value"):
|
||||||
|
query = []
|
||||||
|
for i in range(len(values)):
|
||||||
|
k = f"{value_key}_{i}"
|
||||||
|
query.append(condition.replace("value", k))
|
||||||
|
return "(" + " OR ".join(query) + ")"
|
||||||
|
|
||||||
|
|
||||||
|
def __multiple_values(values, value_key="value"):
|
||||||
|
query_values = {}
|
||||||
|
for i in range(len(values)):
|
||||||
|
k = f"{value_key}_{i}"
|
||||||
|
query_values[k] = values[i]
|
||||||
|
return query_values
|
||||||
|
|
||||||
|
|
||||||
@dev.timed
|
@dev.timed
|
||||||
def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, favorite_only=False, errors_only=False,
|
def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, favorite_only=False, errors_only=False,
|
||||||
error_status="ALL",
|
error_status="ALL",
|
||||||
|
|
@ -257,15 +272,17 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
|
||||||
ss_constraints = [s.decode('UTF-8') for s in ss_constraints]
|
ss_constraints = [s.decode('UTF-8') for s in ss_constraints]
|
||||||
events_query_from = []
|
events_query_from = []
|
||||||
event_index = 0
|
event_index = 0
|
||||||
|
events_joiner = " FULL JOIN " if data.events_order == schemas.SearchEventOrder._or else " INNER JOIN LATERAL "
|
||||||
for event in data.events:
|
for event in data.events:
|
||||||
event_type = event.type.upper()
|
event_type = event.type.upper()
|
||||||
|
if not isinstance(event.value, list):
|
||||||
|
event.value = [event.value]
|
||||||
op = __get_sql_operator(event.operator)
|
op = __get_sql_operator(event.operator)
|
||||||
is_not = False
|
is_not = False
|
||||||
if __is_negation_operator(event.operator):
|
if __is_negation_operator(event.operator):
|
||||||
is_not = True
|
is_not = True
|
||||||
op = __reverse_sql_operator(op)
|
op = __reverse_sql_operator(op)
|
||||||
if event_index == 0:
|
if event_index == 0 or data.events_order == schemas.SearchEventOrder._or:
|
||||||
event_from = "%s INNER JOIN public.sessions AS ms USING (session_id)"
|
event_from = "%s INNER JOIN public.sessions AS ms USING (session_id)"
|
||||||
event_where = ["ms.project_id = %(projectId)s", "main.timestamp >= %(startDate)s",
|
event_where = ["ms.project_id = %(projectId)s", "main.timestamp >= %(startDate)s",
|
||||||
"main.timestamp <= %(endDate)s", "ms.start_ts >= %(startDate)s",
|
"main.timestamp <= %(endDate)s", "ms.start_ts >= %(startDate)s",
|
||||||
|
|
@ -273,13 +290,12 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
|
||||||
else:
|
else:
|
||||||
event_from = "%s"
|
event_from = "%s"
|
||||||
event_where = ["main.timestamp >= %(startDate)s", "main.timestamp <= %(endDate)s",
|
event_where = ["main.timestamp >= %(startDate)s", "main.timestamp <= %(endDate)s",
|
||||||
f"event_{event_index - 1}.timestamp <= main.timestamp",
|
|
||||||
"main.session_id=event_0.session_id"]
|
"main.session_id=event_0.session_id"]
|
||||||
if __is_multivalue(event.operator):
|
if data.events_order == schemas.SearchEventOrder._then:
|
||||||
event_args = {"value": __get_sql_value_multiple(event.value)}
|
event_where.append(f"event_{event_index - 1}.timestamp <= main.timestamp")
|
||||||
else:
|
|
||||||
event.value = helper.string_to_op(value=event.value, op=event.operator)
|
event.value = helper.values_for_operator(value=event.value, op=event.operator)
|
||||||
event_args = {"value": helper.string_to_sql_like_with_op(event.value, op)}
|
event_args = __multiple_values(event.value)
|
||||||
if event_type not in list(events.SUPPORTED_TYPES.keys()) \
|
if event_type not in list(events.SUPPORTED_TYPES.keys()) \
|
||||||
or event.value in [None, "", "*"] \
|
or event.value in [None, "", "*"] \
|
||||||
and (event_type != events.event_type.ERROR.ui_type \
|
and (event_type != events.event_type.ERROR.ui_type \
|
||||||
|
|
@ -287,32 +303,50 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
|
||||||
continue
|
continue
|
||||||
if event_type == events.event_type.CLICK.ui_type:
|
if event_type == events.event_type.CLICK.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.CLICK.table} AS main "
|
event_from = event_from % f"{events.event_type.CLICK.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.CLICK.column} {op} %(value)s")
|
event_where.append(__multiple_conditions(f"main.{events.event_type.CLICK.column} {op} %(value)s",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.CLICK.column} {op} %(value)s")
|
||||||
|
|
||||||
elif event_type == events.event_type.INPUT.ui_type:
|
elif event_type == events.event_type.INPUT.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.INPUT.table} AS main "
|
event_from = event_from % f"{events.event_type.INPUT.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.INPUT.column} {op} %(value)s")
|
event_where.append(__multiple_conditions(f"main.{events.event_type.INPUT.column} {op} %(value)s",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.INPUT.column} {op} %(value)s")
|
||||||
if len(event.custom) > 0:
|
if len(event.custom) > 0:
|
||||||
event_where.append("main.value ILIKE %(custom)s")
|
event_where.append(__multiple_conditions(f"main.value ILIKE %(custom)s",
|
||||||
event_args["custom"] = helper.string_to_sql_like_with_op(event.custom, "ILIKE")
|
event.custom, value_key="custom"))
|
||||||
|
event_args = {**event_args, **__multiple_values(event.custom, value_key="custom")}
|
||||||
|
# event_where.append("main.value ILIKE %(custom)s")
|
||||||
|
# event_args["custom"] = helper.string_to_sql_like_with_op(event.custom, "ILIKE")
|
||||||
elif event_type == events.event_type.LOCATION.ui_type:
|
elif event_type == events.event_type.LOCATION.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.LOCATION.table} AS main "
|
event_from = event_from % f"{events.event_type.LOCATION.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.LOCATION.column} {op} %(value)s")
|
event_where.append(__multiple_conditions(f"main.{events.event_type.LOCATION.column} {op} %(value)s",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.LOCATION.column} {op} %(value)s")
|
||||||
elif event_type == events.event_type.CUSTOM.ui_type:
|
elif event_type == events.event_type.CUSTOM.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.CUSTOM.table} AS main "
|
event_from = event_from % f"{events.event_type.CUSTOM.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.CUSTOM.column} {op} %(value)s")
|
event_where.append(__multiple_conditions(f"main.{events.event_type.CUSTOM.column} {op} %(value)s",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.CUSTOM.column} {op} %(value)s")
|
||||||
elif event_type == events.event_type.REQUEST.ui_type:
|
elif event_type == events.event_type.REQUEST.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.REQUEST.table} AS main "
|
event_from = event_from % f"{events.event_type.REQUEST.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.REQUEST.column} {op} %(value)s")
|
event_where.append(__multiple_conditions(f"main.{events.event_type.REQUEST.column} {op} %(value)s",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.REQUEST.column} {op} %(value)s")
|
||||||
elif event_type == events.event_type.GRAPHQL.ui_type:
|
elif event_type == events.event_type.GRAPHQL.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.GRAPHQL.table} AS main "
|
event_from = event_from % f"{events.event_type.GRAPHQL.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.GRAPHQL.column} {op} %(value)s")
|
event_where.append(__multiple_conditions(f"main.{events.event_type.GRAPHQL.column} {op} %(value)s",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.GRAPHQL.column} {op} %(value)s")
|
||||||
elif event_type == events.event_type.STATEACTION.ui_type:
|
elif event_type == events.event_type.STATEACTION.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.STATEACTION.table} AS main "
|
event_from = event_from % f"{events.event_type.STATEACTION.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.STATEACTION.column} {op} %(value)s")
|
event_where.append(
|
||||||
|
__multiple_conditions(f"main.{events.event_type.STATEACTION.column} {op} %(value)s",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.STATEACTION.column} {op} %(value)s")
|
||||||
elif event_type == events.event_type.ERROR.ui_type:
|
elif event_type == events.event_type.ERROR.ui_type:
|
||||||
if event.source in [None, "*", ""]:
|
# if event.source in [None, "*", ""]:
|
||||||
event.source = "js_exception"
|
# event.source = "js_exception"
|
||||||
event_from = event_from % f"{events.event_type.ERROR.table} AS main INNER JOIN public.errors AS main1 USING(error_id)"
|
event_from = event_from % f"{events.event_type.ERROR.table} AS main INNER JOIN public.errors AS main1 USING(error_id)"
|
||||||
if event.value not in [None, "*", ""]:
|
if event.value not in [None, "*", ""]:
|
||||||
event_where.append(f"(main1.message {op} %(value)s OR main1.name {op} %(value)s)")
|
event_where.append(f"(main1.message {op} %(value)s OR main1.name {op} %(value)s)")
|
||||||
|
|
@ -326,40 +360,55 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
|
||||||
# ----- IOS
|
# ----- IOS
|
||||||
elif event_type == events.event_type.CLICK_IOS.ui_type:
|
elif event_type == events.event_type.CLICK_IOS.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.CLICK_IOS.table} AS main "
|
event_from = event_from % f"{events.event_type.CLICK_IOS.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.CLICK_IOS.column} {op} %(value)s")
|
event_where.append(
|
||||||
|
__multiple_conditions(f"main.{events.event_type.CLICK_IOS.column} {op} %(value)s", event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.CLICK_IOS.column} {op} %(value)s")
|
||||||
|
|
||||||
elif event_type == events.event_type.INPUT_IOS.ui_type:
|
elif event_type == events.event_type.INPUT_IOS.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.INPUT_IOS.table} AS main "
|
event_from = event_from % f"{events.event_type.INPUT_IOS.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.INPUT_IOS.column} {op} %(value)s")
|
event_where.append(
|
||||||
|
__multiple_conditions(f"main.{events.event_type.INPUT_IOS.column} {op} %(value)s", event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.INPUT_IOS.column} {op} %(value)s")
|
||||||
if len(event.custom) > 0:
|
if len(event.custom) > 0:
|
||||||
event_where.append("main.value ILIKE %(custom)s")
|
event_where.append(__multiple_conditions("main.value ILIKE %(custom)s", event.custom))
|
||||||
event_args["custom"] = helper.string_to_sql_like_with_op(event.custom, "ILIKE")
|
event_args = {**event_args, **__multiple_values(event.custom, "custom")}
|
||||||
|
# event_where.append("main.value ILIKE %(custom)s")
|
||||||
|
# event_args["custom"] = helper.string_to_sql_like_with_op(event.custom, "ILIKE")
|
||||||
elif event_type == events.event_type.VIEW_IOS.ui_type:
|
elif event_type == events.event_type.VIEW_IOS.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.VIEW_IOS.table} AS main "
|
event_from = event_from % f"{events.event_type.VIEW_IOS.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.VIEW_IOS.column} {op} %(value)s")
|
event_where.append(
|
||||||
|
__multiple_conditions(f"main.{events.event_type.VIEW_IOS.column} {op} %(value)s", event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.VIEW_IOS.column} {op} %(value)s")
|
||||||
elif event_type == events.event_type.CUSTOM_IOS.ui_type:
|
elif event_type == events.event_type.CUSTOM_IOS.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.CUSTOM_IOS.table} AS main "
|
event_from = event_from % f"{events.event_type.CUSTOM_IOS.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.CUSTOM_IOS.column} {op} %(value)s")
|
event_where.append(
|
||||||
|
__multiple_conditions(f"main.{events.event_type.CUSTOM_IOS.column} {op} %(value)s",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.CUSTOM_IOS.column} {op} %(value)s")
|
||||||
elif event_type == events.event_type.REQUEST_IOS.ui_type:
|
elif event_type == events.event_type.REQUEST_IOS.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.REQUEST_IOS.table} AS main "
|
event_from = event_from % f"{events.event_type.REQUEST_IOS.table} AS main "
|
||||||
event_where.append(f"main.{events.event_type.REQUEST_IOS.column} {op} %(value)s")
|
event_where.append(
|
||||||
|
__multiple_conditions(f"main.{events.event_type.REQUEST_IOS.column} {op} %(value)s",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"main.{events.event_type.REQUEST_IOS.column} {op} %(value)s")
|
||||||
elif event_type == events.event_type.ERROR_IOS.ui_type:
|
elif event_type == events.event_type.ERROR_IOS.ui_type:
|
||||||
event_from = event_from % f"{events.event_type.ERROR_IOS.table} AS main INNER JOIN public.crashes_ios AS main1 USING(crash_id)"
|
event_from = event_from % f"{events.event_type.ERROR_IOS.table} AS main INNER JOIN public.crashes_ios AS main1 USING(crash_id)"
|
||||||
if event.value not in [None, "*", ""]:
|
if event.value not in [None, "*", ""]:
|
||||||
event_where.append(f"(main1.reason {op} %(value)s OR main1.name {op} %(value)s)")
|
event_where.append(
|
||||||
|
__multiple_conditions(f"(main1.reason {op} %(value)s OR main1.name {op} %(value)s)",
|
||||||
|
event.value))
|
||||||
|
# event_where.append(f"(main1.reason {op} %(value)s OR main1.name {op} %(value)s)")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
if event_index == 0:
|
if event_index == 0 or data.events_order == schemas.SearchEventOrder._or:
|
||||||
event_where += ss_constraints
|
event_where += ss_constraints
|
||||||
if is_not:
|
if is_not:
|
||||||
if event_index == 0:
|
if event_index == 0:
|
||||||
events_query_from.append(cur.mogrify(f"""\
|
events_query_from.append(cur.mogrify(f"""\
|
||||||
(SELECT
|
(SELECT
|
||||||
session_id,
|
session_id,
|
||||||
0 AS timestamp,
|
0 AS timestamp
|
||||||
{event_index} AS funnel_step
|
|
||||||
FROM sessions
|
FROM sessions
|
||||||
WHERE EXISTS(SELECT session_id
|
WHERE EXISTS(SELECT session_id
|
||||||
FROM {event_from}
|
FROM {event_from}
|
||||||
|
|
@ -375,14 +424,13 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
|
||||||
events_query_from.append(cur.mogrify(f"""\
|
events_query_from.append(cur.mogrify(f"""\
|
||||||
(SELECT
|
(SELECT
|
||||||
event_0.session_id,
|
event_0.session_id,
|
||||||
event_{event_index - 1}.timestamp AS timestamp,
|
event_{event_index - 1}.timestamp AS timestamp
|
||||||
{event_index} AS funnel_step
|
|
||||||
WHERE EXISTS(SELECT session_id FROM {event_from} WHERE {" AND ".join(event_where)}) IS FALSE
|
WHERE EXISTS(SELECT session_id FROM {event_from} WHERE {" AND ".join(event_where)}) IS FALSE
|
||||||
) AS event_{event_index} {"ON(TRUE)" if event_index > 0 else ""}\
|
) AS event_{event_index} {"ON(TRUE)" if event_index > 0 else ""}\
|
||||||
""", {**generic_args, **event_args}).decode('UTF-8'))
|
""", {**generic_args, **event_args}).decode('UTF-8'))
|
||||||
else:
|
else:
|
||||||
events_query_from.append(cur.mogrify(f"""\
|
events_query_from.append(cur.mogrify(f"""\
|
||||||
(SELECT main.session_id, MIN(timestamp) AS timestamp,{event_index} AS funnel_step
|
(SELECT main.session_id, MIN(timestamp) AS timestamp
|
||||||
FROM {event_from}
|
FROM {event_from}
|
||||||
WHERE {" AND ".join(event_where)}
|
WHERE {" AND ".join(event_where)}
|
||||||
GROUP BY 1
|
GROUP BY 1
|
||||||
|
|
@ -394,7 +442,7 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
|
||||||
event_0.session_id,
|
event_0.session_id,
|
||||||
MIN(event_0.timestamp) AS first_event_ts,
|
MIN(event_0.timestamp) AS first_event_ts,
|
||||||
MAX(event_{event_index - 1}.timestamp) AS last_event_ts
|
MAX(event_{event_index - 1}.timestamp) AS last_event_ts
|
||||||
FROM {(" INNER JOIN LATERAL ").join(events_query_from)}
|
FROM {events_joiner.join(events_query_from)}
|
||||||
GROUP BY 1
|
GROUP BY 1
|
||||||
{fav_only_join}"""
|
{fav_only_join}"""
|
||||||
else:
|
else:
|
||||||
|
|
@ -488,8 +536,8 @@ def search2_pg(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, f
|
||||||
ORDER BY favorite DESC, issue_score DESC, {sort} {order};""",
|
ORDER BY favorite DESC, issue_score DESC, {sort} {order};""",
|
||||||
generic_args)
|
generic_args)
|
||||||
|
|
||||||
# print("--------------------")
|
print("--------------------")
|
||||||
# print(main_query)
|
print(main_query)
|
||||||
|
|
||||||
cur.execute(main_query)
|
cur.execute(main_query)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import math
|
import math
|
||||||
import requests
|
import requests
|
||||||
|
|
@ -168,39 +169,56 @@ def string_to_sql_like(value):
|
||||||
|
|
||||||
|
|
||||||
def string_to_sql_like_with_op(value, op):
|
def string_to_sql_like_with_op(value, op):
|
||||||
if isinstance(value, list) and len(value) > 0:
|
if isinstance(value, list):
|
||||||
_value = value[0]
|
r = []
|
||||||
|
for v in value:
|
||||||
|
r.append(string_to_sql_like_with_op(v, op))
|
||||||
|
return r
|
||||||
else:
|
else:
|
||||||
_value = value
|
_value = value
|
||||||
if _value is None:
|
if _value is None:
|
||||||
return _value
|
return _value
|
||||||
if op.upper() != 'ILIKE':
|
if op.upper() != 'ILIKE':
|
||||||
|
return _value.replace("%", "%%")
|
||||||
|
_value = _value.replace("*", "%")
|
||||||
|
if _value.startswith("^"):
|
||||||
|
_value = _value[1:]
|
||||||
|
elif not _value.startswith("%"):
|
||||||
|
_value = '%' + _value
|
||||||
|
|
||||||
|
if _value.endswith("$"):
|
||||||
|
_value = _value[:-1]
|
||||||
|
elif not _value.endswith("%"):
|
||||||
|
_value = _value + '%'
|
||||||
return _value.replace("%", "%%")
|
return _value.replace("%", "%%")
|
||||||
_value = _value.replace("*", "%")
|
|
||||||
if _value.startswith("^"):
|
|
||||||
_value = _value[1:]
|
|
||||||
elif not _value.startswith("%"):
|
|
||||||
_value = '%' + _value
|
|
||||||
|
|
||||||
if _value.endswith("$"):
|
|
||||||
_value = _value[:-1]
|
|
||||||
elif not _value.endswith("%"):
|
|
||||||
_value = _value + '%'
|
|
||||||
return _value.replace("%", "%%")
|
|
||||||
|
|
||||||
|
|
||||||
def string_to_op(value: str, op: schemas.SearchEventOperator):
|
likable_operators = [schemas.SearchEventOperator._starts_with, schemas.SearchEventOperator._ends_with,
|
||||||
if isinstance(value, list) and len(value) > 0:
|
schemas.SearchEventOperator._contains, schemas.SearchEventOperator._notcontains]
|
||||||
_value = value[0]
|
|
||||||
|
|
||||||
|
def is_likable(op: schemas.SearchEventOperator):
|
||||||
|
return op in likable_operators
|
||||||
|
|
||||||
|
|
||||||
|
def values_for_operator(value: Union[str, list], op: schemas.SearchEventOperator):
|
||||||
|
if not is_likable(op):
|
||||||
|
return value
|
||||||
|
if isinstance(value, list):
|
||||||
|
r = []
|
||||||
|
for v in value:
|
||||||
|
r.append(values_for_operator(v, op))
|
||||||
|
return r
|
||||||
else:
|
else:
|
||||||
_value = value
|
if value is None:
|
||||||
if _value is None:
|
return value
|
||||||
return _value
|
if op == schemas.SearchEventOperator._starts_with:
|
||||||
if op == schemas.SearchEventOperator._starts_with:
|
return value + '%%'
|
||||||
_value = '^' + _value
|
elif op == schemas.SearchEventOperator._ends_with:
|
||||||
elif op == schemas.SearchEventOperator._ends_with:
|
return '%%' + value
|
||||||
_value = _value + '$'
|
elif op == schemas.SearchEventOperator._contains:
|
||||||
return _value
|
return '%%' + value + '%%'
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def is_valid_email(email):
|
def is_valid_email(email):
|
||||||
|
|
|
||||||
|
|
@ -330,6 +330,36 @@ class SourcemapUploadPayloadSchema(BaseModel):
|
||||||
urls: List[str] = Field(..., alias="URL")
|
urls: List[str] = Field(..., alias="URL")
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorSource(str, Enum):
|
||||||
|
js_exception = "js_exception"
|
||||||
|
bugsnag = "bugsnag"
|
||||||
|
cloudwatch = "cloudwatch"
|
||||||
|
datadog = "datadog"
|
||||||
|
newrelic = "newrelic"
|
||||||
|
rollbar = "rollbar"
|
||||||
|
sentry = "sentry"
|
||||||
|
stackdriver = "stackdriver"
|
||||||
|
sumologic = "sumologic"
|
||||||
|
|
||||||
|
|
||||||
|
class EventType(str, Enum):
|
||||||
|
click = "CLICK"
|
||||||
|
input = "INPUT"
|
||||||
|
location = "LOCATION"
|
||||||
|
custom = "CUSTOM"
|
||||||
|
request = "REQUEST"
|
||||||
|
graphql = "GRAPHQL"
|
||||||
|
state_action = "STATEACTION"
|
||||||
|
error = "ERROR"
|
||||||
|
metadata = "METADATA"
|
||||||
|
click_ios = "CLICK_IOS"
|
||||||
|
input_ios = "INPUT_IOS"
|
||||||
|
view_ios = "VIEW_IOS"
|
||||||
|
custom_ios = "CUSTOM_IOS"
|
||||||
|
request_ios = "REQUEST_IOS"
|
||||||
|
error_ios = "ERROR_IOS"
|
||||||
|
|
||||||
|
|
||||||
class SearchEventOperator(str, Enum):
|
class SearchEventOperator(str, Enum):
|
||||||
_is = "is"
|
_is = "is"
|
||||||
_is_any = "isAny"
|
_is_any = "isAny"
|
||||||
|
|
@ -348,13 +378,19 @@ class PlatformType(str, Enum):
|
||||||
desktop = "desktop"
|
desktop = "desktop"
|
||||||
|
|
||||||
|
|
||||||
|
class SearchEventOrder(str, Enum):
|
||||||
|
_then = "then"
|
||||||
|
_or = "or"
|
||||||
|
_and = "and"
|
||||||
|
|
||||||
|
|
||||||
class _SessionSearchEventSchema(BaseModel):
|
class _SessionSearchEventSchema(BaseModel):
|
||||||
custom: Optional[str] = Field(None)
|
custom: Optional[List[str]] = Field(None)
|
||||||
key: Optional[str] = Field(None)
|
key: Optional[str] = Field(None)
|
||||||
value: Union[Optional[str], Optional[List[str]]] = Field(...)
|
value: Union[Optional[str], Optional[List[str]]] = Field(...)
|
||||||
type: str = Field(...)
|
type: EventType = Field(...)
|
||||||
operator: SearchEventOperator = Field(...)
|
operator: SearchEventOperator = Field(...)
|
||||||
source: Optional[str] = Field(...)
|
source: Optional[ErrorSource] = Field(default=ErrorSource.js_exception)
|
||||||
|
|
||||||
|
|
||||||
class _SessionSearchFilterSchema(_SessionSearchEventSchema):
|
class _SessionSearchFilterSchema(_SessionSearchEventSchema):
|
||||||
|
|
@ -371,6 +407,10 @@ class SessionsSearchPayloadSchema(BaseModel):
|
||||||
sort: str = Field(...)
|
sort: str = Field(...)
|
||||||
order: str = Field(default="DESC")
|
order: str = Field(default="DESC")
|
||||||
platform: Optional[PlatformType] = Field(None)
|
platform: Optional[PlatformType] = Field(None)
|
||||||
|
events_order: Optional[SearchEventOrder] = Field(default=SearchEventOrder._then)
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
alias_generator = attribute_to_camel_case
|
||||||
|
|
||||||
|
|
||||||
class FunnelSearchPayloadSchema(SessionsSearchPayloadSchema):
|
class FunnelSearchPayloadSchema(SessionsSearchPayloadSchema):
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue