diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 4405b6caa5d08..682e5dfb750db 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -11,7 +11,7 @@ # experience as rich as possible. Perhaps later down the line it might be worth # rolling our own # -FROM mcr.microsoft.com/vscode/devcontainers/python:3.11-bullseye +FROM mcr.microsoft.com/vscode/devcontainers/python:3.11-bookworm # Make sure all exit codes on pipes cause failures SHELL ["/bin/bash", "-o", "pipefail", "-c"] @@ -31,9 +31,9 @@ CMD ["sleep", "infinity"] RUN apt-get update \ && apt-get -y install --no-install-recommends \ # Add in useful db debugging tools - "postgresql-client=13+*" \ + "postgresql-client=15+*" \ # needed for posthog to run - netcat brotli curl \ + netcat-openbsd brotli curl \ && rm -rf /var/lib/apt/lists/* RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \ diff --git a/.devcontainer/library-scripts/docker-in-docker-debian.sh b/.devcontainer/library-scripts/docker-in-docker-debian.sh index 476e1ef3d38b6..893293f973dae 100755 --- a/.devcontainer/library-scripts/docker-in-docker-debian.sh +++ b/.devcontainer/library-scripts/docker-in-docker-debian.sh @@ -5,22 +5,29 @@ #------------------------------------------------------------------------------------------------------------- # # Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker-in-docker.md -# Maintainer: The VS Code and Codespaces Teams -# -# Syntax: ./docker-in-docker-debian.sh [enable non-root docker access flag] [non-root user] [use moby] [Engine/CLI Version] [Major version for docker-compose] - -ENABLE_NONROOT_DOCKER=${1:-"true"} -USERNAME=${2:-"automatic"} -USE_MOBY=${3:-"true"} -DOCKER_VERSION=${4:-"latest"} # The Docker/Moby Engine + CLI should match in version -DOCKER_DASH_COMPOSE_VERSION=${5:-"v1"} # v1 or v2 +# Maintainer: The Dev Container spec maintainers + + +DOCKER_VERSION="${VERSION:-"latest"}" # The Docker/Moby Engine + CLI should match in version +USE_MOBY="${MOBY:-"true"}" +MOBY_BUILDX_VERSION="${MOBYBUILDXVERSION:-"latest"}" +DOCKER_DASH_COMPOSE_VERSION="${DOCKERDASHCOMPOSEVERSION:-"latest"}" #latest, v2 or none +AZURE_DNS_AUTO_DETECTION="${AZUREDNSAUTODETECTION:-"true"}" +DOCKER_DEFAULT_ADDRESS_POOL="${DOCKERDEFAULTADDRESSPOOL:-""}" +USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" +INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}" +INSTALL_DOCKER_COMPOSE_SWITCH="${INSTALLDOCKERCOMPOSESWITCH:-"true"}" MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" -DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal jammy" -DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute impish jammy" +DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal jammy noble" +DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal hirsute impish jammy noble" +DISABLE_IP6_TABLES="${DISABLEIP6TABLES:-false}" # Default: Exit on any failure. set -e +# Clean up +rm -rf /var/lib/apt/lists/* + # Setup STDERR. err() { echo "(!) $*" >&2 @@ -40,7 +47,7 @@ fi if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then USERNAME="" POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in ${POSSIBLE_USERS[@]}; do + for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do if id -u ${CURRENT_USER} > /dev/null 2>&1; then USERNAME=${CURRENT_USER} break @@ -53,36 +60,18 @@ elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then USERNAME=root fi -# Get central common setting -get_common_setting() { - if [ "${common_settings_file_loaded}" != "true" ]; then - curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping." - common_settings_file_loaded=true - fi - if [ -f "/tmp/vsdc-settings.env" ]; then - local multi_line="" - if [ "$2" = "true" ]; then multi_line="-z"; fi - local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')" - if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi - fi - echo "$1=${!1}" -} - -# Function to run apt-get if needed -apt_get_update_if_needed() +apt_get_update() { - if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then echo "Running apt-get update..." - apt-get update - else - echo "Skipping apt-get update." + apt-get update -y fi } # Checks if packages are installed and installs them if not check_packages() { if ! dpkg -s "$@" > /dev/null 2>&1; then - apt_get_update_if_needed + apt_get_update apt-get -y install --no-install-recommends "$@" fi } @@ -95,7 +84,7 @@ find_version_from_git_tags() { local repository=$2 local prefix=${3:-"tags/v"} local separator=${4:-"."} - local last_part_optional=${5:-"false"} + local last_part_optional=${5:-"false"} if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then local escaped_separator=${separator//./\\.} local last_part @@ -121,6 +110,75 @@ find_version_from_git_tags() { echo "${variable_name}=${!variable_name}" } +# Use semver logic to decrement a version number then look for the closest match +find_prev_version_from_git_tags() { + local variable_name=$1 + local current_version=${!variable_name} + local repository=$2 + # Normally a "v" is used before the version number, but support alternate cases + local prefix=${3:-"tags/v"} + # Some repositories use "_" instead of "." for version number part separation, support that + local separator=${4:-"."} + # Some tools release versions that omit the last digit (e.g. go) + local last_part_optional=${5:-"false"} + # Some repositories may have tags that include a suffix (e.g. actions/node-versions) + local version_suffix_regex=$6 + # Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios. + set +e + major="$(echo "${current_version}" | grep -oE '^[0-9]+' || echo '')" + minor="$(echo "${current_version}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')" + breakfix="$(echo "${current_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')" + + if [ "${minor}" = "0" ] && [ "${breakfix}" = "0" ]; then + ((major=major-1)) + declare -g ${variable_name}="${major}" + # Look for latest version from previous major release + find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}" + # Handle situations like Go's odd version pattern where "0" releases omit the last part + elif [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then + ((minor=minor-1)) + declare -g ${variable_name}="${major}.${minor}" + # Look for latest version from previous minor release + find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${last_part_optional}" + else + ((breakfix=breakfix-1)) + if [ "${breakfix}" = "0" ] && [ "${last_part_optional}" = "true" ]; then + declare -g ${variable_name}="${major}.${minor}" + else + declare -g ${variable_name}="${major}.${minor}.${breakfix}" + fi + fi + set -e +} + +# Function to fetch the version released prior to the latest version +get_previous_version() { + local url=$1 + local repo_url=$2 + local variable_name=$3 + prev_version=${!variable_name} + + output=$(curl -s "$repo_url"); + message=$(echo "$output" | jq -r '.message') + + if [[ $message == "API rate limit exceeded"* ]]; then + echo -e "\nAn attempt to find latest version using GitHub Api Failed... \nReason: ${message}" + echo -e "\nAttempting to find latest version using GitHub tags." + find_prev_version_from_git_tags prev_version "$url" "tags/v" + declare -g ${variable_name}="${prev_version}" + else + echo -e "\nAttempting to find latest version using GitHub Api." + version=$(echo "$output" | jq -r '.tag_name') + declare -g ${variable_name}="${version#v}" + fi + echo "${variable_name}=${!variable_name}" +} + +get_github_api_repo_url() { + local url=$1 + echo "${url/https:\/\/github.com/https:\/\/api.github.com\/repos}/releases/latest" +} + ########################################### # Start docker-in-docker installation ########################################### @@ -134,10 +192,8 @@ export DEBIAN_FRONTEND=noninteractive # Fetch host/container arch. architecture="$(dpkg --print-architecture)" -# Check if distro is suppported +# Check if distro is supported if [ "${USE_MOBY}" = "true" ]; then - # 'get_common_setting' allows attribute to be updated remotely - get_common_setting DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution" err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" @@ -145,7 +201,6 @@ if [ "${USE_MOBY}" = "true" ]; then fi echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'" else - get_common_setting DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution" err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" @@ -155,10 +210,9 @@ else fi # Install dependencies -check_packages apt-transport-https curl ca-certificates pigz iptables gnupg2 dirmngr +check_packages apt-transport-https curl ca-certificates pigz iptables gnupg2 dirmngr wget jq if ! type git > /dev/null 2>&1; then - apt_get_update_if_needed - apt-get -y install git + check_packages git fi # Swap to legacy iptables for compatibility @@ -177,7 +231,6 @@ if [ "${USE_MOBY}" = "true" ]; then cli_package_name="moby-cli" # Import key safely and import Microsoft apt repo - get_common_setting MICROSOFT_GPG_KEYS_URI curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list else @@ -217,6 +270,27 @@ else echo "cli_version_suffix ${cli_version_suffix}" fi +# Version matching for moby-buildx +if [ "${USE_MOBY}" = "true" ]; then + if [ "${MOBY_BUILDX_VERSION}" = "latest" ]; then + # Empty, meaning grab whatever "latest" is in apt repo + buildx_version_suffix="" + else + buildx_version_dot_escaped="${MOBY_BUILDX_VERSION//./\\.}" + buildx_version_dot_plus_escaped="${buildx_version_dot_escaped//+/\\+}" + buildx_version_regex="^(.+:)?${buildx_version_dot_plus_escaped}([\\.\\+ ~:-]|$)" + set +e + buildx_version_suffix="=$(apt-cache madison moby-buildx | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${buildx_version_regex}")" + set -e + if [ -z "${buildx_version_suffix}" ] || [ "${buildx_version_suffix}" = "=" ]; then + err "No full or partial moby-buildx version match found for \"${MOBY_BUILDX_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:" + apt-cache madison moby-buildx | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+' + exit 1 + fi + echo "buildx_version_suffix ${buildx_version_suffix}" + fi +fi + # Install Docker / Moby CLI if not already installed if type docker > /dev/null 2>&1 && type dockerd > /dev/null 2>&1; then echo "Docker / Moby CLI and Engine already installed." @@ -224,97 +298,196 @@ else if [ "${USE_MOBY}" = "true" ]; then # Install engine set +e # Handle error gracefully - apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx moby-engine${engine_version_suffix} - if [ $? -ne 0 ]; then - err "Packages for moby not available in OS ${ID} ${VERSION_CODENAME} (${architecture}). To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS version (eg: 'ubuntu-20.04')." - exit 1 - fi - set -e + apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx${buildx_version_suffix} moby-engine${engine_version_suffix} + exit_code=$? + set -e + + if [ ${exit_code} -ne 0 ]; then + err "Packages for moby not available in OS ${ID} ${VERSION_CODENAME} (${architecture}). To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS version (eg: 'ubuntu-20.04')." + exit 1 + fi # Install compose apt-get -y install --no-install-recommends moby-compose || err "Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping." else apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix} docker-ce${engine_version_suffix} + # Install compose + apt-get -y install --no-install-recommends docker-compose-plugin || echo "(*) Package docker-compose-plugin (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping." fi fi echo "Finished installing docker / moby!" -# Install Docker Compose if not already installed and is on a supported architecture -if type docker-compose > /dev/null 2>&1; then - echo "Docker Compose v1 already installed." -else - target_compose_arch="${architecture}" - if [ "${target_compose_arch}" = "amd64" ]; then - target_compose_arch="x86_64" - fi - if [ "${target_compose_arch}" != "x86_64" ]; then - # Use pip to get a version that runs on this architecture - if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then - apt_get_update_if_needed - apt-get -y install python3-minimal python3-pip libffi-dev python3-venv - fi - export PIPX_HOME=/usr/local/pipx - mkdir -p ${PIPX_HOME} - export PIPX_BIN_DIR=/usr/local/bin - export PYTHONUSERBASE=/tmp/pip-tmp - export PIP_CACHE_DIR=/tmp/pip-tmp/cache - pipx_bin=pipx - if ! type pipx > /dev/null 2>&1; then - pip3 install --disable-pip-version-check --no-cache-dir --user pipx - pipx_bin=/tmp/pip-tmp/bin/pipx +docker_home="/usr/libexec/docker" +cli_plugins_dir="${docker_home}/cli-plugins" + +# fallback for docker-compose +fallback_compose(){ + local url=$1 + local repo_url=$(get_github_api_repo_url "$url") + echo -e "\n(!) Failed to fetch the latest artifacts for docker-compose v${compose_version}..." + get_previous_version "${url}" "${repo_url}" compose_version + echo -e "\nAttempting to install v${compose_version}" + curl -fsSL "https://github.com/docker/compose/releases/download/v${compose_version}/docker-compose-linux-${target_compose_arch}" -o ${docker_compose_path} +} + +# If 'docker-compose' command is to be included +if [ "${DOCKER_DASH_COMPOSE_VERSION}" != "none" ]; then + case "${architecture}" in + amd64) target_compose_arch=x86_64 ;; + arm64) target_compose_arch=aarch64 ;; + *) + echo "(!) Docker in docker does not support machine architecture '$architecture'. Please use an x86-64 or ARM64 machine." + exit 1 + esac + + docker_compose_path="/usr/local/bin/docker-compose" + if [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then + err "The final Compose V1 release, version 1.29.2, was May 10, 2021. These packages haven't received any security updates since then. Use at your own risk." + INSTALL_DOCKER_COMPOSE_SWITCH="false" + + if [ "${target_compose_arch}" = "x86_64" ]; then + echo "(*) Installing docker compose v1..." + curl -fsSL "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64" -o ${docker_compose_path} + chmod +x ${docker_compose_path} + + # Download the SHA256 checksum + DOCKER_COMPOSE_SHA256="$(curl -sSL "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64.sha256" | awk '{print $1}')" + echo "${DOCKER_COMPOSE_SHA256} ${docker_compose_path}" > docker-compose.sha256sum + sha256sum -c docker-compose.sha256sum --ignore-missing + elif [ "${VERSION_CODENAME}" = "bookworm" ]; then + err "Docker compose v1 is unavailable for 'bookworm' on Arm64. Kindly switch to use v2" + exit 1 + else + # Use pip to get a version that runs on this architecture + check_packages python3-minimal python3-pip libffi-dev python3-venv + echo "(*) Installing docker compose v1 via pip..." + export PYTHONUSERBASE=/usr/local + pip3 install --disable-pip-version-check --no-cache-dir --user "Cython<3.0" pyyaml wheel docker-compose --no-build-isolation fi - ${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose - rm -rf /tmp/pip-tmp else - compose_v1_version="1" - find_version_from_git_tags compose_v1_version "https://github.com/docker/compose" "tags/" - echo "(*) Installing docker-compose ${compose_v1_version}..." - curl -fsSL "https://github.com/docker/compose/releases/download/${compose_v1_version}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose - chmod +x /usr/local/bin/docker-compose + compose_version=${DOCKER_DASH_COMPOSE_VERSION#v} + docker_compose_url="https://github.com/docker/compose" + find_version_from_git_tags compose_version "$docker_compose_url" "tags/v" + echo "(*) Installing docker-compose ${compose_version}..." + curl -fsSL "https://github.com/docker/compose/releases/download/v${compose_version}/docker-compose-linux-${target_compose_arch}" -o ${docker_compose_path} || { + if [[ $DOCKER_DASH_COMPOSE_VERSION == "latest" ]]; then + fallback_compose "$docker_compose_url" + else + echo -e "Error: Failed to install docker-compose v${compose_version}" + fi + } + + chmod +x ${docker_compose_path} + + # Download the SHA256 checksum + DOCKER_COMPOSE_SHA256="$(curl -sSL "https://github.com/docker/compose/releases/download/v${compose_version}/docker-compose-linux-${target_compose_arch}.sha256" | awk '{print $1}')" + echo "${DOCKER_COMPOSE_SHA256} ${docker_compose_path}" > docker-compose.sha256sum + sha256sum -c docker-compose.sha256sum --ignore-missing + + mkdir -p ${cli_plugins_dir} + cp ${docker_compose_path} ${cli_plugins_dir} fi fi -# Install docker-compose switch if not already installed - https://github.com/docker/compose-switch#manual-installation -current_v1_compose_path="$(which docker-compose)" -target_v1_compose_path="$(dirname "${current_v1_compose_path}")/docker-compose-v1" -if ! type compose-switch > /dev/null 2>&1; then - echo "(*) Installing compose-switch..." - compose_switch_version="latest" - find_version_from_git_tags compose_switch_version "https://github.com/docker/compose-switch" +# fallback method for compose-switch +fallback_compose-switch() { + local url=$1 + local repo_url=$(get_github_api_repo_url "$url") + echo -e "\n(!) Failed to fetch the latest artifacts for compose-switch v${compose_switch_version}..." + get_previous_version "$url" "$repo_url" compose_switch_version + echo -e "\nAttempting to install v${compose_switch_version}" curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch - chmod +x /usr/local/bin/compose-switch - # TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11 +} - # Setup v1 CLI as alternative in addition to compose-switch (which maps to v2) - mv "${current_v1_compose_path}" "${target_v1_compose_path}" - update-alternatives --install /usr/local/bin/docker-compose docker-compose /usr/local/bin/compose-switch 99 - update-alternatives --install /usr/local/bin/docker-compose docker-compose "${target_v1_compose_path}" 1 -fi -if [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then - update-alternatives --set docker-compose "${target_v1_compose_path}" -else - update-alternatives --set docker-compose /usr/local/bin/compose-switch +# Install docker-compose switch if not already installed - https://github.com/docker/compose-switch#manual-installation +if [ "${INSTALL_DOCKER_COMPOSE_SWITCH}" = "true" ] && ! type compose-switch > /dev/null 2>&1; then + if type docker-compose > /dev/null 2>&1; then + echo "(*) Installing compose-switch..." + current_compose_path="$(which docker-compose)" + target_compose_path="$(dirname "${current_compose_path}")/docker-compose-v1" + compose_switch_version="latest" + compose_switch_url="https://github.com/docker/compose-switch" + find_version_from_git_tags compose_switch_version "$compose_switch_url" + curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch || fallback_compose-switch "$compose_switch_url" + chmod +x /usr/local/bin/compose-switch + # TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11 + # Setup v1 CLI as alternative in addition to compose-switch (which maps to v2) + mv "${current_compose_path}" "${target_compose_path}" + update-alternatives --install ${docker_compose_path} docker-compose /usr/local/bin/compose-switch 99 + update-alternatives --install ${docker_compose_path} docker-compose "${target_compose_path}" 1 + else + err "Skipping installation of compose-switch as docker compose is unavailable..." + fi fi # If init file already exists, exit if [ -f "/usr/local/share/docker-init.sh" ]; then echo "/usr/local/share/docker-init.sh already exists, so exiting." + # Clean up + rm -rf /var/lib/apt/lists/* exit 0 fi -echo "docker-init doesnt exist, adding..." +echo "docker-init doesn't exist, adding..." -# Add user to the docker group -if [ "${ENABLE_NONROOT_DOCKER}" = "true" ]; then - if ! getent group docker > /dev/null 2>&1; then - groupadd docker - fi +if ! cat /etc/group | grep -e "^docker:" > /dev/null 2>&1; then + groupadd -r docker +fi - usermod -aG docker ${USERNAME} +usermod -aG docker ${USERNAME} + +# fallback for docker/buildx +fallback_buildx() { + local url=$1 + local repo_url=$(get_github_api_repo_url "$url") + echo -e "\n(!) Failed to fetch the latest artifacts for docker buildx v${buildx_version}..." + get_previous_version "$url" "$repo_url" buildx_version + buildx_file_name="buildx-v${buildx_version}.linux-${architecture}" + echo -e "\nAttempting to install v${buildx_version}" + wget https://github.com/docker/buildx/releases/download/v${buildx_version}/${buildx_file_name} +} + +if [ "${INSTALL_DOCKER_BUILDX}" = "true" ]; then + buildx_version="latest" + docker_buildx_url="https://github.com/docker/buildx" + find_version_from_git_tags buildx_version "$docker_buildx_url" "refs/tags/v" + echo "(*) Installing buildx ${buildx_version}..." + buildx_file_name="buildx-v${buildx_version}.linux-${architecture}" + + cd /tmp + wget https://github.com/docker/buildx/releases/download/v${buildx_version}/${buildx_file_name} || fallback_buildx "$docker_buildx_url" + + docker_home="/usr/libexec/docker" + cli_plugins_dir="${docker_home}/cli-plugins" + + mkdir -p ${cli_plugins_dir} + mv ${buildx_file_name} ${cli_plugins_dir}/docker-buildx + chmod +x ${cli_plugins_dir}/docker-buildx + + chown -R "${USERNAME}:docker" "${docker_home}" + chmod -R g+r+w "${docker_home}" + find "${docker_home}" -type d -print0 | xargs -n 1 -0 chmod g+s +fi + +DOCKER_DEFAULT_IP6_TABLES="" +if [ "$DISABLE_IP6_TABLES" == true ]; then + requested_version="" + # checking whether the version requested either is in semver format or just a number denoting the major version + # and, extracting the major version number out of the two scenarios + semver_regex="^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$" + if echo "$DOCKER_VERSION" | grep -Eq $semver_regex; then + requested_version=$(echo $DOCKER_VERSION | cut -d. -f1) + elif echo "$DOCKER_VERSION" | grep -Eq "^[1-9][0-9]*$"; then + requested_version=$DOCKER_VERSION + fi + if [ "$DOCKER_VERSION" = "latest" ] || [[ -n "$requested_version" && "$requested_version" -ge 27 ]] ; then + DOCKER_DEFAULT_IP6_TABLES="--ip6tables=false" + echo "(!) As requested, passing '${DOCKER_DEFAULT_IP6_TABLES}'" + fi fi tee /usr/local/share/docker-init.sh > /dev/null \ -<< 'EOF' +<< EOF #!/bin/sh #------------------------------------------------------------------------------------------------------------- # Copyright (c) Microsoft Corporation. All rights reserved. @@ -323,13 +496,19 @@ tee /usr/local/share/docker-init.sh > /dev/null \ set -e -dockerd_start="$(cat << 'INNEREOF' +AZURE_DNS_AUTO_DETECTION=${AZURE_DNS_AUTO_DETECTION} +DOCKER_DEFAULT_ADDRESS_POOL=${DOCKER_DEFAULT_ADDRESS_POOL} +DOCKER_DEFAULT_IP6_TABLES=${DOCKER_DEFAULT_IP6_TABLES} +EOF + +tee -a /usr/local/share/docker-init.sh > /dev/null \ +<< 'EOF' +dockerd_start="AZURE_DNS_AUTO_DETECTION=${AZURE_DNS_AUTO_DETECTION} DOCKER_DEFAULT_ADDRESS_POOL=${DOCKER_DEFAULT_ADDRESS_POOL} DOCKER_DEFAULT_IP6_TABLES=${DOCKER_DEFAULT_IP6_TABLES} $(cat << 'INNEREOF' # explicitly remove dockerd and containerd PID file to ensure that it can start properly if it was stopped uncleanly - # ie: docker kill find /run /var/run -iname 'docker*.pid' -delete || : find /run /var/run -iname 'container*.pid' -delete || : - ## Dind wrapper script from docker team, adapted to a function + # -- Start: dind wrapper script -- # Maintained: https://github.com/moby/moby/blob/master/hack/dind export container=docker @@ -346,45 +525,109 @@ dockerd_start="$(cat << 'INNEREOF' mount -t tmpfs none /tmp fi - # cgroup v2: enable nesting - if [ -f /sys/fs/cgroup/cgroup.controllers ]; then - # move the processes from the root group to the /init group, - # otherwise writing subtree_control fails with EBUSY. - # An error during moving non-existent process (i.e., "cat") is ignored. - mkdir -p /sys/fs/cgroup/init - xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || : - # enable controllers - sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \ - > /sys/fs/cgroup/cgroup.subtree_control - fi - ## Dind wrapper over. + set_cgroup_nesting() + { + # cgroup v2: enable nesting + if [ -f /sys/fs/cgroup/cgroup.controllers ]; then + # move the processes from the root group to the /init group, + # otherwise writing subtree_control fails with EBUSY. + # An error during moving non-existent process (i.e., "cat") is ignored. + mkdir -p /sys/fs/cgroup/init + xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || : + # enable controllers + sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \ + > /sys/fs/cgroup/cgroup.subtree_control + fi + } + + # Set cgroup nesting, retrying if necessary + retry_cgroup_nesting=0 + + until [ "${retry_cgroup_nesting}" -eq "5" ]; + do + set +e + set_cgroup_nesting + + if [ $? -ne 0 ]; then + echo "(*) cgroup v2: Failed to enable nesting, retrying..." + else + break + fi + + retry_cgroup_nesting=`expr $retry_cgroup_nesting + 1` + set -e + done + + # -- End: dind wrapper script -- # Handle DNS set +e - cat /etc/resolv.conf | grep -i 'internal.cloudapp.net' - if [ $? -eq 0 ] + cat /etc/resolv.conf | grep -i 'internal.cloudapp.net' > /dev/null 2>&1 + if [ $? -eq 0 ] && [ "${AZURE_DNS_AUTO_DETECTION}" = "true" ] + then + echo "Setting dockerd Azure DNS." + CUSTOMDNS="--dns 168.63.129.16" + else + echo "Not setting dockerd DNS manually." + CUSTOMDNS="" + fi + set -e + + if [ -z "$DOCKER_DEFAULT_ADDRESS_POOL" ] then - echo "Setting dockerd Azure DNS." - CUSTOMDNS="--dns 168.63.129.16" + DEFAULT_ADDRESS_POOL="" else - echo "Not setting dockerd DNS manually." - CUSTOMDNS="" + DEFAULT_ADDRESS_POOL="--default-address-pool $DOCKER_DEFAULT_ADDRESS_POOL" fi - set -e # Start docker/moby engine - ( dockerd $CUSTOMDNS > /tmp/dockerd.log 2>&1 ) & + ( dockerd $CUSTOMDNS $DEFAULT_ADDRESS_POOL $DOCKER_DEFAULT_IP6_TABLES > /tmp/dockerd.log 2>&1 ) & INNEREOF )" -# Start using sudo if not invoked as root -if [ "$(id -u)" -ne 0 ]; then - sudo /bin/sh -c "${dockerd_start}" -else - eval "${dockerd_start}" -fi +sudo_if() { + COMMAND="$*" + + if [ "$(id -u)" -ne 0 ]; then + sudo $COMMAND + else + $COMMAND + fi +} -set +e +retry_docker_start_count=0 +docker_ok="false" + +until [ "${docker_ok}" = "true" ] || [ "${retry_docker_start_count}" -eq "5" ]; +do + # Start using sudo if not invoked as root + if [ "$(id -u)" -ne 0 ]; then + sudo /bin/sh -c "${dockerd_start}" + else + eval "${dockerd_start}" + fi + + retry_count=0 + until [ "${docker_ok}" = "true" ] || [ "${retry_count}" -eq "5" ]; + do + sleep 1s + set +e + docker info > /dev/null 2>&1 && docker_ok="true" + set -e + + retry_count=`expr $retry_count + 1` + done + + if [ "${docker_ok}" != "true" ] && [ "${retry_docker_start_count}" != "4" ]; then + echo "(*) Failed to start docker, retrying..." + set +e + sudo_if pkill dockerd + sudo_if pkill containerd + set -e + fi + + retry_docker_start_count=`expr $retry_docker_start_count + 1` +done # Execute whatever commands were passed in (if any). This allows us # to set this script to ENTRYPOINT while still executing the default CMD. @@ -394,4 +637,7 @@ EOF chmod +x /usr/local/share/docker-init.sh chown ${USERNAME}:root /usr/local/share/docker-init.sh -echo 'docker-in-docker-debian script has completed!' +# Clean up +rm -rf /var/lib/apt/lists/* + +echo 'docker-in-docker-debian script has completed!' \ No newline at end of file diff --git a/.flox/env/manifest.lock b/.flox/env/manifest.lock index 2fa6bf0f4d140..cc61f70b0cf27 100644 --- a/.flox/env/manifest.lock +++ b/.flox/env/manifest.lock @@ -71,8 +71,7 @@ }, "xmlsec": { "pkg-path": "xmlsec", - "pkg-group": "python", - "version": "1.2.34" + "version": "1.3.6" } }, "vars": { @@ -593,130 +592,6 @@ "group": "python", "priority": 5 }, - { - "attr_path": "xmlsec", - "broken": false, - "derivation": "/nix/store/p85izfrfdj8n22pm9iv1b8a511pm2i5n-xmlsec-1.2.34.drv", - "description": "XML Security Library in C based on libxml2", - "install_id": "xmlsec", - "license": "MIT", - "locked_url": "https://github.com/flox/nixpkgs?rev=9f4128e00b0ae8ec65918efeba59db998750ead6", - "name": "xmlsec-1.2.34", - "pname": "xmlsec", - "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", - "rev_count": 647193, - "rev_date": "2024-07-03T18:27:49Z", - "scrape_date": "2024-07-05T00:14:29Z", - "stabilities": [ - "staging", - "unstable" - ], - "unfree": false, - "version": "1.2.34", - "outputs_to_install": [ - "out" - ], - "outputs": { - "dev": "/nix/store/mnrmn6vndrfgxyxmbqvmrwi7qjwr3d0m-xmlsec-1.2.34-dev", - "out": "/nix/store/ff6sr1v2q8gqmnyvpl6xfxw3r2lb56i3-xmlsec-1.2.34" - }, - "system": "aarch64-darwin", - "group": "python", - "priority": 5 - }, - { - "attr_path": "xmlsec", - "broken": false, - "derivation": "/nix/store/7zscs792mb7v53i43jf4qq0gypgrql8g-xmlsec-1.2.34.drv", - "description": "XML Security Library in C based on libxml2", - "install_id": "xmlsec", - "license": "MIT", - "locked_url": "https://github.com/flox/nixpkgs?rev=9f4128e00b0ae8ec65918efeba59db998750ead6", - "name": "xmlsec-1.2.34", - "pname": "xmlsec", - "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", - "rev_count": 647193, - "rev_date": "2024-07-03T18:27:49Z", - "scrape_date": "2024-07-05T00:14:29Z", - "stabilities": [ - "staging", - "unstable" - ], - "unfree": false, - "version": "1.2.34", - "outputs_to_install": [ - "out" - ], - "outputs": { - "dev": "/nix/store/k8n1j04qrbhlzxklrxhk32aphh58jqcf-xmlsec-1.2.34-dev", - "out": "/nix/store/qdscjbwglk2953azhhhsibf0xlsjig1h-xmlsec-1.2.34" - }, - "system": "aarch64-linux", - "group": "python", - "priority": 5 - }, - { - "attr_path": "xmlsec", - "broken": false, - "derivation": "/nix/store/cbl3q86h6vzzzi50ph82syrkha9g9mah-xmlsec-1.2.34.drv", - "description": "XML Security Library in C based on libxml2", - "install_id": "xmlsec", - "license": "MIT", - "locked_url": "https://github.com/flox/nixpkgs?rev=9f4128e00b0ae8ec65918efeba59db998750ead6", - "name": "xmlsec-1.2.34", - "pname": "xmlsec", - "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", - "rev_count": 647193, - "rev_date": "2024-07-03T18:27:49Z", - "scrape_date": "2024-07-05T00:14:29Z", - "stabilities": [ - "staging", - "unstable" - ], - "unfree": false, - "version": "1.2.34", - "outputs_to_install": [ - "out" - ], - "outputs": { - "dev": "/nix/store/jc3nxrj9kv447gb5ywxqal45b7ick5c7-xmlsec-1.2.34-dev", - "out": "/nix/store/fzhimicsixlzc8k6l0f5vh5lzbxsl39c-xmlsec-1.2.34" - }, - "system": "x86_64-darwin", - "group": "python", - "priority": 5 - }, - { - "attr_path": "xmlsec", - "broken": false, - "derivation": "/nix/store/5z6p8lb90900s6k8lml62mlwjjx6whm2-xmlsec-1.2.34.drv", - "description": "XML Security Library in C based on libxml2", - "install_id": "xmlsec", - "license": "MIT", - "locked_url": "https://github.com/flox/nixpkgs?rev=9f4128e00b0ae8ec65918efeba59db998750ead6", - "name": "xmlsec-1.2.34", - "pname": "xmlsec", - "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", - "rev_count": 647193, - "rev_date": "2024-07-03T18:27:49Z", - "scrape_date": "2024-07-05T00:14:29Z", - "stabilities": [ - "staging", - "unstable" - ], - "unfree": false, - "version": "1.2.34", - "outputs_to_install": [ - "out" - ], - "outputs": { - "dev": "/nix/store/z0k51x5cg15f2r058ypjzhf9bakzq16g-xmlsec-1.2.34-dev", - "out": "/nix/store/rrnig5ybklsrf8kiwn15lvci0rfq6379-xmlsec-1.2.34" - }, - "system": "x86_64-linux", - "group": "python", - "priority": 5 - }, { "attr_path": "cargo", "broken": false, @@ -1357,17 +1232,17 @@ { "attr_path": "mprocs", "broken": false, - "derivation": "/nix/store/5hckam5lgqqmlkjd1r6a0bzkkvhfm349-mprocs-0.7.1.drv", + "derivation": "/nix/store/26d7rl3vql93s040slf69jw5a4wxflhj-mprocs-0.7.1.drv", "description": "TUI tool to run multiple commands in parallel and show the output of each command separately", "install_id": "mprocs", "license": "MIT", - "locked_url": "https://github.com/flox/nixpkgs?rev=9f4128e00b0ae8ec65918efeba59db998750ead6", + "locked_url": "https://github.com/flox/nixpkgs?rev=8f3e1f807051e32d8c95cd12b9b421623850a34d", "name": "mprocs-0.7.1", "pname": "mprocs", - "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", - "rev_count": 647193, - "rev_date": "2024-07-03T18:27:49Z", - "scrape_date": "2024-07-05T00:14:29Z", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev_count": 733374, + "rev_date": "2025-01-04T17:41:09Z", + "scrape_date": "2025-01-06T21:17:53Z", "stabilities": [ "staging", "unstable" @@ -1378,7 +1253,7 @@ "out" ], "outputs": { - "out": "/nix/store/pk1mb41ryrw2izq15pymxjwqvw8mr29p-mprocs-0.7.1" + "out": "/nix/store/6qd6isn3scvlp7vk34xgb1mn9hh93p8v-mprocs-0.7.1" }, "system": "aarch64-darwin", "group": "toplevel", @@ -1387,17 +1262,17 @@ { "attr_path": "mprocs", "broken": false, - "derivation": "/nix/store/lg4yvv4az18g59l7rgfvn2pqxidx5qgb-mprocs-0.7.1.drv", + "derivation": "/nix/store/bi3z98bxszfz04qdpyxkmrcjkq75iasa-mprocs-0.7.1.drv", "description": "TUI tool to run multiple commands in parallel and show the output of each command separately", "install_id": "mprocs", "license": "MIT", - "locked_url": "https://github.com/flox/nixpkgs?rev=9f4128e00b0ae8ec65918efeba59db998750ead6", + "locked_url": "https://github.com/flox/nixpkgs?rev=8f3e1f807051e32d8c95cd12b9b421623850a34d", "name": "mprocs-0.7.1", "pname": "mprocs", - "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", - "rev_count": 647193, - "rev_date": "2024-07-03T18:27:49Z", - "scrape_date": "2024-07-05T00:14:29Z", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev_count": 733374, + "rev_date": "2025-01-04T17:41:09Z", + "scrape_date": "2025-01-06T21:17:53Z", "stabilities": [ "staging", "unstable" @@ -1408,7 +1283,7 @@ "out" ], "outputs": { - "out": "/nix/store/dynm867fg992nb5wlysvrg1p14cwgsca-mprocs-0.7.1" + "out": "/nix/store/4sdqi7adqhxqvyii5n8a1gpg740hbshm-mprocs-0.7.1" }, "system": "aarch64-linux", "group": "toplevel", @@ -1417,17 +1292,17 @@ { "attr_path": "mprocs", "broken": false, - "derivation": "/nix/store/xbicacfnn0gnz08rri7jwc40byxrm404-mprocs-0.7.1.drv", + "derivation": "/nix/store/6qmqgqkjbdsn5z308hp3w84wav6p1phb-mprocs-0.7.1.drv", "description": "TUI tool to run multiple commands in parallel and show the output of each command separately", "install_id": "mprocs", "license": "MIT", - "locked_url": "https://github.com/flox/nixpkgs?rev=9f4128e00b0ae8ec65918efeba59db998750ead6", + "locked_url": "https://github.com/flox/nixpkgs?rev=8f3e1f807051e32d8c95cd12b9b421623850a34d", "name": "mprocs-0.7.1", "pname": "mprocs", - "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", - "rev_count": 647193, - "rev_date": "2024-07-03T18:27:49Z", - "scrape_date": "2024-07-05T00:14:29Z", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev_count": 733374, + "rev_date": "2025-01-04T17:41:09Z", + "scrape_date": "2025-01-06T21:17:53Z", "stabilities": [ "staging", "unstable" @@ -1438,7 +1313,7 @@ "out" ], "outputs": { - "out": "/nix/store/kx138kp8rxjwph6wqp3lk8v6fybij2bz-mprocs-0.7.1" + "out": "/nix/store/cfsrk2za8x6d6n5wdwjwpm9hf6ws28qi-mprocs-0.7.1" }, "system": "x86_64-darwin", "group": "toplevel", @@ -1447,17 +1322,17 @@ { "attr_path": "mprocs", "broken": false, - "derivation": "/nix/store/ydrq0m8lp0m3pjpd9ndg1x30z3bg81qx-mprocs-0.7.1.drv", + "derivation": "/nix/store/hlmqv197aqi8imigjj061mdmhbij7v94-mprocs-0.7.1.drv", "description": "TUI tool to run multiple commands in parallel and show the output of each command separately", "install_id": "mprocs", "license": "MIT", - "locked_url": "https://github.com/flox/nixpkgs?rev=9f4128e00b0ae8ec65918efeba59db998750ead6", + "locked_url": "https://github.com/flox/nixpkgs?rev=8f3e1f807051e32d8c95cd12b9b421623850a34d", "name": "mprocs-0.7.1", "pname": "mprocs", - "rev": "9f4128e00b0ae8ec65918efeba59db998750ead6", - "rev_count": 647193, - "rev_date": "2024-07-03T18:27:49Z", - "scrape_date": "2024-07-05T00:14:29Z", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev_count": 733374, + "rev_date": "2025-01-04T17:41:09Z", + "scrape_date": "2025-01-06T21:17:53Z", "stabilities": [ "staging", "unstable" @@ -1468,7 +1343,131 @@ "out" ], "outputs": { - "out": "/nix/store/ky3hbhjvsr4l5nh1xjipkbswll37j0dq-mprocs-0.7.1" + "out": "/nix/store/bhc6rvwb1n4bh8rsxwnw7s9krlphqkh4-mprocs-0.7.1" + }, + "system": "x86_64-linux", + "group": "toplevel", + "priority": 5 + }, + { + "attr_path": "xmlsec", + "broken": false, + "derivation": "/nix/store/npf1l5rkpmp5swm4jrfypx975wrmjzc8-xmlsec-1.3.6.drv", + "description": "XML Security Library in C based on libxml2", + "install_id": "xmlsec", + "license": "MIT", + "locked_url": "https://github.com/flox/nixpkgs?rev=8f3e1f807051e32d8c95cd12b9b421623850a34d", + "name": "xmlsec-1.3.6", + "pname": "xmlsec", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev_count": 733374, + "rev_date": "2025-01-04T17:41:09Z", + "scrape_date": "2025-01-06T21:17:53Z", + "stabilities": [ + "staging", + "unstable" + ], + "unfree": false, + "version": "1.3.6", + "outputs_to_install": [ + "out" + ], + "outputs": { + "dev": "/nix/store/vj56i5ldpvxfhh7fzh30fsj925rv08a6-xmlsec-1.3.6-dev", + "out": "/nix/store/66can923s5d00xgzzmlbxz2ydgmgglcb-xmlsec-1.3.6" + }, + "system": "aarch64-darwin", + "group": "toplevel", + "priority": 5 + }, + { + "attr_path": "xmlsec", + "broken": false, + "derivation": "/nix/store/l54yryhpx0wfm8sv88ys92z9i0hsip3k-xmlsec-1.3.6.drv", + "description": "XML Security Library in C based on libxml2", + "install_id": "xmlsec", + "license": "MIT", + "locked_url": "https://github.com/flox/nixpkgs?rev=8f3e1f807051e32d8c95cd12b9b421623850a34d", + "name": "xmlsec-1.3.6", + "pname": "xmlsec", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev_count": 733374, + "rev_date": "2025-01-04T17:41:09Z", + "scrape_date": "2025-01-06T21:17:53Z", + "stabilities": [ + "staging", + "unstable" + ], + "unfree": false, + "version": "1.3.6", + "outputs_to_install": [ + "out" + ], + "outputs": { + "dev": "/nix/store/fscw35frjnm34ra3l3jyycfivn20r9va-xmlsec-1.3.6-dev", + "out": "/nix/store/18l3z48p7w2h3anq8zmgrhqp5j25s5g9-xmlsec-1.3.6" + }, + "system": "aarch64-linux", + "group": "toplevel", + "priority": 5 + }, + { + "attr_path": "xmlsec", + "broken": false, + "derivation": "/nix/store/jcjvbiyzq7648zs65v318qzb6hcvp038-xmlsec-1.3.6.drv", + "description": "XML Security Library in C based on libxml2", + "install_id": "xmlsec", + "license": "MIT", + "locked_url": "https://github.com/flox/nixpkgs?rev=8f3e1f807051e32d8c95cd12b9b421623850a34d", + "name": "xmlsec-1.3.6", + "pname": "xmlsec", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev_count": 733374, + "rev_date": "2025-01-04T17:41:09Z", + "scrape_date": "2025-01-06T21:17:53Z", + "stabilities": [ + "staging", + "unstable" + ], + "unfree": false, + "version": "1.3.6", + "outputs_to_install": [ + "out" + ], + "outputs": { + "dev": "/nix/store/brmp8jjpi2x6nzxfwq7czr46fjhif8c2-xmlsec-1.3.6-dev", + "out": "/nix/store/6q35gr9jggmql201i2iz9gjkbfj30lf7-xmlsec-1.3.6" + }, + "system": "x86_64-darwin", + "group": "toplevel", + "priority": 5 + }, + { + "attr_path": "xmlsec", + "broken": false, + "derivation": "/nix/store/7c8zh84rs39q6p5bm10dvwmbzcf96i4c-xmlsec-1.3.6.drv", + "description": "XML Security Library in C based on libxml2", + "install_id": "xmlsec", + "license": "MIT", + "locked_url": "https://github.com/flox/nixpkgs?rev=8f3e1f807051e32d8c95cd12b9b421623850a34d", + "name": "xmlsec-1.3.6", + "pname": "xmlsec", + "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", + "rev_count": 733374, + "rev_date": "2025-01-04T17:41:09Z", + "scrape_date": "2025-01-06T21:17:53Z", + "stabilities": [ + "staging", + "unstable" + ], + "unfree": false, + "version": "1.3.6", + "outputs_to_install": [ + "out" + ], + "outputs": { + "dev": "/nix/store/xixr2nq7pjbbl8mn43c12m2m8gvl4fhq-xmlsec-1.3.6-dev", + "out": "/nix/store/3308q19l44wkil15x2zy821dlz4ig44f-xmlsec-1.3.6" }, "system": "x86_64-linux", "group": "toplevel", diff --git a/.flox/env/manifest.toml b/.flox/env/manifest.toml index 6a3996a00c79e..6af0be27dd18e 100644 --- a/.flox/env/manifest.toml +++ b/.flox/env/manifest.toml @@ -12,7 +12,6 @@ version = 1 # Python python3 = { pkg-path = "python3", version = "3.11", pkg-group = "python" } uv = { pkg-path = "uv", pkg-group = "python" } -xmlsec = { pkg-path = "xmlsec", pkg-group = "python", version = "1.2.34" } libtool = { pkg-path = "libtool", pkg-group = "python" } # Node nodejs = { pkg-path = "nodejs_18", pkg-group = "nodejs" } @@ -38,6 +37,8 @@ libiconv.pkg-group = "rust-toolchain" go = { pkg-path = "go", version = "1.22", pkg-group = "go" } # General CLI tools mprocs.pkg-path = "mprocs" +xmlsec.pkg-path = "xmlsec" +xmlsec.version = "1.3.6" # Set environment variables in the `[vars]` section. These variables may not # reference one another, and are added to the environment without first diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 5bcacc4be27d6..f584005d8d5f6 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -19,4 +19,4 @@ body: - type: markdown attributes: - value: '#### *Thank you* for your bug report – we love squashing them!' + value: '#### *Thank you* for your feature request – we love each and every one!' diff --git a/.github/workflows/build-hogql-parser.yml b/.github/workflows/build-hogql-parser.yml index 005b7d9fa5660..d302c4b3ebd11 100644 --- a/.github/workflows/build-hogql-parser.yml +++ b/.github/workflows/build-hogql-parser.yml @@ -64,26 +64,10 @@ jobs: steps: - uses: actions/checkout@v4 - - if: ${{ !endsWith(matrix.os, '-arm') }} - uses: actions/setup-python@v4 + - uses: actions/setup-python@v4 with: python-version: '3.11' - # Compiling Python 3.11 from source on ARM. We tried using the "deadsnakes" ARM repo, but it was flakey. - - if: ${{ endsWith(matrix.os, '-arm') }} - name: Install Python 3.11 on ARM (compile from source) - run: | - sudo apt-get update - sudo apt-get install -y build-essential libssl-dev zlib1g-dev \ - libncurses5-dev libncursesw5-dev libreadline-dev libsqlite3-dev \ - libgdbm-dev libdb5.3-dev libbz2-dev libexpat1-dev liblzma-dev tk-dev - wget https://www.python.org/ftp/python/3.11.0/Python-3.11.0.tar.xz - tar -xf Python-3.11.0.tar.xz - cd Python-3.11.0 - ./configure --enable-optimizations - make -j 2 - sudo make altinstall - - name: Build sdist if: matrix.os == 'ubuntu-22.04' # Only build the sdist once run: cd common/hogql_parser && python setup.py sdist diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 29268601c4db4..ea90be36f43b2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -83,7 +83,7 @@ jobs: - feature-flags - others needs: changes - runs-on: depot-ubuntu-22.04-4 + runs-on: depot-ubuntu-24.04-4 timeout-minutes: 10 defaults: diff --git a/.vscode/launch.json b/.vscode/launch.json index 9eb3fe62780f2..dd756e6acc43d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -144,7 +144,8 @@ "OBJECT_STORAGE_ENABLED": "True", "HOG_HOOK_URL": "http://localhost:3300/hoghook", "CDP_ASYNC_FUNCTIONS_RUSTY_HOOK_TEAMS": "", - "CDP_CYCLOTRON_ENABLED_TEAMS": "*" + "CDP_CYCLOTRON_ENABLED_TEAMS": "*", + "PLUGIN_SERVER_MODE": "all-v2" }, "presentation": { "group": "main" diff --git a/CODEOWNERS b/CODEOWNERS index a6c7aa4861f5c..d0ae1834cd961 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,8 +1,12 @@ -frontend/src/scenes/settings/project/SurveySettings.tsx @PostHog/team-feature-success -frontend/src/scenes/surveys/ @PostHog/team-feature-success -posthog/admin/admins/survey_admin.py @PostHog/team-feature-success -posthog/api/survey.py @PostHog/team-feature-success -posthog/api/test/test_survey.py @PostHog/team-feature-success -posthog/models/feedback/survey.py @PostHog/team-feature-success -posthog/tasks/stop_surveys_reached_target.py @PostHog/team-feature-success -posthog/tasks/test/test_stop_surveys_reached_target.py @PostHog/team-feature-success \ No newline at end of file +frontend/src/scenes/settings/environment/SurveySettings.tsx @PostHog/team-surveys +frontend/src/scenes/surveys/ @PostHog/team-surveys +frontend/src/scenes/onboarding/sdks/surveys @PostHog/team-surveys +ee/surveys @PostHog/team-surveys +posthog/admin/admins/survey_admin.py @PostHog/team-surveys +posthog/api/survey.py @PostHog/team-surveys +posthog/api/test/test_survey.py @PostHog/team-surveys +posthog/models/feedback/survey.py @PostHog/team-surveys +posthog/tasks/stop_surveys_reached_target.py @PostHog/team-surveys +posthog/tasks/update_survey_adaptive_sampling @PostHog/team-surveys +posthog/tasks/update_survey_iteration @PostHog/team-surveys +posthog/tasks/test/test_stop_surveys_reached_target.py @PostHog/team-surveys \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4fa28e1fe8524..7811713249c48 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ # # --------------------------------------------------------- # -FROM node:18.19.1-bullseye-slim AS frontend-build +FROM node:18.19.1-bookworm-slim AS frontend-build WORKDIR /code SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] @@ -42,7 +42,7 @@ RUN pnpm build # # --------------------------------------------------------- # -FROM ghcr.io/posthog/rust-node-container:bullseye_rust_1.80.1-node_18.19.1 AS plugin-server-build +FROM ghcr.io/posthog/rust-node-container:bookworm_rust_1.80.1-node_18.19.1 AS plugin-server-build WORKDIR /code COPY ./rust ./rust COPY ./common/plugin_transpiler/ ./common/plugin_transpiler/ @@ -90,7 +90,7 @@ RUN corepack enable && \ # # --------------------------------------------------------- # -FROM python:3.11.9-slim-bullseye AS posthog-build +FROM python:3.11.9-slim-bookworm AS posthog-build WORKDIR /code SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] @@ -128,7 +128,7 @@ RUN SKIP_SERVICE_VERSION_REQUIREMENTS=1 STATIC_COLLECTION=1 DATABASE_URL='postgr # # --------------------------------------------------------- # -FROM debian:bullseye-slim AS fetch-geoip-db +FROM debian:bookworm-slim AS fetch-geoip-db WORKDIR /code SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] @@ -148,8 +148,8 @@ RUN apt-get update && \ # # --------------------------------------------------------- # -# NOTE: newer images change the base image from bullseye to bookworm which makes compiled openssl versions have all sorts of issues -FROM unit:1.32.0-python3.11 +# NOTE: v1.32 is running bullseye, v1.33 is running bookworm +FROM unit:1.33.0-python3.11 WORKDIR /code SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] ENV PYTHONUNBUFFERED 1 diff --git a/common/hogql_parser/HogQLParser.cpp b/common/hogql_parser/HogQLParser.cpp index fba02e60605ce..86149fdb733d5 100644 --- a/common/hogql_parser/HogQLParser.cpp +++ b/common/hogql_parser/HogQLParser.cpp @@ -65,8 +65,8 @@ void hogqlparserParserInitialize() { "ratioExpr", "settingExprList", "settingExpr", "windowExpr", "winPartitionByClause", "winOrderByClause", "winFrameClause", "winFrameExtend", "winFrameBound", "expr", "columnTypeExpr", "columnExprList", "columnExpr", "columnLambdaExpr", - "hogqlxTagElement", "hogqlxTagAttribute", "withExprList", "withExpr", - "columnIdentifier", "nestedIdentifier", "tableExpr", "tableFunctionExpr", + "hogqlxChildElement", "hogqlxTagElement", "hogqlxTagAttribute", "withExprList", + "withExpr", "columnIdentifier", "nestedIdentifier", "tableExpr", "tableFunctionExpr", "tableIdentifier", "tableArgList", "databaseIdentifier", "floatingLiteral", "numberLiteral", "literal", "interval", "keyword", "keywordForAlias", "alias", "identifier", "enumValue", "placeholder", "string", "templateString", @@ -114,7 +114,7 @@ void hogqlparserParserInitialize() { } ); static const int32_t serializedATNSegment[] = { - 4,1,162,1317,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6, + 4,1,162,1325,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6, 2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14, 7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21, 7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28, @@ -126,488 +126,491 @@ void hogqlparserParserInitialize() { 7,63,2,64,7,64,2,65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70, 7,70,2,71,7,71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77, 7,77,2,78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84, - 7,84,2,85,7,85,2,86,7,86,1,0,5,0,176,8,0,10,0,12,0,179,9,0,1,0,1,0,1, - 1,1,1,3,1,185,8,1,1,2,1,2,1,3,1,3,1,3,1,3,1,3,3,3,194,8,3,1,4,1,4,1,4, - 5,4,199,8,4,10,4,12,4,202,9,4,1,4,3,4,205,8,4,1,5,1,5,1,5,1,5,1,5,1,5, - 1,5,1,5,1,5,1,5,1,5,1,5,3,5,219,8,5,1,6,1,6,3,6,223,8,6,1,6,3,6,226,8, - 6,1,7,1,7,3,7,230,8,7,1,7,3,7,233,8,7,1,8,1,8,1,8,1,8,1,8,3,8,240,8,8, - 1,8,1,8,3,8,244,8,8,1,8,1,8,1,9,1,9,1,9,5,9,251,8,9,10,9,12,9,254,9,9, - 1,9,1,9,3,9,258,8,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,3,10,267,8,10, - 1,11,1,11,1,11,1,11,1,11,1,11,3,11,275,8,11,1,12,1,12,1,12,1,12,1,12, - 3,12,282,8,12,1,12,1,12,3,12,286,8,12,1,12,1,12,1,12,1,12,3,12,292,8, - 12,1,12,1,12,1,12,3,12,297,8,12,1,13,1,13,1,13,1,13,1,13,1,13,3,13,305, - 8,13,1,13,1,13,1,13,1,13,1,13,3,13,312,8,13,1,14,1,14,1,14,1,14,3,14, - 318,8,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,16,1,16,3,16,330,8, - 16,1,17,1,17,1,18,1,18,5,18,336,8,18,10,18,12,18,339,9,18,1,18,1,18,1, - 19,1,19,1,19,1,19,1,20,1,20,1,20,5,20,350,8,20,10,20,12,20,353,9,20,1, - 20,3,20,356,8,20,1,21,1,21,1,21,3,21,361,8,21,1,21,1,21,1,22,1,22,1,22, - 1,22,1,22,1,22,3,22,371,8,22,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,23, - 3,23,381,8,23,1,23,1,23,1,24,1,24,5,24,387,8,24,10,24,12,24,390,9,24, - 1,25,3,25,393,8,25,1,25,1,25,3,25,397,8,25,1,25,3,25,400,8,25,1,25,1, - 25,3,25,404,8,25,1,25,3,25,407,8,25,1,25,3,25,410,8,25,1,25,3,25,413, - 8,25,1,25,3,25,416,8,25,1,25,1,25,3,25,420,8,25,1,25,1,25,3,25,424,8, - 25,1,25,3,25,427,8,25,1,25,3,25,430,8,25,1,25,3,25,433,8,25,1,25,1,25, - 3,25,437,8,25,1,25,3,25,440,8,25,1,26,1,26,1,26,1,27,1,27,1,27,1,27,3, - 27,449,8,27,1,28,1,28,1,28,1,29,3,29,455,8,29,1,29,1,29,1,29,1,29,1,30, - 1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,5,30,474, - 8,30,10,30,12,30,477,9,30,1,31,1,31,1,31,1,32,1,32,1,32,1,33,1,33,1,33, - 1,33,1,33,1,33,1,33,1,33,3,33,493,8,33,1,34,1,34,1,34,1,35,1,35,1,35, - 1,35,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,3,37,510,8,37,1,37,1,37, - 1,37,1,37,3,37,516,8,37,1,37,1,37,1,37,1,37,3,37,522,8,37,1,37,1,37,1, - 37,1,37,1,37,1,37,1,37,1,37,1,37,3,37,533,8,37,3,37,535,8,37,1,38,1,38, - 1,38,1,39,1,39,1,39,1,40,1,40,1,40,3,40,546,8,40,1,40,3,40,549,8,40,1, - 40,1,40,1,40,1,40,3,40,555,8,40,1,40,1,40,1,40,1,40,1,40,1,40,3,40,563, - 8,40,1,40,1,40,1,40,1,40,5,40,569,8,40,10,40,12,40,572,9,40,1,41,3,41, - 575,8,41,1,41,1,41,1,41,3,41,580,8,41,1,41,3,41,583,8,41,1,41,3,41,586, - 8,41,1,41,1,41,3,41,590,8,41,1,41,1,41,3,41,594,8,41,1,41,3,41,597,8, - 41,3,41,599,8,41,1,41,3,41,602,8,41,1,41,1,41,3,41,606,8,41,1,41,1,41, - 3,41,610,8,41,1,41,3,41,613,8,41,3,41,615,8,41,3,41,617,8,41,1,42,1,42, - 1,42,3,42,622,8,42,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,3,43, - 633,8,43,1,44,1,44,1,44,1,44,3,44,639,8,44,1,45,1,45,1,45,5,45,644,8, - 45,10,45,12,45,647,9,45,1,46,1,46,3,46,651,8,46,1,46,1,46,3,46,655,8, - 46,1,46,1,46,3,46,659,8,46,1,47,1,47,1,47,1,47,3,47,665,8,47,3,47,667, - 8,47,1,48,1,48,1,48,5,48,672,8,48,10,48,12,48,675,9,48,1,49,1,49,1,49, - 1,49,1,50,3,50,682,8,50,1,50,3,50,685,8,50,1,50,3,50,688,8,50,1,51,1, + 7,84,2,85,7,85,2,86,7,86,2,87,7,87,1,0,5,0,178,8,0,10,0,12,0,181,9,0, + 1,0,1,0,1,1,1,1,3,1,187,8,1,1,2,1,2,1,3,1,3,1,3,1,3,1,3,3,3,196,8,3,1, + 4,1,4,1,4,5,4,201,8,4,10,4,12,4,204,9,4,1,4,3,4,207,8,4,1,5,1,5,1,5,1, + 5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,221,8,5,1,6,1,6,3,6,225,8,6,1,6, + 3,6,228,8,6,1,7,1,7,3,7,232,8,7,1,7,3,7,235,8,7,1,8,1,8,1,8,1,8,1,8,3, + 8,242,8,8,1,8,1,8,3,8,246,8,8,1,8,1,8,1,9,1,9,1,9,5,9,253,8,9,10,9,12, + 9,256,9,9,1,9,1,9,3,9,260,8,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,3,10, + 269,8,10,1,11,1,11,1,11,1,11,1,11,1,11,3,11,277,8,11,1,12,1,12,1,12,1, + 12,1,12,3,12,284,8,12,1,12,1,12,3,12,288,8,12,1,12,1,12,1,12,1,12,3,12, + 294,8,12,1,12,1,12,1,12,3,12,299,8,12,1,13,1,13,1,13,1,13,1,13,1,13,3, + 13,307,8,13,1,13,1,13,1,13,1,13,1,13,3,13,314,8,13,1,14,1,14,1,14,1,14, + 3,14,320,8,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,16,1,16,3,16, + 332,8,16,1,17,1,17,1,18,1,18,5,18,338,8,18,10,18,12,18,341,9,18,1,18, + 1,18,1,19,1,19,1,19,1,19,1,20,1,20,1,20,5,20,352,8,20,10,20,12,20,355, + 9,20,1,20,3,20,358,8,20,1,21,1,21,1,21,3,21,363,8,21,1,21,1,21,1,22,1, + 22,1,22,1,22,1,22,1,22,3,22,373,8,22,1,23,1,23,1,23,1,23,1,23,1,23,1, + 23,1,23,3,23,383,8,23,1,23,1,23,1,24,1,24,5,24,389,8,24,10,24,12,24,392, + 9,24,1,25,3,25,395,8,25,1,25,1,25,3,25,399,8,25,1,25,3,25,402,8,25,1, + 25,1,25,3,25,406,8,25,1,25,3,25,409,8,25,1,25,3,25,412,8,25,1,25,3,25, + 415,8,25,1,25,3,25,418,8,25,1,25,1,25,3,25,422,8,25,1,25,1,25,3,25,426, + 8,25,1,25,3,25,429,8,25,1,25,3,25,432,8,25,1,25,3,25,435,8,25,1,25,1, + 25,3,25,439,8,25,1,25,3,25,442,8,25,1,26,1,26,1,26,1,27,1,27,1,27,1,27, + 3,27,451,8,27,1,28,1,28,1,28,1,29,3,29,457,8,29,1,29,1,29,1,29,1,29,1, + 30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,5,30,476, + 8,30,10,30,12,30,479,9,30,1,31,1,31,1,31,1,32,1,32,1,32,1,33,1,33,1,33, + 1,33,1,33,1,33,1,33,1,33,3,33,495,8,33,1,34,1,34,1,34,1,35,1,35,1,35, + 1,35,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,3,37,512,8,37,1,37,1,37, + 1,37,1,37,3,37,518,8,37,1,37,1,37,1,37,1,37,3,37,524,8,37,1,37,1,37,1, + 37,1,37,1,37,1,37,1,37,1,37,1,37,3,37,535,8,37,3,37,537,8,37,1,38,1,38, + 1,38,1,39,1,39,1,39,1,40,1,40,1,40,3,40,548,8,40,1,40,3,40,551,8,40,1, + 40,1,40,1,40,1,40,3,40,557,8,40,1,40,1,40,1,40,1,40,1,40,1,40,3,40,565, + 8,40,1,40,1,40,1,40,1,40,5,40,571,8,40,10,40,12,40,574,9,40,1,41,3,41, + 577,8,41,1,41,1,41,1,41,3,41,582,8,41,1,41,3,41,585,8,41,1,41,3,41,588, + 8,41,1,41,1,41,3,41,592,8,41,1,41,1,41,3,41,596,8,41,1,41,3,41,599,8, + 41,3,41,601,8,41,1,41,3,41,604,8,41,1,41,1,41,3,41,608,8,41,1,41,1,41, + 3,41,612,8,41,1,41,3,41,615,8,41,3,41,617,8,41,3,41,619,8,41,1,42,1,42, + 1,42,3,42,624,8,42,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,3,43, + 635,8,43,1,44,1,44,1,44,1,44,3,44,641,8,44,1,45,1,45,1,45,5,45,646,8, + 45,10,45,12,45,649,9,45,1,46,1,46,3,46,653,8,46,1,46,1,46,3,46,657,8, + 46,1,46,1,46,3,46,661,8,46,1,47,1,47,1,47,1,47,3,47,667,8,47,3,47,669, + 8,47,1,48,1,48,1,48,5,48,674,8,48,10,48,12,48,677,9,48,1,49,1,49,1,49, + 1,49,1,50,3,50,684,8,50,1,50,3,50,687,8,50,1,50,3,50,690,8,50,1,51,1, 51,1,51,1,51,1,52,1,52,1,52,1,52,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1, - 54,1,54,3,54,707,8,54,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1, - 55,1,55,1,55,3,55,721,8,55,1,56,1,56,1,56,1,57,1,57,1,57,1,57,1,57,1, - 57,1,57,1,57,1,57,5,57,735,8,57,10,57,12,57,738,9,57,1,57,3,57,741,8, - 57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,5,57,750,8,57,10,57,12,57,753,9, - 57,1,57,3,57,756,8,57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,5,57,765,8,57, - 10,57,12,57,768,9,57,1,57,3,57,771,8,57,1,57,1,57,1,57,1,57,1,57,3,57, - 778,8,57,1,57,1,57,3,57,782,8,57,1,58,1,58,1,58,5,58,787,8,58,10,58,12, - 58,790,9,58,1,58,3,58,793,8,58,1,59,1,59,1,59,3,59,798,8,59,1,59,1,59, - 1,59,1,59,1,59,4,59,805,8,59,11,59,12,59,806,1,59,1,59,3,59,811,8,59, + 54,1,54,3,54,709,8,54,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1, + 55,1,55,1,55,3,55,723,8,55,1,56,1,56,1,56,1,57,1,57,1,57,1,57,1,57,1, + 57,1,57,1,57,1,57,5,57,737,8,57,10,57,12,57,740,9,57,1,57,3,57,743,8, + 57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,5,57,752,8,57,10,57,12,57,755,9, + 57,1,57,3,57,758,8,57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,5,57,767,8,57, + 10,57,12,57,770,9,57,1,57,3,57,773,8,57,1,57,1,57,1,57,1,57,1,57,3,57, + 780,8,57,1,57,1,57,3,57,784,8,57,1,58,1,58,1,58,5,58,789,8,58,10,58,12, + 58,792,9,58,1,58,3,58,795,8,58,1,59,1,59,1,59,3,59,800,8,59,1,59,1,59, + 1,59,1,59,1,59,4,59,807,8,59,11,59,12,59,808,1,59,1,59,3,59,813,8,59, 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,837,8,59,1,59, + 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,839,8,59,1,59, 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 3,59,854,8,59,1,59,1,59,1,59,1,59,3,59,860,8,59,1,59,3,59,863,8,59,1, - 59,3,59,866,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,876,8,59, - 1,59,1,59,1,59,1,59,3,59,882,8,59,1,59,3,59,885,8,59,1,59,3,59,888,8, - 59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,896,8,59,1,59,3,59,899,8,59,1,59, - 1,59,3,59,903,8,59,1,59,3,59,906,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1, - 59,1,59,1,59,1,59,1,59,1,59,3,59,920,8,59,1,59,1,59,1,59,1,59,1,59,1, - 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,937,8,59,1,59,1, - 59,1,59,3,59,942,8,59,1,59,1,59,1,59,3,59,947,8,59,1,59,1,59,1,59,1,59, - 3,59,953,8,59,1,59,1,59,1,59,1,59,1,59,3,59,960,8,59,1,59,1,59,1,59,1, - 59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,972,8,59,1,59,1,59,3,59,976,8,59, - 1,59,3,59,979,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,988,8,59,1, - 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,1002,8, + 3,59,856,8,59,1,59,1,59,1,59,1,59,3,59,862,8,59,1,59,3,59,865,8,59,1, + 59,3,59,868,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,878,8,59, + 1,59,1,59,1,59,1,59,3,59,884,8,59,1,59,3,59,887,8,59,1,59,3,59,890,8, + 59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,898,8,59,1,59,3,59,901,8,59,1,59, + 1,59,3,59,905,8,59,1,59,3,59,908,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1, + 59,1,59,1,59,1,59,1,59,1,59,3,59,922,8,59,1,59,1,59,1,59,1,59,1,59,1, + 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,939,8,59,1,59,1, + 59,1,59,3,59,944,8,59,1,59,1,59,1,59,3,59,949,8,59,1,59,1,59,1,59,1,59, + 3,59,955,8,59,1,59,1,59,1,59,1,59,1,59,3,59,962,8,59,1,59,1,59,1,59,1, + 59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,974,8,59,1,59,1,59,3,59,978,8,59, + 1,59,3,59,981,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,990,8,59,1, + 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,1004,8, 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1, - 59,3,59,1018,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1, + 59,3,59,1020,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1, 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1, - 59,1,59,1,59,3,59,1047,8,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,1055,8, - 59,5,59,1057,8,59,10,59,12,59,1060,9,59,1,60,1,60,1,60,1,60,5,60,1066, - 8,60,10,60,12,60,1069,9,60,1,60,3,60,1072,8,60,1,60,1,60,1,60,1,60,1, - 60,5,60,1079,8,60,10,60,12,60,1082,9,60,1,60,3,60,1085,8,60,1,60,1,60, - 3,60,1089,8,60,1,60,1,60,1,60,3,60,1094,8,60,1,61,1,61,1,61,5,61,1099, - 8,61,10,61,12,61,1102,9,61,1,61,1,61,1,61,1,61,1,61,1,61,5,61,1110,8, - 61,10,61,12,61,1113,9,61,1,61,1,61,1,61,1,61,1,61,1,61,3,61,1121,8,61, - 1,61,1,61,1,61,1,61,1,61,3,61,1128,8,61,1,62,1,62,1,62,1,62,1,62,1,62, - 1,62,1,62,1,62,1,62,1,62,3,62,1141,8,62,1,63,1,63,1,63,5,63,1146,8,63, - 10,63,12,63,1149,9,63,1,63,3,63,1152,8,63,1,64,1,64,1,64,1,64,1,64,1, - 64,1,64,1,64,1,64,1,64,3,64,1164,8,64,1,65,1,65,1,65,1,65,3,65,1170,8, - 65,1,65,3,65,1173,8,65,1,66,1,66,1,66,5,66,1178,8,66,10,66,12,66,1181, - 9,66,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,3,67,1192,8,67,1,67, - 1,67,1,67,1,67,3,67,1198,8,67,5,67,1200,8,67,10,67,12,67,1203,9,67,1, - 68,1,68,1,68,3,68,1208,8,68,1,68,1,68,1,69,1,69,1,69,3,69,1215,8,69,1, - 69,1,69,1,70,1,70,1,70,5,70,1222,8,70,10,70,12,70,1225,9,70,1,70,3,70, - 1228,8,70,1,71,1,71,1,72,1,72,1,72,1,72,1,72,1,72,3,72,1238,8,72,3,72, - 1240,8,72,1,73,3,73,1243,8,73,1,73,1,73,1,73,1,73,1,73,1,73,3,73,1251, - 8,73,1,74,1,74,1,74,3,74,1256,8,74,1,75,1,75,1,76,1,76,1,77,1,77,1,78, - 1,78,3,78,1266,8,78,1,79,1,79,1,79,3,79,1271,8,79,1,80,1,80,1,80,1,80, - 1,81,1,81,1,81,1,81,1,82,1,82,3,82,1283,8,82,1,83,1,83,5,83,1287,8,83, - 10,83,12,83,1290,9,83,1,83,1,83,1,84,1,84,1,84,1,84,1,84,3,84,1299,8, - 84,1,85,1,85,5,85,1303,8,85,10,85,12,85,1306,9,85,1,85,1,85,1,86,1,86, - 1,86,1,86,1,86,3,86,1315,8,86,1,86,0,3,80,118,134,87,0,2,4,6,8,10,12, - 14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58, - 60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,104, - 106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,140, - 142,144,146,148,150,152,154,156,158,160,162,164,166,168,170,172,0,17, - 2,0,32,32,37,37,2,0,18,18,77,77,2,0,46,46,54,54,3,0,1,1,4,4,8,8,4,0,1, - 1,3,4,8,8,83,83,2,0,54,54,76,76,2,0,1,1,4,4,2,0,7,7,22,23,2,0,31,31,52, - 52,2,0,74,74,79,79,3,0,10,10,53,53,93,93,2,0,43,43,56,56,1,0,110,111, - 2,0,121,121,142,142,7,0,21,21,40,40,58,59,73,73,81,81,100,100,106,106, - 19,0,1,13,15,20,22,26,28,29,31,31,33,36,38,39,41,44,46,46,48,54,56,57, - 61,61,63,72,74,80,82,86,88,95,97,99,101,102,104,105,4,0,20,20,31,31,41, - 41,51,51,1493,0,177,1,0,0,0,2,184,1,0,0,0,4,186,1,0,0,0,6,188,1,0,0,0, - 8,195,1,0,0,0,10,218,1,0,0,0,12,220,1,0,0,0,14,227,1,0,0,0,16,234,1,0, - 0,0,18,247,1,0,0,0,20,259,1,0,0,0,22,268,1,0,0,0,24,276,1,0,0,0,26,298, - 1,0,0,0,28,313,1,0,0,0,30,322,1,0,0,0,32,327,1,0,0,0,34,331,1,0,0,0,36, - 333,1,0,0,0,38,342,1,0,0,0,40,346,1,0,0,0,42,360,1,0,0,0,44,370,1,0,0, - 0,46,380,1,0,0,0,48,384,1,0,0,0,50,392,1,0,0,0,52,441,1,0,0,0,54,444, - 1,0,0,0,56,450,1,0,0,0,58,454,1,0,0,0,60,460,1,0,0,0,62,478,1,0,0,0,64, - 481,1,0,0,0,66,484,1,0,0,0,68,494,1,0,0,0,70,497,1,0,0,0,72,501,1,0,0, - 0,74,534,1,0,0,0,76,536,1,0,0,0,78,539,1,0,0,0,80,554,1,0,0,0,82,616, - 1,0,0,0,84,621,1,0,0,0,86,632,1,0,0,0,88,634,1,0,0,0,90,640,1,0,0,0,92, - 648,1,0,0,0,94,666,1,0,0,0,96,668,1,0,0,0,98,676,1,0,0,0,100,681,1,0, - 0,0,102,689,1,0,0,0,104,693,1,0,0,0,106,697,1,0,0,0,108,706,1,0,0,0,110, - 720,1,0,0,0,112,722,1,0,0,0,114,781,1,0,0,0,116,783,1,0,0,0,118,946,1, - 0,0,0,120,1088,1,0,0,0,122,1127,1,0,0,0,124,1140,1,0,0,0,126,1142,1,0, - 0,0,128,1163,1,0,0,0,130,1172,1,0,0,0,132,1174,1,0,0,0,134,1191,1,0,0, - 0,136,1204,1,0,0,0,138,1214,1,0,0,0,140,1218,1,0,0,0,142,1229,1,0,0,0, - 144,1239,1,0,0,0,146,1242,1,0,0,0,148,1255,1,0,0,0,150,1257,1,0,0,0,152, - 1259,1,0,0,0,154,1261,1,0,0,0,156,1265,1,0,0,0,158,1270,1,0,0,0,160,1272, - 1,0,0,0,162,1276,1,0,0,0,164,1282,1,0,0,0,166,1284,1,0,0,0,168,1298,1, - 0,0,0,170,1300,1,0,0,0,172,1314,1,0,0,0,174,176,3,2,1,0,175,174,1,0,0, - 0,176,179,1,0,0,0,177,175,1,0,0,0,177,178,1,0,0,0,178,180,1,0,0,0,179, - 177,1,0,0,0,180,181,5,0,0,1,181,1,1,0,0,0,182,185,3,6,3,0,183,185,3,10, - 5,0,184,182,1,0,0,0,184,183,1,0,0,0,185,3,1,0,0,0,186,187,3,118,59,0, - 187,5,1,0,0,0,188,189,5,55,0,0,189,193,3,158,79,0,190,191,5,118,0,0,191, - 192,5,125,0,0,192,194,3,4,2,0,193,190,1,0,0,0,193,194,1,0,0,0,194,7,1, - 0,0,0,195,200,3,158,79,0,196,197,5,119,0,0,197,199,3,158,79,0,198,196, - 1,0,0,0,199,202,1,0,0,0,200,198,1,0,0,0,200,201,1,0,0,0,201,204,1,0,0, - 0,202,200,1,0,0,0,203,205,5,119,0,0,204,203,1,0,0,0,204,205,1,0,0,0,205, - 9,1,0,0,0,206,219,3,12,6,0,207,219,3,14,7,0,208,219,3,18,9,0,209,219, - 3,20,10,0,210,219,3,22,11,0,211,219,3,26,13,0,212,219,3,24,12,0,213,219, - 3,28,14,0,214,219,3,30,15,0,215,219,3,36,18,0,216,219,3,32,16,0,217,219, - 3,34,17,0,218,206,1,0,0,0,218,207,1,0,0,0,218,208,1,0,0,0,218,209,1,0, - 0,0,218,210,1,0,0,0,218,211,1,0,0,0,218,212,1,0,0,0,218,213,1,0,0,0,218, - 214,1,0,0,0,218,215,1,0,0,0,218,216,1,0,0,0,218,217,1,0,0,0,219,11,1, - 0,0,0,220,222,5,75,0,0,221,223,3,4,2,0,222,221,1,0,0,0,222,223,1,0,0, - 0,223,225,1,0,0,0,224,226,5,153,0,0,225,224,1,0,0,0,225,226,1,0,0,0,226, - 13,1,0,0,0,227,229,5,87,0,0,228,230,3,4,2,0,229,228,1,0,0,0,229,230,1, - 0,0,0,230,232,1,0,0,0,231,233,5,153,0,0,232,231,1,0,0,0,232,233,1,0,0, - 0,233,15,1,0,0,0,234,243,5,14,0,0,235,236,5,133,0,0,236,239,3,158,79, - 0,237,238,5,118,0,0,238,240,3,158,79,0,239,237,1,0,0,0,239,240,1,0,0, - 0,240,241,1,0,0,0,241,242,5,152,0,0,242,244,1,0,0,0,243,235,1,0,0,0,243, - 244,1,0,0,0,244,245,1,0,0,0,245,246,3,36,18,0,246,17,1,0,0,0,247,248, - 5,96,0,0,248,252,3,36,18,0,249,251,3,16,8,0,250,249,1,0,0,0,251,254,1, - 0,0,0,252,250,1,0,0,0,252,253,1,0,0,0,253,257,1,0,0,0,254,252,1,0,0,0, - 255,256,5,30,0,0,256,258,3,36,18,0,257,255,1,0,0,0,257,258,1,0,0,0,258, - 19,1,0,0,0,259,260,5,42,0,0,260,261,5,133,0,0,261,262,3,4,2,0,262,263, - 5,152,0,0,263,266,3,10,5,0,264,265,5,25,0,0,265,267,3,10,5,0,266,264, - 1,0,0,0,266,267,1,0,0,0,267,21,1,0,0,0,268,269,5,103,0,0,269,270,5,133, - 0,0,270,271,3,4,2,0,271,272,5,152,0,0,272,274,3,10,5,0,273,275,5,153, - 0,0,274,273,1,0,0,0,274,275,1,0,0,0,275,23,1,0,0,0,276,277,5,34,0,0,277, - 281,5,133,0,0,278,282,3,6,3,0,279,282,3,30,15,0,280,282,3,4,2,0,281,278, - 1,0,0,0,281,279,1,0,0,0,281,280,1,0,0,0,281,282,1,0,0,0,282,283,1,0,0, - 0,283,285,5,153,0,0,284,286,3,4,2,0,285,284,1,0,0,0,285,286,1,0,0,0,286, - 287,1,0,0,0,287,291,5,153,0,0,288,292,3,6,3,0,289,292,3,30,15,0,290,292, - 3,4,2,0,291,288,1,0,0,0,291,289,1,0,0,0,291,290,1,0,0,0,291,292,1,0,0, - 0,292,293,1,0,0,0,293,294,5,152,0,0,294,296,3,10,5,0,295,297,5,153,0, - 0,296,295,1,0,0,0,296,297,1,0,0,0,297,25,1,0,0,0,298,299,5,34,0,0,299, - 300,5,133,0,0,300,301,5,55,0,0,301,304,3,158,79,0,302,303,5,119,0,0,303, - 305,3,158,79,0,304,302,1,0,0,0,304,305,1,0,0,0,305,306,1,0,0,0,306,307, - 5,44,0,0,307,308,3,4,2,0,308,309,5,152,0,0,309,311,3,10,5,0,310,312,5, - 153,0,0,311,310,1,0,0,0,311,312,1,0,0,0,312,27,1,0,0,0,313,314,7,0,0, - 0,314,315,3,158,79,0,315,317,5,133,0,0,316,318,3,8,4,0,317,316,1,0,0, - 0,317,318,1,0,0,0,318,319,1,0,0,0,319,320,5,152,0,0,320,321,3,36,18,0, - 321,29,1,0,0,0,322,323,3,4,2,0,323,324,5,118,0,0,324,325,5,125,0,0,325, - 326,3,4,2,0,326,31,1,0,0,0,327,329,3,4,2,0,328,330,5,153,0,0,329,328, - 1,0,0,0,329,330,1,0,0,0,330,33,1,0,0,0,331,332,5,153,0,0,332,35,1,0,0, - 0,333,337,5,131,0,0,334,336,3,2,1,0,335,334,1,0,0,0,336,339,1,0,0,0,337, - 335,1,0,0,0,337,338,1,0,0,0,338,340,1,0,0,0,339,337,1,0,0,0,340,341,5, - 150,0,0,341,37,1,0,0,0,342,343,3,4,2,0,343,344,5,118,0,0,344,345,3,4, - 2,0,345,39,1,0,0,0,346,351,3,38,19,0,347,348,5,119,0,0,348,350,3,38,19, - 0,349,347,1,0,0,0,350,353,1,0,0,0,351,349,1,0,0,0,351,352,1,0,0,0,352, - 355,1,0,0,0,353,351,1,0,0,0,354,356,5,119,0,0,355,354,1,0,0,0,355,356, - 1,0,0,0,356,41,1,0,0,0,357,361,3,48,24,0,358,361,3,50,25,0,359,361,3, - 122,61,0,360,357,1,0,0,0,360,358,1,0,0,0,360,359,1,0,0,0,361,362,1,0, - 0,0,362,363,5,0,0,1,363,43,1,0,0,0,364,371,3,50,25,0,365,366,5,133,0, - 0,366,367,3,48,24,0,367,368,5,152,0,0,368,371,1,0,0,0,369,371,3,162,81, - 0,370,364,1,0,0,0,370,365,1,0,0,0,370,369,1,0,0,0,371,45,1,0,0,0,372, - 381,5,27,0,0,373,374,5,98,0,0,374,381,5,1,0,0,375,376,5,98,0,0,376,381, - 5,24,0,0,377,381,5,47,0,0,378,379,5,47,0,0,379,381,5,24,0,0,380,372,1, - 0,0,0,380,373,1,0,0,0,380,375,1,0,0,0,380,377,1,0,0,0,380,378,1,0,0,0, - 381,382,1,0,0,0,382,383,3,44,22,0,383,47,1,0,0,0,384,388,3,44,22,0,385, - 387,3,46,23,0,386,385,1,0,0,0,387,390,1,0,0,0,388,386,1,0,0,0,388,389, - 1,0,0,0,389,49,1,0,0,0,390,388,1,0,0,0,391,393,3,52,26,0,392,391,1,0, - 0,0,392,393,1,0,0,0,393,394,1,0,0,0,394,396,5,82,0,0,395,397,5,24,0,0, - 396,395,1,0,0,0,396,397,1,0,0,0,397,399,1,0,0,0,398,400,3,54,27,0,399, - 398,1,0,0,0,399,400,1,0,0,0,400,401,1,0,0,0,401,403,3,116,58,0,402,404, - 3,56,28,0,403,402,1,0,0,0,403,404,1,0,0,0,404,406,1,0,0,0,405,407,3,58, - 29,0,406,405,1,0,0,0,406,407,1,0,0,0,407,409,1,0,0,0,408,410,3,62,31, - 0,409,408,1,0,0,0,409,410,1,0,0,0,410,412,1,0,0,0,411,413,3,64,32,0,412, - 411,1,0,0,0,412,413,1,0,0,0,413,415,1,0,0,0,414,416,3,66,33,0,415,414, - 1,0,0,0,415,416,1,0,0,0,416,419,1,0,0,0,417,418,5,105,0,0,418,420,7,1, - 0,0,419,417,1,0,0,0,419,420,1,0,0,0,420,423,1,0,0,0,421,422,5,105,0,0, - 422,424,5,92,0,0,423,421,1,0,0,0,423,424,1,0,0,0,424,426,1,0,0,0,425, - 427,3,68,34,0,426,425,1,0,0,0,426,427,1,0,0,0,427,429,1,0,0,0,428,430, - 3,60,30,0,429,428,1,0,0,0,429,430,1,0,0,0,430,432,1,0,0,0,431,433,3,70, - 35,0,432,431,1,0,0,0,432,433,1,0,0,0,433,436,1,0,0,0,434,437,3,74,37, - 0,435,437,3,76,38,0,436,434,1,0,0,0,436,435,1,0,0,0,436,437,1,0,0,0,437, - 439,1,0,0,0,438,440,3,78,39,0,439,438,1,0,0,0,439,440,1,0,0,0,440,51, - 1,0,0,0,441,442,5,105,0,0,442,443,3,126,63,0,443,53,1,0,0,0,444,445,5, - 91,0,0,445,448,5,111,0,0,446,447,5,105,0,0,447,449,5,88,0,0,448,446,1, - 0,0,0,448,449,1,0,0,0,449,55,1,0,0,0,450,451,5,35,0,0,451,452,3,80,40, - 0,452,57,1,0,0,0,453,455,7,2,0,0,454,453,1,0,0,0,454,455,1,0,0,0,455, - 456,1,0,0,0,456,457,5,5,0,0,457,458,5,50,0,0,458,459,3,116,58,0,459,59, - 1,0,0,0,460,461,5,104,0,0,461,462,3,158,79,0,462,463,5,6,0,0,463,464, - 5,133,0,0,464,465,3,100,50,0,465,475,5,152,0,0,466,467,5,119,0,0,467, - 468,3,158,79,0,468,469,5,6,0,0,469,470,5,133,0,0,470,471,3,100,50,0,471, - 472,5,152,0,0,472,474,1,0,0,0,473,466,1,0,0,0,474,477,1,0,0,0,475,473, - 1,0,0,0,475,476,1,0,0,0,476,61,1,0,0,0,477,475,1,0,0,0,478,479,5,72,0, - 0,479,480,3,118,59,0,480,63,1,0,0,0,481,482,5,102,0,0,482,483,3,118,59, - 0,483,65,1,0,0,0,484,485,5,38,0,0,485,492,5,11,0,0,486,487,7,1,0,0,487, - 488,5,133,0,0,488,489,3,116,58,0,489,490,5,152,0,0,490,493,1,0,0,0,491, - 493,3,116,58,0,492,486,1,0,0,0,492,491,1,0,0,0,493,67,1,0,0,0,494,495, - 5,39,0,0,495,496,3,118,59,0,496,69,1,0,0,0,497,498,5,67,0,0,498,499,5, - 11,0,0,499,500,3,90,45,0,500,71,1,0,0,0,501,502,5,67,0,0,502,503,5,11, - 0,0,503,504,3,116,58,0,504,73,1,0,0,0,505,506,5,57,0,0,506,509,3,118, - 59,0,507,508,5,119,0,0,508,510,3,118,59,0,509,507,1,0,0,0,509,510,1,0, - 0,0,510,515,1,0,0,0,511,512,5,105,0,0,512,516,5,88,0,0,513,514,5,11,0, - 0,514,516,3,116,58,0,515,511,1,0,0,0,515,513,1,0,0,0,515,516,1,0,0,0, - 516,535,1,0,0,0,517,518,5,57,0,0,518,521,3,118,59,0,519,520,5,105,0,0, - 520,522,5,88,0,0,521,519,1,0,0,0,521,522,1,0,0,0,522,523,1,0,0,0,523, - 524,5,64,0,0,524,525,3,118,59,0,525,535,1,0,0,0,526,527,5,57,0,0,527, - 528,3,118,59,0,528,529,5,64,0,0,529,532,3,118,59,0,530,531,5,11,0,0,531, - 533,3,116,58,0,532,530,1,0,0,0,532,533,1,0,0,0,533,535,1,0,0,0,534,505, - 1,0,0,0,534,517,1,0,0,0,534,526,1,0,0,0,535,75,1,0,0,0,536,537,5,64,0, - 0,537,538,3,118,59,0,538,77,1,0,0,0,539,540,5,84,0,0,540,541,3,96,48, - 0,541,79,1,0,0,0,542,543,6,40,-1,0,543,545,3,134,67,0,544,546,5,29,0, - 0,545,544,1,0,0,0,545,546,1,0,0,0,546,548,1,0,0,0,547,549,3,88,44,0,548, - 547,1,0,0,0,548,549,1,0,0,0,549,555,1,0,0,0,550,551,5,133,0,0,551,552, - 3,80,40,0,552,553,5,152,0,0,553,555,1,0,0,0,554,542,1,0,0,0,554,550,1, - 0,0,0,555,570,1,0,0,0,556,557,10,3,0,0,557,558,3,84,42,0,558,559,3,80, - 40,4,559,569,1,0,0,0,560,562,10,4,0,0,561,563,3,82,41,0,562,561,1,0,0, - 0,562,563,1,0,0,0,563,564,1,0,0,0,564,565,5,50,0,0,565,566,3,80,40,0, - 566,567,3,86,43,0,567,569,1,0,0,0,568,556,1,0,0,0,568,560,1,0,0,0,569, - 572,1,0,0,0,570,568,1,0,0,0,570,571,1,0,0,0,571,81,1,0,0,0,572,570,1, - 0,0,0,573,575,7,3,0,0,574,573,1,0,0,0,574,575,1,0,0,0,575,576,1,0,0,0, - 576,583,5,46,0,0,577,579,5,46,0,0,578,580,7,3,0,0,579,578,1,0,0,0,579, - 580,1,0,0,0,580,583,1,0,0,0,581,583,7,3,0,0,582,574,1,0,0,0,582,577,1, - 0,0,0,582,581,1,0,0,0,583,617,1,0,0,0,584,586,7,4,0,0,585,584,1,0,0,0, - 585,586,1,0,0,0,586,587,1,0,0,0,587,589,7,5,0,0,588,590,5,68,0,0,589, - 588,1,0,0,0,589,590,1,0,0,0,590,599,1,0,0,0,591,593,7,5,0,0,592,594,5, - 68,0,0,593,592,1,0,0,0,593,594,1,0,0,0,594,596,1,0,0,0,595,597,7,4,0, - 0,596,595,1,0,0,0,596,597,1,0,0,0,597,599,1,0,0,0,598,585,1,0,0,0,598, - 591,1,0,0,0,599,617,1,0,0,0,600,602,7,6,0,0,601,600,1,0,0,0,601,602,1, - 0,0,0,602,603,1,0,0,0,603,605,5,36,0,0,604,606,5,68,0,0,605,604,1,0,0, - 0,605,606,1,0,0,0,606,615,1,0,0,0,607,609,5,36,0,0,608,610,5,68,0,0,609, - 608,1,0,0,0,609,610,1,0,0,0,610,612,1,0,0,0,611,613,7,6,0,0,612,611,1, - 0,0,0,612,613,1,0,0,0,613,615,1,0,0,0,614,601,1,0,0,0,614,607,1,0,0,0, - 615,617,1,0,0,0,616,582,1,0,0,0,616,598,1,0,0,0,616,614,1,0,0,0,617,83, - 1,0,0,0,618,619,5,17,0,0,619,622,5,50,0,0,620,622,5,119,0,0,621,618,1, - 0,0,0,621,620,1,0,0,0,622,85,1,0,0,0,623,624,5,65,0,0,624,633,3,116,58, - 0,625,626,5,99,0,0,626,627,5,133,0,0,627,628,3,116,58,0,628,629,5,152, - 0,0,629,633,1,0,0,0,630,631,5,99,0,0,631,633,3,116,58,0,632,623,1,0,0, - 0,632,625,1,0,0,0,632,630,1,0,0,0,633,87,1,0,0,0,634,635,5,80,0,0,635, - 638,3,94,47,0,636,637,5,64,0,0,637,639,3,94,47,0,638,636,1,0,0,0,638, - 639,1,0,0,0,639,89,1,0,0,0,640,645,3,92,46,0,641,642,5,119,0,0,642,644, - 3,92,46,0,643,641,1,0,0,0,644,647,1,0,0,0,645,643,1,0,0,0,645,646,1,0, - 0,0,646,91,1,0,0,0,647,645,1,0,0,0,648,650,3,118,59,0,649,651,7,7,0,0, - 650,649,1,0,0,0,650,651,1,0,0,0,651,654,1,0,0,0,652,653,5,63,0,0,653, - 655,7,8,0,0,654,652,1,0,0,0,654,655,1,0,0,0,655,658,1,0,0,0,656,657,5, - 16,0,0,657,659,5,113,0,0,658,656,1,0,0,0,658,659,1,0,0,0,659,93,1,0,0, - 0,660,667,3,162,81,0,661,664,3,146,73,0,662,663,5,154,0,0,663,665,3,146, - 73,0,664,662,1,0,0,0,664,665,1,0,0,0,665,667,1,0,0,0,666,660,1,0,0,0, - 666,661,1,0,0,0,667,95,1,0,0,0,668,673,3,98,49,0,669,670,5,119,0,0,670, - 672,3,98,49,0,671,669,1,0,0,0,672,675,1,0,0,0,673,671,1,0,0,0,673,674, - 1,0,0,0,674,97,1,0,0,0,675,673,1,0,0,0,676,677,3,158,79,0,677,678,5,125, - 0,0,678,679,3,148,74,0,679,99,1,0,0,0,680,682,3,102,51,0,681,680,1,0, - 0,0,681,682,1,0,0,0,682,684,1,0,0,0,683,685,3,104,52,0,684,683,1,0,0, - 0,684,685,1,0,0,0,685,687,1,0,0,0,686,688,3,106,53,0,687,686,1,0,0,0, - 687,688,1,0,0,0,688,101,1,0,0,0,689,690,5,70,0,0,690,691,5,11,0,0,691, - 692,3,116,58,0,692,103,1,0,0,0,693,694,5,67,0,0,694,695,5,11,0,0,695, - 696,3,90,45,0,696,105,1,0,0,0,697,698,7,9,0,0,698,699,3,108,54,0,699, - 107,1,0,0,0,700,707,3,110,55,0,701,702,5,9,0,0,702,703,3,110,55,0,703, - 704,5,2,0,0,704,705,3,110,55,0,705,707,1,0,0,0,706,700,1,0,0,0,706,701, - 1,0,0,0,707,109,1,0,0,0,708,709,5,19,0,0,709,721,5,78,0,0,710,711,5,97, - 0,0,711,721,5,71,0,0,712,713,5,97,0,0,713,721,5,33,0,0,714,715,3,146, - 73,0,715,716,5,71,0,0,716,721,1,0,0,0,717,718,3,146,73,0,718,719,5,33, - 0,0,719,721,1,0,0,0,720,708,1,0,0,0,720,710,1,0,0,0,720,712,1,0,0,0,720, - 714,1,0,0,0,720,717,1,0,0,0,721,111,1,0,0,0,722,723,3,118,59,0,723,724, - 5,0,0,1,724,113,1,0,0,0,725,782,3,158,79,0,726,727,3,158,79,0,727,728, - 5,133,0,0,728,729,3,158,79,0,729,736,3,114,57,0,730,731,5,119,0,0,731, - 732,3,158,79,0,732,733,3,114,57,0,733,735,1,0,0,0,734,730,1,0,0,0,735, - 738,1,0,0,0,736,734,1,0,0,0,736,737,1,0,0,0,737,740,1,0,0,0,738,736,1, - 0,0,0,739,741,5,119,0,0,740,739,1,0,0,0,740,741,1,0,0,0,741,742,1,0,0, - 0,742,743,5,152,0,0,743,782,1,0,0,0,744,745,3,158,79,0,745,746,5,133, - 0,0,746,751,3,160,80,0,747,748,5,119,0,0,748,750,3,160,80,0,749,747,1, - 0,0,0,750,753,1,0,0,0,751,749,1,0,0,0,751,752,1,0,0,0,752,755,1,0,0,0, - 753,751,1,0,0,0,754,756,5,119,0,0,755,754,1,0,0,0,755,756,1,0,0,0,756, - 757,1,0,0,0,757,758,5,152,0,0,758,782,1,0,0,0,759,760,3,158,79,0,760, - 761,5,133,0,0,761,766,3,114,57,0,762,763,5,119,0,0,763,765,3,114,57,0, - 764,762,1,0,0,0,765,768,1,0,0,0,766,764,1,0,0,0,766,767,1,0,0,0,767,770, - 1,0,0,0,768,766,1,0,0,0,769,771,5,119,0,0,770,769,1,0,0,0,770,771,1,0, - 0,0,771,772,1,0,0,0,772,773,5,152,0,0,773,782,1,0,0,0,774,775,3,158,79, - 0,775,777,5,133,0,0,776,778,3,116,58,0,777,776,1,0,0,0,777,778,1,0,0, - 0,778,779,1,0,0,0,779,780,5,152,0,0,780,782,1,0,0,0,781,725,1,0,0,0,781, - 726,1,0,0,0,781,744,1,0,0,0,781,759,1,0,0,0,781,774,1,0,0,0,782,115,1, - 0,0,0,783,788,3,118,59,0,784,785,5,119,0,0,785,787,3,118,59,0,786,784, - 1,0,0,0,787,790,1,0,0,0,788,786,1,0,0,0,788,789,1,0,0,0,789,792,1,0,0, - 0,790,788,1,0,0,0,791,793,5,119,0,0,792,791,1,0,0,0,792,793,1,0,0,0,793, - 117,1,0,0,0,794,795,6,59,-1,0,795,797,5,12,0,0,796,798,3,118,59,0,797, - 796,1,0,0,0,797,798,1,0,0,0,798,804,1,0,0,0,799,800,5,101,0,0,800,801, - 3,118,59,0,801,802,5,86,0,0,802,803,3,118,59,0,803,805,1,0,0,0,804,799, - 1,0,0,0,805,806,1,0,0,0,806,804,1,0,0,0,806,807,1,0,0,0,807,810,1,0,0, - 0,808,809,5,25,0,0,809,811,3,118,59,0,810,808,1,0,0,0,810,811,1,0,0,0, - 811,812,1,0,0,0,812,813,5,26,0,0,813,947,1,0,0,0,814,815,5,13,0,0,815, - 816,5,133,0,0,816,817,3,118,59,0,817,818,5,6,0,0,818,819,3,114,57,0,819, - 820,5,152,0,0,820,947,1,0,0,0,821,822,5,20,0,0,822,947,5,113,0,0,823, - 824,5,48,0,0,824,947,5,113,0,0,825,826,5,48,0,0,826,827,3,118,59,0,827, - 828,3,150,75,0,828,947,1,0,0,0,829,830,5,85,0,0,830,831,5,133,0,0,831, - 832,3,118,59,0,832,833,5,35,0,0,833,836,3,118,59,0,834,835,5,34,0,0,835, - 837,3,118,59,0,836,834,1,0,0,0,836,837,1,0,0,0,837,838,1,0,0,0,838,839, - 5,152,0,0,839,947,1,0,0,0,840,841,5,89,0,0,841,947,5,113,0,0,842,843, - 5,94,0,0,843,844,5,133,0,0,844,845,7,10,0,0,845,846,3,164,82,0,846,847, - 5,35,0,0,847,848,3,118,59,0,848,849,5,152,0,0,849,947,1,0,0,0,850,851, - 3,158,79,0,851,853,5,133,0,0,852,854,3,116,58,0,853,852,1,0,0,0,853,854, - 1,0,0,0,854,855,1,0,0,0,855,856,5,152,0,0,856,865,1,0,0,0,857,859,5,133, - 0,0,858,860,5,24,0,0,859,858,1,0,0,0,859,860,1,0,0,0,860,862,1,0,0,0, - 861,863,3,116,58,0,862,861,1,0,0,0,862,863,1,0,0,0,863,864,1,0,0,0,864, - 866,5,152,0,0,865,857,1,0,0,0,865,866,1,0,0,0,866,867,1,0,0,0,867,868, - 5,69,0,0,868,869,5,133,0,0,869,870,3,100,50,0,870,871,5,152,0,0,871,947, - 1,0,0,0,872,873,3,158,79,0,873,875,5,133,0,0,874,876,3,116,58,0,875,874, - 1,0,0,0,875,876,1,0,0,0,876,877,1,0,0,0,877,878,5,152,0,0,878,887,1,0, - 0,0,879,881,5,133,0,0,880,882,5,24,0,0,881,880,1,0,0,0,881,882,1,0,0, - 0,882,884,1,0,0,0,883,885,3,116,58,0,884,883,1,0,0,0,884,885,1,0,0,0, - 885,886,1,0,0,0,886,888,5,152,0,0,887,879,1,0,0,0,887,888,1,0,0,0,888, - 889,1,0,0,0,889,890,5,69,0,0,890,891,3,158,79,0,891,947,1,0,0,0,892,898, - 3,158,79,0,893,895,5,133,0,0,894,896,3,116,58,0,895,894,1,0,0,0,895,896, - 1,0,0,0,896,897,1,0,0,0,897,899,5,152,0,0,898,893,1,0,0,0,898,899,1,0, - 0,0,899,900,1,0,0,0,900,902,5,133,0,0,901,903,5,24,0,0,902,901,1,0,0, - 0,902,903,1,0,0,0,903,905,1,0,0,0,904,906,3,116,58,0,905,904,1,0,0,0, - 905,906,1,0,0,0,906,907,1,0,0,0,907,908,5,152,0,0,908,947,1,0,0,0,909, - 947,3,122,61,0,910,947,3,166,83,0,911,947,3,148,74,0,912,913,5,121,0, - 0,913,947,3,118,59,20,914,915,5,61,0,0,915,947,3,118,59,14,916,917,3, - 138,69,0,917,918,5,123,0,0,918,920,1,0,0,0,919,916,1,0,0,0,919,920,1, - 0,0,0,920,921,1,0,0,0,921,947,5,115,0,0,922,923,5,133,0,0,923,924,3,48, - 24,0,924,925,5,152,0,0,925,947,1,0,0,0,926,927,5,133,0,0,927,928,3,118, - 59,0,928,929,5,152,0,0,929,947,1,0,0,0,930,931,5,133,0,0,931,932,3,116, - 58,0,932,933,5,152,0,0,933,947,1,0,0,0,934,936,5,132,0,0,935,937,3,116, - 58,0,936,935,1,0,0,0,936,937,1,0,0,0,937,938,1,0,0,0,938,947,5,151,0, - 0,939,941,5,131,0,0,940,942,3,40,20,0,941,940,1,0,0,0,941,942,1,0,0,0, - 942,943,1,0,0,0,943,947,5,150,0,0,944,947,3,120,60,0,945,947,3,130,65, - 0,946,794,1,0,0,0,946,814,1,0,0,0,946,821,1,0,0,0,946,823,1,0,0,0,946, - 825,1,0,0,0,946,829,1,0,0,0,946,840,1,0,0,0,946,842,1,0,0,0,946,850,1, - 0,0,0,946,872,1,0,0,0,946,892,1,0,0,0,946,909,1,0,0,0,946,910,1,0,0,0, - 946,911,1,0,0,0,946,912,1,0,0,0,946,914,1,0,0,0,946,919,1,0,0,0,946,922, - 1,0,0,0,946,926,1,0,0,0,946,930,1,0,0,0,946,934,1,0,0,0,946,939,1,0,0, - 0,946,944,1,0,0,0,946,945,1,0,0,0,947,1058,1,0,0,0,948,952,10,19,0,0, - 949,953,5,115,0,0,950,953,5,154,0,0,951,953,5,141,0,0,952,949,1,0,0,0, - 952,950,1,0,0,0,952,951,1,0,0,0,953,954,1,0,0,0,954,1057,3,118,59,20, - 955,959,10,18,0,0,956,960,5,142,0,0,957,960,5,121,0,0,958,960,5,120,0, - 0,959,956,1,0,0,0,959,957,1,0,0,0,959,958,1,0,0,0,960,961,1,0,0,0,961, - 1057,3,118,59,19,962,987,10,17,0,0,963,988,5,124,0,0,964,988,5,125,0, - 0,965,988,5,136,0,0,966,988,5,134,0,0,967,988,5,135,0,0,968,988,5,126, - 0,0,969,988,5,127,0,0,970,972,5,61,0,0,971,970,1,0,0,0,971,972,1,0,0, - 0,972,973,1,0,0,0,973,975,5,44,0,0,974,976,5,15,0,0,975,974,1,0,0,0,975, - 976,1,0,0,0,976,988,1,0,0,0,977,979,5,61,0,0,978,977,1,0,0,0,978,979, - 1,0,0,0,979,980,1,0,0,0,980,988,7,11,0,0,981,988,5,148,0,0,982,988,5, - 149,0,0,983,988,5,138,0,0,984,988,5,129,0,0,985,988,5,130,0,0,986,988, - 5,137,0,0,987,963,1,0,0,0,987,964,1,0,0,0,987,965,1,0,0,0,987,966,1,0, - 0,0,987,967,1,0,0,0,987,968,1,0,0,0,987,969,1,0,0,0,987,971,1,0,0,0,987, - 978,1,0,0,0,987,981,1,0,0,0,987,982,1,0,0,0,987,983,1,0,0,0,987,984,1, - 0,0,0,987,985,1,0,0,0,987,986,1,0,0,0,988,989,1,0,0,0,989,1057,3,118, - 59,18,990,991,10,15,0,0,991,992,5,140,0,0,992,1057,3,118,59,16,993,994, - 10,13,0,0,994,995,5,2,0,0,995,1057,3,118,59,14,996,997,10,12,0,0,997, - 998,5,66,0,0,998,1057,3,118,59,13,999,1001,10,11,0,0,1000,1002,5,61,0, - 0,1001,1000,1,0,0,0,1001,1002,1,0,0,0,1002,1003,1,0,0,0,1003,1004,5,9, - 0,0,1004,1005,3,118,59,0,1005,1006,5,2,0,0,1006,1007,3,118,59,12,1007, - 1057,1,0,0,0,1008,1009,10,10,0,0,1009,1010,5,143,0,0,1010,1011,3,118, - 59,0,1011,1012,5,118,0,0,1012,1013,3,118,59,10,1013,1057,1,0,0,0,1014, - 1015,10,30,0,0,1015,1017,5,133,0,0,1016,1018,3,116,58,0,1017,1016,1,0, - 0,0,1017,1018,1,0,0,0,1018,1019,1,0,0,0,1019,1057,5,152,0,0,1020,1021, - 10,26,0,0,1021,1022,5,132,0,0,1022,1023,3,118,59,0,1023,1024,5,151,0, - 0,1024,1057,1,0,0,0,1025,1026,10,25,0,0,1026,1027,5,123,0,0,1027,1057, - 5,111,0,0,1028,1029,10,24,0,0,1029,1030,5,123,0,0,1030,1057,3,158,79, - 0,1031,1032,10,23,0,0,1032,1033,5,139,0,0,1033,1034,5,132,0,0,1034,1035, - 3,118,59,0,1035,1036,5,151,0,0,1036,1057,1,0,0,0,1037,1038,10,22,0,0, - 1038,1039,5,139,0,0,1039,1057,5,111,0,0,1040,1041,10,21,0,0,1041,1042, - 5,139,0,0,1042,1057,3,158,79,0,1043,1044,10,16,0,0,1044,1046,5,49,0,0, - 1045,1047,5,61,0,0,1046,1045,1,0,0,0,1046,1047,1,0,0,0,1047,1048,1,0, - 0,0,1048,1057,5,62,0,0,1049,1054,10,9,0,0,1050,1051,5,6,0,0,1051,1055, - 3,158,79,0,1052,1053,5,6,0,0,1053,1055,5,113,0,0,1054,1050,1,0,0,0,1054, - 1052,1,0,0,0,1055,1057,1,0,0,0,1056,948,1,0,0,0,1056,955,1,0,0,0,1056, - 962,1,0,0,0,1056,990,1,0,0,0,1056,993,1,0,0,0,1056,996,1,0,0,0,1056,999, - 1,0,0,0,1056,1008,1,0,0,0,1056,1014,1,0,0,0,1056,1020,1,0,0,0,1056,1025, - 1,0,0,0,1056,1028,1,0,0,0,1056,1031,1,0,0,0,1056,1037,1,0,0,0,1056,1040, - 1,0,0,0,1056,1043,1,0,0,0,1056,1049,1,0,0,0,1057,1060,1,0,0,0,1058,1056, - 1,0,0,0,1058,1059,1,0,0,0,1059,119,1,0,0,0,1060,1058,1,0,0,0,1061,1062, - 5,133,0,0,1062,1067,3,158,79,0,1063,1064,5,119,0,0,1064,1066,3,158,79, - 0,1065,1063,1,0,0,0,1066,1069,1,0,0,0,1067,1065,1,0,0,0,1067,1068,1,0, - 0,0,1068,1071,1,0,0,0,1069,1067,1,0,0,0,1070,1072,5,119,0,0,1071,1070, - 1,0,0,0,1071,1072,1,0,0,0,1072,1073,1,0,0,0,1073,1074,5,152,0,0,1074, - 1089,1,0,0,0,1075,1080,3,158,79,0,1076,1077,5,119,0,0,1077,1079,3,158, - 79,0,1078,1076,1,0,0,0,1079,1082,1,0,0,0,1080,1078,1,0,0,0,1080,1081, - 1,0,0,0,1081,1084,1,0,0,0,1082,1080,1,0,0,0,1083,1085,5,119,0,0,1084, - 1083,1,0,0,0,1084,1085,1,0,0,0,1085,1089,1,0,0,0,1086,1087,5,133,0,0, - 1087,1089,5,152,0,0,1088,1061,1,0,0,0,1088,1075,1,0,0,0,1088,1086,1,0, - 0,0,1089,1090,1,0,0,0,1090,1093,5,114,0,0,1091,1094,3,118,59,0,1092,1094, - 3,36,18,0,1093,1091,1,0,0,0,1093,1092,1,0,0,0,1094,121,1,0,0,0,1095,1096, - 5,135,0,0,1096,1100,3,158,79,0,1097,1099,3,124,62,0,1098,1097,1,0,0,0, - 1099,1102,1,0,0,0,1100,1098,1,0,0,0,1100,1101,1,0,0,0,1101,1103,1,0,0, - 0,1102,1100,1,0,0,0,1103,1104,5,154,0,0,1104,1105,5,127,0,0,1105,1128, - 1,0,0,0,1106,1107,5,135,0,0,1107,1111,3,158,79,0,1108,1110,3,124,62,0, - 1109,1108,1,0,0,0,1110,1113,1,0,0,0,1111,1109,1,0,0,0,1111,1112,1,0,0, - 0,1112,1114,1,0,0,0,1113,1111,1,0,0,0,1114,1120,5,127,0,0,1115,1121,3, - 122,61,0,1116,1117,5,131,0,0,1117,1118,3,118,59,0,1118,1119,5,150,0,0, - 1119,1121,1,0,0,0,1120,1115,1,0,0,0,1120,1116,1,0,0,0,1120,1121,1,0,0, - 0,1121,1122,1,0,0,0,1122,1123,5,135,0,0,1123,1124,5,154,0,0,1124,1125, - 3,158,79,0,1125,1126,5,127,0,0,1126,1128,1,0,0,0,1127,1095,1,0,0,0,1127, - 1106,1,0,0,0,1128,123,1,0,0,0,1129,1130,3,158,79,0,1130,1131,5,125,0, - 0,1131,1132,3,164,82,0,1132,1141,1,0,0,0,1133,1134,3,158,79,0,1134,1135, - 5,125,0,0,1135,1136,5,131,0,0,1136,1137,3,118,59,0,1137,1138,5,150,0, - 0,1138,1141,1,0,0,0,1139,1141,3,158,79,0,1140,1129,1,0,0,0,1140,1133, - 1,0,0,0,1140,1139,1,0,0,0,1141,125,1,0,0,0,1142,1147,3,128,64,0,1143, - 1144,5,119,0,0,1144,1146,3,128,64,0,1145,1143,1,0,0,0,1146,1149,1,0,0, - 0,1147,1145,1,0,0,0,1147,1148,1,0,0,0,1148,1151,1,0,0,0,1149,1147,1,0, - 0,0,1150,1152,5,119,0,0,1151,1150,1,0,0,0,1151,1152,1,0,0,0,1152,127, - 1,0,0,0,1153,1154,3,158,79,0,1154,1155,5,6,0,0,1155,1156,5,133,0,0,1156, - 1157,3,48,24,0,1157,1158,5,152,0,0,1158,1164,1,0,0,0,1159,1160,3,118, - 59,0,1160,1161,5,6,0,0,1161,1162,3,158,79,0,1162,1164,1,0,0,0,1163,1153, - 1,0,0,0,1163,1159,1,0,0,0,1164,129,1,0,0,0,1165,1173,3,162,81,0,1166, - 1167,3,138,69,0,1167,1168,5,123,0,0,1168,1170,1,0,0,0,1169,1166,1,0,0, - 0,1169,1170,1,0,0,0,1170,1171,1,0,0,0,1171,1173,3,132,66,0,1172,1165, - 1,0,0,0,1172,1169,1,0,0,0,1173,131,1,0,0,0,1174,1179,3,158,79,0,1175, - 1176,5,123,0,0,1176,1178,3,158,79,0,1177,1175,1,0,0,0,1178,1181,1,0,0, - 0,1179,1177,1,0,0,0,1179,1180,1,0,0,0,1180,133,1,0,0,0,1181,1179,1,0, - 0,0,1182,1183,6,67,-1,0,1183,1192,3,138,69,0,1184,1192,3,136,68,0,1185, - 1186,5,133,0,0,1186,1187,3,48,24,0,1187,1188,5,152,0,0,1188,1192,1,0, - 0,0,1189,1192,3,122,61,0,1190,1192,3,162,81,0,1191,1182,1,0,0,0,1191, - 1184,1,0,0,0,1191,1185,1,0,0,0,1191,1189,1,0,0,0,1191,1190,1,0,0,0,1192, - 1201,1,0,0,0,1193,1197,10,3,0,0,1194,1198,3,156,78,0,1195,1196,5,6,0, - 0,1196,1198,3,158,79,0,1197,1194,1,0,0,0,1197,1195,1,0,0,0,1198,1200, - 1,0,0,0,1199,1193,1,0,0,0,1200,1203,1,0,0,0,1201,1199,1,0,0,0,1201,1202, - 1,0,0,0,1202,135,1,0,0,0,1203,1201,1,0,0,0,1204,1205,3,158,79,0,1205, - 1207,5,133,0,0,1206,1208,3,140,70,0,1207,1206,1,0,0,0,1207,1208,1,0,0, - 0,1208,1209,1,0,0,0,1209,1210,5,152,0,0,1210,137,1,0,0,0,1211,1212,3, - 142,71,0,1212,1213,5,123,0,0,1213,1215,1,0,0,0,1214,1211,1,0,0,0,1214, - 1215,1,0,0,0,1215,1216,1,0,0,0,1216,1217,3,158,79,0,1217,139,1,0,0,0, - 1218,1223,3,118,59,0,1219,1220,5,119,0,0,1220,1222,3,118,59,0,1221,1219, - 1,0,0,0,1222,1225,1,0,0,0,1223,1221,1,0,0,0,1223,1224,1,0,0,0,1224,1227, - 1,0,0,0,1225,1223,1,0,0,0,1226,1228,5,119,0,0,1227,1226,1,0,0,0,1227, - 1228,1,0,0,0,1228,141,1,0,0,0,1229,1230,3,158,79,0,1230,143,1,0,0,0,1231, - 1240,5,109,0,0,1232,1233,5,123,0,0,1233,1240,7,12,0,0,1234,1235,5,111, - 0,0,1235,1237,5,123,0,0,1236,1238,7,12,0,0,1237,1236,1,0,0,0,1237,1238, - 1,0,0,0,1238,1240,1,0,0,0,1239,1231,1,0,0,0,1239,1232,1,0,0,0,1239,1234, - 1,0,0,0,1240,145,1,0,0,0,1241,1243,7,13,0,0,1242,1241,1,0,0,0,1242,1243, - 1,0,0,0,1243,1250,1,0,0,0,1244,1251,3,144,72,0,1245,1251,5,110,0,0,1246, - 1251,5,111,0,0,1247,1251,5,112,0,0,1248,1251,5,45,0,0,1249,1251,5,60, - 0,0,1250,1244,1,0,0,0,1250,1245,1,0,0,0,1250,1246,1,0,0,0,1250,1247,1, - 0,0,0,1250,1248,1,0,0,0,1250,1249,1,0,0,0,1251,147,1,0,0,0,1252,1256, - 3,146,73,0,1253,1256,5,113,0,0,1254,1256,5,62,0,0,1255,1252,1,0,0,0,1255, - 1253,1,0,0,0,1255,1254,1,0,0,0,1256,149,1,0,0,0,1257,1258,7,14,0,0,1258, - 151,1,0,0,0,1259,1260,7,15,0,0,1260,153,1,0,0,0,1261,1262,7,16,0,0,1262, - 155,1,0,0,0,1263,1266,5,108,0,0,1264,1266,3,154,77,0,1265,1263,1,0,0, - 0,1265,1264,1,0,0,0,1266,157,1,0,0,0,1267,1271,5,108,0,0,1268,1271,3, - 150,75,0,1269,1271,3,152,76,0,1270,1267,1,0,0,0,1270,1268,1,0,0,0,1270, - 1269,1,0,0,0,1271,159,1,0,0,0,1272,1273,3,164,82,0,1273,1274,5,125,0, - 0,1274,1275,3,146,73,0,1275,161,1,0,0,0,1276,1277,5,131,0,0,1277,1278, - 3,118,59,0,1278,1279,5,150,0,0,1279,163,1,0,0,0,1280,1283,5,113,0,0,1281, - 1283,3,166,83,0,1282,1280,1,0,0,0,1282,1281,1,0,0,0,1283,165,1,0,0,0, - 1284,1288,5,145,0,0,1285,1287,3,168,84,0,1286,1285,1,0,0,0,1287,1290, - 1,0,0,0,1288,1286,1,0,0,0,1288,1289,1,0,0,0,1289,1291,1,0,0,0,1290,1288, - 1,0,0,0,1291,1292,5,147,0,0,1292,167,1,0,0,0,1293,1294,5,160,0,0,1294, - 1295,3,118,59,0,1295,1296,5,150,0,0,1296,1299,1,0,0,0,1297,1299,5,159, - 0,0,1298,1293,1,0,0,0,1298,1297,1,0,0,0,1299,169,1,0,0,0,1300,1304,5, - 146,0,0,1301,1303,3,172,86,0,1302,1301,1,0,0,0,1303,1306,1,0,0,0,1304, - 1302,1,0,0,0,1304,1305,1,0,0,0,1305,1307,1,0,0,0,1306,1304,1,0,0,0,1307, - 1308,5,0,0,1,1308,171,1,0,0,0,1309,1310,5,162,0,0,1310,1311,3,118,59, - 0,1311,1312,5,150,0,0,1312,1315,1,0,0,0,1313,1315,5,161,0,0,1314,1309, - 1,0,0,0,1314,1313,1,0,0,0,1315,173,1,0,0,0,168,177,184,193,200,204,218, - 222,225,229,232,239,243,252,257,266,274,281,285,291,296,304,311,317,329, - 337,351,355,360,370,380,388,392,396,399,403,406,409,412,415,419,423,426, - 429,432,436,439,448,454,475,492,509,515,521,532,534,545,548,554,562,568, - 570,574,579,582,585,589,593,596,598,601,605,609,612,614,616,621,632,638, - 645,650,654,658,664,666,673,681,684,687,706,720,736,740,751,755,766,770, - 777,781,788,792,797,806,810,836,853,859,862,865,875,881,884,887,895,898, - 902,905,919,936,941,946,952,959,971,975,978,987,1001,1017,1046,1054,1056, - 1058,1067,1071,1080,1084,1088,1093,1100,1111,1120,1127,1140,1147,1151, - 1163,1169,1172,1179,1191,1197,1201,1207,1214,1223,1227,1237,1239,1242, - 1250,1255,1265,1270,1282,1288,1298,1304,1314 + 59,1,59,1,59,3,59,1049,8,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,1057,8, + 59,5,59,1059,8,59,10,59,12,59,1062,9,59,1,60,1,60,1,60,1,60,5,60,1068, + 8,60,10,60,12,60,1071,9,60,1,60,3,60,1074,8,60,1,60,1,60,1,60,1,60,1, + 60,5,60,1081,8,60,10,60,12,60,1084,9,60,1,60,3,60,1087,8,60,1,60,1,60, + 3,60,1091,8,60,1,60,1,60,1,60,3,60,1096,8,60,1,61,1,61,1,61,1,61,1,61, + 3,61,1103,8,61,1,62,1,62,1,62,5,62,1108,8,62,10,62,12,62,1111,9,62,1, + 62,1,62,1,62,1,62,1,62,1,62,5,62,1119,8,62,10,62,12,62,1122,9,62,1,62, + 1,62,5,62,1126,8,62,10,62,12,62,1129,9,62,1,62,1,62,1,62,1,62,1,62,3, + 62,1136,8,62,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,3, + 63,1149,8,63,1,64,1,64,1,64,5,64,1154,8,64,10,64,12,64,1157,9,64,1,64, + 3,64,1160,8,64,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,1,65,3,65, + 1172,8,65,1,66,1,66,1,66,1,66,3,66,1178,8,66,1,66,3,66,1181,8,66,1,67, + 1,67,1,67,5,67,1186,8,67,10,67,12,67,1189,9,67,1,68,1,68,1,68,1,68,1, + 68,1,68,1,68,1,68,1,68,3,68,1200,8,68,1,68,1,68,1,68,1,68,3,68,1206,8, + 68,5,68,1208,8,68,10,68,12,68,1211,9,68,1,69,1,69,1,69,3,69,1216,8,69, + 1,69,1,69,1,70,1,70,1,70,3,70,1223,8,70,1,70,1,70,1,71,1,71,1,71,5,71, + 1230,8,71,10,71,12,71,1233,9,71,1,71,3,71,1236,8,71,1,72,1,72,1,73,1, + 73,1,73,1,73,1,73,1,73,3,73,1246,8,73,3,73,1248,8,73,1,74,3,74,1251,8, + 74,1,74,1,74,1,74,1,74,1,74,1,74,3,74,1259,8,74,1,75,1,75,1,75,3,75,1264, + 8,75,1,76,1,76,1,77,1,77,1,78,1,78,1,79,1,79,3,79,1274,8,79,1,80,1,80, + 1,80,3,80,1279,8,80,1,81,1,81,1,81,1,81,1,82,1,82,1,82,1,82,1,83,1,83, + 3,83,1291,8,83,1,84,1,84,5,84,1295,8,84,10,84,12,84,1298,9,84,1,84,1, + 84,1,85,1,85,1,85,1,85,1,85,3,85,1307,8,85,1,86,1,86,5,86,1311,8,86,10, + 86,12,86,1314,9,86,1,86,1,86,1,87,1,87,1,87,1,87,1,87,3,87,1323,8,87, + 1,87,0,3,80,118,136,88,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32, + 34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78, + 80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118, + 120,122,124,126,128,130,132,134,136,138,140,142,144,146,148,150,152,154, + 156,158,160,162,164,166,168,170,172,174,0,17,2,0,32,32,37,37,2,0,18,18, + 77,77,2,0,46,46,54,54,3,0,1,1,4,4,8,8,4,0,1,1,3,4,8,8,83,83,2,0,54,54, + 76,76,2,0,1,1,4,4,2,0,7,7,22,23,2,0,31,31,52,52,2,0,74,74,79,79,3,0,10, + 10,53,53,93,93,2,0,43,43,56,56,1,0,110,111,2,0,121,121,142,142,7,0,21, + 21,40,40,58,59,73,73,81,81,100,100,106,106,19,0,1,13,15,20,22,26,28,29, + 31,31,33,36,38,39,41,44,46,46,48,54,56,57,61,61,63,72,74,80,82,86,88, + 95,97,99,101,102,104,105,4,0,20,20,31,31,41,41,51,51,1500,0,179,1,0,0, + 0,2,186,1,0,0,0,4,188,1,0,0,0,6,190,1,0,0,0,8,197,1,0,0,0,10,220,1,0, + 0,0,12,222,1,0,0,0,14,229,1,0,0,0,16,236,1,0,0,0,18,249,1,0,0,0,20,261, + 1,0,0,0,22,270,1,0,0,0,24,278,1,0,0,0,26,300,1,0,0,0,28,315,1,0,0,0,30, + 324,1,0,0,0,32,329,1,0,0,0,34,333,1,0,0,0,36,335,1,0,0,0,38,344,1,0,0, + 0,40,348,1,0,0,0,42,362,1,0,0,0,44,372,1,0,0,0,46,382,1,0,0,0,48,386, + 1,0,0,0,50,394,1,0,0,0,52,443,1,0,0,0,54,446,1,0,0,0,56,452,1,0,0,0,58, + 456,1,0,0,0,60,462,1,0,0,0,62,480,1,0,0,0,64,483,1,0,0,0,66,486,1,0,0, + 0,68,496,1,0,0,0,70,499,1,0,0,0,72,503,1,0,0,0,74,536,1,0,0,0,76,538, + 1,0,0,0,78,541,1,0,0,0,80,556,1,0,0,0,82,618,1,0,0,0,84,623,1,0,0,0,86, + 634,1,0,0,0,88,636,1,0,0,0,90,642,1,0,0,0,92,650,1,0,0,0,94,668,1,0,0, + 0,96,670,1,0,0,0,98,678,1,0,0,0,100,683,1,0,0,0,102,691,1,0,0,0,104,695, + 1,0,0,0,106,699,1,0,0,0,108,708,1,0,0,0,110,722,1,0,0,0,112,724,1,0,0, + 0,114,783,1,0,0,0,116,785,1,0,0,0,118,948,1,0,0,0,120,1090,1,0,0,0,122, + 1102,1,0,0,0,124,1135,1,0,0,0,126,1148,1,0,0,0,128,1150,1,0,0,0,130,1171, + 1,0,0,0,132,1180,1,0,0,0,134,1182,1,0,0,0,136,1199,1,0,0,0,138,1212,1, + 0,0,0,140,1222,1,0,0,0,142,1226,1,0,0,0,144,1237,1,0,0,0,146,1247,1,0, + 0,0,148,1250,1,0,0,0,150,1263,1,0,0,0,152,1265,1,0,0,0,154,1267,1,0,0, + 0,156,1269,1,0,0,0,158,1273,1,0,0,0,160,1278,1,0,0,0,162,1280,1,0,0,0, + 164,1284,1,0,0,0,166,1290,1,0,0,0,168,1292,1,0,0,0,170,1306,1,0,0,0,172, + 1308,1,0,0,0,174,1322,1,0,0,0,176,178,3,2,1,0,177,176,1,0,0,0,178,181, + 1,0,0,0,179,177,1,0,0,0,179,180,1,0,0,0,180,182,1,0,0,0,181,179,1,0,0, + 0,182,183,5,0,0,1,183,1,1,0,0,0,184,187,3,6,3,0,185,187,3,10,5,0,186, + 184,1,0,0,0,186,185,1,0,0,0,187,3,1,0,0,0,188,189,3,118,59,0,189,5,1, + 0,0,0,190,191,5,55,0,0,191,195,3,160,80,0,192,193,5,118,0,0,193,194,5, + 125,0,0,194,196,3,4,2,0,195,192,1,0,0,0,195,196,1,0,0,0,196,7,1,0,0,0, + 197,202,3,160,80,0,198,199,5,119,0,0,199,201,3,160,80,0,200,198,1,0,0, + 0,201,204,1,0,0,0,202,200,1,0,0,0,202,203,1,0,0,0,203,206,1,0,0,0,204, + 202,1,0,0,0,205,207,5,119,0,0,206,205,1,0,0,0,206,207,1,0,0,0,207,9,1, + 0,0,0,208,221,3,12,6,0,209,221,3,14,7,0,210,221,3,18,9,0,211,221,3,20, + 10,0,212,221,3,22,11,0,213,221,3,26,13,0,214,221,3,24,12,0,215,221,3, + 28,14,0,216,221,3,30,15,0,217,221,3,36,18,0,218,221,3,32,16,0,219,221, + 3,34,17,0,220,208,1,0,0,0,220,209,1,0,0,0,220,210,1,0,0,0,220,211,1,0, + 0,0,220,212,1,0,0,0,220,213,1,0,0,0,220,214,1,0,0,0,220,215,1,0,0,0,220, + 216,1,0,0,0,220,217,1,0,0,0,220,218,1,0,0,0,220,219,1,0,0,0,221,11,1, + 0,0,0,222,224,5,75,0,0,223,225,3,4,2,0,224,223,1,0,0,0,224,225,1,0,0, + 0,225,227,1,0,0,0,226,228,5,153,0,0,227,226,1,0,0,0,227,228,1,0,0,0,228, + 13,1,0,0,0,229,231,5,87,0,0,230,232,3,4,2,0,231,230,1,0,0,0,231,232,1, + 0,0,0,232,234,1,0,0,0,233,235,5,153,0,0,234,233,1,0,0,0,234,235,1,0,0, + 0,235,15,1,0,0,0,236,245,5,14,0,0,237,238,5,133,0,0,238,241,3,160,80, + 0,239,240,5,118,0,0,240,242,3,160,80,0,241,239,1,0,0,0,241,242,1,0,0, + 0,242,243,1,0,0,0,243,244,5,152,0,0,244,246,1,0,0,0,245,237,1,0,0,0,245, + 246,1,0,0,0,246,247,1,0,0,0,247,248,3,36,18,0,248,17,1,0,0,0,249,250, + 5,96,0,0,250,254,3,36,18,0,251,253,3,16,8,0,252,251,1,0,0,0,253,256,1, + 0,0,0,254,252,1,0,0,0,254,255,1,0,0,0,255,259,1,0,0,0,256,254,1,0,0,0, + 257,258,5,30,0,0,258,260,3,36,18,0,259,257,1,0,0,0,259,260,1,0,0,0,260, + 19,1,0,0,0,261,262,5,42,0,0,262,263,5,133,0,0,263,264,3,4,2,0,264,265, + 5,152,0,0,265,268,3,10,5,0,266,267,5,25,0,0,267,269,3,10,5,0,268,266, + 1,0,0,0,268,269,1,0,0,0,269,21,1,0,0,0,270,271,5,103,0,0,271,272,5,133, + 0,0,272,273,3,4,2,0,273,274,5,152,0,0,274,276,3,10,5,0,275,277,5,153, + 0,0,276,275,1,0,0,0,276,277,1,0,0,0,277,23,1,0,0,0,278,279,5,34,0,0,279, + 283,5,133,0,0,280,284,3,6,3,0,281,284,3,30,15,0,282,284,3,4,2,0,283,280, + 1,0,0,0,283,281,1,0,0,0,283,282,1,0,0,0,283,284,1,0,0,0,284,285,1,0,0, + 0,285,287,5,153,0,0,286,288,3,4,2,0,287,286,1,0,0,0,287,288,1,0,0,0,288, + 289,1,0,0,0,289,293,5,153,0,0,290,294,3,6,3,0,291,294,3,30,15,0,292,294, + 3,4,2,0,293,290,1,0,0,0,293,291,1,0,0,0,293,292,1,0,0,0,293,294,1,0,0, + 0,294,295,1,0,0,0,295,296,5,152,0,0,296,298,3,10,5,0,297,299,5,153,0, + 0,298,297,1,0,0,0,298,299,1,0,0,0,299,25,1,0,0,0,300,301,5,34,0,0,301, + 302,5,133,0,0,302,303,5,55,0,0,303,306,3,160,80,0,304,305,5,119,0,0,305, + 307,3,160,80,0,306,304,1,0,0,0,306,307,1,0,0,0,307,308,1,0,0,0,308,309, + 5,44,0,0,309,310,3,4,2,0,310,311,5,152,0,0,311,313,3,10,5,0,312,314,5, + 153,0,0,313,312,1,0,0,0,313,314,1,0,0,0,314,27,1,0,0,0,315,316,7,0,0, + 0,316,317,3,160,80,0,317,319,5,133,0,0,318,320,3,8,4,0,319,318,1,0,0, + 0,319,320,1,0,0,0,320,321,1,0,0,0,321,322,5,152,0,0,322,323,3,36,18,0, + 323,29,1,0,0,0,324,325,3,4,2,0,325,326,5,118,0,0,326,327,5,125,0,0,327, + 328,3,4,2,0,328,31,1,0,0,0,329,331,3,4,2,0,330,332,5,153,0,0,331,330, + 1,0,0,0,331,332,1,0,0,0,332,33,1,0,0,0,333,334,5,153,0,0,334,35,1,0,0, + 0,335,339,5,131,0,0,336,338,3,2,1,0,337,336,1,0,0,0,338,341,1,0,0,0,339, + 337,1,0,0,0,339,340,1,0,0,0,340,342,1,0,0,0,341,339,1,0,0,0,342,343,5, + 150,0,0,343,37,1,0,0,0,344,345,3,4,2,0,345,346,5,118,0,0,346,347,3,4, + 2,0,347,39,1,0,0,0,348,353,3,38,19,0,349,350,5,119,0,0,350,352,3,38,19, + 0,351,349,1,0,0,0,352,355,1,0,0,0,353,351,1,0,0,0,353,354,1,0,0,0,354, + 357,1,0,0,0,355,353,1,0,0,0,356,358,5,119,0,0,357,356,1,0,0,0,357,358, + 1,0,0,0,358,41,1,0,0,0,359,363,3,48,24,0,360,363,3,50,25,0,361,363,3, + 124,62,0,362,359,1,0,0,0,362,360,1,0,0,0,362,361,1,0,0,0,363,364,1,0, + 0,0,364,365,5,0,0,1,365,43,1,0,0,0,366,373,3,50,25,0,367,368,5,133,0, + 0,368,369,3,48,24,0,369,370,5,152,0,0,370,373,1,0,0,0,371,373,3,164,82, + 0,372,366,1,0,0,0,372,367,1,0,0,0,372,371,1,0,0,0,373,45,1,0,0,0,374, + 383,5,27,0,0,375,376,5,98,0,0,376,383,5,1,0,0,377,378,5,98,0,0,378,383, + 5,24,0,0,379,383,5,47,0,0,380,381,5,47,0,0,381,383,5,24,0,0,382,374,1, + 0,0,0,382,375,1,0,0,0,382,377,1,0,0,0,382,379,1,0,0,0,382,380,1,0,0,0, + 383,384,1,0,0,0,384,385,3,44,22,0,385,47,1,0,0,0,386,390,3,44,22,0,387, + 389,3,46,23,0,388,387,1,0,0,0,389,392,1,0,0,0,390,388,1,0,0,0,390,391, + 1,0,0,0,391,49,1,0,0,0,392,390,1,0,0,0,393,395,3,52,26,0,394,393,1,0, + 0,0,394,395,1,0,0,0,395,396,1,0,0,0,396,398,5,82,0,0,397,399,5,24,0,0, + 398,397,1,0,0,0,398,399,1,0,0,0,399,401,1,0,0,0,400,402,3,54,27,0,401, + 400,1,0,0,0,401,402,1,0,0,0,402,403,1,0,0,0,403,405,3,116,58,0,404,406, + 3,56,28,0,405,404,1,0,0,0,405,406,1,0,0,0,406,408,1,0,0,0,407,409,3,58, + 29,0,408,407,1,0,0,0,408,409,1,0,0,0,409,411,1,0,0,0,410,412,3,62,31, + 0,411,410,1,0,0,0,411,412,1,0,0,0,412,414,1,0,0,0,413,415,3,64,32,0,414, + 413,1,0,0,0,414,415,1,0,0,0,415,417,1,0,0,0,416,418,3,66,33,0,417,416, + 1,0,0,0,417,418,1,0,0,0,418,421,1,0,0,0,419,420,5,105,0,0,420,422,7,1, + 0,0,421,419,1,0,0,0,421,422,1,0,0,0,422,425,1,0,0,0,423,424,5,105,0,0, + 424,426,5,92,0,0,425,423,1,0,0,0,425,426,1,0,0,0,426,428,1,0,0,0,427, + 429,3,68,34,0,428,427,1,0,0,0,428,429,1,0,0,0,429,431,1,0,0,0,430,432, + 3,60,30,0,431,430,1,0,0,0,431,432,1,0,0,0,432,434,1,0,0,0,433,435,3,70, + 35,0,434,433,1,0,0,0,434,435,1,0,0,0,435,438,1,0,0,0,436,439,3,74,37, + 0,437,439,3,76,38,0,438,436,1,0,0,0,438,437,1,0,0,0,438,439,1,0,0,0,439, + 441,1,0,0,0,440,442,3,78,39,0,441,440,1,0,0,0,441,442,1,0,0,0,442,51, + 1,0,0,0,443,444,5,105,0,0,444,445,3,128,64,0,445,53,1,0,0,0,446,447,5, + 91,0,0,447,450,5,111,0,0,448,449,5,105,0,0,449,451,5,88,0,0,450,448,1, + 0,0,0,450,451,1,0,0,0,451,55,1,0,0,0,452,453,5,35,0,0,453,454,3,80,40, + 0,454,57,1,0,0,0,455,457,7,2,0,0,456,455,1,0,0,0,456,457,1,0,0,0,457, + 458,1,0,0,0,458,459,5,5,0,0,459,460,5,50,0,0,460,461,3,116,58,0,461,59, + 1,0,0,0,462,463,5,104,0,0,463,464,3,160,80,0,464,465,5,6,0,0,465,466, + 5,133,0,0,466,467,3,100,50,0,467,477,5,152,0,0,468,469,5,119,0,0,469, + 470,3,160,80,0,470,471,5,6,0,0,471,472,5,133,0,0,472,473,3,100,50,0,473, + 474,5,152,0,0,474,476,1,0,0,0,475,468,1,0,0,0,476,479,1,0,0,0,477,475, + 1,0,0,0,477,478,1,0,0,0,478,61,1,0,0,0,479,477,1,0,0,0,480,481,5,72,0, + 0,481,482,3,118,59,0,482,63,1,0,0,0,483,484,5,102,0,0,484,485,3,118,59, + 0,485,65,1,0,0,0,486,487,5,38,0,0,487,494,5,11,0,0,488,489,7,1,0,0,489, + 490,5,133,0,0,490,491,3,116,58,0,491,492,5,152,0,0,492,495,1,0,0,0,493, + 495,3,116,58,0,494,488,1,0,0,0,494,493,1,0,0,0,495,67,1,0,0,0,496,497, + 5,39,0,0,497,498,3,118,59,0,498,69,1,0,0,0,499,500,5,67,0,0,500,501,5, + 11,0,0,501,502,3,90,45,0,502,71,1,0,0,0,503,504,5,67,0,0,504,505,5,11, + 0,0,505,506,3,116,58,0,506,73,1,0,0,0,507,508,5,57,0,0,508,511,3,118, + 59,0,509,510,5,119,0,0,510,512,3,118,59,0,511,509,1,0,0,0,511,512,1,0, + 0,0,512,517,1,0,0,0,513,514,5,105,0,0,514,518,5,88,0,0,515,516,5,11,0, + 0,516,518,3,116,58,0,517,513,1,0,0,0,517,515,1,0,0,0,517,518,1,0,0,0, + 518,537,1,0,0,0,519,520,5,57,0,0,520,523,3,118,59,0,521,522,5,105,0,0, + 522,524,5,88,0,0,523,521,1,0,0,0,523,524,1,0,0,0,524,525,1,0,0,0,525, + 526,5,64,0,0,526,527,3,118,59,0,527,537,1,0,0,0,528,529,5,57,0,0,529, + 530,3,118,59,0,530,531,5,64,0,0,531,534,3,118,59,0,532,533,5,11,0,0,533, + 535,3,116,58,0,534,532,1,0,0,0,534,535,1,0,0,0,535,537,1,0,0,0,536,507, + 1,0,0,0,536,519,1,0,0,0,536,528,1,0,0,0,537,75,1,0,0,0,538,539,5,64,0, + 0,539,540,3,118,59,0,540,77,1,0,0,0,541,542,5,84,0,0,542,543,3,96,48, + 0,543,79,1,0,0,0,544,545,6,40,-1,0,545,547,3,136,68,0,546,548,5,29,0, + 0,547,546,1,0,0,0,547,548,1,0,0,0,548,550,1,0,0,0,549,551,3,88,44,0,550, + 549,1,0,0,0,550,551,1,0,0,0,551,557,1,0,0,0,552,553,5,133,0,0,553,554, + 3,80,40,0,554,555,5,152,0,0,555,557,1,0,0,0,556,544,1,0,0,0,556,552,1, + 0,0,0,557,572,1,0,0,0,558,559,10,3,0,0,559,560,3,84,42,0,560,561,3,80, + 40,4,561,571,1,0,0,0,562,564,10,4,0,0,563,565,3,82,41,0,564,563,1,0,0, + 0,564,565,1,0,0,0,565,566,1,0,0,0,566,567,5,50,0,0,567,568,3,80,40,0, + 568,569,3,86,43,0,569,571,1,0,0,0,570,558,1,0,0,0,570,562,1,0,0,0,571, + 574,1,0,0,0,572,570,1,0,0,0,572,573,1,0,0,0,573,81,1,0,0,0,574,572,1, + 0,0,0,575,577,7,3,0,0,576,575,1,0,0,0,576,577,1,0,0,0,577,578,1,0,0,0, + 578,585,5,46,0,0,579,581,5,46,0,0,580,582,7,3,0,0,581,580,1,0,0,0,581, + 582,1,0,0,0,582,585,1,0,0,0,583,585,7,3,0,0,584,576,1,0,0,0,584,579,1, + 0,0,0,584,583,1,0,0,0,585,619,1,0,0,0,586,588,7,4,0,0,587,586,1,0,0,0, + 587,588,1,0,0,0,588,589,1,0,0,0,589,591,7,5,0,0,590,592,5,68,0,0,591, + 590,1,0,0,0,591,592,1,0,0,0,592,601,1,0,0,0,593,595,7,5,0,0,594,596,5, + 68,0,0,595,594,1,0,0,0,595,596,1,0,0,0,596,598,1,0,0,0,597,599,7,4,0, + 0,598,597,1,0,0,0,598,599,1,0,0,0,599,601,1,0,0,0,600,587,1,0,0,0,600, + 593,1,0,0,0,601,619,1,0,0,0,602,604,7,6,0,0,603,602,1,0,0,0,603,604,1, + 0,0,0,604,605,1,0,0,0,605,607,5,36,0,0,606,608,5,68,0,0,607,606,1,0,0, + 0,607,608,1,0,0,0,608,617,1,0,0,0,609,611,5,36,0,0,610,612,5,68,0,0,611, + 610,1,0,0,0,611,612,1,0,0,0,612,614,1,0,0,0,613,615,7,6,0,0,614,613,1, + 0,0,0,614,615,1,0,0,0,615,617,1,0,0,0,616,603,1,0,0,0,616,609,1,0,0,0, + 617,619,1,0,0,0,618,584,1,0,0,0,618,600,1,0,0,0,618,616,1,0,0,0,619,83, + 1,0,0,0,620,621,5,17,0,0,621,624,5,50,0,0,622,624,5,119,0,0,623,620,1, + 0,0,0,623,622,1,0,0,0,624,85,1,0,0,0,625,626,5,65,0,0,626,635,3,116,58, + 0,627,628,5,99,0,0,628,629,5,133,0,0,629,630,3,116,58,0,630,631,5,152, + 0,0,631,635,1,0,0,0,632,633,5,99,0,0,633,635,3,116,58,0,634,625,1,0,0, + 0,634,627,1,0,0,0,634,632,1,0,0,0,635,87,1,0,0,0,636,637,5,80,0,0,637, + 640,3,94,47,0,638,639,5,64,0,0,639,641,3,94,47,0,640,638,1,0,0,0,640, + 641,1,0,0,0,641,89,1,0,0,0,642,647,3,92,46,0,643,644,5,119,0,0,644,646, + 3,92,46,0,645,643,1,0,0,0,646,649,1,0,0,0,647,645,1,0,0,0,647,648,1,0, + 0,0,648,91,1,0,0,0,649,647,1,0,0,0,650,652,3,118,59,0,651,653,7,7,0,0, + 652,651,1,0,0,0,652,653,1,0,0,0,653,656,1,0,0,0,654,655,5,63,0,0,655, + 657,7,8,0,0,656,654,1,0,0,0,656,657,1,0,0,0,657,660,1,0,0,0,658,659,5, + 16,0,0,659,661,5,113,0,0,660,658,1,0,0,0,660,661,1,0,0,0,661,93,1,0,0, + 0,662,669,3,164,82,0,663,666,3,148,74,0,664,665,5,154,0,0,665,667,3,148, + 74,0,666,664,1,0,0,0,666,667,1,0,0,0,667,669,1,0,0,0,668,662,1,0,0,0, + 668,663,1,0,0,0,669,95,1,0,0,0,670,675,3,98,49,0,671,672,5,119,0,0,672, + 674,3,98,49,0,673,671,1,0,0,0,674,677,1,0,0,0,675,673,1,0,0,0,675,676, + 1,0,0,0,676,97,1,0,0,0,677,675,1,0,0,0,678,679,3,160,80,0,679,680,5,125, + 0,0,680,681,3,150,75,0,681,99,1,0,0,0,682,684,3,102,51,0,683,682,1,0, + 0,0,683,684,1,0,0,0,684,686,1,0,0,0,685,687,3,104,52,0,686,685,1,0,0, + 0,686,687,1,0,0,0,687,689,1,0,0,0,688,690,3,106,53,0,689,688,1,0,0,0, + 689,690,1,0,0,0,690,101,1,0,0,0,691,692,5,70,0,0,692,693,5,11,0,0,693, + 694,3,116,58,0,694,103,1,0,0,0,695,696,5,67,0,0,696,697,5,11,0,0,697, + 698,3,90,45,0,698,105,1,0,0,0,699,700,7,9,0,0,700,701,3,108,54,0,701, + 107,1,0,0,0,702,709,3,110,55,0,703,704,5,9,0,0,704,705,3,110,55,0,705, + 706,5,2,0,0,706,707,3,110,55,0,707,709,1,0,0,0,708,702,1,0,0,0,708,703, + 1,0,0,0,709,109,1,0,0,0,710,711,5,19,0,0,711,723,5,78,0,0,712,713,5,97, + 0,0,713,723,5,71,0,0,714,715,5,97,0,0,715,723,5,33,0,0,716,717,3,148, + 74,0,717,718,5,71,0,0,718,723,1,0,0,0,719,720,3,148,74,0,720,721,5,33, + 0,0,721,723,1,0,0,0,722,710,1,0,0,0,722,712,1,0,0,0,722,714,1,0,0,0,722, + 716,1,0,0,0,722,719,1,0,0,0,723,111,1,0,0,0,724,725,3,118,59,0,725,726, + 5,0,0,1,726,113,1,0,0,0,727,784,3,160,80,0,728,729,3,160,80,0,729,730, + 5,133,0,0,730,731,3,160,80,0,731,738,3,114,57,0,732,733,5,119,0,0,733, + 734,3,160,80,0,734,735,3,114,57,0,735,737,1,0,0,0,736,732,1,0,0,0,737, + 740,1,0,0,0,738,736,1,0,0,0,738,739,1,0,0,0,739,742,1,0,0,0,740,738,1, + 0,0,0,741,743,5,119,0,0,742,741,1,0,0,0,742,743,1,0,0,0,743,744,1,0,0, + 0,744,745,5,152,0,0,745,784,1,0,0,0,746,747,3,160,80,0,747,748,5,133, + 0,0,748,753,3,162,81,0,749,750,5,119,0,0,750,752,3,162,81,0,751,749,1, + 0,0,0,752,755,1,0,0,0,753,751,1,0,0,0,753,754,1,0,0,0,754,757,1,0,0,0, + 755,753,1,0,0,0,756,758,5,119,0,0,757,756,1,0,0,0,757,758,1,0,0,0,758, + 759,1,0,0,0,759,760,5,152,0,0,760,784,1,0,0,0,761,762,3,160,80,0,762, + 763,5,133,0,0,763,768,3,114,57,0,764,765,5,119,0,0,765,767,3,114,57,0, + 766,764,1,0,0,0,767,770,1,0,0,0,768,766,1,0,0,0,768,769,1,0,0,0,769,772, + 1,0,0,0,770,768,1,0,0,0,771,773,5,119,0,0,772,771,1,0,0,0,772,773,1,0, + 0,0,773,774,1,0,0,0,774,775,5,152,0,0,775,784,1,0,0,0,776,777,3,160,80, + 0,777,779,5,133,0,0,778,780,3,116,58,0,779,778,1,0,0,0,779,780,1,0,0, + 0,780,781,1,0,0,0,781,782,5,152,0,0,782,784,1,0,0,0,783,727,1,0,0,0,783, + 728,1,0,0,0,783,746,1,0,0,0,783,761,1,0,0,0,783,776,1,0,0,0,784,115,1, + 0,0,0,785,790,3,118,59,0,786,787,5,119,0,0,787,789,3,118,59,0,788,786, + 1,0,0,0,789,792,1,0,0,0,790,788,1,0,0,0,790,791,1,0,0,0,791,794,1,0,0, + 0,792,790,1,0,0,0,793,795,5,119,0,0,794,793,1,0,0,0,794,795,1,0,0,0,795, + 117,1,0,0,0,796,797,6,59,-1,0,797,799,5,12,0,0,798,800,3,118,59,0,799, + 798,1,0,0,0,799,800,1,0,0,0,800,806,1,0,0,0,801,802,5,101,0,0,802,803, + 3,118,59,0,803,804,5,86,0,0,804,805,3,118,59,0,805,807,1,0,0,0,806,801, + 1,0,0,0,807,808,1,0,0,0,808,806,1,0,0,0,808,809,1,0,0,0,809,812,1,0,0, + 0,810,811,5,25,0,0,811,813,3,118,59,0,812,810,1,0,0,0,812,813,1,0,0,0, + 813,814,1,0,0,0,814,815,5,26,0,0,815,949,1,0,0,0,816,817,5,13,0,0,817, + 818,5,133,0,0,818,819,3,118,59,0,819,820,5,6,0,0,820,821,3,114,57,0,821, + 822,5,152,0,0,822,949,1,0,0,0,823,824,5,20,0,0,824,949,5,113,0,0,825, + 826,5,48,0,0,826,949,5,113,0,0,827,828,5,48,0,0,828,829,3,118,59,0,829, + 830,3,152,76,0,830,949,1,0,0,0,831,832,5,85,0,0,832,833,5,133,0,0,833, + 834,3,118,59,0,834,835,5,35,0,0,835,838,3,118,59,0,836,837,5,34,0,0,837, + 839,3,118,59,0,838,836,1,0,0,0,838,839,1,0,0,0,839,840,1,0,0,0,840,841, + 5,152,0,0,841,949,1,0,0,0,842,843,5,89,0,0,843,949,5,113,0,0,844,845, + 5,94,0,0,845,846,5,133,0,0,846,847,7,10,0,0,847,848,3,166,83,0,848,849, + 5,35,0,0,849,850,3,118,59,0,850,851,5,152,0,0,851,949,1,0,0,0,852,853, + 3,160,80,0,853,855,5,133,0,0,854,856,3,116,58,0,855,854,1,0,0,0,855,856, + 1,0,0,0,856,857,1,0,0,0,857,858,5,152,0,0,858,867,1,0,0,0,859,861,5,133, + 0,0,860,862,5,24,0,0,861,860,1,0,0,0,861,862,1,0,0,0,862,864,1,0,0,0, + 863,865,3,116,58,0,864,863,1,0,0,0,864,865,1,0,0,0,865,866,1,0,0,0,866, + 868,5,152,0,0,867,859,1,0,0,0,867,868,1,0,0,0,868,869,1,0,0,0,869,870, + 5,69,0,0,870,871,5,133,0,0,871,872,3,100,50,0,872,873,5,152,0,0,873,949, + 1,0,0,0,874,875,3,160,80,0,875,877,5,133,0,0,876,878,3,116,58,0,877,876, + 1,0,0,0,877,878,1,0,0,0,878,879,1,0,0,0,879,880,5,152,0,0,880,889,1,0, + 0,0,881,883,5,133,0,0,882,884,5,24,0,0,883,882,1,0,0,0,883,884,1,0,0, + 0,884,886,1,0,0,0,885,887,3,116,58,0,886,885,1,0,0,0,886,887,1,0,0,0, + 887,888,1,0,0,0,888,890,5,152,0,0,889,881,1,0,0,0,889,890,1,0,0,0,890, + 891,1,0,0,0,891,892,5,69,0,0,892,893,3,160,80,0,893,949,1,0,0,0,894,900, + 3,160,80,0,895,897,5,133,0,0,896,898,3,116,58,0,897,896,1,0,0,0,897,898, + 1,0,0,0,898,899,1,0,0,0,899,901,5,152,0,0,900,895,1,0,0,0,900,901,1,0, + 0,0,901,902,1,0,0,0,902,904,5,133,0,0,903,905,5,24,0,0,904,903,1,0,0, + 0,904,905,1,0,0,0,905,907,1,0,0,0,906,908,3,116,58,0,907,906,1,0,0,0, + 907,908,1,0,0,0,908,909,1,0,0,0,909,910,5,152,0,0,910,949,1,0,0,0,911, + 949,3,124,62,0,912,949,3,168,84,0,913,949,3,150,75,0,914,915,5,121,0, + 0,915,949,3,118,59,20,916,917,5,61,0,0,917,949,3,118,59,14,918,919,3, + 140,70,0,919,920,5,123,0,0,920,922,1,0,0,0,921,918,1,0,0,0,921,922,1, + 0,0,0,922,923,1,0,0,0,923,949,5,115,0,0,924,925,5,133,0,0,925,926,3,48, + 24,0,926,927,5,152,0,0,927,949,1,0,0,0,928,929,5,133,0,0,929,930,3,118, + 59,0,930,931,5,152,0,0,931,949,1,0,0,0,932,933,5,133,0,0,933,934,3,116, + 58,0,934,935,5,152,0,0,935,949,1,0,0,0,936,938,5,132,0,0,937,939,3,116, + 58,0,938,937,1,0,0,0,938,939,1,0,0,0,939,940,1,0,0,0,940,949,5,151,0, + 0,941,943,5,131,0,0,942,944,3,40,20,0,943,942,1,0,0,0,943,944,1,0,0,0, + 944,945,1,0,0,0,945,949,5,150,0,0,946,949,3,120,60,0,947,949,3,132,66, + 0,948,796,1,0,0,0,948,816,1,0,0,0,948,823,1,0,0,0,948,825,1,0,0,0,948, + 827,1,0,0,0,948,831,1,0,0,0,948,842,1,0,0,0,948,844,1,0,0,0,948,852,1, + 0,0,0,948,874,1,0,0,0,948,894,1,0,0,0,948,911,1,0,0,0,948,912,1,0,0,0, + 948,913,1,0,0,0,948,914,1,0,0,0,948,916,1,0,0,0,948,921,1,0,0,0,948,924, + 1,0,0,0,948,928,1,0,0,0,948,932,1,0,0,0,948,936,1,0,0,0,948,941,1,0,0, + 0,948,946,1,0,0,0,948,947,1,0,0,0,949,1060,1,0,0,0,950,954,10,19,0,0, + 951,955,5,115,0,0,952,955,5,154,0,0,953,955,5,141,0,0,954,951,1,0,0,0, + 954,952,1,0,0,0,954,953,1,0,0,0,955,956,1,0,0,0,956,1059,3,118,59,20, + 957,961,10,18,0,0,958,962,5,142,0,0,959,962,5,121,0,0,960,962,5,120,0, + 0,961,958,1,0,0,0,961,959,1,0,0,0,961,960,1,0,0,0,962,963,1,0,0,0,963, + 1059,3,118,59,19,964,989,10,17,0,0,965,990,5,124,0,0,966,990,5,125,0, + 0,967,990,5,136,0,0,968,990,5,134,0,0,969,990,5,135,0,0,970,990,5,126, + 0,0,971,990,5,127,0,0,972,974,5,61,0,0,973,972,1,0,0,0,973,974,1,0,0, + 0,974,975,1,0,0,0,975,977,5,44,0,0,976,978,5,15,0,0,977,976,1,0,0,0,977, + 978,1,0,0,0,978,990,1,0,0,0,979,981,5,61,0,0,980,979,1,0,0,0,980,981, + 1,0,0,0,981,982,1,0,0,0,982,990,7,11,0,0,983,990,5,148,0,0,984,990,5, + 149,0,0,985,990,5,138,0,0,986,990,5,129,0,0,987,990,5,130,0,0,988,990, + 5,137,0,0,989,965,1,0,0,0,989,966,1,0,0,0,989,967,1,0,0,0,989,968,1,0, + 0,0,989,969,1,0,0,0,989,970,1,0,0,0,989,971,1,0,0,0,989,973,1,0,0,0,989, + 980,1,0,0,0,989,983,1,0,0,0,989,984,1,0,0,0,989,985,1,0,0,0,989,986,1, + 0,0,0,989,987,1,0,0,0,989,988,1,0,0,0,990,991,1,0,0,0,991,1059,3,118, + 59,18,992,993,10,15,0,0,993,994,5,140,0,0,994,1059,3,118,59,16,995,996, + 10,13,0,0,996,997,5,2,0,0,997,1059,3,118,59,14,998,999,10,12,0,0,999, + 1000,5,66,0,0,1000,1059,3,118,59,13,1001,1003,10,11,0,0,1002,1004,5,61, + 0,0,1003,1002,1,0,0,0,1003,1004,1,0,0,0,1004,1005,1,0,0,0,1005,1006,5, + 9,0,0,1006,1007,3,118,59,0,1007,1008,5,2,0,0,1008,1009,3,118,59,12,1009, + 1059,1,0,0,0,1010,1011,10,10,0,0,1011,1012,5,143,0,0,1012,1013,3,118, + 59,0,1013,1014,5,118,0,0,1014,1015,3,118,59,10,1015,1059,1,0,0,0,1016, + 1017,10,30,0,0,1017,1019,5,133,0,0,1018,1020,3,116,58,0,1019,1018,1,0, + 0,0,1019,1020,1,0,0,0,1020,1021,1,0,0,0,1021,1059,5,152,0,0,1022,1023, + 10,26,0,0,1023,1024,5,132,0,0,1024,1025,3,118,59,0,1025,1026,5,151,0, + 0,1026,1059,1,0,0,0,1027,1028,10,25,0,0,1028,1029,5,123,0,0,1029,1059, + 5,111,0,0,1030,1031,10,24,0,0,1031,1032,5,123,0,0,1032,1059,3,160,80, + 0,1033,1034,10,23,0,0,1034,1035,5,139,0,0,1035,1036,5,132,0,0,1036,1037, + 3,118,59,0,1037,1038,5,151,0,0,1038,1059,1,0,0,0,1039,1040,10,22,0,0, + 1040,1041,5,139,0,0,1041,1059,5,111,0,0,1042,1043,10,21,0,0,1043,1044, + 5,139,0,0,1044,1059,3,160,80,0,1045,1046,10,16,0,0,1046,1048,5,49,0,0, + 1047,1049,5,61,0,0,1048,1047,1,0,0,0,1048,1049,1,0,0,0,1049,1050,1,0, + 0,0,1050,1059,5,62,0,0,1051,1056,10,9,0,0,1052,1053,5,6,0,0,1053,1057, + 3,160,80,0,1054,1055,5,6,0,0,1055,1057,5,113,0,0,1056,1052,1,0,0,0,1056, + 1054,1,0,0,0,1057,1059,1,0,0,0,1058,950,1,0,0,0,1058,957,1,0,0,0,1058, + 964,1,0,0,0,1058,992,1,0,0,0,1058,995,1,0,0,0,1058,998,1,0,0,0,1058,1001, + 1,0,0,0,1058,1010,1,0,0,0,1058,1016,1,0,0,0,1058,1022,1,0,0,0,1058,1027, + 1,0,0,0,1058,1030,1,0,0,0,1058,1033,1,0,0,0,1058,1039,1,0,0,0,1058,1042, + 1,0,0,0,1058,1045,1,0,0,0,1058,1051,1,0,0,0,1059,1062,1,0,0,0,1060,1058, + 1,0,0,0,1060,1061,1,0,0,0,1061,119,1,0,0,0,1062,1060,1,0,0,0,1063,1064, + 5,133,0,0,1064,1069,3,160,80,0,1065,1066,5,119,0,0,1066,1068,3,160,80, + 0,1067,1065,1,0,0,0,1068,1071,1,0,0,0,1069,1067,1,0,0,0,1069,1070,1,0, + 0,0,1070,1073,1,0,0,0,1071,1069,1,0,0,0,1072,1074,5,119,0,0,1073,1072, + 1,0,0,0,1073,1074,1,0,0,0,1074,1075,1,0,0,0,1075,1076,5,152,0,0,1076, + 1091,1,0,0,0,1077,1082,3,160,80,0,1078,1079,5,119,0,0,1079,1081,3,160, + 80,0,1080,1078,1,0,0,0,1081,1084,1,0,0,0,1082,1080,1,0,0,0,1082,1083, + 1,0,0,0,1083,1086,1,0,0,0,1084,1082,1,0,0,0,1085,1087,5,119,0,0,1086, + 1085,1,0,0,0,1086,1087,1,0,0,0,1087,1091,1,0,0,0,1088,1089,5,133,0,0, + 1089,1091,5,152,0,0,1090,1063,1,0,0,0,1090,1077,1,0,0,0,1090,1088,1,0, + 0,0,1091,1092,1,0,0,0,1092,1095,5,114,0,0,1093,1096,3,118,59,0,1094,1096, + 3,36,18,0,1095,1093,1,0,0,0,1095,1094,1,0,0,0,1096,121,1,0,0,0,1097,1103, + 3,124,62,0,1098,1099,5,131,0,0,1099,1100,3,118,59,0,1100,1101,5,150,0, + 0,1101,1103,1,0,0,0,1102,1097,1,0,0,0,1102,1098,1,0,0,0,1103,123,1,0, + 0,0,1104,1105,5,135,0,0,1105,1109,3,160,80,0,1106,1108,3,126,63,0,1107, + 1106,1,0,0,0,1108,1111,1,0,0,0,1109,1107,1,0,0,0,1109,1110,1,0,0,0,1110, + 1112,1,0,0,0,1111,1109,1,0,0,0,1112,1113,5,154,0,0,1113,1114,5,127,0, + 0,1114,1136,1,0,0,0,1115,1116,5,135,0,0,1116,1120,3,160,80,0,1117,1119, + 3,126,63,0,1118,1117,1,0,0,0,1119,1122,1,0,0,0,1120,1118,1,0,0,0,1120, + 1121,1,0,0,0,1121,1123,1,0,0,0,1122,1120,1,0,0,0,1123,1127,5,127,0,0, + 1124,1126,3,122,61,0,1125,1124,1,0,0,0,1126,1129,1,0,0,0,1127,1125,1, + 0,0,0,1127,1128,1,0,0,0,1128,1130,1,0,0,0,1129,1127,1,0,0,0,1130,1131, + 5,135,0,0,1131,1132,5,154,0,0,1132,1133,3,160,80,0,1133,1134,5,127,0, + 0,1134,1136,1,0,0,0,1135,1104,1,0,0,0,1135,1115,1,0,0,0,1136,125,1,0, + 0,0,1137,1138,3,160,80,0,1138,1139,5,125,0,0,1139,1140,3,166,83,0,1140, + 1149,1,0,0,0,1141,1142,3,160,80,0,1142,1143,5,125,0,0,1143,1144,5,131, + 0,0,1144,1145,3,118,59,0,1145,1146,5,150,0,0,1146,1149,1,0,0,0,1147,1149, + 3,160,80,0,1148,1137,1,0,0,0,1148,1141,1,0,0,0,1148,1147,1,0,0,0,1149, + 127,1,0,0,0,1150,1155,3,130,65,0,1151,1152,5,119,0,0,1152,1154,3,130, + 65,0,1153,1151,1,0,0,0,1154,1157,1,0,0,0,1155,1153,1,0,0,0,1155,1156, + 1,0,0,0,1156,1159,1,0,0,0,1157,1155,1,0,0,0,1158,1160,5,119,0,0,1159, + 1158,1,0,0,0,1159,1160,1,0,0,0,1160,129,1,0,0,0,1161,1162,3,160,80,0, + 1162,1163,5,6,0,0,1163,1164,5,133,0,0,1164,1165,3,48,24,0,1165,1166,5, + 152,0,0,1166,1172,1,0,0,0,1167,1168,3,118,59,0,1168,1169,5,6,0,0,1169, + 1170,3,160,80,0,1170,1172,1,0,0,0,1171,1161,1,0,0,0,1171,1167,1,0,0,0, + 1172,131,1,0,0,0,1173,1181,3,164,82,0,1174,1175,3,140,70,0,1175,1176, + 5,123,0,0,1176,1178,1,0,0,0,1177,1174,1,0,0,0,1177,1178,1,0,0,0,1178, + 1179,1,0,0,0,1179,1181,3,134,67,0,1180,1173,1,0,0,0,1180,1177,1,0,0,0, + 1181,133,1,0,0,0,1182,1187,3,160,80,0,1183,1184,5,123,0,0,1184,1186,3, + 160,80,0,1185,1183,1,0,0,0,1186,1189,1,0,0,0,1187,1185,1,0,0,0,1187,1188, + 1,0,0,0,1188,135,1,0,0,0,1189,1187,1,0,0,0,1190,1191,6,68,-1,0,1191,1200, + 3,140,70,0,1192,1200,3,138,69,0,1193,1194,5,133,0,0,1194,1195,3,48,24, + 0,1195,1196,5,152,0,0,1196,1200,1,0,0,0,1197,1200,3,124,62,0,1198,1200, + 3,164,82,0,1199,1190,1,0,0,0,1199,1192,1,0,0,0,1199,1193,1,0,0,0,1199, + 1197,1,0,0,0,1199,1198,1,0,0,0,1200,1209,1,0,0,0,1201,1205,10,3,0,0,1202, + 1206,3,158,79,0,1203,1204,5,6,0,0,1204,1206,3,160,80,0,1205,1202,1,0, + 0,0,1205,1203,1,0,0,0,1206,1208,1,0,0,0,1207,1201,1,0,0,0,1208,1211,1, + 0,0,0,1209,1207,1,0,0,0,1209,1210,1,0,0,0,1210,137,1,0,0,0,1211,1209, + 1,0,0,0,1212,1213,3,160,80,0,1213,1215,5,133,0,0,1214,1216,3,142,71,0, + 1215,1214,1,0,0,0,1215,1216,1,0,0,0,1216,1217,1,0,0,0,1217,1218,5,152, + 0,0,1218,139,1,0,0,0,1219,1220,3,144,72,0,1220,1221,5,123,0,0,1221,1223, + 1,0,0,0,1222,1219,1,0,0,0,1222,1223,1,0,0,0,1223,1224,1,0,0,0,1224,1225, + 3,160,80,0,1225,141,1,0,0,0,1226,1231,3,118,59,0,1227,1228,5,119,0,0, + 1228,1230,3,118,59,0,1229,1227,1,0,0,0,1230,1233,1,0,0,0,1231,1229,1, + 0,0,0,1231,1232,1,0,0,0,1232,1235,1,0,0,0,1233,1231,1,0,0,0,1234,1236, + 5,119,0,0,1235,1234,1,0,0,0,1235,1236,1,0,0,0,1236,143,1,0,0,0,1237,1238, + 3,160,80,0,1238,145,1,0,0,0,1239,1248,5,109,0,0,1240,1241,5,123,0,0,1241, + 1248,7,12,0,0,1242,1243,5,111,0,0,1243,1245,5,123,0,0,1244,1246,7,12, + 0,0,1245,1244,1,0,0,0,1245,1246,1,0,0,0,1246,1248,1,0,0,0,1247,1239,1, + 0,0,0,1247,1240,1,0,0,0,1247,1242,1,0,0,0,1248,147,1,0,0,0,1249,1251, + 7,13,0,0,1250,1249,1,0,0,0,1250,1251,1,0,0,0,1251,1258,1,0,0,0,1252,1259, + 3,146,73,0,1253,1259,5,110,0,0,1254,1259,5,111,0,0,1255,1259,5,112,0, + 0,1256,1259,5,45,0,0,1257,1259,5,60,0,0,1258,1252,1,0,0,0,1258,1253,1, + 0,0,0,1258,1254,1,0,0,0,1258,1255,1,0,0,0,1258,1256,1,0,0,0,1258,1257, + 1,0,0,0,1259,149,1,0,0,0,1260,1264,3,148,74,0,1261,1264,5,113,0,0,1262, + 1264,5,62,0,0,1263,1260,1,0,0,0,1263,1261,1,0,0,0,1263,1262,1,0,0,0,1264, + 151,1,0,0,0,1265,1266,7,14,0,0,1266,153,1,0,0,0,1267,1268,7,15,0,0,1268, + 155,1,0,0,0,1269,1270,7,16,0,0,1270,157,1,0,0,0,1271,1274,5,108,0,0,1272, + 1274,3,156,78,0,1273,1271,1,0,0,0,1273,1272,1,0,0,0,1274,159,1,0,0,0, + 1275,1279,5,108,0,0,1276,1279,3,152,76,0,1277,1279,3,154,77,0,1278,1275, + 1,0,0,0,1278,1276,1,0,0,0,1278,1277,1,0,0,0,1279,161,1,0,0,0,1280,1281, + 3,166,83,0,1281,1282,5,125,0,0,1282,1283,3,148,74,0,1283,163,1,0,0,0, + 1284,1285,5,131,0,0,1285,1286,3,118,59,0,1286,1287,5,150,0,0,1287,165, + 1,0,0,0,1288,1291,5,113,0,0,1289,1291,3,168,84,0,1290,1288,1,0,0,0,1290, + 1289,1,0,0,0,1291,167,1,0,0,0,1292,1296,5,145,0,0,1293,1295,3,170,85, + 0,1294,1293,1,0,0,0,1295,1298,1,0,0,0,1296,1294,1,0,0,0,1296,1297,1,0, + 0,0,1297,1299,1,0,0,0,1298,1296,1,0,0,0,1299,1300,5,147,0,0,1300,169, + 1,0,0,0,1301,1302,5,160,0,0,1302,1303,3,118,59,0,1303,1304,5,150,0,0, + 1304,1307,1,0,0,0,1305,1307,5,159,0,0,1306,1301,1,0,0,0,1306,1305,1,0, + 0,0,1307,171,1,0,0,0,1308,1312,5,146,0,0,1309,1311,3,174,87,0,1310,1309, + 1,0,0,0,1311,1314,1,0,0,0,1312,1310,1,0,0,0,1312,1313,1,0,0,0,1313,1315, + 1,0,0,0,1314,1312,1,0,0,0,1315,1316,5,0,0,1,1316,173,1,0,0,0,1317,1318, + 5,162,0,0,1318,1319,3,118,59,0,1319,1320,5,150,0,0,1320,1323,1,0,0,0, + 1321,1323,5,161,0,0,1322,1317,1,0,0,0,1322,1321,1,0,0,0,1323,175,1,0, + 0,0,169,179,186,195,202,206,220,224,227,231,234,241,245,254,259,268,276, + 283,287,293,298,306,313,319,331,339,353,357,362,372,382,390,394,398,401, + 405,408,411,414,417,421,425,428,431,434,438,441,450,456,477,494,511,517, + 523,534,536,547,550,556,564,570,572,576,581,584,587,591,595,598,600,603, + 607,611,614,616,618,623,634,640,647,652,656,660,666,668,675,683,686,689, + 708,722,738,742,753,757,768,772,779,783,790,794,799,808,812,838,855,861, + 864,867,877,883,886,889,897,900,904,907,921,938,943,948,954,961,973,977, + 980,989,1003,1019,1048,1056,1058,1060,1069,1073,1082,1086,1090,1095,1102, + 1109,1120,1127,1135,1148,1155,1159,1171,1177,1180,1187,1199,1205,1209, + 1215,1222,1231,1235,1245,1247,1250,1258,1263,1273,1278,1290,1296,1306, + 1312,1322 }; staticData->serializedATN = antlr4::atn::SerializedATNView(serializedATNSegment, sizeof(serializedATNSegment) / sizeof(serializedATNSegment[0])); @@ -701,20 +704,20 @@ HogQLParser::ProgramContext* HogQLParser::program() { }); try { enterOuterAlt(_localctx, 1); - setState(177); + setState(179); _errHandler->sync(this); _la = _input->LA(1); while ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -140738696331266) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944844006785023) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 4212759) != 0)) { - setState(174); + setState(176); declaration(); - setState(179); + setState(181); _errHandler->sync(this); _la = _input->LA(1); } - setState(180); + setState(182); match(HogQLParser::EOF); } @@ -766,12 +769,12 @@ HogQLParser::DeclarationContext* HogQLParser::declaration() { exitRule(); }); try { - setState(184); + setState(186); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::LET: { enterOuterAlt(_localctx, 1); - setState(182); + setState(184); varDecl(); break; } @@ -894,7 +897,7 @@ HogQLParser::DeclarationContext* HogQLParser::declaration() { case HogQLParser::QUOTE_SINGLE_TEMPLATE: case HogQLParser::SEMICOLON: { enterOuterAlt(_localctx, 2); - setState(183); + setState(185); statement(); break; } @@ -949,7 +952,7 @@ HogQLParser::ExpressionContext* HogQLParser::expression() { }); try { enterOuterAlt(_localctx, 1); - setState(186); + setState(188); columnExpr(0); } @@ -1015,20 +1018,20 @@ HogQLParser::VarDeclContext* HogQLParser::varDecl() { }); try { enterOuterAlt(_localctx, 1); - setState(188); + setState(190); match(HogQLParser::LET); - setState(189); + setState(191); identifier(); - setState(193); + setState(195); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COLON) { - setState(190); + setState(192); match(HogQLParser::COLON); - setState(191); + setState(193); match(HogQLParser::EQ_SINGLE); - setState(192); + setState(194); expression(); } @@ -1092,28 +1095,28 @@ HogQLParser::IdentifierListContext* HogQLParser::identifierList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(195); + setState(197); identifier(); - setState(200); + setState(202); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 3, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(196); + setState(198); match(HogQLParser::COMMA); - setState(197); + setState(199); identifier(); } - setState(202); + setState(204); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 3, _ctx); } - setState(204); + setState(206); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(203); + setState(205); match(HogQLParser::COMMA); } @@ -1206,89 +1209,89 @@ HogQLParser::StatementContext* HogQLParser::statement() { exitRule(); }); try { - setState(218); + setState(220); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 5, _ctx)) { case 1: { enterOuterAlt(_localctx, 1); - setState(206); + setState(208); returnStmt(); break; } case 2: { enterOuterAlt(_localctx, 2); - setState(207); + setState(209); throwStmt(); break; } case 3: { enterOuterAlt(_localctx, 3); - setState(208); + setState(210); tryCatchStmt(); break; } case 4: { enterOuterAlt(_localctx, 4); - setState(209); + setState(211); ifStmt(); break; } case 5: { enterOuterAlt(_localctx, 5); - setState(210); + setState(212); whileStmt(); break; } case 6: { enterOuterAlt(_localctx, 6); - setState(211); + setState(213); forInStmt(); break; } case 7: { enterOuterAlt(_localctx, 7); - setState(212); + setState(214); forStmt(); break; } case 8: { enterOuterAlt(_localctx, 8); - setState(213); + setState(215); funcStmt(); break; } case 9: { enterOuterAlt(_localctx, 9); - setState(214); + setState(216); varAssignment(); break; } case 10: { enterOuterAlt(_localctx, 10); - setState(215); + setState(217); block(); break; } case 11: { enterOuterAlt(_localctx, 11); - setState(216); + setState(218); exprStmt(); break; } case 12: { enterOuterAlt(_localctx, 12); - setState(217); + setState(219); emptyStmt(); break; } @@ -1351,14 +1354,14 @@ HogQLParser::ReturnStmtContext* HogQLParser::returnStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(220); - match(HogQLParser::RETURN); setState(222); + match(HogQLParser::RETURN); + setState(224); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 6, _ctx)) { case 1: { - setState(221); + setState(223); expression(); break; } @@ -1366,12 +1369,12 @@ HogQLParser::ReturnStmtContext* HogQLParser::returnStmt() { default: break; } - setState(225); + setState(227); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 7, _ctx)) { case 1: { - setState(224); + setState(226); match(HogQLParser::SEMICOLON); break; } @@ -1434,14 +1437,14 @@ HogQLParser::ThrowStmtContext* HogQLParser::throwStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(227); - match(HogQLParser::THROW); setState(229); + match(HogQLParser::THROW); + setState(231); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 8, _ctx)) { case 1: { - setState(228); + setState(230); expression(); break; } @@ -1449,12 +1452,12 @@ HogQLParser::ThrowStmtContext* HogQLParser::throwStmt() { default: break; } - setState(232); + setState(234); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 9, _ctx)) { case 1: { - setState(231); + setState(233); match(HogQLParser::SEMICOLON); break; } @@ -1534,31 +1537,31 @@ HogQLParser::CatchBlockContext* HogQLParser::catchBlock() { }); try { enterOuterAlt(_localctx, 1); - setState(234); + setState(236); match(HogQLParser::CATCH); - setState(243); + setState(245); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::LPAREN) { - setState(235); + setState(237); match(HogQLParser::LPAREN); - setState(236); + setState(238); antlrcpp::downCast(_localctx)->catchVar = identifier(); - setState(239); + setState(241); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COLON) { - setState(237); + setState(239); match(HogQLParser::COLON); - setState(238); + setState(240); antlrcpp::downCast(_localctx)->catchType = identifier(); } - setState(241); + setState(243); match(HogQLParser::RPAREN); } - setState(245); + setState(247); antlrcpp::downCast(_localctx)->catchStmt = block(); } @@ -1628,28 +1631,28 @@ HogQLParser::TryCatchStmtContext* HogQLParser::tryCatchStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(247); + setState(249); match(HogQLParser::TRY); - setState(248); + setState(250); antlrcpp::downCast(_localctx)->tryStmt = block(); - setState(252); + setState(254); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::CATCH) { - setState(249); + setState(251); catchBlock(); - setState(254); + setState(256); _errHandler->sync(this); _la = _input->LA(1); } - setState(257); + setState(259); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::FINALLY) { - setState(255); + setState(257); match(HogQLParser::FINALLY); - setState(256); + setState(258); antlrcpp::downCast(_localctx)->finallyStmt = block(); } @@ -1723,24 +1726,24 @@ HogQLParser::IfStmtContext* HogQLParser::ifStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(259); + setState(261); match(HogQLParser::IF); - setState(260); + setState(262); match(HogQLParser::LPAREN); - setState(261); + setState(263); expression(); - setState(262); + setState(264); match(HogQLParser::RPAREN); - setState(263); + setState(265); statement(); - setState(266); + setState(268); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 14, _ctx)) { case 1: { - setState(264); + setState(266); match(HogQLParser::ELSE); - setState(265); + setState(267); statement(); break; } @@ -1815,22 +1818,22 @@ HogQLParser::WhileStmtContext* HogQLParser::whileStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(268); + setState(270); match(HogQLParser::WHILE); - setState(269); + setState(271); match(HogQLParser::LPAREN); - setState(270); + setState(272); expression(); - setState(271); + setState(273); match(HogQLParser::RPAREN); - setState(272); - statement(); setState(274); + statement(); + setState(276); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 15, _ctx)) { case 1: { - setState(273); + setState(275); match(HogQLParser::SEMICOLON); break; } @@ -1930,28 +1933,28 @@ HogQLParser::ForStmtContext* HogQLParser::forStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(276); + setState(278); match(HogQLParser::FOR); - setState(277); + setState(279); match(HogQLParser::LPAREN); - setState(281); + setState(283); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 16, _ctx)) { case 1: { - setState(278); + setState(280); antlrcpp::downCast(_localctx)->initializerVarDeclr = varDecl(); break; } case 2: { - setState(279); + setState(281); antlrcpp::downCast(_localctx)->initializerVarAssignment = varAssignment(); break; } case 3: { - setState(280); + setState(282); antlrcpp::downCast(_localctx)->initializerExpression = expression(); break; } @@ -1959,9 +1962,9 @@ HogQLParser::ForStmtContext* HogQLParser::forStmt() { default: break; } - setState(283); - match(HogQLParser::SEMICOLON); setState(285); + match(HogQLParser::SEMICOLON); + setState(287); _errHandler->sync(this); _la = _input->LA(1); @@ -1969,29 +1972,29 @@ HogQLParser::ForStmtContext* HogQLParser::forStmt() { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(284); + setState(286); antlrcpp::downCast(_localctx)->condition = expression(); } - setState(287); + setState(289); match(HogQLParser::SEMICOLON); - setState(291); + setState(293); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 18, _ctx)) { case 1: { - setState(288); + setState(290); antlrcpp::downCast(_localctx)->incrementVarDeclr = varDecl(); break; } case 2: { - setState(289); + setState(291); antlrcpp::downCast(_localctx)->incrementVarAssignment = varAssignment(); break; } case 3: { - setState(290); + setState(292); antlrcpp::downCast(_localctx)->incrementExpression = expression(); break; } @@ -1999,16 +2002,16 @@ HogQLParser::ForStmtContext* HogQLParser::forStmt() { default: break; } - setState(293); + setState(295); match(HogQLParser::RPAREN); - setState(294); - statement(); setState(296); + statement(); + setState(298); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 19, _ctx)) { case 1: { - setState(295); + setState(297); match(HogQLParser::SEMICOLON); break; } @@ -2104,38 +2107,38 @@ HogQLParser::ForInStmtContext* HogQLParser::forInStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(298); + setState(300); match(HogQLParser::FOR); - setState(299); + setState(301); match(HogQLParser::LPAREN); - setState(300); + setState(302); match(HogQLParser::LET); - setState(301); + setState(303); identifier(); - setState(304); + setState(306); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(302); + setState(304); match(HogQLParser::COMMA); - setState(303); + setState(305); identifier(); } - setState(306); + setState(308); match(HogQLParser::IN); - setState(307); + setState(309); expression(); - setState(308); + setState(310); match(HogQLParser::RPAREN); - setState(309); - statement(); setState(311); + statement(); + setState(313); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 21, _ctx)) { case 1: { - setState(310); + setState(312); match(HogQLParser::SEMICOLON); break; } @@ -2215,7 +2218,7 @@ HogQLParser::FuncStmtContext* HogQLParser::funcStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(313); + setState(315); _la = _input->LA(1); if (!(_la == HogQLParser::FN @@ -2226,23 +2229,23 @@ HogQLParser::FuncStmtContext* HogQLParser::funcStmt() { _errHandler->reportMatch(this); consume(); } - setState(314); + setState(316); identifier(); - setState(315); - match(HogQLParser::LPAREN); setState(317); + match(HogQLParser::LPAREN); + setState(319); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -5800812384855539714) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 25834219896831) != 0)) { - setState(316); + setState(318); identifierList(); } - setState(319); + setState(321); match(HogQLParser::RPAREN); - setState(320); + setState(322); block(); } @@ -2303,13 +2306,13 @@ HogQLParser::VarAssignmentContext* HogQLParser::varAssignment() { }); try { enterOuterAlt(_localctx, 1); - setState(322); + setState(324); expression(); - setState(323); + setState(325); match(HogQLParser::COLON); - setState(324); + setState(326); match(HogQLParser::EQ_SINGLE); - setState(325); + setState(327); expression(); } @@ -2362,14 +2365,14 @@ HogQLParser::ExprStmtContext* HogQLParser::exprStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(327); - expression(); setState(329); + expression(); + setState(331); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 23, _ctx)) { case 1: { - setState(328); + setState(330); match(HogQLParser::SEMICOLON); break; } @@ -2424,7 +2427,7 @@ HogQLParser::EmptyStmtContext* HogQLParser::emptyStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(331); + setState(333); match(HogQLParser::SEMICOLON); } @@ -2486,22 +2489,22 @@ HogQLParser::BlockContext* HogQLParser::block() { }); try { enterOuterAlt(_localctx, 1); - setState(333); + setState(335); match(HogQLParser::LBRACE); - setState(337); + setState(339); _errHandler->sync(this); _la = _input->LA(1); while ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -140738696331266) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944844006785023) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 4212759) != 0)) { - setState(334); + setState(336); declaration(); - setState(339); + setState(341); _errHandler->sync(this); _la = _input->LA(1); } - setState(340); + setState(342); match(HogQLParser::RBRACE); } @@ -2558,11 +2561,11 @@ HogQLParser::KvPairContext* HogQLParser::kvPair() { }); try { enterOuterAlt(_localctx, 1); - setState(342); + setState(344); expression(); - setState(343); + setState(345); match(HogQLParser::COLON); - setState(344); + setState(346); expression(); } @@ -2625,28 +2628,28 @@ HogQLParser::KvPairListContext* HogQLParser::kvPairList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(346); + setState(348); kvPair(); - setState(351); + setState(353); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 25, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(347); + setState(349); match(HogQLParser::COMMA); - setState(348); + setState(350); kvPair(); } - setState(353); + setState(355); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 25, _ctx); } - setState(355); + setState(357); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(354); + setState(356); match(HogQLParser::COMMA); } @@ -2708,23 +2711,23 @@ HogQLParser::SelectContext* HogQLParser::select() { }); try { enterOuterAlt(_localctx, 1); - setState(360); + setState(362); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 27, _ctx)) { case 1: { - setState(357); + setState(359); selectSetStmt(); break; } case 2: { - setState(358); + setState(360); selectStmt(); break; } case 3: { - setState(359); + setState(361); hogqlxTagElement(); break; } @@ -2732,7 +2735,7 @@ HogQLParser::SelectContext* HogQLParser::select() { default: break; } - setState(362); + setState(364); match(HogQLParser::EOF); } @@ -2796,31 +2799,31 @@ HogQLParser::SelectStmtWithParensContext* HogQLParser::selectStmtWithParens() { exitRule(); }); try { - setState(370); + setState(372); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::SELECT: case HogQLParser::WITH: { enterOuterAlt(_localctx, 1); - setState(364); + setState(366); selectStmt(); break; } case HogQLParser::LPAREN: { enterOuterAlt(_localctx, 2); - setState(365); + setState(367); match(HogQLParser::LPAREN); - setState(366); + setState(368); selectSetStmt(); - setState(367); + setState(369); match(HogQLParser::RPAREN); break; } case HogQLParser::LBRACE: { enterOuterAlt(_localctx, 3); - setState(369); + setState(371); placeholder(); break; } @@ -2895,41 +2898,41 @@ HogQLParser::SubsequentSelectSetClauseContext* HogQLParser::subsequentSelectSetC }); try { enterOuterAlt(_localctx, 1); - setState(380); + setState(382); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 29, _ctx)) { case 1: { - setState(372); + setState(374); match(HogQLParser::EXCEPT); break; } case 2: { - setState(373); + setState(375); match(HogQLParser::UNION); - setState(374); + setState(376); match(HogQLParser::ALL); break; } case 3: { - setState(375); + setState(377); match(HogQLParser::UNION); - setState(376); + setState(378); match(HogQLParser::DISTINCT); break; } case 4: { - setState(377); + setState(379); match(HogQLParser::INTERSECT); break; } case 5: { - setState(378); + setState(380); match(HogQLParser::INTERSECT); - setState(379); + setState(381); match(HogQLParser::DISTINCT); break; } @@ -2937,7 +2940,7 @@ HogQLParser::SubsequentSelectSetClauseContext* HogQLParser::subsequentSelectSetC default: break; } - setState(382); + setState(384); selectStmtWithParens(); } @@ -2995,17 +2998,17 @@ HogQLParser::SelectSetStmtContext* HogQLParser::selectSetStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(384); + setState(386); selectStmtWithParens(); - setState(388); + setState(390); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::EXCEPT || _la == HogQLParser::INTERSECT || _la == HogQLParser::UNION) { - setState(385); + setState(387); subsequentSelectSetClause(); - setState(390); + setState(392); _errHandler->sync(this); _la = _input->LA(1); } @@ -3137,22 +3140,22 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { }); try { enterOuterAlt(_localctx, 1); - setState(392); + setState(394); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WITH) { - setState(391); + setState(393); antlrcpp::downCast(_localctx)->with = withClause(); } - setState(394); - match(HogQLParser::SELECT); setState(396); + match(HogQLParser::SELECT); + setState(398); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 32, _ctx)) { case 1: { - setState(395); + setState(397); match(HogQLParser::DISTINCT); break; } @@ -3160,12 +3163,12 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { default: break; } - setState(399); + setState(401); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 33, _ctx)) { case 1: { - setState(398); + setState(400); topClause(); break; } @@ -3173,57 +3176,57 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { default: break; } - setState(401); - antlrcpp::downCast(_localctx)->columns = columnExprList(); setState(403); + antlrcpp::downCast(_localctx)->columns = columnExprList(); + setState(405); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::FROM) { - setState(402); + setState(404); antlrcpp::downCast(_localctx)->from = fromClause(); } - setState(406); + setState(408); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 18084767253659680) != 0)) { - setState(405); + setState(407); arrayJoinClause(); } - setState(409); + setState(411); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::PREWHERE) { - setState(408); + setState(410); prewhereClause(); } - setState(412); + setState(414); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WHERE) { - setState(411); + setState(413); antlrcpp::downCast(_localctx)->where = whereClause(); } - setState(415); + setState(417); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::GROUP) { - setState(414); + setState(416); groupByClause(); } - setState(419); + setState(421); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 39, _ctx)) { case 1: { - setState(417); + setState(419); match(HogQLParser::WITH); - setState(418); + setState(420); _la = _input->LA(1); if (!(_la == HogQLParser::CUBE @@ -3240,51 +3243,51 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { default: break; } - setState(423); + setState(425); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WITH) { - setState(421); + setState(423); match(HogQLParser::WITH); - setState(422); + setState(424); match(HogQLParser::TOTALS); } - setState(426); + setState(428); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::HAVING) { - setState(425); + setState(427); havingClause(); } - setState(429); + setState(431); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WINDOW) { - setState(428); + setState(430); windowClause(); } - setState(432); + setState(434); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ORDER) { - setState(431); + setState(433); orderByClause(); } - setState(436); + setState(438); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::LIMIT: { - setState(434); + setState(436); limitAndOffsetClause(); break; } case HogQLParser::OFFSET: { - setState(435); + setState(437); offsetOnlyClause(); break; } @@ -3301,12 +3304,12 @@ HogQLParser::SelectStmtContext* HogQLParser::selectStmt() { default: break; } - setState(439); + setState(441); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::SETTINGS) { - setState(438); + setState(440); settingsClause(); } @@ -3360,9 +3363,9 @@ HogQLParser::WithClauseContext* HogQLParser::withClause() { }); try { enterOuterAlt(_localctx, 1); - setState(441); + setState(443); match(HogQLParser::WITH); - setState(442); + setState(444); withExprList(); } @@ -3423,18 +3426,18 @@ HogQLParser::TopClauseContext* HogQLParser::topClause() { }); try { enterOuterAlt(_localctx, 1); - setState(444); + setState(446); match(HogQLParser::TOP); - setState(445); + setState(447); match(HogQLParser::DECIMAL_LITERAL); - setState(448); + setState(450); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 46, _ctx)) { case 1: { - setState(446); + setState(448); match(HogQLParser::WITH); - setState(447); + setState(449); match(HogQLParser::TIES); break; } @@ -3493,9 +3496,9 @@ HogQLParser::FromClauseContext* HogQLParser::fromClause() { }); try { enterOuterAlt(_localctx, 1); - setState(450); + setState(452); match(HogQLParser::FROM); - setState(451); + setState(453); joinExpr(0); } @@ -3561,14 +3564,14 @@ HogQLParser::ArrayJoinClauseContext* HogQLParser::arrayJoinClause() { }); try { enterOuterAlt(_localctx, 1); - setState(454); + setState(456); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::INNER || _la == HogQLParser::LEFT) { - setState(453); + setState(455); _la = _input->LA(1); if (!(_la == HogQLParser::INNER @@ -3580,11 +3583,11 @@ HogQLParser::ArrayJoinClauseContext* HogQLParser::arrayJoinClause() { consume(); } } - setState(456); + setState(458); match(HogQLParser::ARRAY); - setState(457); + setState(459); match(HogQLParser::JOIN); - setState(458); + setState(460); columnExprList(); } @@ -3682,35 +3685,35 @@ HogQLParser::WindowClauseContext* HogQLParser::windowClause() { }); try { enterOuterAlt(_localctx, 1); - setState(460); + setState(462); match(HogQLParser::WINDOW); - setState(461); + setState(463); identifier(); - setState(462); + setState(464); match(HogQLParser::AS); - setState(463); + setState(465); match(HogQLParser::LPAREN); - setState(464); + setState(466); windowExpr(); - setState(465); + setState(467); match(HogQLParser::RPAREN); - setState(475); + setState(477); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::COMMA) { - setState(466); + setState(468); match(HogQLParser::COMMA); - setState(467); + setState(469); identifier(); - setState(468); + setState(470); match(HogQLParser::AS); - setState(469); + setState(471); match(HogQLParser::LPAREN); - setState(470); + setState(472); windowExpr(); - setState(471); + setState(473); match(HogQLParser::RPAREN); - setState(477); + setState(479); _errHandler->sync(this); _la = _input->LA(1); } @@ -3765,9 +3768,9 @@ HogQLParser::PrewhereClauseContext* HogQLParser::prewhereClause() { }); try { enterOuterAlt(_localctx, 1); - setState(478); + setState(480); match(HogQLParser::PREWHERE); - setState(479); + setState(481); columnExpr(0); } @@ -3820,9 +3823,9 @@ HogQLParser::WhereClauseContext* HogQLParser::whereClause() { }); try { enterOuterAlt(_localctx, 1); - setState(481); + setState(483); match(HogQLParser::WHERE); - setState(482); + setState(484); columnExpr(0); } @@ -3896,15 +3899,15 @@ HogQLParser::GroupByClauseContext* HogQLParser::groupByClause() { }); try { enterOuterAlt(_localctx, 1); - setState(484); + setState(486); match(HogQLParser::GROUP); - setState(485); + setState(487); match(HogQLParser::BY); - setState(492); + setState(494); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 49, _ctx)) { case 1: { - setState(486); + setState(488); _la = _input->LA(1); if (!(_la == HogQLParser::CUBE @@ -3915,17 +3918,17 @@ HogQLParser::GroupByClauseContext* HogQLParser::groupByClause() { _errHandler->reportMatch(this); consume(); } - setState(487); + setState(489); match(HogQLParser::LPAREN); - setState(488); + setState(490); columnExprList(); - setState(489); + setState(491); match(HogQLParser::RPAREN); break; } case 2: { - setState(491); + setState(493); columnExprList(); break; } @@ -3984,9 +3987,9 @@ HogQLParser::HavingClauseContext* HogQLParser::havingClause() { }); try { enterOuterAlt(_localctx, 1); - setState(494); + setState(496); match(HogQLParser::HAVING); - setState(495); + setState(497); columnExpr(0); } @@ -4043,11 +4046,11 @@ HogQLParser::OrderByClauseContext* HogQLParser::orderByClause() { }); try { enterOuterAlt(_localctx, 1); - setState(497); + setState(499); match(HogQLParser::ORDER); - setState(498); + setState(500); match(HogQLParser::BY); - setState(499); + setState(501); orderExprList(); } @@ -4104,11 +4107,11 @@ HogQLParser::ProjectionOrderByClauseContext* HogQLParser::projectionOrderByClaus }); try { enterOuterAlt(_localctx, 1); - setState(501); + setState(503); match(HogQLParser::ORDER); - setState(502); + setState(504); match(HogQLParser::BY); - setState(503); + setState(505); columnExprList(); } @@ -4189,40 +4192,40 @@ HogQLParser::LimitAndOffsetClauseContext* HogQLParser::limitAndOffsetClause() { exitRule(); }); try { - setState(534); + setState(536); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 54, _ctx)) { case 1: { enterOuterAlt(_localctx, 1); - setState(505); + setState(507); match(HogQLParser::LIMIT); - setState(506); + setState(508); columnExpr(0); - setState(509); + setState(511); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(507); + setState(509); match(HogQLParser::COMMA); - setState(508); + setState(510); columnExpr(0); } - setState(515); + setState(517); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::WITH: { - setState(511); + setState(513); match(HogQLParser::WITH); - setState(512); + setState(514); match(HogQLParser::TIES); break; } case HogQLParser::BY: { - setState(513); + setState(515); match(HogQLParser::BY); - setState(514); + setState(516); columnExprList(); break; } @@ -4244,45 +4247,45 @@ HogQLParser::LimitAndOffsetClauseContext* HogQLParser::limitAndOffsetClause() { case 2: { enterOuterAlt(_localctx, 2); - setState(517); + setState(519); match(HogQLParser::LIMIT); - setState(518); + setState(520); columnExpr(0); - setState(521); + setState(523); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::WITH) { - setState(519); + setState(521); match(HogQLParser::WITH); - setState(520); + setState(522); match(HogQLParser::TIES); } - setState(523); + setState(525); match(HogQLParser::OFFSET); - setState(524); + setState(526); columnExpr(0); break; } case 3: { enterOuterAlt(_localctx, 3); - setState(526); + setState(528); match(HogQLParser::LIMIT); - setState(527); + setState(529); columnExpr(0); - setState(528); + setState(530); match(HogQLParser::OFFSET); - setState(529); + setState(531); columnExpr(0); - setState(532); + setState(534); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::BY) { - setState(530); + setState(532); match(HogQLParser::BY); - setState(531); + setState(533); columnExprList(); } break; @@ -4342,9 +4345,9 @@ HogQLParser::OffsetOnlyClauseContext* HogQLParser::offsetOnlyClause() { }); try { enterOuterAlt(_localctx, 1); - setState(536); + setState(538); match(HogQLParser::OFFSET); - setState(537); + setState(539); columnExpr(0); } @@ -4397,9 +4400,9 @@ HogQLParser::SettingsClauseContext* HogQLParser::settingsClause() { }); try { enterOuterAlt(_localctx, 1); - setState(539); + setState(541); match(HogQLParser::SETTINGS); - setState(540); + setState(542); settingExprList(); } @@ -4553,7 +4556,7 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(554); + setState(556); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 57, _ctx)) { case 1: { @@ -4561,14 +4564,14 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { _ctx = _localctx; previousContext = _localctx; - setState(543); - tableExpr(0); setState(545); + tableExpr(0); + setState(547); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 55, _ctx)) { case 1: { - setState(544); + setState(546); match(HogQLParser::FINAL); break; } @@ -4576,12 +4579,12 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { default: break; } - setState(548); + setState(550); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 56, _ctx)) { case 1: { - setState(547); + setState(549); sampleClause(); break; } @@ -4596,11 +4599,11 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(550); + setState(552); match(HogQLParser::LPAREN); - setState(551); + setState(553); joinExpr(0); - setState(552); + setState(554); match(HogQLParser::RPAREN); break; } @@ -4609,7 +4612,7 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { break; } _ctx->stop = _input->LT(-1); - setState(570); + setState(572); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 60, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { @@ -4617,19 +4620,19 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { if (!_parseListeners.empty()) triggerExitRuleEvent(); previousContext = _localctx; - setState(568); + setState(570); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 59, _ctx)) { case 1: { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleJoinExpr); - setState(556); + setState(558); if (!(precpred(_ctx, 3))) throw FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(557); + setState(559); joinOpCross(); - setState(558); + setState(560); joinExpr(4); break; } @@ -4638,10 +4641,10 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleJoinExpr); - setState(560); + setState(562); if (!(precpred(_ctx, 4))) throw FailedPredicateException(this, "precpred(_ctx, 4)"); - setState(562); + setState(564); _errHandler->sync(this); _la = _input->LA(1); @@ -4649,14 +4652,14 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { ((1ULL << _la) & 18084835973136666) != 0) || _la == HogQLParser::RIGHT || _la == HogQLParser::SEMI) { - setState(561); + setState(563); joinOp(); } - setState(564); + setState(566); match(HogQLParser::JOIN); - setState(565); + setState(567); joinExpr(0); - setState(566); + setState(568); joinConstraintClause(); break; } @@ -4665,7 +4668,7 @@ HogQLParser::JoinExprContext* HogQLParser::joinExpr(int precedence) { break; } } - setState(572); + setState(574); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 60, _ctx); } @@ -4803,23 +4806,23 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { exitRule(); }); try { - setState(616); + setState(618); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 74, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(582); + setState(584); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 63, _ctx)) { case 1: { - setState(574); + setState(576); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0)) { - setState(573); + setState(575); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0))) { @@ -4830,21 +4833,21 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { consume(); } } - setState(576); + setState(578); match(HogQLParser::INNER); break; } case 2: { - setState(577); - match(HogQLParser::INNER); setState(579); + match(HogQLParser::INNER); + setState(581); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0)) { - setState(578); + setState(580); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0))) { @@ -4859,7 +4862,7 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { } case 3: { - setState(581); + setState(583); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 274) != 0))) { @@ -4881,17 +4884,17 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { case 2: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(598); + setState(600); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 68, _ctx)) { case 1: { - setState(585); + setState(587); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 282) != 0) || _la == HogQLParser::SEMI) { - setState(584); + setState(586); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 282) != 0) || _la == HogQLParser::SEMI)) { @@ -4902,7 +4905,7 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { consume(); } } - setState(587); + setState(589); _la = _input->LA(1); if (!(_la == HogQLParser::LEFT @@ -4913,19 +4916,19 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { _errHandler->reportMatch(this); consume(); } - setState(589); + setState(591); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::OUTER) { - setState(588); + setState(590); match(HogQLParser::OUTER); } break; } case 2: { - setState(591); + setState(593); _la = _input->LA(1); if (!(_la == HogQLParser::LEFT @@ -4936,21 +4939,21 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { _errHandler->reportMatch(this); consume(); } - setState(593); + setState(595); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::OUTER) { - setState(592); + setState(594); match(HogQLParser::OUTER); } - setState(596); + setState(598); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 282) != 0) || _la == HogQLParser::SEMI) { - setState(595); + setState(597); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 282) != 0) || _la == HogQLParser::SEMI)) { @@ -4973,18 +4976,18 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { case 3: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 3); - setState(614); + setState(616); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 73, _ctx)) { case 1: { - setState(601); + setState(603); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ALL || _la == HogQLParser::ANY) { - setState(600); + setState(602); _la = _input->LA(1); if (!(_la == HogQLParser::ALL @@ -4996,38 +4999,38 @@ HogQLParser::JoinOpContext* HogQLParser::joinOp() { consume(); } } - setState(603); - match(HogQLParser::FULL); setState(605); - _errHandler->sync(this); + match(HogQLParser::FULL); + setState(607); + _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::OUTER) { - setState(604); + setState(606); match(HogQLParser::OUTER); } break; } case 2: { - setState(607); - match(HogQLParser::FULL); setState(609); + match(HogQLParser::FULL); + setState(611); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::OUTER) { - setState(608); + setState(610); match(HogQLParser::OUTER); } - setState(612); + setState(614); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ALL || _la == HogQLParser::ANY) { - setState(611); + setState(613); _la = _input->LA(1); if (!(_la == HogQLParser::ALL @@ -5105,21 +5108,21 @@ HogQLParser::JoinOpCrossContext* HogQLParser::joinOpCross() { exitRule(); }); try { - setState(621); + setState(623); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::CROSS: { enterOuterAlt(_localctx, 1); - setState(618); + setState(620); match(HogQLParser::CROSS); - setState(619); + setState(621); match(HogQLParser::JOIN); break; } case HogQLParser::COMMA: { enterOuterAlt(_localctx, 2); - setState(620); + setState(622); match(HogQLParser::COMMA); break; } @@ -5189,36 +5192,36 @@ HogQLParser::JoinConstraintClauseContext* HogQLParser::joinConstraintClause() { exitRule(); }); try { - setState(632); + setState(634); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 76, _ctx)) { case 1: { enterOuterAlt(_localctx, 1); - setState(623); + setState(625); match(HogQLParser::ON); - setState(624); + setState(626); columnExprList(); break; } case 2: { enterOuterAlt(_localctx, 2); - setState(625); + setState(627); match(HogQLParser::USING); - setState(626); + setState(628); match(HogQLParser::LPAREN); - setState(627); + setState(629); columnExprList(); - setState(628); + setState(630); match(HogQLParser::RPAREN); break; } case 3: { enterOuterAlt(_localctx, 3); - setState(630); + setState(632); match(HogQLParser::USING); - setState(631); + setState(633); columnExprList(); break; } @@ -5285,18 +5288,18 @@ HogQLParser::SampleClauseContext* HogQLParser::sampleClause() { }); try { enterOuterAlt(_localctx, 1); - setState(634); + setState(636); match(HogQLParser::SAMPLE); - setState(635); + setState(637); ratioExpr(); - setState(638); + setState(640); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 77, _ctx)) { case 1: { - setState(636); + setState(638); match(HogQLParser::OFFSET); - setState(637); + setState(639); ratioExpr(); break; } @@ -5364,17 +5367,17 @@ HogQLParser::OrderExprListContext* HogQLParser::orderExprList() { }); try { enterOuterAlt(_localctx, 1); - setState(640); + setState(642); orderExpr(); - setState(645); + setState(647); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::COMMA) { - setState(641); + setState(643); match(HogQLParser::COMMA); - setState(642); + setState(644); orderExpr(); - setState(647); + setState(649); _errHandler->sync(this); _la = _input->LA(1); } @@ -5458,15 +5461,15 @@ HogQLParser::OrderExprContext* HogQLParser::orderExpr() { }); try { enterOuterAlt(_localctx, 1); - setState(648); - columnExpr(0); setState(650); + columnExpr(0); + setState(652); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 12583040) != 0)) { - setState(649); + setState(651); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 12583040) != 0))) { @@ -5477,14 +5480,14 @@ HogQLParser::OrderExprContext* HogQLParser::orderExpr() { consume(); } } - setState(654); + setState(656); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NULLS) { - setState(652); + setState(654); match(HogQLParser::NULLS); - setState(653); + setState(655); _la = _input->LA(1); if (!(_la == HogQLParser::FIRST @@ -5496,14 +5499,14 @@ HogQLParser::OrderExprContext* HogQLParser::orderExpr() { consume(); } } - setState(658); + setState(660); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COLLATE) { - setState(656); + setState(658); match(HogQLParser::COLLATE); - setState(657); + setState(659); match(HogQLParser::STRING_LITERAL); } @@ -5564,12 +5567,12 @@ HogQLParser::RatioExprContext* HogQLParser::ratioExpr() { exitRule(); }); try { - setState(666); + setState(668); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::LBRACE: { enterOuterAlt(_localctx, 1); - setState(660); + setState(662); placeholder(); break; } @@ -5584,16 +5587,16 @@ HogQLParser::RatioExprContext* HogQLParser::ratioExpr() { case HogQLParser::DOT: case HogQLParser::PLUS: { enterOuterAlt(_localctx, 2); - setState(661); + setState(663); numberLiteral(); - setState(664); + setState(666); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 82, _ctx)) { case 1: { - setState(662); + setState(664); match(HogQLParser::SLASH); - setState(663); + setState(665); numberLiteral(); break; } @@ -5667,17 +5670,17 @@ HogQLParser::SettingExprListContext* HogQLParser::settingExprList() { }); try { enterOuterAlt(_localctx, 1); - setState(668); + setState(670); settingExpr(); - setState(673); + setState(675); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::COMMA) { - setState(669); + setState(671); match(HogQLParser::COMMA); - setState(670); + setState(672); settingExpr(); - setState(675); + setState(677); _errHandler->sync(this); _la = _input->LA(1); } @@ -5736,11 +5739,11 @@ HogQLParser::SettingExprContext* HogQLParser::settingExpr() { }); try { enterOuterAlt(_localctx, 1); - setState(676); + setState(678); identifier(); - setState(677); + setState(679); match(HogQLParser::EQ_SINGLE); - setState(678); + setState(680); literal(); } @@ -5798,30 +5801,30 @@ HogQLParser::WindowExprContext* HogQLParser::windowExpr() { }); try { enterOuterAlt(_localctx, 1); - setState(681); + setState(683); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::PARTITION) { - setState(680); + setState(682); winPartitionByClause(); } - setState(684); + setState(686); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ORDER) { - setState(683); + setState(685); winOrderByClause(); } - setState(687); + setState(689); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::RANGE || _la == HogQLParser::ROWS) { - setState(686); + setState(688); winFrameClause(); } @@ -5879,11 +5882,11 @@ HogQLParser::WinPartitionByClauseContext* HogQLParser::winPartitionByClause() { }); try { enterOuterAlt(_localctx, 1); - setState(689); + setState(691); match(HogQLParser::PARTITION); - setState(690); + setState(692); match(HogQLParser::BY); - setState(691); + setState(693); columnExprList(); } @@ -5940,11 +5943,11 @@ HogQLParser::WinOrderByClauseContext* HogQLParser::winOrderByClause() { }); try { enterOuterAlt(_localctx, 1); - setState(693); + setState(695); match(HogQLParser::ORDER); - setState(694); + setState(696); match(HogQLParser::BY); - setState(695); + setState(697); orderExprList(); } @@ -6002,7 +6005,7 @@ HogQLParser::WinFrameClauseContext* HogQLParser::winFrameClause() { }); try { enterOuterAlt(_localctx, 1); - setState(697); + setState(699); _la = _input->LA(1); if (!(_la == HogQLParser::RANGE @@ -6013,7 +6016,7 @@ HogQLParser::WinFrameClauseContext* HogQLParser::winFrameClause() { _errHandler->reportMatch(this); consume(); } - setState(698); + setState(700); winFrameExtend(); } @@ -6095,7 +6098,7 @@ HogQLParser::WinFrameExtendContext* HogQLParser::winFrameExtend() { exitRule(); }); try { - setState(706); + setState(708); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::CURRENT: @@ -6111,7 +6114,7 @@ HogQLParser::WinFrameExtendContext* HogQLParser::winFrameExtend() { case HogQLParser::PLUS: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(700); + setState(702); winFrameBound(); break; } @@ -6119,13 +6122,13 @@ HogQLParser::WinFrameExtendContext* HogQLParser::winFrameExtend() { case HogQLParser::BETWEEN: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(701); + setState(703); match(HogQLParser::BETWEEN); - setState(702); + setState(704); winFrameBound(); - setState(703); + setState(705); match(HogQLParser::AND); - setState(704); + setState(706); winFrameBound(); break; } @@ -6200,45 +6203,45 @@ HogQLParser::WinFrameBoundContext* HogQLParser::winFrameBound() { }); try { enterOuterAlt(_localctx, 1); - setState(720); + setState(722); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 89, _ctx)) { case 1: { - setState(708); + setState(710); match(HogQLParser::CURRENT); - setState(709); + setState(711); match(HogQLParser::ROW); break; } case 2: { - setState(710); + setState(712); match(HogQLParser::UNBOUNDED); - setState(711); + setState(713); match(HogQLParser::PRECEDING); break; } case 3: { - setState(712); + setState(714); match(HogQLParser::UNBOUNDED); - setState(713); + setState(715); match(HogQLParser::FOLLOWING); break; } case 4: { - setState(714); + setState(716); numberLiteral(); - setState(715); + setState(717); match(HogQLParser::PRECEDING); break; } case 5: { - setState(717); + setState(719); numberLiteral(); - setState(718); + setState(720); match(HogQLParser::FOLLOWING); break; } @@ -6297,9 +6300,9 @@ HogQLParser::ExprContext* HogQLParser::expr() { }); try { enterOuterAlt(_localctx, 1); - setState(722); + setState(724); columnExpr(0); - setState(723); + setState(725); match(HogQLParser::EOF); } @@ -6504,13 +6507,13 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { }); try { size_t alt; - setState(781); + setState(783); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 97, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(725); + setState(727); identifier(); break; } @@ -6518,39 +6521,39 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { case 2: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(726); - identifier(); - setState(727); - match(HogQLParser::LPAREN); setState(728); identifier(); setState(729); + match(HogQLParser::LPAREN); + setState(730); + identifier(); + setState(731); columnTypeExpr(); - setState(736); + setState(738); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 90, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(730); + setState(732); match(HogQLParser::COMMA); - setState(731); + setState(733); identifier(); - setState(732); + setState(734); columnTypeExpr(); } - setState(738); + setState(740); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 90, _ctx); } - setState(740); + setState(742); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(739); + setState(741); match(HogQLParser::COMMA); } - setState(742); + setState(744); match(HogQLParser::RPAREN); break; } @@ -6558,35 +6561,35 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { case 3: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 3); - setState(744); + setState(746); identifier(); - setState(745); + setState(747); match(HogQLParser::LPAREN); - setState(746); + setState(748); enumValue(); - setState(751); + setState(753); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 92, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(747); + setState(749); match(HogQLParser::COMMA); - setState(748); + setState(750); enumValue(); } - setState(753); + setState(755); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 92, _ctx); } - setState(755); + setState(757); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(754); + setState(756); match(HogQLParser::COMMA); } - setState(757); + setState(759); match(HogQLParser::RPAREN); break; } @@ -6594,35 +6597,35 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { case 4: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 4); - setState(759); + setState(761); identifier(); - setState(760); + setState(762); match(HogQLParser::LPAREN); - setState(761); + setState(763); columnTypeExpr(); - setState(766); + setState(768); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 94, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(762); + setState(764); match(HogQLParser::COMMA); - setState(763); + setState(765); columnTypeExpr(); } - setState(768); + setState(770); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 94, _ctx); } - setState(770); + setState(772); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(769); + setState(771); match(HogQLParser::COMMA); } - setState(772); + setState(774); match(HogQLParser::RPAREN); break; } @@ -6630,11 +6633,11 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { case 5: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 5); - setState(774); + setState(776); identifier(); - setState(775); - match(HogQLParser::LPAREN); setState(777); + match(HogQLParser::LPAREN); + setState(779); _errHandler->sync(this); _la = _input->LA(1); @@ -6642,10 +6645,10 @@ HogQLParser::ColumnTypeExprContext* HogQLParser::columnTypeExpr() { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(776); + setState(778); columnExprList(); } - setState(779); + setState(781); match(HogQLParser::RPAREN); break; } @@ -6713,28 +6716,28 @@ HogQLParser::ColumnExprListContext* HogQLParser::columnExprList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(783); + setState(785); columnExpr(0); - setState(788); + setState(790); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 98, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(784); + setState(786); match(HogQLParser::COMMA); - setState(785); + setState(787); columnExpr(0); } - setState(790); + setState(792); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 98, _ctx); } - setState(792); + setState(794); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 99, _ctx)) { case 1: { - setState(791); + setState(793); match(HogQLParser::COMMA); break; } @@ -7949,7 +7952,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(946); + setState(948); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 119, _ctx)) { case 1: { @@ -7957,14 +7960,14 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _ctx = _localctx; previousContext = _localctx; - setState(795); - match(HogQLParser::CASE); setState(797); + match(HogQLParser::CASE); + setState(799); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 100, _ctx)) { case 1: { - setState(796); + setState(798); antlrcpp::downCast(_localctx)->caseExpr = columnExpr(0); break; } @@ -7972,33 +7975,33 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(804); + setState(806); _errHandler->sync(this); _la = _input->LA(1); do { - setState(799); + setState(801); match(HogQLParser::WHEN); - setState(800); + setState(802); antlrcpp::downCast(_localctx)->whenExpr = columnExpr(0); - setState(801); + setState(803); match(HogQLParser::THEN); - setState(802); + setState(804); antlrcpp::downCast(_localctx)->thenExpr = columnExpr(0); - setState(806); + setState(808); _errHandler->sync(this); _la = _input->LA(1); } while (_la == HogQLParser::WHEN); - setState(810); + setState(812); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::ELSE) { - setState(808); + setState(810); match(HogQLParser::ELSE); - setState(809); + setState(811); antlrcpp::downCast(_localctx)->elseExpr = columnExpr(0); } - setState(812); + setState(814); match(HogQLParser::END); break; } @@ -8007,17 +8010,17 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(814); + setState(816); match(HogQLParser::CAST); - setState(815); + setState(817); match(HogQLParser::LPAREN); - setState(816); + setState(818); columnExpr(0); - setState(817); + setState(819); match(HogQLParser::AS); - setState(818); + setState(820); columnTypeExpr(); - setState(819); + setState(821); match(HogQLParser::RPAREN); break; } @@ -8026,9 +8029,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(821); + setState(823); match(HogQLParser::DATE); - setState(822); + setState(824); match(HogQLParser::STRING_LITERAL); break; } @@ -8037,9 +8040,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(823); + setState(825); match(HogQLParser::INTERVAL); - setState(824); + setState(826); match(HogQLParser::STRING_LITERAL); break; } @@ -8048,11 +8051,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(825); + setState(827); match(HogQLParser::INTERVAL); - setState(826); + setState(828); columnExpr(0); - setState(827); + setState(829); interval(); break; } @@ -8061,27 +8064,27 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(829); + setState(831); match(HogQLParser::SUBSTRING); - setState(830); + setState(832); match(HogQLParser::LPAREN); - setState(831); + setState(833); columnExpr(0); - setState(832); + setState(834); match(HogQLParser::FROM); - setState(833); + setState(835); columnExpr(0); - setState(836); + setState(838); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::FOR) { - setState(834); + setState(836); match(HogQLParser::FOR); - setState(835); + setState(837); columnExpr(0); } - setState(838); + setState(840); match(HogQLParser::RPAREN); break; } @@ -8090,9 +8093,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(840); + setState(842); match(HogQLParser::TIMESTAMP); - setState(841); + setState(843); match(HogQLParser::STRING_LITERAL); break; } @@ -8101,11 +8104,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(842); + setState(844); match(HogQLParser::TRIM); - setState(843); + setState(845); match(HogQLParser::LPAREN); - setState(844); + setState(846); _la = _input->LA(1); if (!(_la == HogQLParser::BOTH @@ -8116,13 +8119,13 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _errHandler->reportMatch(this); consume(); } - setState(845); + setState(847); string(); - setState(846); + setState(848); match(HogQLParser::FROM); - setState(847); + setState(849); columnExpr(0); - setState(848); + setState(850); match(HogQLParser::RPAREN); break; } @@ -8131,12 +8134,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(850); + setState(852); identifier(); - setState(851); - match(HogQLParser::LPAREN); setState(853); + match(HogQLParser::LPAREN); + setState(855); _errHandler->sync(this); _la = _input->LA(1); @@ -8144,24 +8147,24 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(852); + setState(854); antlrcpp::downCast(_localctx)->columnExprs = columnExprList(); } - setState(855); + setState(857); match(HogQLParser::RPAREN); - setState(865); + setState(867); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::LPAREN) { - setState(857); - match(HogQLParser::LPAREN); setState(859); + match(HogQLParser::LPAREN); + setState(861); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 105, _ctx)) { case 1: { - setState(858); + setState(860); match(HogQLParser::DISTINCT); break; } @@ -8169,7 +8172,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(862); + setState(864); _errHandler->sync(this); _la = _input->LA(1); @@ -8177,19 +8180,19 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(861); + setState(863); antlrcpp::downCast(_localctx)->columnArgList = columnExprList(); } - setState(864); + setState(866); match(HogQLParser::RPAREN); } - setState(867); + setState(869); match(HogQLParser::OVER); - setState(868); + setState(870); match(HogQLParser::LPAREN); - setState(869); + setState(871); windowExpr(); - setState(870); + setState(872); match(HogQLParser::RPAREN); break; } @@ -8198,12 +8201,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(872); + setState(874); identifier(); - setState(873); - match(HogQLParser::LPAREN); setState(875); + match(HogQLParser::LPAREN); + setState(877); _errHandler->sync(this); _la = _input->LA(1); @@ -8211,24 +8214,24 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(874); + setState(876); antlrcpp::downCast(_localctx)->columnExprs = columnExprList(); } - setState(877); + setState(879); match(HogQLParser::RPAREN); - setState(887); + setState(889); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::LPAREN) { - setState(879); - match(HogQLParser::LPAREN); setState(881); + match(HogQLParser::LPAREN); + setState(883); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 109, _ctx)) { case 1: { - setState(880); + setState(882); match(HogQLParser::DISTINCT); break; } @@ -8236,7 +8239,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(884); + setState(886); _errHandler->sync(this); _la = _input->LA(1); @@ -8244,15 +8247,15 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(883); + setState(885); antlrcpp::downCast(_localctx)->columnArgList = columnExprList(); } - setState(886); + setState(888); match(HogQLParser::RPAREN); } - setState(889); + setState(891); match(HogQLParser::OVER); - setState(890); + setState(892); identifier(); break; } @@ -8261,16 +8264,16 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(892); + setState(894); identifier(); - setState(898); + setState(900); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 113, _ctx)) { case 1: { - setState(893); - match(HogQLParser::LPAREN); setState(895); + match(HogQLParser::LPAREN); + setState(897); _errHandler->sync(this); _la = _input->LA(1); @@ -8278,10 +8281,10 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(894); + setState(896); antlrcpp::downCast(_localctx)->columnExprs = columnExprList(); } - setState(897); + setState(899); match(HogQLParser::RPAREN); break; } @@ -8289,14 +8292,14 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(900); - match(HogQLParser::LPAREN); setState(902); + match(HogQLParser::LPAREN); + setState(904); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 114, _ctx)) { case 1: { - setState(901); + setState(903); match(HogQLParser::DISTINCT); break; } @@ -8304,7 +8307,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(905); + setState(907); _errHandler->sync(this); _la = _input->LA(1); @@ -8312,10 +8315,10 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(904); + setState(906); antlrcpp::downCast(_localctx)->columnArgList = columnExprList(); } - setState(907); + setState(909); match(HogQLParser::RPAREN); break; } @@ -8324,7 +8327,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(909); + setState(911); hogqlxTagElement(); break; } @@ -8333,7 +8336,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(910); + setState(912); templateString(); break; } @@ -8342,7 +8345,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(911); + setState(913); literal(); break; } @@ -8351,9 +8354,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(912); + setState(914); match(HogQLParser::DASH); - setState(913); + setState(915); columnExpr(20); break; } @@ -8362,9 +8365,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(914); + setState(916); match(HogQLParser::NOT); - setState(915); + setState(917); columnExpr(14); break; } @@ -8373,19 +8376,19 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(919); + setState(921); _errHandler->sync(this); _la = _input->LA(1); if ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -5800812384855539714) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 25834219896831) != 0)) { - setState(916); + setState(918); tableIdentifier(); - setState(917); + setState(919); match(HogQLParser::DOT); } - setState(921); + setState(923); match(HogQLParser::ASTERISK); break; } @@ -8394,11 +8397,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(922); + setState(924); match(HogQLParser::LPAREN); - setState(923); + setState(925); selectSetStmt(); - setState(924); + setState(926); match(HogQLParser::RPAREN); break; } @@ -8407,11 +8410,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(926); + setState(928); match(HogQLParser::LPAREN); - setState(927); + setState(929); columnExpr(0); - setState(928); + setState(930); match(HogQLParser::RPAREN); break; } @@ -8420,11 +8423,11 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(930); + setState(932); match(HogQLParser::LPAREN); - setState(931); + setState(933); columnExprList(); - setState(932); + setState(934); match(HogQLParser::RPAREN); break; } @@ -8433,9 +8436,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(934); - match(HogQLParser::LBRACKET); setState(936); + match(HogQLParser::LBRACKET); + setState(938); _errHandler->sync(this); _la = _input->LA(1); @@ -8443,10 +8446,10 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(935); + setState(937); columnExprList(); } - setState(938); + setState(940); match(HogQLParser::RBRACKET); break; } @@ -8455,9 +8458,9 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(939); - match(HogQLParser::LBRACE); setState(941); + match(HogQLParser::LBRACE); + setState(943); _errHandler->sync(this); _la = _input->LA(1); @@ -8465,10 +8468,10 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(940); + setState(942); kvPairList(); } - setState(943); + setState(945); match(HogQLParser::RBRACE); break; } @@ -8477,7 +8480,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(944); + setState(946); columnLambdaExpr(); break; } @@ -8486,7 +8489,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(945); + setState(947); columnIdentifier(); break; } @@ -8495,7 +8498,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { break; } _ctx->stop = _input->LT(-1); - setState(1058); + setState(1060); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 131, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { @@ -8503,7 +8506,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { if (!_parseListeners.empty()) triggerExitRuleEvent(); previousContext = _localctx; - setState(1056); + setState(1058); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 130, _ctx)) { case 1: { @@ -8511,26 +8514,26 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = newContext; newContext->left = previousContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(948); + setState(950); if (!(precpred(_ctx, 19))) throw FailedPredicateException(this, "precpred(_ctx, 19)"); - setState(952); + setState(954); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::ASTERISK: { - setState(949); + setState(951); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::ASTERISK); break; } case HogQLParser::SLASH: { - setState(950); + setState(952); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::SLASH); break; } case HogQLParser::PERCENT: { - setState(951); + setState(953); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::PERCENT); break; } @@ -8538,7 +8541,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: throw NoViableAltException(this); } - setState(954); + setState(956); antlrcpp::downCast(_localctx)->right = columnExpr(20); break; } @@ -8548,26 +8551,26 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = newContext; newContext->left = previousContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(955); + setState(957); if (!(precpred(_ctx, 18))) throw FailedPredicateException(this, "precpred(_ctx, 18)"); - setState(959); + setState(961); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::PLUS: { - setState(956); + setState(958); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::PLUS); break; } case HogQLParser::DASH: { - setState(957); + setState(959); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::DASH); break; } case HogQLParser::CONCAT: { - setState(958); + setState(960); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::CONCAT); break; } @@ -8575,7 +8578,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: throw NoViableAltException(this); } - setState(961); + setState(963); antlrcpp::downCast(_localctx)->right = columnExpr(19); break; } @@ -8585,71 +8588,71 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { _localctx = newContext; newContext->left = previousContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(962); + setState(964); if (!(precpred(_ctx, 17))) throw FailedPredicateException(this, "precpred(_ctx, 17)"); - setState(987); + setState(989); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 125, _ctx)) { case 1: { - setState(963); + setState(965); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::EQ_DOUBLE); break; } case 2: { - setState(964); + setState(966); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::EQ_SINGLE); break; } case 3: { - setState(965); + setState(967); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT_EQ); break; } case 4: { - setState(966); + setState(968); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::LT_EQ); break; } case 5: { - setState(967); + setState(969); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::LT); break; } case 6: { - setState(968); + setState(970); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::GT_EQ); break; } case 7: { - setState(969); + setState(971); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::GT); break; } case 8: { - setState(971); + setState(973); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NOT) { - setState(970); + setState(972); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT); } - setState(973); - match(HogQLParser::IN); setState(975); + match(HogQLParser::IN); + setState(977); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 123, _ctx)) { case 1: { - setState(974); + setState(976); match(HogQLParser::COHORT); break; } @@ -8661,15 +8664,15 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { } case 9: { - setState(978); + setState(980); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NOT) { - setState(977); + setState(979); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT); } - setState(980); + setState(982); _la = _input->LA(1); if (!(_la == HogQLParser::ILIKE @@ -8684,37 +8687,37 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { } case 10: { - setState(981); + setState(983); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::REGEX_SINGLE); break; } case 11: { - setState(982); + setState(984); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::REGEX_DOUBLE); break; } case 12: { - setState(983); + setState(985); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT_REGEX); break; } case 13: { - setState(984); + setState(986); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::IREGEX_SINGLE); break; } case 14: { - setState(985); + setState(987); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::IREGEX_DOUBLE); break; } case 15: { - setState(986); + setState(988); antlrcpp::downCast(_localctx)->operator_ = match(HogQLParser::NOT_IREGEX); break; } @@ -8722,7 +8725,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { default: break; } - setState(989); + setState(991); antlrcpp::downCast(_localctx)->right = columnExpr(18); break; } @@ -8731,12 +8734,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(990); + setState(992); if (!(precpred(_ctx, 15))) throw FailedPredicateException(this, "precpred(_ctx, 15)"); - setState(991); + setState(993); match(HogQLParser::NULLISH); - setState(992); + setState(994); columnExpr(16); break; } @@ -8745,12 +8748,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(993); + setState(995); if (!(precpred(_ctx, 13))) throw FailedPredicateException(this, "precpred(_ctx, 13)"); - setState(994); + setState(996); match(HogQLParser::AND); - setState(995); + setState(997); columnExpr(14); break; } @@ -8759,12 +8762,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(996); + setState(998); if (!(precpred(_ctx, 12))) throw FailedPredicateException(this, "precpred(_ctx, 12)"); - setState(997); + setState(999); match(HogQLParser::OR); - setState(998); + setState(1000); columnExpr(13); break; } @@ -8773,24 +8776,24 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(999); + setState(1001); if (!(precpred(_ctx, 11))) throw FailedPredicateException(this, "precpred(_ctx, 11)"); - setState(1001); + setState(1003); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NOT) { - setState(1000); + setState(1002); match(HogQLParser::NOT); } - setState(1003); + setState(1005); match(HogQLParser::BETWEEN); - setState(1004); + setState(1006); columnExpr(0); - setState(1005); + setState(1007); match(HogQLParser::AND); - setState(1006); + setState(1008); columnExpr(12); break; } @@ -8799,16 +8802,16 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1008); + setState(1010); if (!(precpred(_ctx, 10))) throw FailedPredicateException(this, "precpred(_ctx, 10)"); - setState(1009); + setState(1011); match(HogQLParser::QUERY); - setState(1010); + setState(1012); columnExpr(0); - setState(1011); + setState(1013); match(HogQLParser::COLON); - setState(1012); + setState(1014); columnExpr(10); break; } @@ -8817,12 +8820,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1014); + setState(1016); if (!(precpred(_ctx, 30))) throw FailedPredicateException(this, "precpred(_ctx, 30)"); - setState(1015); - match(HogQLParser::LPAREN); setState(1017); + match(HogQLParser::LPAREN); + setState(1019); _errHandler->sync(this); _la = _input->LA(1); @@ -8830,10 +8833,10 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(1016); + setState(1018); columnExprList(); } - setState(1019); + setState(1021); match(HogQLParser::RPAREN); break; } @@ -8842,14 +8845,14 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1020); + setState(1022); if (!(precpred(_ctx, 26))) throw FailedPredicateException(this, "precpred(_ctx, 26)"); - setState(1021); + setState(1023); match(HogQLParser::LBRACKET); - setState(1022); + setState(1024); columnExpr(0); - setState(1023); + setState(1025); match(HogQLParser::RBRACKET); break; } @@ -8858,12 +8861,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1025); + setState(1027); if (!(precpred(_ctx, 25))) throw FailedPredicateException(this, "precpred(_ctx, 25)"); - setState(1026); + setState(1028); match(HogQLParser::DOT); - setState(1027); + setState(1029); match(HogQLParser::DECIMAL_LITERAL); break; } @@ -8872,12 +8875,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1028); + setState(1030); if (!(precpred(_ctx, 24))) throw FailedPredicateException(this, "precpred(_ctx, 24)"); - setState(1029); + setState(1031); match(HogQLParser::DOT); - setState(1030); + setState(1032); identifier(); break; } @@ -8886,16 +8889,16 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1031); + setState(1033); if (!(precpred(_ctx, 23))) throw FailedPredicateException(this, "precpred(_ctx, 23)"); - setState(1032); + setState(1034); match(HogQLParser::NULL_PROPERTY); - setState(1033); + setState(1035); match(HogQLParser::LBRACKET); - setState(1034); + setState(1036); columnExpr(0); - setState(1035); + setState(1037); match(HogQLParser::RBRACKET); break; } @@ -8904,12 +8907,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1037); + setState(1039); if (!(precpred(_ctx, 22))) throw FailedPredicateException(this, "precpred(_ctx, 22)"); - setState(1038); + setState(1040); match(HogQLParser::NULL_PROPERTY); - setState(1039); + setState(1041); match(HogQLParser::DECIMAL_LITERAL); break; } @@ -8918,12 +8921,12 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1040); + setState(1042); if (!(precpred(_ctx, 21))) throw FailedPredicateException(this, "precpred(_ctx, 21)"); - setState(1041); + setState(1043); match(HogQLParser::NULL_PROPERTY); - setState(1042); + setState(1044); identifier(); break; } @@ -8932,20 +8935,20 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1043); + setState(1045); if (!(precpred(_ctx, 16))) throw FailedPredicateException(this, "precpred(_ctx, 16)"); - setState(1044); - match(HogQLParser::IS); setState(1046); + match(HogQLParser::IS); + setState(1048); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::NOT) { - setState(1045); + setState(1047); match(HogQLParser::NOT); } - setState(1048); + setState(1050); match(HogQLParser::NULL_SQL); break; } @@ -8954,24 +8957,24 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleColumnExpr); - setState(1049); + setState(1051); if (!(precpred(_ctx, 9))) throw FailedPredicateException(this, "precpred(_ctx, 9)"); - setState(1054); + setState(1056); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 129, _ctx)) { case 1: { - setState(1050); + setState(1052); match(HogQLParser::AS); - setState(1051); + setState(1053); identifier(); break; } case 2: { - setState(1052); + setState(1054); match(HogQLParser::AS); - setState(1053); + setState(1055); match(HogQLParser::STRING_LITERAL); break; } @@ -8986,7 +8989,7 @@ HogQLParser::ColumnExprContext* HogQLParser::columnExpr(int precedence) { break; } } - setState(1060); + setState(1062); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 131, _ctx); } @@ -9069,73 +9072,73 @@ HogQLParser::ColumnLambdaExprContext* HogQLParser::columnLambdaExpr() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1088); + setState(1090); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 136, _ctx)) { case 1: { - setState(1061); + setState(1063); match(HogQLParser::LPAREN); - setState(1062); + setState(1064); identifier(); - setState(1067); + setState(1069); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 132, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1063); + setState(1065); match(HogQLParser::COMMA); - setState(1064); + setState(1066); identifier(); } - setState(1069); + setState(1071); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 132, _ctx); } - setState(1071); + setState(1073); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(1070); + setState(1072); match(HogQLParser::COMMA); } - setState(1073); + setState(1075); match(HogQLParser::RPAREN); break; } case 2: { - setState(1075); + setState(1077); identifier(); - setState(1080); + setState(1082); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 134, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1076); + setState(1078); match(HogQLParser::COMMA); - setState(1077); + setState(1079); identifier(); } - setState(1082); + setState(1084); _errHandler->sync(this); alt = getInterpreter()->adaptivePredict(_input, 134, _ctx); } - setState(1084); + setState(1086); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(1083); + setState(1085); match(HogQLParser::COMMA); } break; } case 3: { - setState(1086); + setState(1088); match(HogQLParser::LPAREN); - setState(1087); + setState(1089); match(HogQLParser::RPAREN); break; } @@ -9143,19 +9146,19 @@ HogQLParser::ColumnLambdaExprContext* HogQLParser::columnLambdaExpr() { default: break; } - setState(1090); + setState(1092); match(HogQLParser::ARROW); - setState(1093); + setState(1095); _errHandler->sync(this); switch (getInterpreter()->adaptivePredict(_input, 137, _ctx)) { case 1: { - setState(1091); + setState(1093); columnExpr(0); break; } case 2: { - setState(1092); + setState(1094); block(); break; } @@ -9174,6 +9177,88 @@ HogQLParser::ColumnLambdaExprContext* HogQLParser::columnLambdaExpr() { return _localctx; } +//----------------- HogqlxChildElementContext ------------------------------------------------------------------ + +HogQLParser::HogqlxChildElementContext::HogqlxChildElementContext(ParserRuleContext *parent, size_t invokingState) + : ParserRuleContext(parent, invokingState) { +} + +HogQLParser::HogqlxTagElementContext* HogQLParser::HogqlxChildElementContext::hogqlxTagElement() { + return getRuleContext(0); +} + +tree::TerminalNode* HogQLParser::HogqlxChildElementContext::LBRACE() { + return getToken(HogQLParser::LBRACE, 0); +} + +HogQLParser::ColumnExprContext* HogQLParser::HogqlxChildElementContext::columnExpr() { + return getRuleContext(0); +} + +tree::TerminalNode* HogQLParser::HogqlxChildElementContext::RBRACE() { + return getToken(HogQLParser::RBRACE, 0); +} + + +size_t HogQLParser::HogqlxChildElementContext::getRuleIndex() const { + return HogQLParser::RuleHogqlxChildElement; +} + + +std::any HogQLParser::HogqlxChildElementContext::accept(tree::ParseTreeVisitor *visitor) { + if (auto parserVisitor = dynamic_cast(visitor)) + return parserVisitor->visitHogqlxChildElement(this); + else + return visitor->visitChildren(this); +} + +HogQLParser::HogqlxChildElementContext* HogQLParser::hogqlxChildElement() { + HogqlxChildElementContext *_localctx = _tracker.createInstance(_ctx, getState()); + enterRule(_localctx, 122, HogQLParser::RuleHogqlxChildElement); + +#if __cplusplus > 201703L + auto onExit = finally([=, this] { +#else + auto onExit = finally([=] { +#endif + exitRule(); + }); + try { + setState(1102); + _errHandler->sync(this); + switch (_input->LA(1)) { + case HogQLParser::LT: { + enterOuterAlt(_localctx, 1); + setState(1097); + hogqlxTagElement(); + break; + } + + case HogQLParser::LBRACE: { + enterOuterAlt(_localctx, 2); + setState(1098); + match(HogQLParser::LBRACE); + setState(1099); + columnExpr(0); + setState(1100); + match(HogQLParser::RBRACE); + break; + } + + default: + throw NoViableAltException(this); + } + + } + catch (RecognitionException &e) { + _errHandler->reportError(this, e); + _localctx->exception = std::current_exception(); + _errHandler->recover(this, _localctx->exception); + } + + return _localctx; +} + //----------------- HogqlxTagElementContext ------------------------------------------------------------------ HogQLParser::HogqlxTagElementContext::HogqlxTagElementContext(ParserRuleContext *parent, size_t invokingState) @@ -9262,20 +9347,12 @@ HogQLParser::HogqlxTagAttributeContext* HogQLParser::HogqlxTagElementNestedConte return getRuleContext(i); } -HogQLParser::HogqlxTagElementContext* HogQLParser::HogqlxTagElementNestedContext::hogqlxTagElement() { - return getRuleContext(0); +std::vector HogQLParser::HogqlxTagElementNestedContext::hogqlxChildElement() { + return getRuleContexts(); } -tree::TerminalNode* HogQLParser::HogqlxTagElementNestedContext::LBRACE() { - return getToken(HogQLParser::LBRACE, 0); -} - -HogQLParser::ColumnExprContext* HogQLParser::HogqlxTagElementNestedContext::columnExpr() { - return getRuleContext(0); -} - -tree::TerminalNode* HogQLParser::HogqlxTagElementNestedContext::RBRACE() { - return getToken(HogQLParser::RBRACE, 0); +HogQLParser::HogqlxChildElementContext* HogQLParser::HogqlxTagElementNestedContext::hogqlxChildElement(size_t i) { + return getRuleContext(i); } HogQLParser::HogqlxTagElementNestedContext::HogqlxTagElementNestedContext(HogqlxTagElementContext *ctx) { copyFrom(ctx); } @@ -9289,7 +9366,7 @@ std::any HogQLParser::HogqlxTagElementNestedContext::accept(tree::ParseTreeVisit } HogQLParser::HogqlxTagElementContext* HogQLParser::hogqlxTagElement() { HogqlxTagElementContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 122, HogQLParser::RuleHogqlxTagElement); + enterRule(_localctx, 124, HogQLParser::RuleHogqlxTagElement); size_t _la = 0; #if __cplusplus > 201703L @@ -9300,31 +9377,32 @@ HogQLParser::HogqlxTagElementContext* HogQLParser::hogqlxTagElement() { exitRule(); }); try { - setState(1127); + size_t alt; + setState(1135); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 141, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 142, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(1095); + setState(1104); match(HogQLParser::LT); - setState(1096); + setState(1105); identifier(); - setState(1100); + setState(1109); _errHandler->sync(this); _la = _input->LA(1); while ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -5800812384855539714) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 25834219896831) != 0)) { - setState(1097); + setState(1106); hogqlxTagAttribute(); - setState(1102); + setState(1111); _errHandler->sync(this); _la = _input->LA(1); } - setState(1103); + setState(1112); match(HogQLParser::SLASH); - setState(1104); + setState(1113); match(HogQLParser::GT); break; } @@ -9332,54 +9410,43 @@ HogQLParser::HogqlxTagElementContext* HogQLParser::hogqlxTagElement() { case 2: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(1106); + setState(1115); match(HogQLParser::LT); - setState(1107); + setState(1116); identifier(); - setState(1111); + setState(1120); _errHandler->sync(this); _la = _input->LA(1); while ((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -5800812384855539714) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 25834219896831) != 0)) { - setState(1108); + setState(1117); hogqlxTagAttribute(); - setState(1113); + setState(1122); _errHandler->sync(this); _la = _input->LA(1); } - setState(1114); + setState(1123); match(HogQLParser::GT); - setState(1120); + setState(1127); _errHandler->sync(this); - - switch (getInterpreter()->adaptivePredict(_input, 140, _ctx)) { - case 1: { - setState(1115); - hogqlxTagElement(); - break; - } - - case 2: { - setState(1116); - match(HogQLParser::LBRACE); - setState(1117); - columnExpr(0); - setState(1118); - match(HogQLParser::RBRACE); - break; - } - - default: - break; + alt = getInterpreter()->adaptivePredict(_input, 141, _ctx); + while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { + if (alt == 1) { + setState(1124); + hogqlxChildElement(); + } + setState(1129); + _errHandler->sync(this); + alt = getInterpreter()->adaptivePredict(_input, 141, _ctx); } - setState(1122); + setState(1130); match(HogQLParser::LT); - setState(1123); + setState(1131); match(HogQLParser::SLASH); - setState(1124); + setState(1132); identifier(); - setState(1125); + setState(1133); match(HogQLParser::GT); break; } @@ -9443,7 +9510,7 @@ std::any HogQLParser::HogqlxTagAttributeContext::accept(tree::ParseTreeVisitor * HogQLParser::HogqlxTagAttributeContext* HogQLParser::hogqlxTagAttribute() { HogqlxTagAttributeContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 124, HogQLParser::RuleHogqlxTagAttribute); + enterRule(_localctx, 126, HogQLParser::RuleHogqlxTagAttribute); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -9453,38 +9520,38 @@ HogQLParser::HogqlxTagAttributeContext* HogQLParser::hogqlxTagAttribute() { exitRule(); }); try { - setState(1140); + setState(1148); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 142, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 143, _ctx)) { case 1: { enterOuterAlt(_localctx, 1); - setState(1129); + setState(1137); identifier(); - setState(1130); + setState(1138); match(HogQLParser::EQ_SINGLE); - setState(1131); + setState(1139); string(); break; } case 2: { enterOuterAlt(_localctx, 2); - setState(1133); + setState(1141); identifier(); - setState(1134); + setState(1142); match(HogQLParser::EQ_SINGLE); - setState(1135); + setState(1143); match(HogQLParser::LBRACE); - setState(1136); + setState(1144); columnExpr(0); - setState(1137); + setState(1145); match(HogQLParser::RBRACE); break; } case 3: { enterOuterAlt(_localctx, 3); - setState(1139); + setState(1147); identifier(); break; } @@ -9540,7 +9607,7 @@ std::any HogQLParser::WithExprListContext::accept(tree::ParseTreeVisitor *visito HogQLParser::WithExprListContext* HogQLParser::withExprList() { WithExprListContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 126, HogQLParser::RuleWithExprList); + enterRule(_localctx, 128, HogQLParser::RuleWithExprList); size_t _la = 0; #if __cplusplus > 201703L @@ -9553,28 +9620,28 @@ HogQLParser::WithExprListContext* HogQLParser::withExprList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1142); + setState(1150); withExpr(); - setState(1147); + setState(1155); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 143, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 144, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1143); + setState(1151); match(HogQLParser::COMMA); - setState(1144); + setState(1152); withExpr(); } - setState(1149); + setState(1157); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 143, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 144, _ctx); } - setState(1151); + setState(1159); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(1150); + setState(1158); match(HogQLParser::COMMA); } @@ -9659,7 +9726,7 @@ std::any HogQLParser::WithExprSubqueryContext::accept(tree::ParseTreeVisitor *vi } HogQLParser::WithExprContext* HogQLParser::withExpr() { WithExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 128, HogQLParser::RuleWithExpr); + enterRule(_localctx, 130, HogQLParser::RuleWithExpr); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -9669,21 +9736,21 @@ HogQLParser::WithExprContext* HogQLParser::withExpr() { exitRule(); }); try { - setState(1163); + setState(1171); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 145, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 146, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 1); - setState(1153); + setState(1161); identifier(); - setState(1154); + setState(1162); match(HogQLParser::AS); - setState(1155); + setState(1163); match(HogQLParser::LPAREN); - setState(1156); + setState(1164); selectSetStmt(); - setState(1157); + setState(1165); match(HogQLParser::RPAREN); break; } @@ -9691,11 +9758,11 @@ HogQLParser::WithExprContext* HogQLParser::withExpr() { case 2: { _localctx = _tracker.createInstance(_localctx); enterOuterAlt(_localctx, 2); - setState(1159); + setState(1167); columnExpr(0); - setState(1160); + setState(1168); match(HogQLParser::AS); - setState(1161); + setState(1169); identifier(); break; } @@ -9751,7 +9818,7 @@ std::any HogQLParser::ColumnIdentifierContext::accept(tree::ParseTreeVisitor *vi HogQLParser::ColumnIdentifierContext* HogQLParser::columnIdentifier() { ColumnIdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 130, HogQLParser::RuleColumnIdentifier); + enterRule(_localctx, 132, HogQLParser::RuleColumnIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -9761,12 +9828,12 @@ HogQLParser::ColumnIdentifierContext* HogQLParser::columnIdentifier() { exitRule(); }); try { - setState(1172); + setState(1180); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::LBRACE: { enterOuterAlt(_localctx, 1); - setState(1165); + setState(1173); placeholder(); break; } @@ -9866,14 +9933,14 @@ HogQLParser::ColumnIdentifierContext* HogQLParser::columnIdentifier() { case HogQLParser::YEAR: case HogQLParser::IDENTIFIER: { enterOuterAlt(_localctx, 2); - setState(1169); + setState(1177); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 146, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 147, _ctx)) { case 1: { - setState(1166); + setState(1174); tableIdentifier(); - setState(1167); + setState(1175); match(HogQLParser::DOT); break; } @@ -9881,7 +9948,7 @@ HogQLParser::ColumnIdentifierContext* HogQLParser::columnIdentifier() { default: break; } - setState(1171); + setState(1179); nestedIdentifier(); break; } @@ -9937,7 +10004,7 @@ std::any HogQLParser::NestedIdentifierContext::accept(tree::ParseTreeVisitor *vi HogQLParser::NestedIdentifierContext* HogQLParser::nestedIdentifier() { NestedIdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 132, HogQLParser::RuleNestedIdentifier); + enterRule(_localctx, 134, HogQLParser::RuleNestedIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -9949,21 +10016,21 @@ HogQLParser::NestedIdentifierContext* HogQLParser::nestedIdentifier() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1174); + setState(1182); identifier(); - setState(1179); + setState(1187); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 148, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 149, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1175); + setState(1183); match(HogQLParser::DOT); - setState(1176); + setState(1184); identifier(); } - setState(1181); + setState(1189); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 148, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 149, _ctx); } } @@ -10112,8 +10179,8 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { HogQLParser::TableExprContext *_localctx = _tracker.createInstance(_ctx, parentState); HogQLParser::TableExprContext *previousContext = _localctx; (void)previousContext; // Silence compiler, in case the context is not used by generated code. - size_t startState = 134; - enterRecursionRule(_localctx, 134, HogQLParser::RuleTableExpr, precedence); + size_t startState = 136; + enterRecursionRule(_localctx, 136, HogQLParser::RuleTableExpr, precedence); @@ -10127,15 +10194,15 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1191); + setState(1199); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 149, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 150, _ctx)) { case 1: { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1183); + setState(1191); tableIdentifier(); break; } @@ -10144,7 +10211,7 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1184); + setState(1192); tableFunctionExpr(); break; } @@ -10153,11 +10220,11 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1185); + setState(1193); match(HogQLParser::LPAREN); - setState(1186); + setState(1194); selectSetStmt(); - setState(1187); + setState(1195); match(HogQLParser::RPAREN); break; } @@ -10166,7 +10233,7 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1189); + setState(1197); hogqlxTagElement(); break; } @@ -10175,7 +10242,7 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { _localctx = _tracker.createInstance(_localctx); _ctx = _localctx; previousContext = _localctx; - setState(1190); + setState(1198); placeholder(); break; } @@ -10184,9 +10251,9 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { break; } _ctx->stop = _input->LT(-1); - setState(1201); + setState(1209); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 151, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 152, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { if (!_parseListeners.empty()) @@ -10195,10 +10262,10 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { auto newContext = _tracker.createInstance(_tracker.createInstance(parentContext, parentState)); _localctx = newContext; pushNewRecursionContext(newContext, startState, RuleTableExpr); - setState(1193); + setState(1201); if (!(precpred(_ctx, 3))) throw FailedPredicateException(this, "precpred(_ctx, 3)"); - setState(1197); + setState(1205); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::DATE: @@ -10206,15 +10273,15 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { case HogQLParser::ID: case HogQLParser::KEY: case HogQLParser::IDENTIFIER: { - setState(1194); + setState(1202); alias(); break; } case HogQLParser::AS: { - setState(1195); + setState(1203); match(HogQLParser::AS); - setState(1196); + setState(1204); identifier(); break; } @@ -10223,9 +10290,9 @@ HogQLParser::TableExprContext* HogQLParser::tableExpr(int precedence) { throw NoViableAltException(this); } } - setState(1203); + setState(1211); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 151, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 152, _ctx); } } catch (RecognitionException &e) { @@ -10273,7 +10340,7 @@ std::any HogQLParser::TableFunctionExprContext::accept(tree::ParseTreeVisitor *v HogQLParser::TableFunctionExprContext* HogQLParser::tableFunctionExpr() { TableFunctionExprContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 136, HogQLParser::RuleTableFunctionExpr); + enterRule(_localctx, 138, HogQLParser::RuleTableFunctionExpr); size_t _la = 0; #if __cplusplus > 201703L @@ -10285,11 +10352,11 @@ HogQLParser::TableFunctionExprContext* HogQLParser::tableFunctionExpr() { }); try { enterOuterAlt(_localctx, 1); - setState(1204); + setState(1212); identifier(); - setState(1205); + setState(1213); match(HogQLParser::LPAREN); - setState(1207); + setState(1215); _errHandler->sync(this); _la = _input->LA(1); @@ -10297,10 +10364,10 @@ HogQLParser::TableFunctionExprContext* HogQLParser::tableFunctionExpr() { ((1ULL << _la) & -36169677449216002) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 64)) & 723944289947615231) != 0) || ((((_la - 131) & ~ 0x3fULL) == 0) && ((1ULL << (_la - 131)) & 18455) != 0)) { - setState(1206); + setState(1214); tableArgList(); } - setState(1209); + setState(1217); match(HogQLParser::RPAREN); } @@ -10346,7 +10413,7 @@ std::any HogQLParser::TableIdentifierContext::accept(tree::ParseTreeVisitor *vis HogQLParser::TableIdentifierContext* HogQLParser::tableIdentifier() { TableIdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 138, HogQLParser::RuleTableIdentifier); + enterRule(_localctx, 140, HogQLParser::RuleTableIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -10357,14 +10424,14 @@ HogQLParser::TableIdentifierContext* HogQLParser::tableIdentifier() { }); try { enterOuterAlt(_localctx, 1); - setState(1214); + setState(1222); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 153, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 154, _ctx)) { case 1: { - setState(1211); + setState(1219); databaseIdentifier(); - setState(1212); + setState(1220); match(HogQLParser::DOT); break; } @@ -10372,7 +10439,7 @@ HogQLParser::TableIdentifierContext* HogQLParser::tableIdentifier() { default: break; } - setState(1216); + setState(1224); identifier(); } @@ -10422,7 +10489,7 @@ std::any HogQLParser::TableArgListContext::accept(tree::ParseTreeVisitor *visito HogQLParser::TableArgListContext* HogQLParser::tableArgList() { TableArgListContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 140, HogQLParser::RuleTableArgList); + enterRule(_localctx, 142, HogQLParser::RuleTableArgList); size_t _la = 0; #if __cplusplus > 201703L @@ -10435,28 +10502,28 @@ HogQLParser::TableArgListContext* HogQLParser::tableArgList() { try { size_t alt; enterOuterAlt(_localctx, 1); - setState(1218); + setState(1226); columnExpr(0); - setState(1223); + setState(1231); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 154, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 155, _ctx); while (alt != 2 && alt != atn::ATN::INVALID_ALT_NUMBER) { if (alt == 1) { - setState(1219); + setState(1227); match(HogQLParser::COMMA); - setState(1220); + setState(1228); columnExpr(0); } - setState(1225); + setState(1233); _errHandler->sync(this); - alt = getInterpreter()->adaptivePredict(_input, 154, _ctx); + alt = getInterpreter()->adaptivePredict(_input, 155, _ctx); } - setState(1227); + setState(1235); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::COMMA) { - setState(1226); + setState(1234); match(HogQLParser::COMMA); } @@ -10495,7 +10562,7 @@ std::any HogQLParser::DatabaseIdentifierContext::accept(tree::ParseTreeVisitor * HogQLParser::DatabaseIdentifierContext* HogQLParser::databaseIdentifier() { DatabaseIdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 142, HogQLParser::RuleDatabaseIdentifier); + enterRule(_localctx, 144, HogQLParser::RuleDatabaseIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -10506,7 +10573,7 @@ HogQLParser::DatabaseIdentifierContext* HogQLParser::databaseIdentifier() { }); try { enterOuterAlt(_localctx, 1); - setState(1229); + setState(1237); identifier(); } @@ -10560,7 +10627,7 @@ std::any HogQLParser::FloatingLiteralContext::accept(tree::ParseTreeVisitor *vis HogQLParser::FloatingLiteralContext* HogQLParser::floatingLiteral() { FloatingLiteralContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 144, HogQLParser::RuleFloatingLiteral); + enterRule(_localctx, 146, HogQLParser::RuleFloatingLiteral); size_t _la = 0; #if __cplusplus > 201703L @@ -10571,21 +10638,21 @@ HogQLParser::FloatingLiteralContext* HogQLParser::floatingLiteral() { exitRule(); }); try { - setState(1239); + setState(1247); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::FLOATING_LITERAL: { enterOuterAlt(_localctx, 1); - setState(1231); + setState(1239); match(HogQLParser::FLOATING_LITERAL); break; } case HogQLParser::DOT: { enterOuterAlt(_localctx, 2); - setState(1232); + setState(1240); match(HogQLParser::DOT); - setState(1233); + setState(1241); _la = _input->LA(1); if (!(_la == HogQLParser::OCTAL_LITERAL @@ -10601,16 +10668,16 @@ HogQLParser::FloatingLiteralContext* HogQLParser::floatingLiteral() { case HogQLParser::DECIMAL_LITERAL: { enterOuterAlt(_localctx, 3); - setState(1234); + setState(1242); match(HogQLParser::DECIMAL_LITERAL); - setState(1235); + setState(1243); match(HogQLParser::DOT); - setState(1237); + setState(1245); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 156, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 157, _ctx)) { case 1: { - setState(1236); + setState(1244); _la = _input->LA(1); if (!(_la == HogQLParser::OCTAL_LITERAL @@ -10697,7 +10764,7 @@ std::any HogQLParser::NumberLiteralContext::accept(tree::ParseTreeVisitor *visit HogQLParser::NumberLiteralContext* HogQLParser::numberLiteral() { NumberLiteralContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 146, HogQLParser::RuleNumberLiteral); + enterRule(_localctx, 148, HogQLParser::RuleNumberLiteral); size_t _la = 0; #if __cplusplus > 201703L @@ -10709,14 +10776,14 @@ HogQLParser::NumberLiteralContext* HogQLParser::numberLiteral() { }); try { enterOuterAlt(_localctx, 1); - setState(1242); + setState(1250); _errHandler->sync(this); _la = _input->LA(1); if (_la == HogQLParser::DASH || _la == HogQLParser::PLUS) { - setState(1241); + setState(1249); _la = _input->LA(1); if (!(_la == HogQLParser::DASH @@ -10728,41 +10795,41 @@ HogQLParser::NumberLiteralContext* HogQLParser::numberLiteral() { consume(); } } - setState(1250); + setState(1258); _errHandler->sync(this); - switch (getInterpreter()->adaptivePredict(_input, 159, _ctx)) { + switch (getInterpreter()->adaptivePredict(_input, 160, _ctx)) { case 1: { - setState(1244); + setState(1252); floatingLiteral(); break; } case 2: { - setState(1245); + setState(1253); match(HogQLParser::OCTAL_LITERAL); break; } case 3: { - setState(1246); + setState(1254); match(HogQLParser::DECIMAL_LITERAL); break; } case 4: { - setState(1247); + setState(1255); match(HogQLParser::HEXADECIMAL_LITERAL); break; } case 5: { - setState(1248); + setState(1256); match(HogQLParser::INF); break; } case 6: { - setState(1249); + setState(1257); match(HogQLParser::NAN_SQL); break; } @@ -10814,7 +10881,7 @@ std::any HogQLParser::LiteralContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::LiteralContext* HogQLParser::literal() { LiteralContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 148, HogQLParser::RuleLiteral); + enterRule(_localctx, 150, HogQLParser::RuleLiteral); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -10824,7 +10891,7 @@ HogQLParser::LiteralContext* HogQLParser::literal() { exitRule(); }); try { - setState(1255); + setState(1263); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::INF: @@ -10837,21 +10904,21 @@ HogQLParser::LiteralContext* HogQLParser::literal() { case HogQLParser::DOT: case HogQLParser::PLUS: { enterOuterAlt(_localctx, 1); - setState(1252); + setState(1260); numberLiteral(); break; } case HogQLParser::STRING_LITERAL: { enterOuterAlt(_localctx, 2); - setState(1253); + setState(1261); match(HogQLParser::STRING_LITERAL); break; } case HogQLParser::NULL_SQL: { enterOuterAlt(_localctx, 3); - setState(1254); + setState(1262); match(HogQLParser::NULL_SQL); break; } @@ -10923,7 +10990,7 @@ std::any HogQLParser::IntervalContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::IntervalContext* HogQLParser::interval() { IntervalContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 150, HogQLParser::RuleInterval); + enterRule(_localctx, 152, HogQLParser::RuleInterval); size_t _la = 0; #if __cplusplus > 201703L @@ -10935,7 +11002,7 @@ HogQLParser::IntervalContext* HogQLParser::interval() { }); try { enterOuterAlt(_localctx, 1); - setState(1257); + setState(1265); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 864692227968860160) != 0) || ((((_la - 73) & ~ 0x3fULL) == 0) && @@ -11318,7 +11385,7 @@ std::any HogQLParser::KeywordContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::KeywordContext* HogQLParser::keyword() { KeywordContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 152, HogQLParser::RuleKeyword); + enterRule(_localctx, 154, HogQLParser::RuleKeyword); size_t _la = 0; #if __cplusplus > 201703L @@ -11330,7 +11397,7 @@ HogQLParser::KeywordContext* HogQLParser::keyword() { }); try { enterOuterAlt(_localctx, 1); - setState(1259); + setState(1267); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & -6665504612824399874) != 0) || ((((_la - 64) & ~ 0x3fULL) == 0) && @@ -11389,7 +11456,7 @@ std::any HogQLParser::KeywordForAliasContext::accept(tree::ParseTreeVisitor *vis HogQLParser::KeywordForAliasContext* HogQLParser::keywordForAlias() { KeywordForAliasContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 154, HogQLParser::RuleKeywordForAlias); + enterRule(_localctx, 156, HogQLParser::RuleKeywordForAlias); size_t _la = 0; #if __cplusplus > 201703L @@ -11401,7 +11468,7 @@ HogQLParser::KeywordForAliasContext* HogQLParser::keywordForAlias() { }); try { enterOuterAlt(_localctx, 1); - setState(1261); + setState(1269); _la = _input->LA(1); if (!((((_la & ~ 0x3fULL) == 0) && ((1ULL << _la) & 2254000985473024) != 0))) { @@ -11451,7 +11518,7 @@ std::any HogQLParser::AliasContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::AliasContext* HogQLParser::alias() { AliasContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 156, HogQLParser::RuleAlias); + enterRule(_localctx, 158, HogQLParser::RuleAlias); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11461,12 +11528,12 @@ HogQLParser::AliasContext* HogQLParser::alias() { exitRule(); }); try { - setState(1265); + setState(1273); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::IDENTIFIER: { enterOuterAlt(_localctx, 1); - setState(1263); + setState(1271); match(HogQLParser::IDENTIFIER); break; } @@ -11476,7 +11543,7 @@ HogQLParser::AliasContext* HogQLParser::alias() { case HogQLParser::ID: case HogQLParser::KEY: { enterOuterAlt(_localctx, 2); - setState(1264); + setState(1272); keywordForAlias(); break; } @@ -11528,7 +11595,7 @@ std::any HogQLParser::IdentifierContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::IdentifierContext* HogQLParser::identifier() { IdentifierContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 158, HogQLParser::RuleIdentifier); + enterRule(_localctx, 160, HogQLParser::RuleIdentifier); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11538,12 +11605,12 @@ HogQLParser::IdentifierContext* HogQLParser::identifier() { exitRule(); }); try { - setState(1270); + setState(1278); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::IDENTIFIER: { enterOuterAlt(_localctx, 1); - setState(1267); + setState(1275); match(HogQLParser::IDENTIFIER); break; } @@ -11557,7 +11624,7 @@ HogQLParser::IdentifierContext* HogQLParser::identifier() { case HogQLParser::WEEK: case HogQLParser::YEAR: { enterOuterAlt(_localctx, 2); - setState(1268); + setState(1276); interval(); break; } @@ -11648,7 +11715,7 @@ HogQLParser::IdentifierContext* HogQLParser::identifier() { case HogQLParser::WINDOW: case HogQLParser::WITH: { enterOuterAlt(_localctx, 3); - setState(1269); + setState(1277); keyword(); break; } @@ -11700,7 +11767,7 @@ std::any HogQLParser::EnumValueContext::accept(tree::ParseTreeVisitor *visitor) HogQLParser::EnumValueContext* HogQLParser::enumValue() { EnumValueContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 160, HogQLParser::RuleEnumValue); + enterRule(_localctx, 162, HogQLParser::RuleEnumValue); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11711,11 +11778,11 @@ HogQLParser::EnumValueContext* HogQLParser::enumValue() { }); try { enterOuterAlt(_localctx, 1); - setState(1272); + setState(1280); string(); - setState(1273); + setState(1281); match(HogQLParser::EQ_SINGLE); - setState(1274); + setState(1282); numberLiteral(); } @@ -11761,7 +11828,7 @@ std::any HogQLParser::PlaceholderContext::accept(tree::ParseTreeVisitor *visitor HogQLParser::PlaceholderContext* HogQLParser::placeholder() { PlaceholderContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 162, HogQLParser::RulePlaceholder); + enterRule(_localctx, 164, HogQLParser::RulePlaceholder); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11772,11 +11839,11 @@ HogQLParser::PlaceholderContext* HogQLParser::placeholder() { }); try { enterOuterAlt(_localctx, 1); - setState(1276); + setState(1284); match(HogQLParser::LBRACE); - setState(1277); + setState(1285); columnExpr(0); - setState(1278); + setState(1286); match(HogQLParser::RBRACE); } @@ -11818,7 +11885,7 @@ std::any HogQLParser::StringContext::accept(tree::ParseTreeVisitor *visitor) { HogQLParser::StringContext* HogQLParser::string() { StringContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 164, HogQLParser::RuleString); + enterRule(_localctx, 166, HogQLParser::RuleString); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11828,19 +11895,19 @@ HogQLParser::StringContext* HogQLParser::string() { exitRule(); }); try { - setState(1282); + setState(1290); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::STRING_LITERAL: { enterOuterAlt(_localctx, 1); - setState(1280); + setState(1288); match(HogQLParser::STRING_LITERAL); break; } case HogQLParser::QUOTE_SINGLE_TEMPLATE: { enterOuterAlt(_localctx, 2); - setState(1281); + setState(1289); templateString(); break; } @@ -11896,7 +11963,7 @@ std::any HogQLParser::TemplateStringContext::accept(tree::ParseTreeVisitor *visi HogQLParser::TemplateStringContext* HogQLParser::templateString() { TemplateStringContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 166, HogQLParser::RuleTemplateString); + enterRule(_localctx, 168, HogQLParser::RuleTemplateString); size_t _la = 0; #if __cplusplus > 201703L @@ -11908,21 +11975,21 @@ HogQLParser::TemplateStringContext* HogQLParser::templateString() { }); try { enterOuterAlt(_localctx, 1); - setState(1284); + setState(1292); match(HogQLParser::QUOTE_SINGLE_TEMPLATE); - setState(1288); + setState(1296); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::STRING_TEXT || _la == HogQLParser::STRING_ESCAPE_TRIGGER) { - setState(1285); + setState(1293); stringContents(); - setState(1290); + setState(1298); _errHandler->sync(this); _la = _input->LA(1); } - setState(1291); + setState(1299); match(HogQLParser::QUOTE_SINGLE); } @@ -11972,7 +12039,7 @@ std::any HogQLParser::StringContentsContext::accept(tree::ParseTreeVisitor *visi HogQLParser::StringContentsContext* HogQLParser::stringContents() { StringContentsContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 168, HogQLParser::RuleStringContents); + enterRule(_localctx, 170, HogQLParser::RuleStringContents); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -11982,23 +12049,23 @@ HogQLParser::StringContentsContext* HogQLParser::stringContents() { exitRule(); }); try { - setState(1298); + setState(1306); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::STRING_ESCAPE_TRIGGER: { enterOuterAlt(_localctx, 1); - setState(1293); + setState(1301); match(HogQLParser::STRING_ESCAPE_TRIGGER); - setState(1294); + setState(1302); columnExpr(0); - setState(1295); + setState(1303); match(HogQLParser::RBRACE); break; } case HogQLParser::STRING_TEXT: { enterOuterAlt(_localctx, 2); - setState(1297); + setState(1305); match(HogQLParser::STRING_TEXT); break; } @@ -12054,7 +12121,7 @@ std::any HogQLParser::FullTemplateStringContext::accept(tree::ParseTreeVisitor * HogQLParser::FullTemplateStringContext* HogQLParser::fullTemplateString() { FullTemplateStringContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 170, HogQLParser::RuleFullTemplateString); + enterRule(_localctx, 172, HogQLParser::RuleFullTemplateString); size_t _la = 0; #if __cplusplus > 201703L @@ -12066,21 +12133,21 @@ HogQLParser::FullTemplateStringContext* HogQLParser::fullTemplateString() { }); try { enterOuterAlt(_localctx, 1); - setState(1300); + setState(1308); match(HogQLParser::QUOTE_SINGLE_TEMPLATE_FULL); - setState(1304); + setState(1312); _errHandler->sync(this); _la = _input->LA(1); while (_la == HogQLParser::FULL_STRING_TEXT || _la == HogQLParser::FULL_STRING_ESCAPE_TRIGGER) { - setState(1301); + setState(1309); stringContentsFull(); - setState(1306); + setState(1314); _errHandler->sync(this); _la = _input->LA(1); } - setState(1307); + setState(1315); match(HogQLParser::EOF); } @@ -12130,7 +12197,7 @@ std::any HogQLParser::StringContentsFullContext::accept(tree::ParseTreeVisitor * HogQLParser::StringContentsFullContext* HogQLParser::stringContentsFull() { StringContentsFullContext *_localctx = _tracker.createInstance(_ctx, getState()); - enterRule(_localctx, 172, HogQLParser::RuleStringContentsFull); + enterRule(_localctx, 174, HogQLParser::RuleStringContentsFull); #if __cplusplus > 201703L auto onExit = finally([=, this] { @@ -12140,23 +12207,23 @@ HogQLParser::StringContentsFullContext* HogQLParser::stringContentsFull() { exitRule(); }); try { - setState(1314); + setState(1322); _errHandler->sync(this); switch (_input->LA(1)) { case HogQLParser::FULL_STRING_ESCAPE_TRIGGER: { enterOuterAlt(_localctx, 1); - setState(1309); + setState(1317); match(HogQLParser::FULL_STRING_ESCAPE_TRIGGER); - setState(1310); + setState(1318); columnExpr(0); - setState(1311); + setState(1319); match(HogQLParser::RBRACE); break; } case HogQLParser::FULL_STRING_TEXT: { enterOuterAlt(_localctx, 2); - setState(1313); + setState(1321); match(HogQLParser::FULL_STRING_TEXT); break; } @@ -12179,7 +12246,7 @@ bool HogQLParser::sempred(RuleContext *context, size_t ruleIndex, size_t predica switch (ruleIndex) { case 40: return joinExprSempred(antlrcpp::downCast(context), predicateIndex); case 59: return columnExprSempred(antlrcpp::downCast(context), predicateIndex); - case 67: return tableExprSempred(antlrcpp::downCast(context), predicateIndex); + case 68: return tableExprSempred(antlrcpp::downCast(context), predicateIndex); default: break; diff --git a/common/hogql_parser/HogQLParser.h b/common/hogql_parser/HogQLParser.h index 4a3ff86cef0c0..6b9c5a46bc501 100644 --- a/common/hogql_parser/HogQLParser.h +++ b/common/hogql_parser/HogQLParser.h @@ -63,15 +63,15 @@ class HogQLParser : public antlr4::Parser { RuleWindowExpr = 50, RuleWinPartitionByClause = 51, RuleWinOrderByClause = 52, RuleWinFrameClause = 53, RuleWinFrameExtend = 54, RuleWinFrameBound = 55, RuleExpr = 56, RuleColumnTypeExpr = 57, RuleColumnExprList = 58, RuleColumnExpr = 59, - RuleColumnLambdaExpr = 60, RuleHogqlxTagElement = 61, RuleHogqlxTagAttribute = 62, - RuleWithExprList = 63, RuleWithExpr = 64, RuleColumnIdentifier = 65, - RuleNestedIdentifier = 66, RuleTableExpr = 67, RuleTableFunctionExpr = 68, - RuleTableIdentifier = 69, RuleTableArgList = 70, RuleDatabaseIdentifier = 71, - RuleFloatingLiteral = 72, RuleNumberLiteral = 73, RuleLiteral = 74, - RuleInterval = 75, RuleKeyword = 76, RuleKeywordForAlias = 77, RuleAlias = 78, - RuleIdentifier = 79, RuleEnumValue = 80, RulePlaceholder = 81, RuleString = 82, - RuleTemplateString = 83, RuleStringContents = 84, RuleFullTemplateString = 85, - RuleStringContentsFull = 86 + RuleColumnLambdaExpr = 60, RuleHogqlxChildElement = 61, RuleHogqlxTagElement = 62, + RuleHogqlxTagAttribute = 63, RuleWithExprList = 64, RuleWithExpr = 65, + RuleColumnIdentifier = 66, RuleNestedIdentifier = 67, RuleTableExpr = 68, + RuleTableFunctionExpr = 69, RuleTableIdentifier = 70, RuleTableArgList = 71, + RuleDatabaseIdentifier = 72, RuleFloatingLiteral = 73, RuleNumberLiteral = 74, + RuleLiteral = 75, RuleInterval = 76, RuleKeyword = 77, RuleKeywordForAlias = 78, + RuleAlias = 79, RuleIdentifier = 80, RuleEnumValue = 81, RulePlaceholder = 82, + RuleString = 83, RuleTemplateString = 84, RuleStringContents = 85, RuleFullTemplateString = 86, + RuleStringContentsFull = 87 }; explicit HogQLParser(antlr4::TokenStream *input); @@ -152,6 +152,7 @@ class HogQLParser : public antlr4::Parser { class ColumnExprListContext; class ColumnExprContext; class ColumnLambdaExprContext; + class HogqlxChildElementContext; class HogqlxTagElementContext; class HogqlxTagAttributeContext; class WithExprListContext; @@ -1912,6 +1913,22 @@ class HogQLParser : public antlr4::Parser { ColumnLambdaExprContext* columnLambdaExpr(); + class HogqlxChildElementContext : public antlr4::ParserRuleContext { + public: + HogqlxChildElementContext(antlr4::ParserRuleContext *parent, size_t invokingState); + virtual size_t getRuleIndex() const override; + HogqlxTagElementContext *hogqlxTagElement(); + antlr4::tree::TerminalNode *LBRACE(); + ColumnExprContext *columnExpr(); + antlr4::tree::TerminalNode *RBRACE(); + + + virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override; + + }; + + HogqlxChildElementContext* hogqlxChildElement(); + class HogqlxTagElementContext : public antlr4::ParserRuleContext { public: HogqlxTagElementContext(antlr4::ParserRuleContext *parent, size_t invokingState); @@ -1952,10 +1969,8 @@ class HogQLParser : public antlr4::Parser { antlr4::tree::TerminalNode *SLASH(); std::vector hogqlxTagAttribute(); HogqlxTagAttributeContext* hogqlxTagAttribute(size_t i); - HogqlxTagElementContext *hogqlxTagElement(); - antlr4::tree::TerminalNode *LBRACE(); - ColumnExprContext *columnExpr(); - antlr4::tree::TerminalNode *RBRACE(); + std::vector hogqlxChildElement(); + HogqlxChildElementContext* hogqlxChildElement(size_t i); virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override; }; diff --git a/common/hogql_parser/HogQLParser.interp b/common/hogql_parser/HogQLParser.interp index 1d049b6b744a8..996d438b4aa59 100644 --- a/common/hogql_parser/HogQLParser.interp +++ b/common/hogql_parser/HogQLParser.interp @@ -390,6 +390,7 @@ columnTypeExpr columnExprList columnExpr columnLambdaExpr +hogqlxChildElement hogqlxTagElement hogqlxTagAttribute withExprList @@ -419,4 +420,4 @@ stringContentsFull atn: -[4, 1, 162, 1317, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 1, 0, 5, 0, 176, 8, 0, 10, 0, 12, 0, 179, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 185, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 194, 8, 3, 1, 4, 1, 4, 1, 4, 5, 4, 199, 8, 4, 10, 4, 12, 4, 202, 9, 4, 1, 4, 3, 4, 205, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 219, 8, 5, 1, 6, 1, 6, 3, 6, 223, 8, 6, 1, 6, 3, 6, 226, 8, 6, 1, 7, 1, 7, 3, 7, 230, 8, 7, 1, 7, 3, 7, 233, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 240, 8, 8, 1, 8, 1, 8, 3, 8, 244, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 5, 9, 251, 8, 9, 10, 9, 12, 9, 254, 9, 9, 1, 9, 1, 9, 3, 9, 258, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 267, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 275, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 282, 8, 12, 1, 12, 1, 12, 3, 12, 286, 8, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 292, 8, 12, 1, 12, 1, 12, 1, 12, 3, 12, 297, 8, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 305, 8, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 312, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 318, 8, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 3, 16, 330, 8, 16, 1, 17, 1, 17, 1, 18, 1, 18, 5, 18, 336, 8, 18, 10, 18, 12, 18, 339, 9, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 5, 20, 350, 8, 20, 10, 20, 12, 20, 353, 9, 20, 1, 20, 3, 20, 356, 8, 20, 1, 21, 1, 21, 1, 21, 3, 21, 361, 8, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 371, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 381, 8, 23, 1, 23, 1, 23, 1, 24, 1, 24, 5, 24, 387, 8, 24, 10, 24, 12, 24, 390, 9, 24, 1, 25, 3, 25, 393, 8, 25, 1, 25, 1, 25, 3, 25, 397, 8, 25, 1, 25, 3, 25, 400, 8, 25, 1, 25, 1, 25, 3, 25, 404, 8, 25, 1, 25, 3, 25, 407, 8, 25, 1, 25, 3, 25, 410, 8, 25, 1, 25, 3, 25, 413, 8, 25, 1, 25, 3, 25, 416, 8, 25, 1, 25, 1, 25, 3, 25, 420, 8, 25, 1, 25, 1, 25, 3, 25, 424, 8, 25, 1, 25, 3, 25, 427, 8, 25, 1, 25, 3, 25, 430, 8, 25, 1, 25, 3, 25, 433, 8, 25, 1, 25, 1, 25, 3, 25, 437, 8, 25, 1, 25, 3, 25, 440, 8, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 449, 8, 27, 1, 28, 1, 28, 1, 28, 1, 29, 3, 29, 455, 8, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 474, 8, 30, 10, 30, 12, 30, 477, 9, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 493, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 510, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 516, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 522, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 533, 8, 37, 3, 37, 535, 8, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 3, 40, 546, 8, 40, 1, 40, 3, 40, 549, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 555, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 563, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 5, 40, 569, 8, 40, 10, 40, 12, 40, 572, 9, 40, 1, 41, 3, 41, 575, 8, 41, 1, 41, 1, 41, 1, 41, 3, 41, 580, 8, 41, 1, 41, 3, 41, 583, 8, 41, 1, 41, 3, 41, 586, 8, 41, 1, 41, 1, 41, 3, 41, 590, 8, 41, 1, 41, 1, 41, 3, 41, 594, 8, 41, 1, 41, 3, 41, 597, 8, 41, 3, 41, 599, 8, 41, 1, 41, 3, 41, 602, 8, 41, 1, 41, 1, 41, 3, 41, 606, 8, 41, 1, 41, 1, 41, 3, 41, 610, 8, 41, 1, 41, 3, 41, 613, 8, 41, 3, 41, 615, 8, 41, 3, 41, 617, 8, 41, 1, 42, 1, 42, 1, 42, 3, 42, 622, 8, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 633, 8, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 639, 8, 44, 1, 45, 1, 45, 1, 45, 5, 45, 644, 8, 45, 10, 45, 12, 45, 647, 9, 45, 1, 46, 1, 46, 3, 46, 651, 8, 46, 1, 46, 1, 46, 3, 46, 655, 8, 46, 1, 46, 1, 46, 3, 46, 659, 8, 46, 1, 47, 1, 47, 1, 47, 1, 47, 3, 47, 665, 8, 47, 3, 47, 667, 8, 47, 1, 48, 1, 48, 1, 48, 5, 48, 672, 8, 48, 10, 48, 12, 48, 675, 9, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 3, 50, 682, 8, 50, 1, 50, 3, 50, 685, 8, 50, 1, 50, 3, 50, 688, 8, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 707, 8, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 721, 8, 55, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 735, 8, 57, 10, 57, 12, 57, 738, 9, 57, 1, 57, 3, 57, 741, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 750, 8, 57, 10, 57, 12, 57, 753, 9, 57, 1, 57, 3, 57, 756, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 765, 8, 57, 10, 57, 12, 57, 768, 9, 57, 1, 57, 3, 57, 771, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 3, 57, 778, 8, 57, 1, 57, 1, 57, 3, 57, 782, 8, 57, 1, 58, 1, 58, 1, 58, 5, 58, 787, 8, 58, 10, 58, 12, 58, 790, 9, 58, 1, 58, 3, 58, 793, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 798, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 4, 59, 805, 8, 59, 11, 59, 12, 59, 806, 1, 59, 1, 59, 3, 59, 811, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 837, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 854, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 860, 8, 59, 1, 59, 3, 59, 863, 8, 59, 1, 59, 3, 59, 866, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 876, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 882, 8, 59, 1, 59, 3, 59, 885, 8, 59, 1, 59, 3, 59, 888, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 896, 8, 59, 1, 59, 3, 59, 899, 8, 59, 1, 59, 1, 59, 3, 59, 903, 8, 59, 1, 59, 3, 59, 906, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 920, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 937, 8, 59, 1, 59, 1, 59, 1, 59, 3, 59, 942, 8, 59, 1, 59, 1, 59, 1, 59, 3, 59, 947, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 953, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 960, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 972, 8, 59, 1, 59, 1, 59, 3, 59, 976, 8, 59, 1, 59, 3, 59, 979, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 988, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1002, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1018, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1047, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1055, 8, 59, 5, 59, 1057, 8, 59, 10, 59, 12, 59, 1060, 9, 59, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 1066, 8, 60, 10, 60, 12, 60, 1069, 9, 60, 1, 60, 3, 60, 1072, 8, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 1079, 8, 60, 10, 60, 12, 60, 1082, 9, 60, 1, 60, 3, 60, 1085, 8, 60, 1, 60, 1, 60, 3, 60, 1089, 8, 60, 1, 60, 1, 60, 1, 60, 3, 60, 1094, 8, 60, 1, 61, 1, 61, 1, 61, 5, 61, 1099, 8, 61, 10, 61, 12, 61, 1102, 9, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 5, 61, 1110, 8, 61, 10, 61, 12, 61, 1113, 9, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 1121, 8, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 1128, 8, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 3, 62, 1141, 8, 62, 1, 63, 1, 63, 1, 63, 5, 63, 1146, 8, 63, 10, 63, 12, 63, 1149, 9, 63, 1, 63, 3, 63, 1152, 8, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 3, 64, 1164, 8, 64, 1, 65, 1, 65, 1, 65, 1, 65, 3, 65, 1170, 8, 65, 1, 65, 3, 65, 1173, 8, 65, 1, 66, 1, 66, 1, 66, 5, 66, 1178, 8, 66, 10, 66, 12, 66, 1181, 9, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 1192, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 1198, 8, 67, 5, 67, 1200, 8, 67, 10, 67, 12, 67, 1203, 9, 67, 1, 68, 1, 68, 1, 68, 3, 68, 1208, 8, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 3, 69, 1215, 8, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 5, 70, 1222, 8, 70, 10, 70, 12, 70, 1225, 9, 70, 1, 70, 3, 70, 1228, 8, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 3, 72, 1238, 8, 72, 3, 72, 1240, 8, 72, 1, 73, 3, 73, 1243, 8, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 3, 73, 1251, 8, 73, 1, 74, 1, 74, 1, 74, 3, 74, 1256, 8, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 3, 78, 1266, 8, 78, 1, 79, 1, 79, 1, 79, 3, 79, 1271, 8, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 3, 82, 1283, 8, 82, 1, 83, 1, 83, 5, 83, 1287, 8, 83, 10, 83, 12, 83, 1290, 9, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 3, 84, 1299, 8, 84, 1, 85, 1, 85, 5, 85, 1303, 8, 85, 10, 85, 12, 85, 1306, 9, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 3, 86, 1315, 8, 86, 1, 86, 0, 3, 80, 118, 134, 87, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 0, 17, 2, 0, 32, 32, 37, 37, 2, 0, 18, 18, 77, 77, 2, 0, 46, 46, 54, 54, 3, 0, 1, 1, 4, 4, 8, 8, 4, 0, 1, 1, 3, 4, 8, 8, 83, 83, 2, 0, 54, 54, 76, 76, 2, 0, 1, 1, 4, 4, 2, 0, 7, 7, 22, 23, 2, 0, 31, 31, 52, 52, 2, 0, 74, 74, 79, 79, 3, 0, 10, 10, 53, 53, 93, 93, 2, 0, 43, 43, 56, 56, 1, 0, 110, 111, 2, 0, 121, 121, 142, 142, 7, 0, 21, 21, 40, 40, 58, 59, 73, 73, 81, 81, 100, 100, 106, 106, 19, 0, 1, 13, 15, 20, 22, 26, 28, 29, 31, 31, 33, 36, 38, 39, 41, 44, 46, 46, 48, 54, 56, 57, 61, 61, 63, 72, 74, 80, 82, 86, 88, 95, 97, 99, 101, 102, 104, 105, 4, 0, 20, 20, 31, 31, 41, 41, 51, 51, 1493, 0, 177, 1, 0, 0, 0, 2, 184, 1, 0, 0, 0, 4, 186, 1, 0, 0, 0, 6, 188, 1, 0, 0, 0, 8, 195, 1, 0, 0, 0, 10, 218, 1, 0, 0, 0, 12, 220, 1, 0, 0, 0, 14, 227, 1, 0, 0, 0, 16, 234, 1, 0, 0, 0, 18, 247, 1, 0, 0, 0, 20, 259, 1, 0, 0, 0, 22, 268, 1, 0, 0, 0, 24, 276, 1, 0, 0, 0, 26, 298, 1, 0, 0, 0, 28, 313, 1, 0, 0, 0, 30, 322, 1, 0, 0, 0, 32, 327, 1, 0, 0, 0, 34, 331, 1, 0, 0, 0, 36, 333, 1, 0, 0, 0, 38, 342, 1, 0, 0, 0, 40, 346, 1, 0, 0, 0, 42, 360, 1, 0, 0, 0, 44, 370, 1, 0, 0, 0, 46, 380, 1, 0, 0, 0, 48, 384, 1, 0, 0, 0, 50, 392, 1, 0, 0, 0, 52, 441, 1, 0, 0, 0, 54, 444, 1, 0, 0, 0, 56, 450, 1, 0, 0, 0, 58, 454, 1, 0, 0, 0, 60, 460, 1, 0, 0, 0, 62, 478, 1, 0, 0, 0, 64, 481, 1, 0, 0, 0, 66, 484, 1, 0, 0, 0, 68, 494, 1, 0, 0, 0, 70, 497, 1, 0, 0, 0, 72, 501, 1, 0, 0, 0, 74, 534, 1, 0, 0, 0, 76, 536, 1, 0, 0, 0, 78, 539, 1, 0, 0, 0, 80, 554, 1, 0, 0, 0, 82, 616, 1, 0, 0, 0, 84, 621, 1, 0, 0, 0, 86, 632, 1, 0, 0, 0, 88, 634, 1, 0, 0, 0, 90, 640, 1, 0, 0, 0, 92, 648, 1, 0, 0, 0, 94, 666, 1, 0, 0, 0, 96, 668, 1, 0, 0, 0, 98, 676, 1, 0, 0, 0, 100, 681, 1, 0, 0, 0, 102, 689, 1, 0, 0, 0, 104, 693, 1, 0, 0, 0, 106, 697, 1, 0, 0, 0, 108, 706, 1, 0, 0, 0, 110, 720, 1, 0, 0, 0, 112, 722, 1, 0, 0, 0, 114, 781, 1, 0, 0, 0, 116, 783, 1, 0, 0, 0, 118, 946, 1, 0, 0, 0, 120, 1088, 1, 0, 0, 0, 122, 1127, 1, 0, 0, 0, 124, 1140, 1, 0, 0, 0, 126, 1142, 1, 0, 0, 0, 128, 1163, 1, 0, 0, 0, 130, 1172, 1, 0, 0, 0, 132, 1174, 1, 0, 0, 0, 134, 1191, 1, 0, 0, 0, 136, 1204, 1, 0, 0, 0, 138, 1214, 1, 0, 0, 0, 140, 1218, 1, 0, 0, 0, 142, 1229, 1, 0, 0, 0, 144, 1239, 1, 0, 0, 0, 146, 1242, 1, 0, 0, 0, 148, 1255, 1, 0, 0, 0, 150, 1257, 1, 0, 0, 0, 152, 1259, 1, 0, 0, 0, 154, 1261, 1, 0, 0, 0, 156, 1265, 1, 0, 0, 0, 158, 1270, 1, 0, 0, 0, 160, 1272, 1, 0, 0, 0, 162, 1276, 1, 0, 0, 0, 164, 1282, 1, 0, 0, 0, 166, 1284, 1, 0, 0, 0, 168, 1298, 1, 0, 0, 0, 170, 1300, 1, 0, 0, 0, 172, 1314, 1, 0, 0, 0, 174, 176, 3, 2, 1, 0, 175, 174, 1, 0, 0, 0, 176, 179, 1, 0, 0, 0, 177, 175, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 180, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 180, 181, 5, 0, 0, 1, 181, 1, 1, 0, 0, 0, 182, 185, 3, 6, 3, 0, 183, 185, 3, 10, 5, 0, 184, 182, 1, 0, 0, 0, 184, 183, 1, 0, 0, 0, 185, 3, 1, 0, 0, 0, 186, 187, 3, 118, 59, 0, 187, 5, 1, 0, 0, 0, 188, 189, 5, 55, 0, 0, 189, 193, 3, 158, 79, 0, 190, 191, 5, 118, 0, 0, 191, 192, 5, 125, 0, 0, 192, 194, 3, 4, 2, 0, 193, 190, 1, 0, 0, 0, 193, 194, 1, 0, 0, 0, 194, 7, 1, 0, 0, 0, 195, 200, 3, 158, 79, 0, 196, 197, 5, 119, 0, 0, 197, 199, 3, 158, 79, 0, 198, 196, 1, 0, 0, 0, 199, 202, 1, 0, 0, 0, 200, 198, 1, 0, 0, 0, 200, 201, 1, 0, 0, 0, 201, 204, 1, 0, 0, 0, 202, 200, 1, 0, 0, 0, 203, 205, 5, 119, 0, 0, 204, 203, 1, 0, 0, 0, 204, 205, 1, 0, 0, 0, 205, 9, 1, 0, 0, 0, 206, 219, 3, 12, 6, 0, 207, 219, 3, 14, 7, 0, 208, 219, 3, 18, 9, 0, 209, 219, 3, 20, 10, 0, 210, 219, 3, 22, 11, 0, 211, 219, 3, 26, 13, 0, 212, 219, 3, 24, 12, 0, 213, 219, 3, 28, 14, 0, 214, 219, 3, 30, 15, 0, 215, 219, 3, 36, 18, 0, 216, 219, 3, 32, 16, 0, 217, 219, 3, 34, 17, 0, 218, 206, 1, 0, 0, 0, 218, 207, 1, 0, 0, 0, 218, 208, 1, 0, 0, 0, 218, 209, 1, 0, 0, 0, 218, 210, 1, 0, 0, 0, 218, 211, 1, 0, 0, 0, 218, 212, 1, 0, 0, 0, 218, 213, 1, 0, 0, 0, 218, 214, 1, 0, 0, 0, 218, 215, 1, 0, 0, 0, 218, 216, 1, 0, 0, 0, 218, 217, 1, 0, 0, 0, 219, 11, 1, 0, 0, 0, 220, 222, 5, 75, 0, 0, 221, 223, 3, 4, 2, 0, 222, 221, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 225, 1, 0, 0, 0, 224, 226, 5, 153, 0, 0, 225, 224, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 13, 1, 0, 0, 0, 227, 229, 5, 87, 0, 0, 228, 230, 3, 4, 2, 0, 229, 228, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 232, 1, 0, 0, 0, 231, 233, 5, 153, 0, 0, 232, 231, 1, 0, 0, 0, 232, 233, 1, 0, 0, 0, 233, 15, 1, 0, 0, 0, 234, 243, 5, 14, 0, 0, 235, 236, 5, 133, 0, 0, 236, 239, 3, 158, 79, 0, 237, 238, 5, 118, 0, 0, 238, 240, 3, 158, 79, 0, 239, 237, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 242, 5, 152, 0, 0, 242, 244, 1, 0, 0, 0, 243, 235, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 246, 3, 36, 18, 0, 246, 17, 1, 0, 0, 0, 247, 248, 5, 96, 0, 0, 248, 252, 3, 36, 18, 0, 249, 251, 3, 16, 8, 0, 250, 249, 1, 0, 0, 0, 251, 254, 1, 0, 0, 0, 252, 250, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0, 253, 257, 1, 0, 0, 0, 254, 252, 1, 0, 0, 0, 255, 256, 5, 30, 0, 0, 256, 258, 3, 36, 18, 0, 257, 255, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 19, 1, 0, 0, 0, 259, 260, 5, 42, 0, 0, 260, 261, 5, 133, 0, 0, 261, 262, 3, 4, 2, 0, 262, 263, 5, 152, 0, 0, 263, 266, 3, 10, 5, 0, 264, 265, 5, 25, 0, 0, 265, 267, 3, 10, 5, 0, 266, 264, 1, 0, 0, 0, 266, 267, 1, 0, 0, 0, 267, 21, 1, 0, 0, 0, 268, 269, 5, 103, 0, 0, 269, 270, 5, 133, 0, 0, 270, 271, 3, 4, 2, 0, 271, 272, 5, 152, 0, 0, 272, 274, 3, 10, 5, 0, 273, 275, 5, 153, 0, 0, 274, 273, 1, 0, 0, 0, 274, 275, 1, 0, 0, 0, 275, 23, 1, 0, 0, 0, 276, 277, 5, 34, 0, 0, 277, 281, 5, 133, 0, 0, 278, 282, 3, 6, 3, 0, 279, 282, 3, 30, 15, 0, 280, 282, 3, 4, 2, 0, 281, 278, 1, 0, 0, 0, 281, 279, 1, 0, 0, 0, 281, 280, 1, 0, 0, 0, 281, 282, 1, 0, 0, 0, 282, 283, 1, 0, 0, 0, 283, 285, 5, 153, 0, 0, 284, 286, 3, 4, 2, 0, 285, 284, 1, 0, 0, 0, 285, 286, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 291, 5, 153, 0, 0, 288, 292, 3, 6, 3, 0, 289, 292, 3, 30, 15, 0, 290, 292, 3, 4, 2, 0, 291, 288, 1, 0, 0, 0, 291, 289, 1, 0, 0, 0, 291, 290, 1, 0, 0, 0, 291, 292, 1, 0, 0, 0, 292, 293, 1, 0, 0, 0, 293, 294, 5, 152, 0, 0, 294, 296, 3, 10, 5, 0, 295, 297, 5, 153, 0, 0, 296, 295, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 25, 1, 0, 0, 0, 298, 299, 5, 34, 0, 0, 299, 300, 5, 133, 0, 0, 300, 301, 5, 55, 0, 0, 301, 304, 3, 158, 79, 0, 302, 303, 5, 119, 0, 0, 303, 305, 3, 158, 79, 0, 304, 302, 1, 0, 0, 0, 304, 305, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 307, 5, 44, 0, 0, 307, 308, 3, 4, 2, 0, 308, 309, 5, 152, 0, 0, 309, 311, 3, 10, 5, 0, 310, 312, 5, 153, 0, 0, 311, 310, 1, 0, 0, 0, 311, 312, 1, 0, 0, 0, 312, 27, 1, 0, 0, 0, 313, 314, 7, 0, 0, 0, 314, 315, 3, 158, 79, 0, 315, 317, 5, 133, 0, 0, 316, 318, 3, 8, 4, 0, 317, 316, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 319, 1, 0, 0, 0, 319, 320, 5, 152, 0, 0, 320, 321, 3, 36, 18, 0, 321, 29, 1, 0, 0, 0, 322, 323, 3, 4, 2, 0, 323, 324, 5, 118, 0, 0, 324, 325, 5, 125, 0, 0, 325, 326, 3, 4, 2, 0, 326, 31, 1, 0, 0, 0, 327, 329, 3, 4, 2, 0, 328, 330, 5, 153, 0, 0, 329, 328, 1, 0, 0, 0, 329, 330, 1, 0, 0, 0, 330, 33, 1, 0, 0, 0, 331, 332, 5, 153, 0, 0, 332, 35, 1, 0, 0, 0, 333, 337, 5, 131, 0, 0, 334, 336, 3, 2, 1, 0, 335, 334, 1, 0, 0, 0, 336, 339, 1, 0, 0, 0, 337, 335, 1, 0, 0, 0, 337, 338, 1, 0, 0, 0, 338, 340, 1, 0, 0, 0, 339, 337, 1, 0, 0, 0, 340, 341, 5, 150, 0, 0, 341, 37, 1, 0, 0, 0, 342, 343, 3, 4, 2, 0, 343, 344, 5, 118, 0, 0, 344, 345, 3, 4, 2, 0, 345, 39, 1, 0, 0, 0, 346, 351, 3, 38, 19, 0, 347, 348, 5, 119, 0, 0, 348, 350, 3, 38, 19, 0, 349, 347, 1, 0, 0, 0, 350, 353, 1, 0, 0, 0, 351, 349, 1, 0, 0, 0, 351, 352, 1, 0, 0, 0, 352, 355, 1, 0, 0, 0, 353, 351, 1, 0, 0, 0, 354, 356, 5, 119, 0, 0, 355, 354, 1, 0, 0, 0, 355, 356, 1, 0, 0, 0, 356, 41, 1, 0, 0, 0, 357, 361, 3, 48, 24, 0, 358, 361, 3, 50, 25, 0, 359, 361, 3, 122, 61, 0, 360, 357, 1, 0, 0, 0, 360, 358, 1, 0, 0, 0, 360, 359, 1, 0, 0, 0, 361, 362, 1, 0, 0, 0, 362, 363, 5, 0, 0, 1, 363, 43, 1, 0, 0, 0, 364, 371, 3, 50, 25, 0, 365, 366, 5, 133, 0, 0, 366, 367, 3, 48, 24, 0, 367, 368, 5, 152, 0, 0, 368, 371, 1, 0, 0, 0, 369, 371, 3, 162, 81, 0, 370, 364, 1, 0, 0, 0, 370, 365, 1, 0, 0, 0, 370, 369, 1, 0, 0, 0, 371, 45, 1, 0, 0, 0, 372, 381, 5, 27, 0, 0, 373, 374, 5, 98, 0, 0, 374, 381, 5, 1, 0, 0, 375, 376, 5, 98, 0, 0, 376, 381, 5, 24, 0, 0, 377, 381, 5, 47, 0, 0, 378, 379, 5, 47, 0, 0, 379, 381, 5, 24, 0, 0, 380, 372, 1, 0, 0, 0, 380, 373, 1, 0, 0, 0, 380, 375, 1, 0, 0, 0, 380, 377, 1, 0, 0, 0, 380, 378, 1, 0, 0, 0, 381, 382, 1, 0, 0, 0, 382, 383, 3, 44, 22, 0, 383, 47, 1, 0, 0, 0, 384, 388, 3, 44, 22, 0, 385, 387, 3, 46, 23, 0, 386, 385, 1, 0, 0, 0, 387, 390, 1, 0, 0, 0, 388, 386, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 49, 1, 0, 0, 0, 390, 388, 1, 0, 0, 0, 391, 393, 3, 52, 26, 0, 392, 391, 1, 0, 0, 0, 392, 393, 1, 0, 0, 0, 393, 394, 1, 0, 0, 0, 394, 396, 5, 82, 0, 0, 395, 397, 5, 24, 0, 0, 396, 395, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 399, 1, 0, 0, 0, 398, 400, 3, 54, 27, 0, 399, 398, 1, 0, 0, 0, 399, 400, 1, 0, 0, 0, 400, 401, 1, 0, 0, 0, 401, 403, 3, 116, 58, 0, 402, 404, 3, 56, 28, 0, 403, 402, 1, 0, 0, 0, 403, 404, 1, 0, 0, 0, 404, 406, 1, 0, 0, 0, 405, 407, 3, 58, 29, 0, 406, 405, 1, 0, 0, 0, 406, 407, 1, 0, 0, 0, 407, 409, 1, 0, 0, 0, 408, 410, 3, 62, 31, 0, 409, 408, 1, 0, 0, 0, 409, 410, 1, 0, 0, 0, 410, 412, 1, 0, 0, 0, 411, 413, 3, 64, 32, 0, 412, 411, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 415, 1, 0, 0, 0, 414, 416, 3, 66, 33, 0, 415, 414, 1, 0, 0, 0, 415, 416, 1, 0, 0, 0, 416, 419, 1, 0, 0, 0, 417, 418, 5, 105, 0, 0, 418, 420, 7, 1, 0, 0, 419, 417, 1, 0, 0, 0, 419, 420, 1, 0, 0, 0, 420, 423, 1, 0, 0, 0, 421, 422, 5, 105, 0, 0, 422, 424, 5, 92, 0, 0, 423, 421, 1, 0, 0, 0, 423, 424, 1, 0, 0, 0, 424, 426, 1, 0, 0, 0, 425, 427, 3, 68, 34, 0, 426, 425, 1, 0, 0, 0, 426, 427, 1, 0, 0, 0, 427, 429, 1, 0, 0, 0, 428, 430, 3, 60, 30, 0, 429, 428, 1, 0, 0, 0, 429, 430, 1, 0, 0, 0, 430, 432, 1, 0, 0, 0, 431, 433, 3, 70, 35, 0, 432, 431, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 436, 1, 0, 0, 0, 434, 437, 3, 74, 37, 0, 435, 437, 3, 76, 38, 0, 436, 434, 1, 0, 0, 0, 436, 435, 1, 0, 0, 0, 436, 437, 1, 0, 0, 0, 437, 439, 1, 0, 0, 0, 438, 440, 3, 78, 39, 0, 439, 438, 1, 0, 0, 0, 439, 440, 1, 0, 0, 0, 440, 51, 1, 0, 0, 0, 441, 442, 5, 105, 0, 0, 442, 443, 3, 126, 63, 0, 443, 53, 1, 0, 0, 0, 444, 445, 5, 91, 0, 0, 445, 448, 5, 111, 0, 0, 446, 447, 5, 105, 0, 0, 447, 449, 5, 88, 0, 0, 448, 446, 1, 0, 0, 0, 448, 449, 1, 0, 0, 0, 449, 55, 1, 0, 0, 0, 450, 451, 5, 35, 0, 0, 451, 452, 3, 80, 40, 0, 452, 57, 1, 0, 0, 0, 453, 455, 7, 2, 0, 0, 454, 453, 1, 0, 0, 0, 454, 455, 1, 0, 0, 0, 455, 456, 1, 0, 0, 0, 456, 457, 5, 5, 0, 0, 457, 458, 5, 50, 0, 0, 458, 459, 3, 116, 58, 0, 459, 59, 1, 0, 0, 0, 460, 461, 5, 104, 0, 0, 461, 462, 3, 158, 79, 0, 462, 463, 5, 6, 0, 0, 463, 464, 5, 133, 0, 0, 464, 465, 3, 100, 50, 0, 465, 475, 5, 152, 0, 0, 466, 467, 5, 119, 0, 0, 467, 468, 3, 158, 79, 0, 468, 469, 5, 6, 0, 0, 469, 470, 5, 133, 0, 0, 470, 471, 3, 100, 50, 0, 471, 472, 5, 152, 0, 0, 472, 474, 1, 0, 0, 0, 473, 466, 1, 0, 0, 0, 474, 477, 1, 0, 0, 0, 475, 473, 1, 0, 0, 0, 475, 476, 1, 0, 0, 0, 476, 61, 1, 0, 0, 0, 477, 475, 1, 0, 0, 0, 478, 479, 5, 72, 0, 0, 479, 480, 3, 118, 59, 0, 480, 63, 1, 0, 0, 0, 481, 482, 5, 102, 0, 0, 482, 483, 3, 118, 59, 0, 483, 65, 1, 0, 0, 0, 484, 485, 5, 38, 0, 0, 485, 492, 5, 11, 0, 0, 486, 487, 7, 1, 0, 0, 487, 488, 5, 133, 0, 0, 488, 489, 3, 116, 58, 0, 489, 490, 5, 152, 0, 0, 490, 493, 1, 0, 0, 0, 491, 493, 3, 116, 58, 0, 492, 486, 1, 0, 0, 0, 492, 491, 1, 0, 0, 0, 493, 67, 1, 0, 0, 0, 494, 495, 5, 39, 0, 0, 495, 496, 3, 118, 59, 0, 496, 69, 1, 0, 0, 0, 497, 498, 5, 67, 0, 0, 498, 499, 5, 11, 0, 0, 499, 500, 3, 90, 45, 0, 500, 71, 1, 0, 0, 0, 501, 502, 5, 67, 0, 0, 502, 503, 5, 11, 0, 0, 503, 504, 3, 116, 58, 0, 504, 73, 1, 0, 0, 0, 505, 506, 5, 57, 0, 0, 506, 509, 3, 118, 59, 0, 507, 508, 5, 119, 0, 0, 508, 510, 3, 118, 59, 0, 509, 507, 1, 0, 0, 0, 509, 510, 1, 0, 0, 0, 510, 515, 1, 0, 0, 0, 511, 512, 5, 105, 0, 0, 512, 516, 5, 88, 0, 0, 513, 514, 5, 11, 0, 0, 514, 516, 3, 116, 58, 0, 515, 511, 1, 0, 0, 0, 515, 513, 1, 0, 0, 0, 515, 516, 1, 0, 0, 0, 516, 535, 1, 0, 0, 0, 517, 518, 5, 57, 0, 0, 518, 521, 3, 118, 59, 0, 519, 520, 5, 105, 0, 0, 520, 522, 5, 88, 0, 0, 521, 519, 1, 0, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 524, 5, 64, 0, 0, 524, 525, 3, 118, 59, 0, 525, 535, 1, 0, 0, 0, 526, 527, 5, 57, 0, 0, 527, 528, 3, 118, 59, 0, 528, 529, 5, 64, 0, 0, 529, 532, 3, 118, 59, 0, 530, 531, 5, 11, 0, 0, 531, 533, 3, 116, 58, 0, 532, 530, 1, 0, 0, 0, 532, 533, 1, 0, 0, 0, 533, 535, 1, 0, 0, 0, 534, 505, 1, 0, 0, 0, 534, 517, 1, 0, 0, 0, 534, 526, 1, 0, 0, 0, 535, 75, 1, 0, 0, 0, 536, 537, 5, 64, 0, 0, 537, 538, 3, 118, 59, 0, 538, 77, 1, 0, 0, 0, 539, 540, 5, 84, 0, 0, 540, 541, 3, 96, 48, 0, 541, 79, 1, 0, 0, 0, 542, 543, 6, 40, -1, 0, 543, 545, 3, 134, 67, 0, 544, 546, 5, 29, 0, 0, 545, 544, 1, 0, 0, 0, 545, 546, 1, 0, 0, 0, 546, 548, 1, 0, 0, 0, 547, 549, 3, 88, 44, 0, 548, 547, 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 555, 1, 0, 0, 0, 550, 551, 5, 133, 0, 0, 551, 552, 3, 80, 40, 0, 552, 553, 5, 152, 0, 0, 553, 555, 1, 0, 0, 0, 554, 542, 1, 0, 0, 0, 554, 550, 1, 0, 0, 0, 555, 570, 1, 0, 0, 0, 556, 557, 10, 3, 0, 0, 557, 558, 3, 84, 42, 0, 558, 559, 3, 80, 40, 4, 559, 569, 1, 0, 0, 0, 560, 562, 10, 4, 0, 0, 561, 563, 3, 82, 41, 0, 562, 561, 1, 0, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 1, 0, 0, 0, 564, 565, 5, 50, 0, 0, 565, 566, 3, 80, 40, 0, 566, 567, 3, 86, 43, 0, 567, 569, 1, 0, 0, 0, 568, 556, 1, 0, 0, 0, 568, 560, 1, 0, 0, 0, 569, 572, 1, 0, 0, 0, 570, 568, 1, 0, 0, 0, 570, 571, 1, 0, 0, 0, 571, 81, 1, 0, 0, 0, 572, 570, 1, 0, 0, 0, 573, 575, 7, 3, 0, 0, 574, 573, 1, 0, 0, 0, 574, 575, 1, 0, 0, 0, 575, 576, 1, 0, 0, 0, 576, 583, 5, 46, 0, 0, 577, 579, 5, 46, 0, 0, 578, 580, 7, 3, 0, 0, 579, 578, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 583, 1, 0, 0, 0, 581, 583, 7, 3, 0, 0, 582, 574, 1, 0, 0, 0, 582, 577, 1, 0, 0, 0, 582, 581, 1, 0, 0, 0, 583, 617, 1, 0, 0, 0, 584, 586, 7, 4, 0, 0, 585, 584, 1, 0, 0, 0, 585, 586, 1, 0, 0, 0, 586, 587, 1, 0, 0, 0, 587, 589, 7, 5, 0, 0, 588, 590, 5, 68, 0, 0, 589, 588, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 599, 1, 0, 0, 0, 591, 593, 7, 5, 0, 0, 592, 594, 5, 68, 0, 0, 593, 592, 1, 0, 0, 0, 593, 594, 1, 0, 0, 0, 594, 596, 1, 0, 0, 0, 595, 597, 7, 4, 0, 0, 596, 595, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 599, 1, 0, 0, 0, 598, 585, 1, 0, 0, 0, 598, 591, 1, 0, 0, 0, 599, 617, 1, 0, 0, 0, 600, 602, 7, 6, 0, 0, 601, 600, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 605, 5, 36, 0, 0, 604, 606, 5, 68, 0, 0, 605, 604, 1, 0, 0, 0, 605, 606, 1, 0, 0, 0, 606, 615, 1, 0, 0, 0, 607, 609, 5, 36, 0, 0, 608, 610, 5, 68, 0, 0, 609, 608, 1, 0, 0, 0, 609, 610, 1, 0, 0, 0, 610, 612, 1, 0, 0, 0, 611, 613, 7, 6, 0, 0, 612, 611, 1, 0, 0, 0, 612, 613, 1, 0, 0, 0, 613, 615, 1, 0, 0, 0, 614, 601, 1, 0, 0, 0, 614, 607, 1, 0, 0, 0, 615, 617, 1, 0, 0, 0, 616, 582, 1, 0, 0, 0, 616, 598, 1, 0, 0, 0, 616, 614, 1, 0, 0, 0, 617, 83, 1, 0, 0, 0, 618, 619, 5, 17, 0, 0, 619, 622, 5, 50, 0, 0, 620, 622, 5, 119, 0, 0, 621, 618, 1, 0, 0, 0, 621, 620, 1, 0, 0, 0, 622, 85, 1, 0, 0, 0, 623, 624, 5, 65, 0, 0, 624, 633, 3, 116, 58, 0, 625, 626, 5, 99, 0, 0, 626, 627, 5, 133, 0, 0, 627, 628, 3, 116, 58, 0, 628, 629, 5, 152, 0, 0, 629, 633, 1, 0, 0, 0, 630, 631, 5, 99, 0, 0, 631, 633, 3, 116, 58, 0, 632, 623, 1, 0, 0, 0, 632, 625, 1, 0, 0, 0, 632, 630, 1, 0, 0, 0, 633, 87, 1, 0, 0, 0, 634, 635, 5, 80, 0, 0, 635, 638, 3, 94, 47, 0, 636, 637, 5, 64, 0, 0, 637, 639, 3, 94, 47, 0, 638, 636, 1, 0, 0, 0, 638, 639, 1, 0, 0, 0, 639, 89, 1, 0, 0, 0, 640, 645, 3, 92, 46, 0, 641, 642, 5, 119, 0, 0, 642, 644, 3, 92, 46, 0, 643, 641, 1, 0, 0, 0, 644, 647, 1, 0, 0, 0, 645, 643, 1, 0, 0, 0, 645, 646, 1, 0, 0, 0, 646, 91, 1, 0, 0, 0, 647, 645, 1, 0, 0, 0, 648, 650, 3, 118, 59, 0, 649, 651, 7, 7, 0, 0, 650, 649, 1, 0, 0, 0, 650, 651, 1, 0, 0, 0, 651, 654, 1, 0, 0, 0, 652, 653, 5, 63, 0, 0, 653, 655, 7, 8, 0, 0, 654, 652, 1, 0, 0, 0, 654, 655, 1, 0, 0, 0, 655, 658, 1, 0, 0, 0, 656, 657, 5, 16, 0, 0, 657, 659, 5, 113, 0, 0, 658, 656, 1, 0, 0, 0, 658, 659, 1, 0, 0, 0, 659, 93, 1, 0, 0, 0, 660, 667, 3, 162, 81, 0, 661, 664, 3, 146, 73, 0, 662, 663, 5, 154, 0, 0, 663, 665, 3, 146, 73, 0, 664, 662, 1, 0, 0, 0, 664, 665, 1, 0, 0, 0, 665, 667, 1, 0, 0, 0, 666, 660, 1, 0, 0, 0, 666, 661, 1, 0, 0, 0, 667, 95, 1, 0, 0, 0, 668, 673, 3, 98, 49, 0, 669, 670, 5, 119, 0, 0, 670, 672, 3, 98, 49, 0, 671, 669, 1, 0, 0, 0, 672, 675, 1, 0, 0, 0, 673, 671, 1, 0, 0, 0, 673, 674, 1, 0, 0, 0, 674, 97, 1, 0, 0, 0, 675, 673, 1, 0, 0, 0, 676, 677, 3, 158, 79, 0, 677, 678, 5, 125, 0, 0, 678, 679, 3, 148, 74, 0, 679, 99, 1, 0, 0, 0, 680, 682, 3, 102, 51, 0, 681, 680, 1, 0, 0, 0, 681, 682, 1, 0, 0, 0, 682, 684, 1, 0, 0, 0, 683, 685, 3, 104, 52, 0, 684, 683, 1, 0, 0, 0, 684, 685, 1, 0, 0, 0, 685, 687, 1, 0, 0, 0, 686, 688, 3, 106, 53, 0, 687, 686, 1, 0, 0, 0, 687, 688, 1, 0, 0, 0, 688, 101, 1, 0, 0, 0, 689, 690, 5, 70, 0, 0, 690, 691, 5, 11, 0, 0, 691, 692, 3, 116, 58, 0, 692, 103, 1, 0, 0, 0, 693, 694, 5, 67, 0, 0, 694, 695, 5, 11, 0, 0, 695, 696, 3, 90, 45, 0, 696, 105, 1, 0, 0, 0, 697, 698, 7, 9, 0, 0, 698, 699, 3, 108, 54, 0, 699, 107, 1, 0, 0, 0, 700, 707, 3, 110, 55, 0, 701, 702, 5, 9, 0, 0, 702, 703, 3, 110, 55, 0, 703, 704, 5, 2, 0, 0, 704, 705, 3, 110, 55, 0, 705, 707, 1, 0, 0, 0, 706, 700, 1, 0, 0, 0, 706, 701, 1, 0, 0, 0, 707, 109, 1, 0, 0, 0, 708, 709, 5, 19, 0, 0, 709, 721, 5, 78, 0, 0, 710, 711, 5, 97, 0, 0, 711, 721, 5, 71, 0, 0, 712, 713, 5, 97, 0, 0, 713, 721, 5, 33, 0, 0, 714, 715, 3, 146, 73, 0, 715, 716, 5, 71, 0, 0, 716, 721, 1, 0, 0, 0, 717, 718, 3, 146, 73, 0, 718, 719, 5, 33, 0, 0, 719, 721, 1, 0, 0, 0, 720, 708, 1, 0, 0, 0, 720, 710, 1, 0, 0, 0, 720, 712, 1, 0, 0, 0, 720, 714, 1, 0, 0, 0, 720, 717, 1, 0, 0, 0, 721, 111, 1, 0, 0, 0, 722, 723, 3, 118, 59, 0, 723, 724, 5, 0, 0, 1, 724, 113, 1, 0, 0, 0, 725, 782, 3, 158, 79, 0, 726, 727, 3, 158, 79, 0, 727, 728, 5, 133, 0, 0, 728, 729, 3, 158, 79, 0, 729, 736, 3, 114, 57, 0, 730, 731, 5, 119, 0, 0, 731, 732, 3, 158, 79, 0, 732, 733, 3, 114, 57, 0, 733, 735, 1, 0, 0, 0, 734, 730, 1, 0, 0, 0, 735, 738, 1, 0, 0, 0, 736, 734, 1, 0, 0, 0, 736, 737, 1, 0, 0, 0, 737, 740, 1, 0, 0, 0, 738, 736, 1, 0, 0, 0, 739, 741, 5, 119, 0, 0, 740, 739, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 742, 1, 0, 0, 0, 742, 743, 5, 152, 0, 0, 743, 782, 1, 0, 0, 0, 744, 745, 3, 158, 79, 0, 745, 746, 5, 133, 0, 0, 746, 751, 3, 160, 80, 0, 747, 748, 5, 119, 0, 0, 748, 750, 3, 160, 80, 0, 749, 747, 1, 0, 0, 0, 750, 753, 1, 0, 0, 0, 751, 749, 1, 0, 0, 0, 751, 752, 1, 0, 0, 0, 752, 755, 1, 0, 0, 0, 753, 751, 1, 0, 0, 0, 754, 756, 5, 119, 0, 0, 755, 754, 1, 0, 0, 0, 755, 756, 1, 0, 0, 0, 756, 757, 1, 0, 0, 0, 757, 758, 5, 152, 0, 0, 758, 782, 1, 0, 0, 0, 759, 760, 3, 158, 79, 0, 760, 761, 5, 133, 0, 0, 761, 766, 3, 114, 57, 0, 762, 763, 5, 119, 0, 0, 763, 765, 3, 114, 57, 0, 764, 762, 1, 0, 0, 0, 765, 768, 1, 0, 0, 0, 766, 764, 1, 0, 0, 0, 766, 767, 1, 0, 0, 0, 767, 770, 1, 0, 0, 0, 768, 766, 1, 0, 0, 0, 769, 771, 5, 119, 0, 0, 770, 769, 1, 0, 0, 0, 770, 771, 1, 0, 0, 0, 771, 772, 1, 0, 0, 0, 772, 773, 5, 152, 0, 0, 773, 782, 1, 0, 0, 0, 774, 775, 3, 158, 79, 0, 775, 777, 5, 133, 0, 0, 776, 778, 3, 116, 58, 0, 777, 776, 1, 0, 0, 0, 777, 778, 1, 0, 0, 0, 778, 779, 1, 0, 0, 0, 779, 780, 5, 152, 0, 0, 780, 782, 1, 0, 0, 0, 781, 725, 1, 0, 0, 0, 781, 726, 1, 0, 0, 0, 781, 744, 1, 0, 0, 0, 781, 759, 1, 0, 0, 0, 781, 774, 1, 0, 0, 0, 782, 115, 1, 0, 0, 0, 783, 788, 3, 118, 59, 0, 784, 785, 5, 119, 0, 0, 785, 787, 3, 118, 59, 0, 786, 784, 1, 0, 0, 0, 787, 790, 1, 0, 0, 0, 788, 786, 1, 0, 0, 0, 788, 789, 1, 0, 0, 0, 789, 792, 1, 0, 0, 0, 790, 788, 1, 0, 0, 0, 791, 793, 5, 119, 0, 0, 792, 791, 1, 0, 0, 0, 792, 793, 1, 0, 0, 0, 793, 117, 1, 0, 0, 0, 794, 795, 6, 59, -1, 0, 795, 797, 5, 12, 0, 0, 796, 798, 3, 118, 59, 0, 797, 796, 1, 0, 0, 0, 797, 798, 1, 0, 0, 0, 798, 804, 1, 0, 0, 0, 799, 800, 5, 101, 0, 0, 800, 801, 3, 118, 59, 0, 801, 802, 5, 86, 0, 0, 802, 803, 3, 118, 59, 0, 803, 805, 1, 0, 0, 0, 804, 799, 1, 0, 0, 0, 805, 806, 1, 0, 0, 0, 806, 804, 1, 0, 0, 0, 806, 807, 1, 0, 0, 0, 807, 810, 1, 0, 0, 0, 808, 809, 5, 25, 0, 0, 809, 811, 3, 118, 59, 0, 810, 808, 1, 0, 0, 0, 810, 811, 1, 0, 0, 0, 811, 812, 1, 0, 0, 0, 812, 813, 5, 26, 0, 0, 813, 947, 1, 0, 0, 0, 814, 815, 5, 13, 0, 0, 815, 816, 5, 133, 0, 0, 816, 817, 3, 118, 59, 0, 817, 818, 5, 6, 0, 0, 818, 819, 3, 114, 57, 0, 819, 820, 5, 152, 0, 0, 820, 947, 1, 0, 0, 0, 821, 822, 5, 20, 0, 0, 822, 947, 5, 113, 0, 0, 823, 824, 5, 48, 0, 0, 824, 947, 5, 113, 0, 0, 825, 826, 5, 48, 0, 0, 826, 827, 3, 118, 59, 0, 827, 828, 3, 150, 75, 0, 828, 947, 1, 0, 0, 0, 829, 830, 5, 85, 0, 0, 830, 831, 5, 133, 0, 0, 831, 832, 3, 118, 59, 0, 832, 833, 5, 35, 0, 0, 833, 836, 3, 118, 59, 0, 834, 835, 5, 34, 0, 0, 835, 837, 3, 118, 59, 0, 836, 834, 1, 0, 0, 0, 836, 837, 1, 0, 0, 0, 837, 838, 1, 0, 0, 0, 838, 839, 5, 152, 0, 0, 839, 947, 1, 0, 0, 0, 840, 841, 5, 89, 0, 0, 841, 947, 5, 113, 0, 0, 842, 843, 5, 94, 0, 0, 843, 844, 5, 133, 0, 0, 844, 845, 7, 10, 0, 0, 845, 846, 3, 164, 82, 0, 846, 847, 5, 35, 0, 0, 847, 848, 3, 118, 59, 0, 848, 849, 5, 152, 0, 0, 849, 947, 1, 0, 0, 0, 850, 851, 3, 158, 79, 0, 851, 853, 5, 133, 0, 0, 852, 854, 3, 116, 58, 0, 853, 852, 1, 0, 0, 0, 853, 854, 1, 0, 0, 0, 854, 855, 1, 0, 0, 0, 855, 856, 5, 152, 0, 0, 856, 865, 1, 0, 0, 0, 857, 859, 5, 133, 0, 0, 858, 860, 5, 24, 0, 0, 859, 858, 1, 0, 0, 0, 859, 860, 1, 0, 0, 0, 860, 862, 1, 0, 0, 0, 861, 863, 3, 116, 58, 0, 862, 861, 1, 0, 0, 0, 862, 863, 1, 0, 0, 0, 863, 864, 1, 0, 0, 0, 864, 866, 5, 152, 0, 0, 865, 857, 1, 0, 0, 0, 865, 866, 1, 0, 0, 0, 866, 867, 1, 0, 0, 0, 867, 868, 5, 69, 0, 0, 868, 869, 5, 133, 0, 0, 869, 870, 3, 100, 50, 0, 870, 871, 5, 152, 0, 0, 871, 947, 1, 0, 0, 0, 872, 873, 3, 158, 79, 0, 873, 875, 5, 133, 0, 0, 874, 876, 3, 116, 58, 0, 875, 874, 1, 0, 0, 0, 875, 876, 1, 0, 0, 0, 876, 877, 1, 0, 0, 0, 877, 878, 5, 152, 0, 0, 878, 887, 1, 0, 0, 0, 879, 881, 5, 133, 0, 0, 880, 882, 5, 24, 0, 0, 881, 880, 1, 0, 0, 0, 881, 882, 1, 0, 0, 0, 882, 884, 1, 0, 0, 0, 883, 885, 3, 116, 58, 0, 884, 883, 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 886, 1, 0, 0, 0, 886, 888, 5, 152, 0, 0, 887, 879, 1, 0, 0, 0, 887, 888, 1, 0, 0, 0, 888, 889, 1, 0, 0, 0, 889, 890, 5, 69, 0, 0, 890, 891, 3, 158, 79, 0, 891, 947, 1, 0, 0, 0, 892, 898, 3, 158, 79, 0, 893, 895, 5, 133, 0, 0, 894, 896, 3, 116, 58, 0, 895, 894, 1, 0, 0, 0, 895, 896, 1, 0, 0, 0, 896, 897, 1, 0, 0, 0, 897, 899, 5, 152, 0, 0, 898, 893, 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 900, 1, 0, 0, 0, 900, 902, 5, 133, 0, 0, 901, 903, 5, 24, 0, 0, 902, 901, 1, 0, 0, 0, 902, 903, 1, 0, 0, 0, 903, 905, 1, 0, 0, 0, 904, 906, 3, 116, 58, 0, 905, 904, 1, 0, 0, 0, 905, 906, 1, 0, 0, 0, 906, 907, 1, 0, 0, 0, 907, 908, 5, 152, 0, 0, 908, 947, 1, 0, 0, 0, 909, 947, 3, 122, 61, 0, 910, 947, 3, 166, 83, 0, 911, 947, 3, 148, 74, 0, 912, 913, 5, 121, 0, 0, 913, 947, 3, 118, 59, 20, 914, 915, 5, 61, 0, 0, 915, 947, 3, 118, 59, 14, 916, 917, 3, 138, 69, 0, 917, 918, 5, 123, 0, 0, 918, 920, 1, 0, 0, 0, 919, 916, 1, 0, 0, 0, 919, 920, 1, 0, 0, 0, 920, 921, 1, 0, 0, 0, 921, 947, 5, 115, 0, 0, 922, 923, 5, 133, 0, 0, 923, 924, 3, 48, 24, 0, 924, 925, 5, 152, 0, 0, 925, 947, 1, 0, 0, 0, 926, 927, 5, 133, 0, 0, 927, 928, 3, 118, 59, 0, 928, 929, 5, 152, 0, 0, 929, 947, 1, 0, 0, 0, 930, 931, 5, 133, 0, 0, 931, 932, 3, 116, 58, 0, 932, 933, 5, 152, 0, 0, 933, 947, 1, 0, 0, 0, 934, 936, 5, 132, 0, 0, 935, 937, 3, 116, 58, 0, 936, 935, 1, 0, 0, 0, 936, 937, 1, 0, 0, 0, 937, 938, 1, 0, 0, 0, 938, 947, 5, 151, 0, 0, 939, 941, 5, 131, 0, 0, 940, 942, 3, 40, 20, 0, 941, 940, 1, 0, 0, 0, 941, 942, 1, 0, 0, 0, 942, 943, 1, 0, 0, 0, 943, 947, 5, 150, 0, 0, 944, 947, 3, 120, 60, 0, 945, 947, 3, 130, 65, 0, 946, 794, 1, 0, 0, 0, 946, 814, 1, 0, 0, 0, 946, 821, 1, 0, 0, 0, 946, 823, 1, 0, 0, 0, 946, 825, 1, 0, 0, 0, 946, 829, 1, 0, 0, 0, 946, 840, 1, 0, 0, 0, 946, 842, 1, 0, 0, 0, 946, 850, 1, 0, 0, 0, 946, 872, 1, 0, 0, 0, 946, 892, 1, 0, 0, 0, 946, 909, 1, 0, 0, 0, 946, 910, 1, 0, 0, 0, 946, 911, 1, 0, 0, 0, 946, 912, 1, 0, 0, 0, 946, 914, 1, 0, 0, 0, 946, 919, 1, 0, 0, 0, 946, 922, 1, 0, 0, 0, 946, 926, 1, 0, 0, 0, 946, 930, 1, 0, 0, 0, 946, 934, 1, 0, 0, 0, 946, 939, 1, 0, 0, 0, 946, 944, 1, 0, 0, 0, 946, 945, 1, 0, 0, 0, 947, 1058, 1, 0, 0, 0, 948, 952, 10, 19, 0, 0, 949, 953, 5, 115, 0, 0, 950, 953, 5, 154, 0, 0, 951, 953, 5, 141, 0, 0, 952, 949, 1, 0, 0, 0, 952, 950, 1, 0, 0, 0, 952, 951, 1, 0, 0, 0, 953, 954, 1, 0, 0, 0, 954, 1057, 3, 118, 59, 20, 955, 959, 10, 18, 0, 0, 956, 960, 5, 142, 0, 0, 957, 960, 5, 121, 0, 0, 958, 960, 5, 120, 0, 0, 959, 956, 1, 0, 0, 0, 959, 957, 1, 0, 0, 0, 959, 958, 1, 0, 0, 0, 960, 961, 1, 0, 0, 0, 961, 1057, 3, 118, 59, 19, 962, 987, 10, 17, 0, 0, 963, 988, 5, 124, 0, 0, 964, 988, 5, 125, 0, 0, 965, 988, 5, 136, 0, 0, 966, 988, 5, 134, 0, 0, 967, 988, 5, 135, 0, 0, 968, 988, 5, 126, 0, 0, 969, 988, 5, 127, 0, 0, 970, 972, 5, 61, 0, 0, 971, 970, 1, 0, 0, 0, 971, 972, 1, 0, 0, 0, 972, 973, 1, 0, 0, 0, 973, 975, 5, 44, 0, 0, 974, 976, 5, 15, 0, 0, 975, 974, 1, 0, 0, 0, 975, 976, 1, 0, 0, 0, 976, 988, 1, 0, 0, 0, 977, 979, 5, 61, 0, 0, 978, 977, 1, 0, 0, 0, 978, 979, 1, 0, 0, 0, 979, 980, 1, 0, 0, 0, 980, 988, 7, 11, 0, 0, 981, 988, 5, 148, 0, 0, 982, 988, 5, 149, 0, 0, 983, 988, 5, 138, 0, 0, 984, 988, 5, 129, 0, 0, 985, 988, 5, 130, 0, 0, 986, 988, 5, 137, 0, 0, 987, 963, 1, 0, 0, 0, 987, 964, 1, 0, 0, 0, 987, 965, 1, 0, 0, 0, 987, 966, 1, 0, 0, 0, 987, 967, 1, 0, 0, 0, 987, 968, 1, 0, 0, 0, 987, 969, 1, 0, 0, 0, 987, 971, 1, 0, 0, 0, 987, 978, 1, 0, 0, 0, 987, 981, 1, 0, 0, 0, 987, 982, 1, 0, 0, 0, 987, 983, 1, 0, 0, 0, 987, 984, 1, 0, 0, 0, 987, 985, 1, 0, 0, 0, 987, 986, 1, 0, 0, 0, 988, 989, 1, 0, 0, 0, 989, 1057, 3, 118, 59, 18, 990, 991, 10, 15, 0, 0, 991, 992, 5, 140, 0, 0, 992, 1057, 3, 118, 59, 16, 993, 994, 10, 13, 0, 0, 994, 995, 5, 2, 0, 0, 995, 1057, 3, 118, 59, 14, 996, 997, 10, 12, 0, 0, 997, 998, 5, 66, 0, 0, 998, 1057, 3, 118, 59, 13, 999, 1001, 10, 11, 0, 0, 1000, 1002, 5, 61, 0, 0, 1001, 1000, 1, 0, 0, 0, 1001, 1002, 1, 0, 0, 0, 1002, 1003, 1, 0, 0, 0, 1003, 1004, 5, 9, 0, 0, 1004, 1005, 3, 118, 59, 0, 1005, 1006, 5, 2, 0, 0, 1006, 1007, 3, 118, 59, 12, 1007, 1057, 1, 0, 0, 0, 1008, 1009, 10, 10, 0, 0, 1009, 1010, 5, 143, 0, 0, 1010, 1011, 3, 118, 59, 0, 1011, 1012, 5, 118, 0, 0, 1012, 1013, 3, 118, 59, 10, 1013, 1057, 1, 0, 0, 0, 1014, 1015, 10, 30, 0, 0, 1015, 1017, 5, 133, 0, 0, 1016, 1018, 3, 116, 58, 0, 1017, 1016, 1, 0, 0, 0, 1017, 1018, 1, 0, 0, 0, 1018, 1019, 1, 0, 0, 0, 1019, 1057, 5, 152, 0, 0, 1020, 1021, 10, 26, 0, 0, 1021, 1022, 5, 132, 0, 0, 1022, 1023, 3, 118, 59, 0, 1023, 1024, 5, 151, 0, 0, 1024, 1057, 1, 0, 0, 0, 1025, 1026, 10, 25, 0, 0, 1026, 1027, 5, 123, 0, 0, 1027, 1057, 5, 111, 0, 0, 1028, 1029, 10, 24, 0, 0, 1029, 1030, 5, 123, 0, 0, 1030, 1057, 3, 158, 79, 0, 1031, 1032, 10, 23, 0, 0, 1032, 1033, 5, 139, 0, 0, 1033, 1034, 5, 132, 0, 0, 1034, 1035, 3, 118, 59, 0, 1035, 1036, 5, 151, 0, 0, 1036, 1057, 1, 0, 0, 0, 1037, 1038, 10, 22, 0, 0, 1038, 1039, 5, 139, 0, 0, 1039, 1057, 5, 111, 0, 0, 1040, 1041, 10, 21, 0, 0, 1041, 1042, 5, 139, 0, 0, 1042, 1057, 3, 158, 79, 0, 1043, 1044, 10, 16, 0, 0, 1044, 1046, 5, 49, 0, 0, 1045, 1047, 5, 61, 0, 0, 1046, 1045, 1, 0, 0, 0, 1046, 1047, 1, 0, 0, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1057, 5, 62, 0, 0, 1049, 1054, 10, 9, 0, 0, 1050, 1051, 5, 6, 0, 0, 1051, 1055, 3, 158, 79, 0, 1052, 1053, 5, 6, 0, 0, 1053, 1055, 5, 113, 0, 0, 1054, 1050, 1, 0, 0, 0, 1054, 1052, 1, 0, 0, 0, 1055, 1057, 1, 0, 0, 0, 1056, 948, 1, 0, 0, 0, 1056, 955, 1, 0, 0, 0, 1056, 962, 1, 0, 0, 0, 1056, 990, 1, 0, 0, 0, 1056, 993, 1, 0, 0, 0, 1056, 996, 1, 0, 0, 0, 1056, 999, 1, 0, 0, 0, 1056, 1008, 1, 0, 0, 0, 1056, 1014, 1, 0, 0, 0, 1056, 1020, 1, 0, 0, 0, 1056, 1025, 1, 0, 0, 0, 1056, 1028, 1, 0, 0, 0, 1056, 1031, 1, 0, 0, 0, 1056, 1037, 1, 0, 0, 0, 1056, 1040, 1, 0, 0, 0, 1056, 1043, 1, 0, 0, 0, 1056, 1049, 1, 0, 0, 0, 1057, 1060, 1, 0, 0, 0, 1058, 1056, 1, 0, 0, 0, 1058, 1059, 1, 0, 0, 0, 1059, 119, 1, 0, 0, 0, 1060, 1058, 1, 0, 0, 0, 1061, 1062, 5, 133, 0, 0, 1062, 1067, 3, 158, 79, 0, 1063, 1064, 5, 119, 0, 0, 1064, 1066, 3, 158, 79, 0, 1065, 1063, 1, 0, 0, 0, 1066, 1069, 1, 0, 0, 0, 1067, 1065, 1, 0, 0, 0, 1067, 1068, 1, 0, 0, 0, 1068, 1071, 1, 0, 0, 0, 1069, 1067, 1, 0, 0, 0, 1070, 1072, 5, 119, 0, 0, 1071, 1070, 1, 0, 0, 0, 1071, 1072, 1, 0, 0, 0, 1072, 1073, 1, 0, 0, 0, 1073, 1074, 5, 152, 0, 0, 1074, 1089, 1, 0, 0, 0, 1075, 1080, 3, 158, 79, 0, 1076, 1077, 5, 119, 0, 0, 1077, 1079, 3, 158, 79, 0, 1078, 1076, 1, 0, 0, 0, 1079, 1082, 1, 0, 0, 0, 1080, 1078, 1, 0, 0, 0, 1080, 1081, 1, 0, 0, 0, 1081, 1084, 1, 0, 0, 0, 1082, 1080, 1, 0, 0, 0, 1083, 1085, 5, 119, 0, 0, 1084, 1083, 1, 0, 0, 0, 1084, 1085, 1, 0, 0, 0, 1085, 1089, 1, 0, 0, 0, 1086, 1087, 5, 133, 0, 0, 1087, 1089, 5, 152, 0, 0, 1088, 1061, 1, 0, 0, 0, 1088, 1075, 1, 0, 0, 0, 1088, 1086, 1, 0, 0, 0, 1089, 1090, 1, 0, 0, 0, 1090, 1093, 5, 114, 0, 0, 1091, 1094, 3, 118, 59, 0, 1092, 1094, 3, 36, 18, 0, 1093, 1091, 1, 0, 0, 0, 1093, 1092, 1, 0, 0, 0, 1094, 121, 1, 0, 0, 0, 1095, 1096, 5, 135, 0, 0, 1096, 1100, 3, 158, 79, 0, 1097, 1099, 3, 124, 62, 0, 1098, 1097, 1, 0, 0, 0, 1099, 1102, 1, 0, 0, 0, 1100, 1098, 1, 0, 0, 0, 1100, 1101, 1, 0, 0, 0, 1101, 1103, 1, 0, 0, 0, 1102, 1100, 1, 0, 0, 0, 1103, 1104, 5, 154, 0, 0, 1104, 1105, 5, 127, 0, 0, 1105, 1128, 1, 0, 0, 0, 1106, 1107, 5, 135, 0, 0, 1107, 1111, 3, 158, 79, 0, 1108, 1110, 3, 124, 62, 0, 1109, 1108, 1, 0, 0, 0, 1110, 1113, 1, 0, 0, 0, 1111, 1109, 1, 0, 0, 0, 1111, 1112, 1, 0, 0, 0, 1112, 1114, 1, 0, 0, 0, 1113, 1111, 1, 0, 0, 0, 1114, 1120, 5, 127, 0, 0, 1115, 1121, 3, 122, 61, 0, 1116, 1117, 5, 131, 0, 0, 1117, 1118, 3, 118, 59, 0, 1118, 1119, 5, 150, 0, 0, 1119, 1121, 1, 0, 0, 0, 1120, 1115, 1, 0, 0, 0, 1120, 1116, 1, 0, 0, 0, 1120, 1121, 1, 0, 0, 0, 1121, 1122, 1, 0, 0, 0, 1122, 1123, 5, 135, 0, 0, 1123, 1124, 5, 154, 0, 0, 1124, 1125, 3, 158, 79, 0, 1125, 1126, 5, 127, 0, 0, 1126, 1128, 1, 0, 0, 0, 1127, 1095, 1, 0, 0, 0, 1127, 1106, 1, 0, 0, 0, 1128, 123, 1, 0, 0, 0, 1129, 1130, 3, 158, 79, 0, 1130, 1131, 5, 125, 0, 0, 1131, 1132, 3, 164, 82, 0, 1132, 1141, 1, 0, 0, 0, 1133, 1134, 3, 158, 79, 0, 1134, 1135, 5, 125, 0, 0, 1135, 1136, 5, 131, 0, 0, 1136, 1137, 3, 118, 59, 0, 1137, 1138, 5, 150, 0, 0, 1138, 1141, 1, 0, 0, 0, 1139, 1141, 3, 158, 79, 0, 1140, 1129, 1, 0, 0, 0, 1140, 1133, 1, 0, 0, 0, 1140, 1139, 1, 0, 0, 0, 1141, 125, 1, 0, 0, 0, 1142, 1147, 3, 128, 64, 0, 1143, 1144, 5, 119, 0, 0, 1144, 1146, 3, 128, 64, 0, 1145, 1143, 1, 0, 0, 0, 1146, 1149, 1, 0, 0, 0, 1147, 1145, 1, 0, 0, 0, 1147, 1148, 1, 0, 0, 0, 1148, 1151, 1, 0, 0, 0, 1149, 1147, 1, 0, 0, 0, 1150, 1152, 5, 119, 0, 0, 1151, 1150, 1, 0, 0, 0, 1151, 1152, 1, 0, 0, 0, 1152, 127, 1, 0, 0, 0, 1153, 1154, 3, 158, 79, 0, 1154, 1155, 5, 6, 0, 0, 1155, 1156, 5, 133, 0, 0, 1156, 1157, 3, 48, 24, 0, 1157, 1158, 5, 152, 0, 0, 1158, 1164, 1, 0, 0, 0, 1159, 1160, 3, 118, 59, 0, 1160, 1161, 5, 6, 0, 0, 1161, 1162, 3, 158, 79, 0, 1162, 1164, 1, 0, 0, 0, 1163, 1153, 1, 0, 0, 0, 1163, 1159, 1, 0, 0, 0, 1164, 129, 1, 0, 0, 0, 1165, 1173, 3, 162, 81, 0, 1166, 1167, 3, 138, 69, 0, 1167, 1168, 5, 123, 0, 0, 1168, 1170, 1, 0, 0, 0, 1169, 1166, 1, 0, 0, 0, 1169, 1170, 1, 0, 0, 0, 1170, 1171, 1, 0, 0, 0, 1171, 1173, 3, 132, 66, 0, 1172, 1165, 1, 0, 0, 0, 1172, 1169, 1, 0, 0, 0, 1173, 131, 1, 0, 0, 0, 1174, 1179, 3, 158, 79, 0, 1175, 1176, 5, 123, 0, 0, 1176, 1178, 3, 158, 79, 0, 1177, 1175, 1, 0, 0, 0, 1178, 1181, 1, 0, 0, 0, 1179, 1177, 1, 0, 0, 0, 1179, 1180, 1, 0, 0, 0, 1180, 133, 1, 0, 0, 0, 1181, 1179, 1, 0, 0, 0, 1182, 1183, 6, 67, -1, 0, 1183, 1192, 3, 138, 69, 0, 1184, 1192, 3, 136, 68, 0, 1185, 1186, 5, 133, 0, 0, 1186, 1187, 3, 48, 24, 0, 1187, 1188, 5, 152, 0, 0, 1188, 1192, 1, 0, 0, 0, 1189, 1192, 3, 122, 61, 0, 1190, 1192, 3, 162, 81, 0, 1191, 1182, 1, 0, 0, 0, 1191, 1184, 1, 0, 0, 0, 1191, 1185, 1, 0, 0, 0, 1191, 1189, 1, 0, 0, 0, 1191, 1190, 1, 0, 0, 0, 1192, 1201, 1, 0, 0, 0, 1193, 1197, 10, 3, 0, 0, 1194, 1198, 3, 156, 78, 0, 1195, 1196, 5, 6, 0, 0, 1196, 1198, 3, 158, 79, 0, 1197, 1194, 1, 0, 0, 0, 1197, 1195, 1, 0, 0, 0, 1198, 1200, 1, 0, 0, 0, 1199, 1193, 1, 0, 0, 0, 1200, 1203, 1, 0, 0, 0, 1201, 1199, 1, 0, 0, 0, 1201, 1202, 1, 0, 0, 0, 1202, 135, 1, 0, 0, 0, 1203, 1201, 1, 0, 0, 0, 1204, 1205, 3, 158, 79, 0, 1205, 1207, 5, 133, 0, 0, 1206, 1208, 3, 140, 70, 0, 1207, 1206, 1, 0, 0, 0, 1207, 1208, 1, 0, 0, 0, 1208, 1209, 1, 0, 0, 0, 1209, 1210, 5, 152, 0, 0, 1210, 137, 1, 0, 0, 0, 1211, 1212, 3, 142, 71, 0, 1212, 1213, 5, 123, 0, 0, 1213, 1215, 1, 0, 0, 0, 1214, 1211, 1, 0, 0, 0, 1214, 1215, 1, 0, 0, 0, 1215, 1216, 1, 0, 0, 0, 1216, 1217, 3, 158, 79, 0, 1217, 139, 1, 0, 0, 0, 1218, 1223, 3, 118, 59, 0, 1219, 1220, 5, 119, 0, 0, 1220, 1222, 3, 118, 59, 0, 1221, 1219, 1, 0, 0, 0, 1222, 1225, 1, 0, 0, 0, 1223, 1221, 1, 0, 0, 0, 1223, 1224, 1, 0, 0, 0, 1224, 1227, 1, 0, 0, 0, 1225, 1223, 1, 0, 0, 0, 1226, 1228, 5, 119, 0, 0, 1227, 1226, 1, 0, 0, 0, 1227, 1228, 1, 0, 0, 0, 1228, 141, 1, 0, 0, 0, 1229, 1230, 3, 158, 79, 0, 1230, 143, 1, 0, 0, 0, 1231, 1240, 5, 109, 0, 0, 1232, 1233, 5, 123, 0, 0, 1233, 1240, 7, 12, 0, 0, 1234, 1235, 5, 111, 0, 0, 1235, 1237, 5, 123, 0, 0, 1236, 1238, 7, 12, 0, 0, 1237, 1236, 1, 0, 0, 0, 1237, 1238, 1, 0, 0, 0, 1238, 1240, 1, 0, 0, 0, 1239, 1231, 1, 0, 0, 0, 1239, 1232, 1, 0, 0, 0, 1239, 1234, 1, 0, 0, 0, 1240, 145, 1, 0, 0, 0, 1241, 1243, 7, 13, 0, 0, 1242, 1241, 1, 0, 0, 0, 1242, 1243, 1, 0, 0, 0, 1243, 1250, 1, 0, 0, 0, 1244, 1251, 3, 144, 72, 0, 1245, 1251, 5, 110, 0, 0, 1246, 1251, 5, 111, 0, 0, 1247, 1251, 5, 112, 0, 0, 1248, 1251, 5, 45, 0, 0, 1249, 1251, 5, 60, 0, 0, 1250, 1244, 1, 0, 0, 0, 1250, 1245, 1, 0, 0, 0, 1250, 1246, 1, 0, 0, 0, 1250, 1247, 1, 0, 0, 0, 1250, 1248, 1, 0, 0, 0, 1250, 1249, 1, 0, 0, 0, 1251, 147, 1, 0, 0, 0, 1252, 1256, 3, 146, 73, 0, 1253, 1256, 5, 113, 0, 0, 1254, 1256, 5, 62, 0, 0, 1255, 1252, 1, 0, 0, 0, 1255, 1253, 1, 0, 0, 0, 1255, 1254, 1, 0, 0, 0, 1256, 149, 1, 0, 0, 0, 1257, 1258, 7, 14, 0, 0, 1258, 151, 1, 0, 0, 0, 1259, 1260, 7, 15, 0, 0, 1260, 153, 1, 0, 0, 0, 1261, 1262, 7, 16, 0, 0, 1262, 155, 1, 0, 0, 0, 1263, 1266, 5, 108, 0, 0, 1264, 1266, 3, 154, 77, 0, 1265, 1263, 1, 0, 0, 0, 1265, 1264, 1, 0, 0, 0, 1266, 157, 1, 0, 0, 0, 1267, 1271, 5, 108, 0, 0, 1268, 1271, 3, 150, 75, 0, 1269, 1271, 3, 152, 76, 0, 1270, 1267, 1, 0, 0, 0, 1270, 1268, 1, 0, 0, 0, 1270, 1269, 1, 0, 0, 0, 1271, 159, 1, 0, 0, 0, 1272, 1273, 3, 164, 82, 0, 1273, 1274, 5, 125, 0, 0, 1274, 1275, 3, 146, 73, 0, 1275, 161, 1, 0, 0, 0, 1276, 1277, 5, 131, 0, 0, 1277, 1278, 3, 118, 59, 0, 1278, 1279, 5, 150, 0, 0, 1279, 163, 1, 0, 0, 0, 1280, 1283, 5, 113, 0, 0, 1281, 1283, 3, 166, 83, 0, 1282, 1280, 1, 0, 0, 0, 1282, 1281, 1, 0, 0, 0, 1283, 165, 1, 0, 0, 0, 1284, 1288, 5, 145, 0, 0, 1285, 1287, 3, 168, 84, 0, 1286, 1285, 1, 0, 0, 0, 1287, 1290, 1, 0, 0, 0, 1288, 1286, 1, 0, 0, 0, 1288, 1289, 1, 0, 0, 0, 1289, 1291, 1, 0, 0, 0, 1290, 1288, 1, 0, 0, 0, 1291, 1292, 5, 147, 0, 0, 1292, 167, 1, 0, 0, 0, 1293, 1294, 5, 160, 0, 0, 1294, 1295, 3, 118, 59, 0, 1295, 1296, 5, 150, 0, 0, 1296, 1299, 1, 0, 0, 0, 1297, 1299, 5, 159, 0, 0, 1298, 1293, 1, 0, 0, 0, 1298, 1297, 1, 0, 0, 0, 1299, 169, 1, 0, 0, 0, 1300, 1304, 5, 146, 0, 0, 1301, 1303, 3, 172, 86, 0, 1302, 1301, 1, 0, 0, 0, 1303, 1306, 1, 0, 0, 0, 1304, 1302, 1, 0, 0, 0, 1304, 1305, 1, 0, 0, 0, 1305, 1307, 1, 0, 0, 0, 1306, 1304, 1, 0, 0, 0, 1307, 1308, 5, 0, 0, 1, 1308, 171, 1, 0, 0, 0, 1309, 1310, 5, 162, 0, 0, 1310, 1311, 3, 118, 59, 0, 1311, 1312, 5, 150, 0, 0, 1312, 1315, 1, 0, 0, 0, 1313, 1315, 5, 161, 0, 0, 1314, 1309, 1, 0, 0, 0, 1314, 1313, 1, 0, 0, 0, 1315, 173, 1, 0, 0, 0, 168, 177, 184, 193, 200, 204, 218, 222, 225, 229, 232, 239, 243, 252, 257, 266, 274, 281, 285, 291, 296, 304, 311, 317, 329, 337, 351, 355, 360, 370, 380, 388, 392, 396, 399, 403, 406, 409, 412, 415, 419, 423, 426, 429, 432, 436, 439, 448, 454, 475, 492, 509, 515, 521, 532, 534, 545, 548, 554, 562, 568, 570, 574, 579, 582, 585, 589, 593, 596, 598, 601, 605, 609, 612, 614, 616, 621, 632, 638, 645, 650, 654, 658, 664, 666, 673, 681, 684, 687, 706, 720, 736, 740, 751, 755, 766, 770, 777, 781, 788, 792, 797, 806, 810, 836, 853, 859, 862, 865, 875, 881, 884, 887, 895, 898, 902, 905, 919, 936, 941, 946, 952, 959, 971, 975, 978, 987, 1001, 1017, 1046, 1054, 1056, 1058, 1067, 1071, 1080, 1084, 1088, 1093, 1100, 1111, 1120, 1127, 1140, 1147, 1151, 1163, 1169, 1172, 1179, 1191, 1197, 1201, 1207, 1214, 1223, 1227, 1237, 1239, 1242, 1250, 1255, 1265, 1270, 1282, 1288, 1298, 1304, 1314] \ No newline at end of file +[4, 1, 162, 1325, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 1, 0, 5, 0, 178, 8, 0, 10, 0, 12, 0, 181, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 187, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 196, 8, 3, 1, 4, 1, 4, 1, 4, 5, 4, 201, 8, 4, 10, 4, 12, 4, 204, 9, 4, 1, 4, 3, 4, 207, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 221, 8, 5, 1, 6, 1, 6, 3, 6, 225, 8, 6, 1, 6, 3, 6, 228, 8, 6, 1, 7, 1, 7, 3, 7, 232, 8, 7, 1, 7, 3, 7, 235, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 242, 8, 8, 1, 8, 1, 8, 3, 8, 246, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 5, 9, 253, 8, 9, 10, 9, 12, 9, 256, 9, 9, 1, 9, 1, 9, 3, 9, 260, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 269, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 277, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 284, 8, 12, 1, 12, 1, 12, 3, 12, 288, 8, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 294, 8, 12, 1, 12, 1, 12, 1, 12, 3, 12, 299, 8, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 307, 8, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 314, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 320, 8, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 3, 16, 332, 8, 16, 1, 17, 1, 17, 1, 18, 1, 18, 5, 18, 338, 8, 18, 10, 18, 12, 18, 341, 9, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 5, 20, 352, 8, 20, 10, 20, 12, 20, 355, 9, 20, 1, 20, 3, 20, 358, 8, 20, 1, 21, 1, 21, 1, 21, 3, 21, 363, 8, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 373, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 383, 8, 23, 1, 23, 1, 23, 1, 24, 1, 24, 5, 24, 389, 8, 24, 10, 24, 12, 24, 392, 9, 24, 1, 25, 3, 25, 395, 8, 25, 1, 25, 1, 25, 3, 25, 399, 8, 25, 1, 25, 3, 25, 402, 8, 25, 1, 25, 1, 25, 3, 25, 406, 8, 25, 1, 25, 3, 25, 409, 8, 25, 1, 25, 3, 25, 412, 8, 25, 1, 25, 3, 25, 415, 8, 25, 1, 25, 3, 25, 418, 8, 25, 1, 25, 1, 25, 3, 25, 422, 8, 25, 1, 25, 1, 25, 3, 25, 426, 8, 25, 1, 25, 3, 25, 429, 8, 25, 1, 25, 3, 25, 432, 8, 25, 1, 25, 3, 25, 435, 8, 25, 1, 25, 1, 25, 3, 25, 439, 8, 25, 1, 25, 3, 25, 442, 8, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 451, 8, 27, 1, 28, 1, 28, 1, 28, 1, 29, 3, 29, 457, 8, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 476, 8, 30, 10, 30, 12, 30, 479, 9, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 495, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 512, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 518, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 524, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 535, 8, 37, 3, 37, 537, 8, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 3, 40, 548, 8, 40, 1, 40, 3, 40, 551, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 557, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 565, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 5, 40, 571, 8, 40, 10, 40, 12, 40, 574, 9, 40, 1, 41, 3, 41, 577, 8, 41, 1, 41, 1, 41, 1, 41, 3, 41, 582, 8, 41, 1, 41, 3, 41, 585, 8, 41, 1, 41, 3, 41, 588, 8, 41, 1, 41, 1, 41, 3, 41, 592, 8, 41, 1, 41, 1, 41, 3, 41, 596, 8, 41, 1, 41, 3, 41, 599, 8, 41, 3, 41, 601, 8, 41, 1, 41, 3, 41, 604, 8, 41, 1, 41, 1, 41, 3, 41, 608, 8, 41, 1, 41, 1, 41, 3, 41, 612, 8, 41, 1, 41, 3, 41, 615, 8, 41, 3, 41, 617, 8, 41, 3, 41, 619, 8, 41, 1, 42, 1, 42, 1, 42, 3, 42, 624, 8, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 635, 8, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 641, 8, 44, 1, 45, 1, 45, 1, 45, 5, 45, 646, 8, 45, 10, 45, 12, 45, 649, 9, 45, 1, 46, 1, 46, 3, 46, 653, 8, 46, 1, 46, 1, 46, 3, 46, 657, 8, 46, 1, 46, 1, 46, 3, 46, 661, 8, 46, 1, 47, 1, 47, 1, 47, 1, 47, 3, 47, 667, 8, 47, 3, 47, 669, 8, 47, 1, 48, 1, 48, 1, 48, 5, 48, 674, 8, 48, 10, 48, 12, 48, 677, 9, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 3, 50, 684, 8, 50, 1, 50, 3, 50, 687, 8, 50, 1, 50, 3, 50, 690, 8, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 709, 8, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 723, 8, 55, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 737, 8, 57, 10, 57, 12, 57, 740, 9, 57, 1, 57, 3, 57, 743, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 752, 8, 57, 10, 57, 12, 57, 755, 9, 57, 1, 57, 3, 57, 758, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 767, 8, 57, 10, 57, 12, 57, 770, 9, 57, 1, 57, 3, 57, 773, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 3, 57, 780, 8, 57, 1, 57, 1, 57, 3, 57, 784, 8, 57, 1, 58, 1, 58, 1, 58, 5, 58, 789, 8, 58, 10, 58, 12, 58, 792, 9, 58, 1, 58, 3, 58, 795, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 800, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 4, 59, 807, 8, 59, 11, 59, 12, 59, 808, 1, 59, 1, 59, 3, 59, 813, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 839, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 856, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 862, 8, 59, 1, 59, 3, 59, 865, 8, 59, 1, 59, 3, 59, 868, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 878, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 884, 8, 59, 1, 59, 3, 59, 887, 8, 59, 1, 59, 3, 59, 890, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 898, 8, 59, 1, 59, 3, 59, 901, 8, 59, 1, 59, 1, 59, 3, 59, 905, 8, 59, 1, 59, 3, 59, 908, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 922, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 939, 8, 59, 1, 59, 1, 59, 1, 59, 3, 59, 944, 8, 59, 1, 59, 1, 59, 1, 59, 3, 59, 949, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 955, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 962, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 974, 8, 59, 1, 59, 1, 59, 3, 59, 978, 8, 59, 1, 59, 3, 59, 981, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 990, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1004, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1020, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1049, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1057, 8, 59, 5, 59, 1059, 8, 59, 10, 59, 12, 59, 1062, 9, 59, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 1068, 8, 60, 10, 60, 12, 60, 1071, 9, 60, 1, 60, 3, 60, 1074, 8, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 1081, 8, 60, 10, 60, 12, 60, 1084, 9, 60, 1, 60, 3, 60, 1087, 8, 60, 1, 60, 1, 60, 3, 60, 1091, 8, 60, 1, 60, 1, 60, 1, 60, 3, 60, 1096, 8, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 1103, 8, 61, 1, 62, 1, 62, 1, 62, 5, 62, 1108, 8, 62, 10, 62, 12, 62, 1111, 9, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 5, 62, 1119, 8, 62, 10, 62, 12, 62, 1122, 9, 62, 1, 62, 1, 62, 5, 62, 1126, 8, 62, 10, 62, 12, 62, 1129, 9, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 3, 62, 1136, 8, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 3, 63, 1149, 8, 63, 1, 64, 1, 64, 1, 64, 5, 64, 1154, 8, 64, 10, 64, 12, 64, 1157, 9, 64, 1, 64, 3, 64, 1160, 8, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 3, 65, 1172, 8, 65, 1, 66, 1, 66, 1, 66, 1, 66, 3, 66, 1178, 8, 66, 1, 66, 3, 66, 1181, 8, 66, 1, 67, 1, 67, 1, 67, 5, 67, 1186, 8, 67, 10, 67, 12, 67, 1189, 9, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 3, 68, 1200, 8, 68, 1, 68, 1, 68, 1, 68, 1, 68, 3, 68, 1206, 8, 68, 5, 68, 1208, 8, 68, 10, 68, 12, 68, 1211, 9, 68, 1, 69, 1, 69, 1, 69, 3, 69, 1216, 8, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 3, 70, 1223, 8, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 5, 71, 1230, 8, 71, 10, 71, 12, 71, 1233, 9, 71, 1, 71, 3, 71, 1236, 8, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 3, 73, 1246, 8, 73, 3, 73, 1248, 8, 73, 1, 74, 3, 74, 1251, 8, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 3, 74, 1259, 8, 74, 1, 75, 1, 75, 1, 75, 3, 75, 1264, 8, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 79, 1, 79, 3, 79, 1274, 8, 79, 1, 80, 1, 80, 1, 80, 3, 80, 1279, 8, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 3, 83, 1291, 8, 83, 1, 84, 1, 84, 5, 84, 1295, 8, 84, 10, 84, 12, 84, 1298, 9, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 3, 85, 1307, 8, 85, 1, 86, 1, 86, 5, 86, 1311, 8, 86, 10, 86, 12, 86, 1314, 9, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 3, 87, 1323, 8, 87, 1, 87, 0, 3, 80, 118, 136, 88, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 0, 17, 2, 0, 32, 32, 37, 37, 2, 0, 18, 18, 77, 77, 2, 0, 46, 46, 54, 54, 3, 0, 1, 1, 4, 4, 8, 8, 4, 0, 1, 1, 3, 4, 8, 8, 83, 83, 2, 0, 54, 54, 76, 76, 2, 0, 1, 1, 4, 4, 2, 0, 7, 7, 22, 23, 2, 0, 31, 31, 52, 52, 2, 0, 74, 74, 79, 79, 3, 0, 10, 10, 53, 53, 93, 93, 2, 0, 43, 43, 56, 56, 1, 0, 110, 111, 2, 0, 121, 121, 142, 142, 7, 0, 21, 21, 40, 40, 58, 59, 73, 73, 81, 81, 100, 100, 106, 106, 19, 0, 1, 13, 15, 20, 22, 26, 28, 29, 31, 31, 33, 36, 38, 39, 41, 44, 46, 46, 48, 54, 56, 57, 61, 61, 63, 72, 74, 80, 82, 86, 88, 95, 97, 99, 101, 102, 104, 105, 4, 0, 20, 20, 31, 31, 41, 41, 51, 51, 1500, 0, 179, 1, 0, 0, 0, 2, 186, 1, 0, 0, 0, 4, 188, 1, 0, 0, 0, 6, 190, 1, 0, 0, 0, 8, 197, 1, 0, 0, 0, 10, 220, 1, 0, 0, 0, 12, 222, 1, 0, 0, 0, 14, 229, 1, 0, 0, 0, 16, 236, 1, 0, 0, 0, 18, 249, 1, 0, 0, 0, 20, 261, 1, 0, 0, 0, 22, 270, 1, 0, 0, 0, 24, 278, 1, 0, 0, 0, 26, 300, 1, 0, 0, 0, 28, 315, 1, 0, 0, 0, 30, 324, 1, 0, 0, 0, 32, 329, 1, 0, 0, 0, 34, 333, 1, 0, 0, 0, 36, 335, 1, 0, 0, 0, 38, 344, 1, 0, 0, 0, 40, 348, 1, 0, 0, 0, 42, 362, 1, 0, 0, 0, 44, 372, 1, 0, 0, 0, 46, 382, 1, 0, 0, 0, 48, 386, 1, 0, 0, 0, 50, 394, 1, 0, 0, 0, 52, 443, 1, 0, 0, 0, 54, 446, 1, 0, 0, 0, 56, 452, 1, 0, 0, 0, 58, 456, 1, 0, 0, 0, 60, 462, 1, 0, 0, 0, 62, 480, 1, 0, 0, 0, 64, 483, 1, 0, 0, 0, 66, 486, 1, 0, 0, 0, 68, 496, 1, 0, 0, 0, 70, 499, 1, 0, 0, 0, 72, 503, 1, 0, 0, 0, 74, 536, 1, 0, 0, 0, 76, 538, 1, 0, 0, 0, 78, 541, 1, 0, 0, 0, 80, 556, 1, 0, 0, 0, 82, 618, 1, 0, 0, 0, 84, 623, 1, 0, 0, 0, 86, 634, 1, 0, 0, 0, 88, 636, 1, 0, 0, 0, 90, 642, 1, 0, 0, 0, 92, 650, 1, 0, 0, 0, 94, 668, 1, 0, 0, 0, 96, 670, 1, 0, 0, 0, 98, 678, 1, 0, 0, 0, 100, 683, 1, 0, 0, 0, 102, 691, 1, 0, 0, 0, 104, 695, 1, 0, 0, 0, 106, 699, 1, 0, 0, 0, 108, 708, 1, 0, 0, 0, 110, 722, 1, 0, 0, 0, 112, 724, 1, 0, 0, 0, 114, 783, 1, 0, 0, 0, 116, 785, 1, 0, 0, 0, 118, 948, 1, 0, 0, 0, 120, 1090, 1, 0, 0, 0, 122, 1102, 1, 0, 0, 0, 124, 1135, 1, 0, 0, 0, 126, 1148, 1, 0, 0, 0, 128, 1150, 1, 0, 0, 0, 130, 1171, 1, 0, 0, 0, 132, 1180, 1, 0, 0, 0, 134, 1182, 1, 0, 0, 0, 136, 1199, 1, 0, 0, 0, 138, 1212, 1, 0, 0, 0, 140, 1222, 1, 0, 0, 0, 142, 1226, 1, 0, 0, 0, 144, 1237, 1, 0, 0, 0, 146, 1247, 1, 0, 0, 0, 148, 1250, 1, 0, 0, 0, 150, 1263, 1, 0, 0, 0, 152, 1265, 1, 0, 0, 0, 154, 1267, 1, 0, 0, 0, 156, 1269, 1, 0, 0, 0, 158, 1273, 1, 0, 0, 0, 160, 1278, 1, 0, 0, 0, 162, 1280, 1, 0, 0, 0, 164, 1284, 1, 0, 0, 0, 166, 1290, 1, 0, 0, 0, 168, 1292, 1, 0, 0, 0, 170, 1306, 1, 0, 0, 0, 172, 1308, 1, 0, 0, 0, 174, 1322, 1, 0, 0, 0, 176, 178, 3, 2, 1, 0, 177, 176, 1, 0, 0, 0, 178, 181, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 179, 180, 1, 0, 0, 0, 180, 182, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 182, 183, 5, 0, 0, 1, 183, 1, 1, 0, 0, 0, 184, 187, 3, 6, 3, 0, 185, 187, 3, 10, 5, 0, 186, 184, 1, 0, 0, 0, 186, 185, 1, 0, 0, 0, 187, 3, 1, 0, 0, 0, 188, 189, 3, 118, 59, 0, 189, 5, 1, 0, 0, 0, 190, 191, 5, 55, 0, 0, 191, 195, 3, 160, 80, 0, 192, 193, 5, 118, 0, 0, 193, 194, 5, 125, 0, 0, 194, 196, 3, 4, 2, 0, 195, 192, 1, 0, 0, 0, 195, 196, 1, 0, 0, 0, 196, 7, 1, 0, 0, 0, 197, 202, 3, 160, 80, 0, 198, 199, 5, 119, 0, 0, 199, 201, 3, 160, 80, 0, 200, 198, 1, 0, 0, 0, 201, 204, 1, 0, 0, 0, 202, 200, 1, 0, 0, 0, 202, 203, 1, 0, 0, 0, 203, 206, 1, 0, 0, 0, 204, 202, 1, 0, 0, 0, 205, 207, 5, 119, 0, 0, 206, 205, 1, 0, 0, 0, 206, 207, 1, 0, 0, 0, 207, 9, 1, 0, 0, 0, 208, 221, 3, 12, 6, 0, 209, 221, 3, 14, 7, 0, 210, 221, 3, 18, 9, 0, 211, 221, 3, 20, 10, 0, 212, 221, 3, 22, 11, 0, 213, 221, 3, 26, 13, 0, 214, 221, 3, 24, 12, 0, 215, 221, 3, 28, 14, 0, 216, 221, 3, 30, 15, 0, 217, 221, 3, 36, 18, 0, 218, 221, 3, 32, 16, 0, 219, 221, 3, 34, 17, 0, 220, 208, 1, 0, 0, 0, 220, 209, 1, 0, 0, 0, 220, 210, 1, 0, 0, 0, 220, 211, 1, 0, 0, 0, 220, 212, 1, 0, 0, 0, 220, 213, 1, 0, 0, 0, 220, 214, 1, 0, 0, 0, 220, 215, 1, 0, 0, 0, 220, 216, 1, 0, 0, 0, 220, 217, 1, 0, 0, 0, 220, 218, 1, 0, 0, 0, 220, 219, 1, 0, 0, 0, 221, 11, 1, 0, 0, 0, 222, 224, 5, 75, 0, 0, 223, 225, 3, 4, 2, 0, 224, 223, 1, 0, 0, 0, 224, 225, 1, 0, 0, 0, 225, 227, 1, 0, 0, 0, 226, 228, 5, 153, 0, 0, 227, 226, 1, 0, 0, 0, 227, 228, 1, 0, 0, 0, 228, 13, 1, 0, 0, 0, 229, 231, 5, 87, 0, 0, 230, 232, 3, 4, 2, 0, 231, 230, 1, 0, 0, 0, 231, 232, 1, 0, 0, 0, 232, 234, 1, 0, 0, 0, 233, 235, 5, 153, 0, 0, 234, 233, 1, 0, 0, 0, 234, 235, 1, 0, 0, 0, 235, 15, 1, 0, 0, 0, 236, 245, 5, 14, 0, 0, 237, 238, 5, 133, 0, 0, 238, 241, 3, 160, 80, 0, 239, 240, 5, 118, 0, 0, 240, 242, 3, 160, 80, 0, 241, 239, 1, 0, 0, 0, 241, 242, 1, 0, 0, 0, 242, 243, 1, 0, 0, 0, 243, 244, 5, 152, 0, 0, 244, 246, 1, 0, 0, 0, 245, 237, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 247, 1, 0, 0, 0, 247, 248, 3, 36, 18, 0, 248, 17, 1, 0, 0, 0, 249, 250, 5, 96, 0, 0, 250, 254, 3, 36, 18, 0, 251, 253, 3, 16, 8, 0, 252, 251, 1, 0, 0, 0, 253, 256, 1, 0, 0, 0, 254, 252, 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 259, 1, 0, 0, 0, 256, 254, 1, 0, 0, 0, 257, 258, 5, 30, 0, 0, 258, 260, 3, 36, 18, 0, 259, 257, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, 19, 1, 0, 0, 0, 261, 262, 5, 42, 0, 0, 262, 263, 5, 133, 0, 0, 263, 264, 3, 4, 2, 0, 264, 265, 5, 152, 0, 0, 265, 268, 3, 10, 5, 0, 266, 267, 5, 25, 0, 0, 267, 269, 3, 10, 5, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 21, 1, 0, 0, 0, 270, 271, 5, 103, 0, 0, 271, 272, 5, 133, 0, 0, 272, 273, 3, 4, 2, 0, 273, 274, 5, 152, 0, 0, 274, 276, 3, 10, 5, 0, 275, 277, 5, 153, 0, 0, 276, 275, 1, 0, 0, 0, 276, 277, 1, 0, 0, 0, 277, 23, 1, 0, 0, 0, 278, 279, 5, 34, 0, 0, 279, 283, 5, 133, 0, 0, 280, 284, 3, 6, 3, 0, 281, 284, 3, 30, 15, 0, 282, 284, 3, 4, 2, 0, 283, 280, 1, 0, 0, 0, 283, 281, 1, 0, 0, 0, 283, 282, 1, 0, 0, 0, 283, 284, 1, 0, 0, 0, 284, 285, 1, 0, 0, 0, 285, 287, 5, 153, 0, 0, 286, 288, 3, 4, 2, 0, 287, 286, 1, 0, 0, 0, 287, 288, 1, 0, 0, 0, 288, 289, 1, 0, 0, 0, 289, 293, 5, 153, 0, 0, 290, 294, 3, 6, 3, 0, 291, 294, 3, 30, 15, 0, 292, 294, 3, 4, 2, 0, 293, 290, 1, 0, 0, 0, 293, 291, 1, 0, 0, 0, 293, 292, 1, 0, 0, 0, 293, 294, 1, 0, 0, 0, 294, 295, 1, 0, 0, 0, 295, 296, 5, 152, 0, 0, 296, 298, 3, 10, 5, 0, 297, 299, 5, 153, 0, 0, 298, 297, 1, 0, 0, 0, 298, 299, 1, 0, 0, 0, 299, 25, 1, 0, 0, 0, 300, 301, 5, 34, 0, 0, 301, 302, 5, 133, 0, 0, 302, 303, 5, 55, 0, 0, 303, 306, 3, 160, 80, 0, 304, 305, 5, 119, 0, 0, 305, 307, 3, 160, 80, 0, 306, 304, 1, 0, 0, 0, 306, 307, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308, 309, 5, 44, 0, 0, 309, 310, 3, 4, 2, 0, 310, 311, 5, 152, 0, 0, 311, 313, 3, 10, 5, 0, 312, 314, 5, 153, 0, 0, 313, 312, 1, 0, 0, 0, 313, 314, 1, 0, 0, 0, 314, 27, 1, 0, 0, 0, 315, 316, 7, 0, 0, 0, 316, 317, 3, 160, 80, 0, 317, 319, 5, 133, 0, 0, 318, 320, 3, 8, 4, 0, 319, 318, 1, 0, 0, 0, 319, 320, 1, 0, 0, 0, 320, 321, 1, 0, 0, 0, 321, 322, 5, 152, 0, 0, 322, 323, 3, 36, 18, 0, 323, 29, 1, 0, 0, 0, 324, 325, 3, 4, 2, 0, 325, 326, 5, 118, 0, 0, 326, 327, 5, 125, 0, 0, 327, 328, 3, 4, 2, 0, 328, 31, 1, 0, 0, 0, 329, 331, 3, 4, 2, 0, 330, 332, 5, 153, 0, 0, 331, 330, 1, 0, 0, 0, 331, 332, 1, 0, 0, 0, 332, 33, 1, 0, 0, 0, 333, 334, 5, 153, 0, 0, 334, 35, 1, 0, 0, 0, 335, 339, 5, 131, 0, 0, 336, 338, 3, 2, 1, 0, 337, 336, 1, 0, 0, 0, 338, 341, 1, 0, 0, 0, 339, 337, 1, 0, 0, 0, 339, 340, 1, 0, 0, 0, 340, 342, 1, 0, 0, 0, 341, 339, 1, 0, 0, 0, 342, 343, 5, 150, 0, 0, 343, 37, 1, 0, 0, 0, 344, 345, 3, 4, 2, 0, 345, 346, 5, 118, 0, 0, 346, 347, 3, 4, 2, 0, 347, 39, 1, 0, 0, 0, 348, 353, 3, 38, 19, 0, 349, 350, 5, 119, 0, 0, 350, 352, 3, 38, 19, 0, 351, 349, 1, 0, 0, 0, 352, 355, 1, 0, 0, 0, 353, 351, 1, 0, 0, 0, 353, 354, 1, 0, 0, 0, 354, 357, 1, 0, 0, 0, 355, 353, 1, 0, 0, 0, 356, 358, 5, 119, 0, 0, 357, 356, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 41, 1, 0, 0, 0, 359, 363, 3, 48, 24, 0, 360, 363, 3, 50, 25, 0, 361, 363, 3, 124, 62, 0, 362, 359, 1, 0, 0, 0, 362, 360, 1, 0, 0, 0, 362, 361, 1, 0, 0, 0, 363, 364, 1, 0, 0, 0, 364, 365, 5, 0, 0, 1, 365, 43, 1, 0, 0, 0, 366, 373, 3, 50, 25, 0, 367, 368, 5, 133, 0, 0, 368, 369, 3, 48, 24, 0, 369, 370, 5, 152, 0, 0, 370, 373, 1, 0, 0, 0, 371, 373, 3, 164, 82, 0, 372, 366, 1, 0, 0, 0, 372, 367, 1, 0, 0, 0, 372, 371, 1, 0, 0, 0, 373, 45, 1, 0, 0, 0, 374, 383, 5, 27, 0, 0, 375, 376, 5, 98, 0, 0, 376, 383, 5, 1, 0, 0, 377, 378, 5, 98, 0, 0, 378, 383, 5, 24, 0, 0, 379, 383, 5, 47, 0, 0, 380, 381, 5, 47, 0, 0, 381, 383, 5, 24, 0, 0, 382, 374, 1, 0, 0, 0, 382, 375, 1, 0, 0, 0, 382, 377, 1, 0, 0, 0, 382, 379, 1, 0, 0, 0, 382, 380, 1, 0, 0, 0, 383, 384, 1, 0, 0, 0, 384, 385, 3, 44, 22, 0, 385, 47, 1, 0, 0, 0, 386, 390, 3, 44, 22, 0, 387, 389, 3, 46, 23, 0, 388, 387, 1, 0, 0, 0, 389, 392, 1, 0, 0, 0, 390, 388, 1, 0, 0, 0, 390, 391, 1, 0, 0, 0, 391, 49, 1, 0, 0, 0, 392, 390, 1, 0, 0, 0, 393, 395, 3, 52, 26, 0, 394, 393, 1, 0, 0, 0, 394, 395, 1, 0, 0, 0, 395, 396, 1, 0, 0, 0, 396, 398, 5, 82, 0, 0, 397, 399, 5, 24, 0, 0, 398, 397, 1, 0, 0, 0, 398, 399, 1, 0, 0, 0, 399, 401, 1, 0, 0, 0, 400, 402, 3, 54, 27, 0, 401, 400, 1, 0, 0, 0, 401, 402, 1, 0, 0, 0, 402, 403, 1, 0, 0, 0, 403, 405, 3, 116, 58, 0, 404, 406, 3, 56, 28, 0, 405, 404, 1, 0, 0, 0, 405, 406, 1, 0, 0, 0, 406, 408, 1, 0, 0, 0, 407, 409, 3, 58, 29, 0, 408, 407, 1, 0, 0, 0, 408, 409, 1, 0, 0, 0, 409, 411, 1, 0, 0, 0, 410, 412, 3, 62, 31, 0, 411, 410, 1, 0, 0, 0, 411, 412, 1, 0, 0, 0, 412, 414, 1, 0, 0, 0, 413, 415, 3, 64, 32, 0, 414, 413, 1, 0, 0, 0, 414, 415, 1, 0, 0, 0, 415, 417, 1, 0, 0, 0, 416, 418, 3, 66, 33, 0, 417, 416, 1, 0, 0, 0, 417, 418, 1, 0, 0, 0, 418, 421, 1, 0, 0, 0, 419, 420, 5, 105, 0, 0, 420, 422, 7, 1, 0, 0, 421, 419, 1, 0, 0, 0, 421, 422, 1, 0, 0, 0, 422, 425, 1, 0, 0, 0, 423, 424, 5, 105, 0, 0, 424, 426, 5, 92, 0, 0, 425, 423, 1, 0, 0, 0, 425, 426, 1, 0, 0, 0, 426, 428, 1, 0, 0, 0, 427, 429, 3, 68, 34, 0, 428, 427, 1, 0, 0, 0, 428, 429, 1, 0, 0, 0, 429, 431, 1, 0, 0, 0, 430, 432, 3, 60, 30, 0, 431, 430, 1, 0, 0, 0, 431, 432, 1, 0, 0, 0, 432, 434, 1, 0, 0, 0, 433, 435, 3, 70, 35, 0, 434, 433, 1, 0, 0, 0, 434, 435, 1, 0, 0, 0, 435, 438, 1, 0, 0, 0, 436, 439, 3, 74, 37, 0, 437, 439, 3, 76, 38, 0, 438, 436, 1, 0, 0, 0, 438, 437, 1, 0, 0, 0, 438, 439, 1, 0, 0, 0, 439, 441, 1, 0, 0, 0, 440, 442, 3, 78, 39, 0, 441, 440, 1, 0, 0, 0, 441, 442, 1, 0, 0, 0, 442, 51, 1, 0, 0, 0, 443, 444, 5, 105, 0, 0, 444, 445, 3, 128, 64, 0, 445, 53, 1, 0, 0, 0, 446, 447, 5, 91, 0, 0, 447, 450, 5, 111, 0, 0, 448, 449, 5, 105, 0, 0, 449, 451, 5, 88, 0, 0, 450, 448, 1, 0, 0, 0, 450, 451, 1, 0, 0, 0, 451, 55, 1, 0, 0, 0, 452, 453, 5, 35, 0, 0, 453, 454, 3, 80, 40, 0, 454, 57, 1, 0, 0, 0, 455, 457, 7, 2, 0, 0, 456, 455, 1, 0, 0, 0, 456, 457, 1, 0, 0, 0, 457, 458, 1, 0, 0, 0, 458, 459, 5, 5, 0, 0, 459, 460, 5, 50, 0, 0, 460, 461, 3, 116, 58, 0, 461, 59, 1, 0, 0, 0, 462, 463, 5, 104, 0, 0, 463, 464, 3, 160, 80, 0, 464, 465, 5, 6, 0, 0, 465, 466, 5, 133, 0, 0, 466, 467, 3, 100, 50, 0, 467, 477, 5, 152, 0, 0, 468, 469, 5, 119, 0, 0, 469, 470, 3, 160, 80, 0, 470, 471, 5, 6, 0, 0, 471, 472, 5, 133, 0, 0, 472, 473, 3, 100, 50, 0, 473, 474, 5, 152, 0, 0, 474, 476, 1, 0, 0, 0, 475, 468, 1, 0, 0, 0, 476, 479, 1, 0, 0, 0, 477, 475, 1, 0, 0, 0, 477, 478, 1, 0, 0, 0, 478, 61, 1, 0, 0, 0, 479, 477, 1, 0, 0, 0, 480, 481, 5, 72, 0, 0, 481, 482, 3, 118, 59, 0, 482, 63, 1, 0, 0, 0, 483, 484, 5, 102, 0, 0, 484, 485, 3, 118, 59, 0, 485, 65, 1, 0, 0, 0, 486, 487, 5, 38, 0, 0, 487, 494, 5, 11, 0, 0, 488, 489, 7, 1, 0, 0, 489, 490, 5, 133, 0, 0, 490, 491, 3, 116, 58, 0, 491, 492, 5, 152, 0, 0, 492, 495, 1, 0, 0, 0, 493, 495, 3, 116, 58, 0, 494, 488, 1, 0, 0, 0, 494, 493, 1, 0, 0, 0, 495, 67, 1, 0, 0, 0, 496, 497, 5, 39, 0, 0, 497, 498, 3, 118, 59, 0, 498, 69, 1, 0, 0, 0, 499, 500, 5, 67, 0, 0, 500, 501, 5, 11, 0, 0, 501, 502, 3, 90, 45, 0, 502, 71, 1, 0, 0, 0, 503, 504, 5, 67, 0, 0, 504, 505, 5, 11, 0, 0, 505, 506, 3, 116, 58, 0, 506, 73, 1, 0, 0, 0, 507, 508, 5, 57, 0, 0, 508, 511, 3, 118, 59, 0, 509, 510, 5, 119, 0, 0, 510, 512, 3, 118, 59, 0, 511, 509, 1, 0, 0, 0, 511, 512, 1, 0, 0, 0, 512, 517, 1, 0, 0, 0, 513, 514, 5, 105, 0, 0, 514, 518, 5, 88, 0, 0, 515, 516, 5, 11, 0, 0, 516, 518, 3, 116, 58, 0, 517, 513, 1, 0, 0, 0, 517, 515, 1, 0, 0, 0, 517, 518, 1, 0, 0, 0, 518, 537, 1, 0, 0, 0, 519, 520, 5, 57, 0, 0, 520, 523, 3, 118, 59, 0, 521, 522, 5, 105, 0, 0, 522, 524, 5, 88, 0, 0, 523, 521, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 525, 1, 0, 0, 0, 525, 526, 5, 64, 0, 0, 526, 527, 3, 118, 59, 0, 527, 537, 1, 0, 0, 0, 528, 529, 5, 57, 0, 0, 529, 530, 3, 118, 59, 0, 530, 531, 5, 64, 0, 0, 531, 534, 3, 118, 59, 0, 532, 533, 5, 11, 0, 0, 533, 535, 3, 116, 58, 0, 534, 532, 1, 0, 0, 0, 534, 535, 1, 0, 0, 0, 535, 537, 1, 0, 0, 0, 536, 507, 1, 0, 0, 0, 536, 519, 1, 0, 0, 0, 536, 528, 1, 0, 0, 0, 537, 75, 1, 0, 0, 0, 538, 539, 5, 64, 0, 0, 539, 540, 3, 118, 59, 0, 540, 77, 1, 0, 0, 0, 541, 542, 5, 84, 0, 0, 542, 543, 3, 96, 48, 0, 543, 79, 1, 0, 0, 0, 544, 545, 6, 40, -1, 0, 545, 547, 3, 136, 68, 0, 546, 548, 5, 29, 0, 0, 547, 546, 1, 0, 0, 0, 547, 548, 1, 0, 0, 0, 548, 550, 1, 0, 0, 0, 549, 551, 3, 88, 44, 0, 550, 549, 1, 0, 0, 0, 550, 551, 1, 0, 0, 0, 551, 557, 1, 0, 0, 0, 552, 553, 5, 133, 0, 0, 553, 554, 3, 80, 40, 0, 554, 555, 5, 152, 0, 0, 555, 557, 1, 0, 0, 0, 556, 544, 1, 0, 0, 0, 556, 552, 1, 0, 0, 0, 557, 572, 1, 0, 0, 0, 558, 559, 10, 3, 0, 0, 559, 560, 3, 84, 42, 0, 560, 561, 3, 80, 40, 4, 561, 571, 1, 0, 0, 0, 562, 564, 10, 4, 0, 0, 563, 565, 3, 82, 41, 0, 564, 563, 1, 0, 0, 0, 564, 565, 1, 0, 0, 0, 565, 566, 1, 0, 0, 0, 566, 567, 5, 50, 0, 0, 567, 568, 3, 80, 40, 0, 568, 569, 3, 86, 43, 0, 569, 571, 1, 0, 0, 0, 570, 558, 1, 0, 0, 0, 570, 562, 1, 0, 0, 0, 571, 574, 1, 0, 0, 0, 572, 570, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 81, 1, 0, 0, 0, 574, 572, 1, 0, 0, 0, 575, 577, 7, 3, 0, 0, 576, 575, 1, 0, 0, 0, 576, 577, 1, 0, 0, 0, 577, 578, 1, 0, 0, 0, 578, 585, 5, 46, 0, 0, 579, 581, 5, 46, 0, 0, 580, 582, 7, 3, 0, 0, 581, 580, 1, 0, 0, 0, 581, 582, 1, 0, 0, 0, 582, 585, 1, 0, 0, 0, 583, 585, 7, 3, 0, 0, 584, 576, 1, 0, 0, 0, 584, 579, 1, 0, 0, 0, 584, 583, 1, 0, 0, 0, 585, 619, 1, 0, 0, 0, 586, 588, 7, 4, 0, 0, 587, 586, 1, 0, 0, 0, 587, 588, 1, 0, 0, 0, 588, 589, 1, 0, 0, 0, 589, 591, 7, 5, 0, 0, 590, 592, 5, 68, 0, 0, 591, 590, 1, 0, 0, 0, 591, 592, 1, 0, 0, 0, 592, 601, 1, 0, 0, 0, 593, 595, 7, 5, 0, 0, 594, 596, 5, 68, 0, 0, 595, 594, 1, 0, 0, 0, 595, 596, 1, 0, 0, 0, 596, 598, 1, 0, 0, 0, 597, 599, 7, 4, 0, 0, 598, 597, 1, 0, 0, 0, 598, 599, 1, 0, 0, 0, 599, 601, 1, 0, 0, 0, 600, 587, 1, 0, 0, 0, 600, 593, 1, 0, 0, 0, 601, 619, 1, 0, 0, 0, 602, 604, 7, 6, 0, 0, 603, 602, 1, 0, 0, 0, 603, 604, 1, 0, 0, 0, 604, 605, 1, 0, 0, 0, 605, 607, 5, 36, 0, 0, 606, 608, 5, 68, 0, 0, 607, 606, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 617, 1, 0, 0, 0, 609, 611, 5, 36, 0, 0, 610, 612, 5, 68, 0, 0, 611, 610, 1, 0, 0, 0, 611, 612, 1, 0, 0, 0, 612, 614, 1, 0, 0, 0, 613, 615, 7, 6, 0, 0, 614, 613, 1, 0, 0, 0, 614, 615, 1, 0, 0, 0, 615, 617, 1, 0, 0, 0, 616, 603, 1, 0, 0, 0, 616, 609, 1, 0, 0, 0, 617, 619, 1, 0, 0, 0, 618, 584, 1, 0, 0, 0, 618, 600, 1, 0, 0, 0, 618, 616, 1, 0, 0, 0, 619, 83, 1, 0, 0, 0, 620, 621, 5, 17, 0, 0, 621, 624, 5, 50, 0, 0, 622, 624, 5, 119, 0, 0, 623, 620, 1, 0, 0, 0, 623, 622, 1, 0, 0, 0, 624, 85, 1, 0, 0, 0, 625, 626, 5, 65, 0, 0, 626, 635, 3, 116, 58, 0, 627, 628, 5, 99, 0, 0, 628, 629, 5, 133, 0, 0, 629, 630, 3, 116, 58, 0, 630, 631, 5, 152, 0, 0, 631, 635, 1, 0, 0, 0, 632, 633, 5, 99, 0, 0, 633, 635, 3, 116, 58, 0, 634, 625, 1, 0, 0, 0, 634, 627, 1, 0, 0, 0, 634, 632, 1, 0, 0, 0, 635, 87, 1, 0, 0, 0, 636, 637, 5, 80, 0, 0, 637, 640, 3, 94, 47, 0, 638, 639, 5, 64, 0, 0, 639, 641, 3, 94, 47, 0, 640, 638, 1, 0, 0, 0, 640, 641, 1, 0, 0, 0, 641, 89, 1, 0, 0, 0, 642, 647, 3, 92, 46, 0, 643, 644, 5, 119, 0, 0, 644, 646, 3, 92, 46, 0, 645, 643, 1, 0, 0, 0, 646, 649, 1, 0, 0, 0, 647, 645, 1, 0, 0, 0, 647, 648, 1, 0, 0, 0, 648, 91, 1, 0, 0, 0, 649, 647, 1, 0, 0, 0, 650, 652, 3, 118, 59, 0, 651, 653, 7, 7, 0, 0, 652, 651, 1, 0, 0, 0, 652, 653, 1, 0, 0, 0, 653, 656, 1, 0, 0, 0, 654, 655, 5, 63, 0, 0, 655, 657, 7, 8, 0, 0, 656, 654, 1, 0, 0, 0, 656, 657, 1, 0, 0, 0, 657, 660, 1, 0, 0, 0, 658, 659, 5, 16, 0, 0, 659, 661, 5, 113, 0, 0, 660, 658, 1, 0, 0, 0, 660, 661, 1, 0, 0, 0, 661, 93, 1, 0, 0, 0, 662, 669, 3, 164, 82, 0, 663, 666, 3, 148, 74, 0, 664, 665, 5, 154, 0, 0, 665, 667, 3, 148, 74, 0, 666, 664, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 669, 1, 0, 0, 0, 668, 662, 1, 0, 0, 0, 668, 663, 1, 0, 0, 0, 669, 95, 1, 0, 0, 0, 670, 675, 3, 98, 49, 0, 671, 672, 5, 119, 0, 0, 672, 674, 3, 98, 49, 0, 673, 671, 1, 0, 0, 0, 674, 677, 1, 0, 0, 0, 675, 673, 1, 0, 0, 0, 675, 676, 1, 0, 0, 0, 676, 97, 1, 0, 0, 0, 677, 675, 1, 0, 0, 0, 678, 679, 3, 160, 80, 0, 679, 680, 5, 125, 0, 0, 680, 681, 3, 150, 75, 0, 681, 99, 1, 0, 0, 0, 682, 684, 3, 102, 51, 0, 683, 682, 1, 0, 0, 0, 683, 684, 1, 0, 0, 0, 684, 686, 1, 0, 0, 0, 685, 687, 3, 104, 52, 0, 686, 685, 1, 0, 0, 0, 686, 687, 1, 0, 0, 0, 687, 689, 1, 0, 0, 0, 688, 690, 3, 106, 53, 0, 689, 688, 1, 0, 0, 0, 689, 690, 1, 0, 0, 0, 690, 101, 1, 0, 0, 0, 691, 692, 5, 70, 0, 0, 692, 693, 5, 11, 0, 0, 693, 694, 3, 116, 58, 0, 694, 103, 1, 0, 0, 0, 695, 696, 5, 67, 0, 0, 696, 697, 5, 11, 0, 0, 697, 698, 3, 90, 45, 0, 698, 105, 1, 0, 0, 0, 699, 700, 7, 9, 0, 0, 700, 701, 3, 108, 54, 0, 701, 107, 1, 0, 0, 0, 702, 709, 3, 110, 55, 0, 703, 704, 5, 9, 0, 0, 704, 705, 3, 110, 55, 0, 705, 706, 5, 2, 0, 0, 706, 707, 3, 110, 55, 0, 707, 709, 1, 0, 0, 0, 708, 702, 1, 0, 0, 0, 708, 703, 1, 0, 0, 0, 709, 109, 1, 0, 0, 0, 710, 711, 5, 19, 0, 0, 711, 723, 5, 78, 0, 0, 712, 713, 5, 97, 0, 0, 713, 723, 5, 71, 0, 0, 714, 715, 5, 97, 0, 0, 715, 723, 5, 33, 0, 0, 716, 717, 3, 148, 74, 0, 717, 718, 5, 71, 0, 0, 718, 723, 1, 0, 0, 0, 719, 720, 3, 148, 74, 0, 720, 721, 5, 33, 0, 0, 721, 723, 1, 0, 0, 0, 722, 710, 1, 0, 0, 0, 722, 712, 1, 0, 0, 0, 722, 714, 1, 0, 0, 0, 722, 716, 1, 0, 0, 0, 722, 719, 1, 0, 0, 0, 723, 111, 1, 0, 0, 0, 724, 725, 3, 118, 59, 0, 725, 726, 5, 0, 0, 1, 726, 113, 1, 0, 0, 0, 727, 784, 3, 160, 80, 0, 728, 729, 3, 160, 80, 0, 729, 730, 5, 133, 0, 0, 730, 731, 3, 160, 80, 0, 731, 738, 3, 114, 57, 0, 732, 733, 5, 119, 0, 0, 733, 734, 3, 160, 80, 0, 734, 735, 3, 114, 57, 0, 735, 737, 1, 0, 0, 0, 736, 732, 1, 0, 0, 0, 737, 740, 1, 0, 0, 0, 738, 736, 1, 0, 0, 0, 738, 739, 1, 0, 0, 0, 739, 742, 1, 0, 0, 0, 740, 738, 1, 0, 0, 0, 741, 743, 5, 119, 0, 0, 742, 741, 1, 0, 0, 0, 742, 743, 1, 0, 0, 0, 743, 744, 1, 0, 0, 0, 744, 745, 5, 152, 0, 0, 745, 784, 1, 0, 0, 0, 746, 747, 3, 160, 80, 0, 747, 748, 5, 133, 0, 0, 748, 753, 3, 162, 81, 0, 749, 750, 5, 119, 0, 0, 750, 752, 3, 162, 81, 0, 751, 749, 1, 0, 0, 0, 752, 755, 1, 0, 0, 0, 753, 751, 1, 0, 0, 0, 753, 754, 1, 0, 0, 0, 754, 757, 1, 0, 0, 0, 755, 753, 1, 0, 0, 0, 756, 758, 5, 119, 0, 0, 757, 756, 1, 0, 0, 0, 757, 758, 1, 0, 0, 0, 758, 759, 1, 0, 0, 0, 759, 760, 5, 152, 0, 0, 760, 784, 1, 0, 0, 0, 761, 762, 3, 160, 80, 0, 762, 763, 5, 133, 0, 0, 763, 768, 3, 114, 57, 0, 764, 765, 5, 119, 0, 0, 765, 767, 3, 114, 57, 0, 766, 764, 1, 0, 0, 0, 767, 770, 1, 0, 0, 0, 768, 766, 1, 0, 0, 0, 768, 769, 1, 0, 0, 0, 769, 772, 1, 0, 0, 0, 770, 768, 1, 0, 0, 0, 771, 773, 5, 119, 0, 0, 772, 771, 1, 0, 0, 0, 772, 773, 1, 0, 0, 0, 773, 774, 1, 0, 0, 0, 774, 775, 5, 152, 0, 0, 775, 784, 1, 0, 0, 0, 776, 777, 3, 160, 80, 0, 777, 779, 5, 133, 0, 0, 778, 780, 3, 116, 58, 0, 779, 778, 1, 0, 0, 0, 779, 780, 1, 0, 0, 0, 780, 781, 1, 0, 0, 0, 781, 782, 5, 152, 0, 0, 782, 784, 1, 0, 0, 0, 783, 727, 1, 0, 0, 0, 783, 728, 1, 0, 0, 0, 783, 746, 1, 0, 0, 0, 783, 761, 1, 0, 0, 0, 783, 776, 1, 0, 0, 0, 784, 115, 1, 0, 0, 0, 785, 790, 3, 118, 59, 0, 786, 787, 5, 119, 0, 0, 787, 789, 3, 118, 59, 0, 788, 786, 1, 0, 0, 0, 789, 792, 1, 0, 0, 0, 790, 788, 1, 0, 0, 0, 790, 791, 1, 0, 0, 0, 791, 794, 1, 0, 0, 0, 792, 790, 1, 0, 0, 0, 793, 795, 5, 119, 0, 0, 794, 793, 1, 0, 0, 0, 794, 795, 1, 0, 0, 0, 795, 117, 1, 0, 0, 0, 796, 797, 6, 59, -1, 0, 797, 799, 5, 12, 0, 0, 798, 800, 3, 118, 59, 0, 799, 798, 1, 0, 0, 0, 799, 800, 1, 0, 0, 0, 800, 806, 1, 0, 0, 0, 801, 802, 5, 101, 0, 0, 802, 803, 3, 118, 59, 0, 803, 804, 5, 86, 0, 0, 804, 805, 3, 118, 59, 0, 805, 807, 1, 0, 0, 0, 806, 801, 1, 0, 0, 0, 807, 808, 1, 0, 0, 0, 808, 806, 1, 0, 0, 0, 808, 809, 1, 0, 0, 0, 809, 812, 1, 0, 0, 0, 810, 811, 5, 25, 0, 0, 811, 813, 3, 118, 59, 0, 812, 810, 1, 0, 0, 0, 812, 813, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 815, 5, 26, 0, 0, 815, 949, 1, 0, 0, 0, 816, 817, 5, 13, 0, 0, 817, 818, 5, 133, 0, 0, 818, 819, 3, 118, 59, 0, 819, 820, 5, 6, 0, 0, 820, 821, 3, 114, 57, 0, 821, 822, 5, 152, 0, 0, 822, 949, 1, 0, 0, 0, 823, 824, 5, 20, 0, 0, 824, 949, 5, 113, 0, 0, 825, 826, 5, 48, 0, 0, 826, 949, 5, 113, 0, 0, 827, 828, 5, 48, 0, 0, 828, 829, 3, 118, 59, 0, 829, 830, 3, 152, 76, 0, 830, 949, 1, 0, 0, 0, 831, 832, 5, 85, 0, 0, 832, 833, 5, 133, 0, 0, 833, 834, 3, 118, 59, 0, 834, 835, 5, 35, 0, 0, 835, 838, 3, 118, 59, 0, 836, 837, 5, 34, 0, 0, 837, 839, 3, 118, 59, 0, 838, 836, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 840, 1, 0, 0, 0, 840, 841, 5, 152, 0, 0, 841, 949, 1, 0, 0, 0, 842, 843, 5, 89, 0, 0, 843, 949, 5, 113, 0, 0, 844, 845, 5, 94, 0, 0, 845, 846, 5, 133, 0, 0, 846, 847, 7, 10, 0, 0, 847, 848, 3, 166, 83, 0, 848, 849, 5, 35, 0, 0, 849, 850, 3, 118, 59, 0, 850, 851, 5, 152, 0, 0, 851, 949, 1, 0, 0, 0, 852, 853, 3, 160, 80, 0, 853, 855, 5, 133, 0, 0, 854, 856, 3, 116, 58, 0, 855, 854, 1, 0, 0, 0, 855, 856, 1, 0, 0, 0, 856, 857, 1, 0, 0, 0, 857, 858, 5, 152, 0, 0, 858, 867, 1, 0, 0, 0, 859, 861, 5, 133, 0, 0, 860, 862, 5, 24, 0, 0, 861, 860, 1, 0, 0, 0, 861, 862, 1, 0, 0, 0, 862, 864, 1, 0, 0, 0, 863, 865, 3, 116, 58, 0, 864, 863, 1, 0, 0, 0, 864, 865, 1, 0, 0, 0, 865, 866, 1, 0, 0, 0, 866, 868, 5, 152, 0, 0, 867, 859, 1, 0, 0, 0, 867, 868, 1, 0, 0, 0, 868, 869, 1, 0, 0, 0, 869, 870, 5, 69, 0, 0, 870, 871, 5, 133, 0, 0, 871, 872, 3, 100, 50, 0, 872, 873, 5, 152, 0, 0, 873, 949, 1, 0, 0, 0, 874, 875, 3, 160, 80, 0, 875, 877, 5, 133, 0, 0, 876, 878, 3, 116, 58, 0, 877, 876, 1, 0, 0, 0, 877, 878, 1, 0, 0, 0, 878, 879, 1, 0, 0, 0, 879, 880, 5, 152, 0, 0, 880, 889, 1, 0, 0, 0, 881, 883, 5, 133, 0, 0, 882, 884, 5, 24, 0, 0, 883, 882, 1, 0, 0, 0, 883, 884, 1, 0, 0, 0, 884, 886, 1, 0, 0, 0, 885, 887, 3, 116, 58, 0, 886, 885, 1, 0, 0, 0, 886, 887, 1, 0, 0, 0, 887, 888, 1, 0, 0, 0, 888, 890, 5, 152, 0, 0, 889, 881, 1, 0, 0, 0, 889, 890, 1, 0, 0, 0, 890, 891, 1, 0, 0, 0, 891, 892, 5, 69, 0, 0, 892, 893, 3, 160, 80, 0, 893, 949, 1, 0, 0, 0, 894, 900, 3, 160, 80, 0, 895, 897, 5, 133, 0, 0, 896, 898, 3, 116, 58, 0, 897, 896, 1, 0, 0, 0, 897, 898, 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 901, 5, 152, 0, 0, 900, 895, 1, 0, 0, 0, 900, 901, 1, 0, 0, 0, 901, 902, 1, 0, 0, 0, 902, 904, 5, 133, 0, 0, 903, 905, 5, 24, 0, 0, 904, 903, 1, 0, 0, 0, 904, 905, 1, 0, 0, 0, 905, 907, 1, 0, 0, 0, 906, 908, 3, 116, 58, 0, 907, 906, 1, 0, 0, 0, 907, 908, 1, 0, 0, 0, 908, 909, 1, 0, 0, 0, 909, 910, 5, 152, 0, 0, 910, 949, 1, 0, 0, 0, 911, 949, 3, 124, 62, 0, 912, 949, 3, 168, 84, 0, 913, 949, 3, 150, 75, 0, 914, 915, 5, 121, 0, 0, 915, 949, 3, 118, 59, 20, 916, 917, 5, 61, 0, 0, 917, 949, 3, 118, 59, 14, 918, 919, 3, 140, 70, 0, 919, 920, 5, 123, 0, 0, 920, 922, 1, 0, 0, 0, 921, 918, 1, 0, 0, 0, 921, 922, 1, 0, 0, 0, 922, 923, 1, 0, 0, 0, 923, 949, 5, 115, 0, 0, 924, 925, 5, 133, 0, 0, 925, 926, 3, 48, 24, 0, 926, 927, 5, 152, 0, 0, 927, 949, 1, 0, 0, 0, 928, 929, 5, 133, 0, 0, 929, 930, 3, 118, 59, 0, 930, 931, 5, 152, 0, 0, 931, 949, 1, 0, 0, 0, 932, 933, 5, 133, 0, 0, 933, 934, 3, 116, 58, 0, 934, 935, 5, 152, 0, 0, 935, 949, 1, 0, 0, 0, 936, 938, 5, 132, 0, 0, 937, 939, 3, 116, 58, 0, 938, 937, 1, 0, 0, 0, 938, 939, 1, 0, 0, 0, 939, 940, 1, 0, 0, 0, 940, 949, 5, 151, 0, 0, 941, 943, 5, 131, 0, 0, 942, 944, 3, 40, 20, 0, 943, 942, 1, 0, 0, 0, 943, 944, 1, 0, 0, 0, 944, 945, 1, 0, 0, 0, 945, 949, 5, 150, 0, 0, 946, 949, 3, 120, 60, 0, 947, 949, 3, 132, 66, 0, 948, 796, 1, 0, 0, 0, 948, 816, 1, 0, 0, 0, 948, 823, 1, 0, 0, 0, 948, 825, 1, 0, 0, 0, 948, 827, 1, 0, 0, 0, 948, 831, 1, 0, 0, 0, 948, 842, 1, 0, 0, 0, 948, 844, 1, 0, 0, 0, 948, 852, 1, 0, 0, 0, 948, 874, 1, 0, 0, 0, 948, 894, 1, 0, 0, 0, 948, 911, 1, 0, 0, 0, 948, 912, 1, 0, 0, 0, 948, 913, 1, 0, 0, 0, 948, 914, 1, 0, 0, 0, 948, 916, 1, 0, 0, 0, 948, 921, 1, 0, 0, 0, 948, 924, 1, 0, 0, 0, 948, 928, 1, 0, 0, 0, 948, 932, 1, 0, 0, 0, 948, 936, 1, 0, 0, 0, 948, 941, 1, 0, 0, 0, 948, 946, 1, 0, 0, 0, 948, 947, 1, 0, 0, 0, 949, 1060, 1, 0, 0, 0, 950, 954, 10, 19, 0, 0, 951, 955, 5, 115, 0, 0, 952, 955, 5, 154, 0, 0, 953, 955, 5, 141, 0, 0, 954, 951, 1, 0, 0, 0, 954, 952, 1, 0, 0, 0, 954, 953, 1, 0, 0, 0, 955, 956, 1, 0, 0, 0, 956, 1059, 3, 118, 59, 20, 957, 961, 10, 18, 0, 0, 958, 962, 5, 142, 0, 0, 959, 962, 5, 121, 0, 0, 960, 962, 5, 120, 0, 0, 961, 958, 1, 0, 0, 0, 961, 959, 1, 0, 0, 0, 961, 960, 1, 0, 0, 0, 962, 963, 1, 0, 0, 0, 963, 1059, 3, 118, 59, 19, 964, 989, 10, 17, 0, 0, 965, 990, 5, 124, 0, 0, 966, 990, 5, 125, 0, 0, 967, 990, 5, 136, 0, 0, 968, 990, 5, 134, 0, 0, 969, 990, 5, 135, 0, 0, 970, 990, 5, 126, 0, 0, 971, 990, 5, 127, 0, 0, 972, 974, 5, 61, 0, 0, 973, 972, 1, 0, 0, 0, 973, 974, 1, 0, 0, 0, 974, 975, 1, 0, 0, 0, 975, 977, 5, 44, 0, 0, 976, 978, 5, 15, 0, 0, 977, 976, 1, 0, 0, 0, 977, 978, 1, 0, 0, 0, 978, 990, 1, 0, 0, 0, 979, 981, 5, 61, 0, 0, 980, 979, 1, 0, 0, 0, 980, 981, 1, 0, 0, 0, 981, 982, 1, 0, 0, 0, 982, 990, 7, 11, 0, 0, 983, 990, 5, 148, 0, 0, 984, 990, 5, 149, 0, 0, 985, 990, 5, 138, 0, 0, 986, 990, 5, 129, 0, 0, 987, 990, 5, 130, 0, 0, 988, 990, 5, 137, 0, 0, 989, 965, 1, 0, 0, 0, 989, 966, 1, 0, 0, 0, 989, 967, 1, 0, 0, 0, 989, 968, 1, 0, 0, 0, 989, 969, 1, 0, 0, 0, 989, 970, 1, 0, 0, 0, 989, 971, 1, 0, 0, 0, 989, 973, 1, 0, 0, 0, 989, 980, 1, 0, 0, 0, 989, 983, 1, 0, 0, 0, 989, 984, 1, 0, 0, 0, 989, 985, 1, 0, 0, 0, 989, 986, 1, 0, 0, 0, 989, 987, 1, 0, 0, 0, 989, 988, 1, 0, 0, 0, 990, 991, 1, 0, 0, 0, 991, 1059, 3, 118, 59, 18, 992, 993, 10, 15, 0, 0, 993, 994, 5, 140, 0, 0, 994, 1059, 3, 118, 59, 16, 995, 996, 10, 13, 0, 0, 996, 997, 5, 2, 0, 0, 997, 1059, 3, 118, 59, 14, 998, 999, 10, 12, 0, 0, 999, 1000, 5, 66, 0, 0, 1000, 1059, 3, 118, 59, 13, 1001, 1003, 10, 11, 0, 0, 1002, 1004, 5, 61, 0, 0, 1003, 1002, 1, 0, 0, 0, 1003, 1004, 1, 0, 0, 0, 1004, 1005, 1, 0, 0, 0, 1005, 1006, 5, 9, 0, 0, 1006, 1007, 3, 118, 59, 0, 1007, 1008, 5, 2, 0, 0, 1008, 1009, 3, 118, 59, 12, 1009, 1059, 1, 0, 0, 0, 1010, 1011, 10, 10, 0, 0, 1011, 1012, 5, 143, 0, 0, 1012, 1013, 3, 118, 59, 0, 1013, 1014, 5, 118, 0, 0, 1014, 1015, 3, 118, 59, 10, 1015, 1059, 1, 0, 0, 0, 1016, 1017, 10, 30, 0, 0, 1017, 1019, 5, 133, 0, 0, 1018, 1020, 3, 116, 58, 0, 1019, 1018, 1, 0, 0, 0, 1019, 1020, 1, 0, 0, 0, 1020, 1021, 1, 0, 0, 0, 1021, 1059, 5, 152, 0, 0, 1022, 1023, 10, 26, 0, 0, 1023, 1024, 5, 132, 0, 0, 1024, 1025, 3, 118, 59, 0, 1025, 1026, 5, 151, 0, 0, 1026, 1059, 1, 0, 0, 0, 1027, 1028, 10, 25, 0, 0, 1028, 1029, 5, 123, 0, 0, 1029, 1059, 5, 111, 0, 0, 1030, 1031, 10, 24, 0, 0, 1031, 1032, 5, 123, 0, 0, 1032, 1059, 3, 160, 80, 0, 1033, 1034, 10, 23, 0, 0, 1034, 1035, 5, 139, 0, 0, 1035, 1036, 5, 132, 0, 0, 1036, 1037, 3, 118, 59, 0, 1037, 1038, 5, 151, 0, 0, 1038, 1059, 1, 0, 0, 0, 1039, 1040, 10, 22, 0, 0, 1040, 1041, 5, 139, 0, 0, 1041, 1059, 5, 111, 0, 0, 1042, 1043, 10, 21, 0, 0, 1043, 1044, 5, 139, 0, 0, 1044, 1059, 3, 160, 80, 0, 1045, 1046, 10, 16, 0, 0, 1046, 1048, 5, 49, 0, 0, 1047, 1049, 5, 61, 0, 0, 1048, 1047, 1, 0, 0, 0, 1048, 1049, 1, 0, 0, 0, 1049, 1050, 1, 0, 0, 0, 1050, 1059, 5, 62, 0, 0, 1051, 1056, 10, 9, 0, 0, 1052, 1053, 5, 6, 0, 0, 1053, 1057, 3, 160, 80, 0, 1054, 1055, 5, 6, 0, 0, 1055, 1057, 5, 113, 0, 0, 1056, 1052, 1, 0, 0, 0, 1056, 1054, 1, 0, 0, 0, 1057, 1059, 1, 0, 0, 0, 1058, 950, 1, 0, 0, 0, 1058, 957, 1, 0, 0, 0, 1058, 964, 1, 0, 0, 0, 1058, 992, 1, 0, 0, 0, 1058, 995, 1, 0, 0, 0, 1058, 998, 1, 0, 0, 0, 1058, 1001, 1, 0, 0, 0, 1058, 1010, 1, 0, 0, 0, 1058, 1016, 1, 0, 0, 0, 1058, 1022, 1, 0, 0, 0, 1058, 1027, 1, 0, 0, 0, 1058, 1030, 1, 0, 0, 0, 1058, 1033, 1, 0, 0, 0, 1058, 1039, 1, 0, 0, 0, 1058, 1042, 1, 0, 0, 0, 1058, 1045, 1, 0, 0, 0, 1058, 1051, 1, 0, 0, 0, 1059, 1062, 1, 0, 0, 0, 1060, 1058, 1, 0, 0, 0, 1060, 1061, 1, 0, 0, 0, 1061, 119, 1, 0, 0, 0, 1062, 1060, 1, 0, 0, 0, 1063, 1064, 5, 133, 0, 0, 1064, 1069, 3, 160, 80, 0, 1065, 1066, 5, 119, 0, 0, 1066, 1068, 3, 160, 80, 0, 1067, 1065, 1, 0, 0, 0, 1068, 1071, 1, 0, 0, 0, 1069, 1067, 1, 0, 0, 0, 1069, 1070, 1, 0, 0, 0, 1070, 1073, 1, 0, 0, 0, 1071, 1069, 1, 0, 0, 0, 1072, 1074, 5, 119, 0, 0, 1073, 1072, 1, 0, 0, 0, 1073, 1074, 1, 0, 0, 0, 1074, 1075, 1, 0, 0, 0, 1075, 1076, 5, 152, 0, 0, 1076, 1091, 1, 0, 0, 0, 1077, 1082, 3, 160, 80, 0, 1078, 1079, 5, 119, 0, 0, 1079, 1081, 3, 160, 80, 0, 1080, 1078, 1, 0, 0, 0, 1081, 1084, 1, 0, 0, 0, 1082, 1080, 1, 0, 0, 0, 1082, 1083, 1, 0, 0, 0, 1083, 1086, 1, 0, 0, 0, 1084, 1082, 1, 0, 0, 0, 1085, 1087, 5, 119, 0, 0, 1086, 1085, 1, 0, 0, 0, 1086, 1087, 1, 0, 0, 0, 1087, 1091, 1, 0, 0, 0, 1088, 1089, 5, 133, 0, 0, 1089, 1091, 5, 152, 0, 0, 1090, 1063, 1, 0, 0, 0, 1090, 1077, 1, 0, 0, 0, 1090, 1088, 1, 0, 0, 0, 1091, 1092, 1, 0, 0, 0, 1092, 1095, 5, 114, 0, 0, 1093, 1096, 3, 118, 59, 0, 1094, 1096, 3, 36, 18, 0, 1095, 1093, 1, 0, 0, 0, 1095, 1094, 1, 0, 0, 0, 1096, 121, 1, 0, 0, 0, 1097, 1103, 3, 124, 62, 0, 1098, 1099, 5, 131, 0, 0, 1099, 1100, 3, 118, 59, 0, 1100, 1101, 5, 150, 0, 0, 1101, 1103, 1, 0, 0, 0, 1102, 1097, 1, 0, 0, 0, 1102, 1098, 1, 0, 0, 0, 1103, 123, 1, 0, 0, 0, 1104, 1105, 5, 135, 0, 0, 1105, 1109, 3, 160, 80, 0, 1106, 1108, 3, 126, 63, 0, 1107, 1106, 1, 0, 0, 0, 1108, 1111, 1, 0, 0, 0, 1109, 1107, 1, 0, 0, 0, 1109, 1110, 1, 0, 0, 0, 1110, 1112, 1, 0, 0, 0, 1111, 1109, 1, 0, 0, 0, 1112, 1113, 5, 154, 0, 0, 1113, 1114, 5, 127, 0, 0, 1114, 1136, 1, 0, 0, 0, 1115, 1116, 5, 135, 0, 0, 1116, 1120, 3, 160, 80, 0, 1117, 1119, 3, 126, 63, 0, 1118, 1117, 1, 0, 0, 0, 1119, 1122, 1, 0, 0, 0, 1120, 1118, 1, 0, 0, 0, 1120, 1121, 1, 0, 0, 0, 1121, 1123, 1, 0, 0, 0, 1122, 1120, 1, 0, 0, 0, 1123, 1127, 5, 127, 0, 0, 1124, 1126, 3, 122, 61, 0, 1125, 1124, 1, 0, 0, 0, 1126, 1129, 1, 0, 0, 0, 1127, 1125, 1, 0, 0, 0, 1127, 1128, 1, 0, 0, 0, 1128, 1130, 1, 0, 0, 0, 1129, 1127, 1, 0, 0, 0, 1130, 1131, 5, 135, 0, 0, 1131, 1132, 5, 154, 0, 0, 1132, 1133, 3, 160, 80, 0, 1133, 1134, 5, 127, 0, 0, 1134, 1136, 1, 0, 0, 0, 1135, 1104, 1, 0, 0, 0, 1135, 1115, 1, 0, 0, 0, 1136, 125, 1, 0, 0, 0, 1137, 1138, 3, 160, 80, 0, 1138, 1139, 5, 125, 0, 0, 1139, 1140, 3, 166, 83, 0, 1140, 1149, 1, 0, 0, 0, 1141, 1142, 3, 160, 80, 0, 1142, 1143, 5, 125, 0, 0, 1143, 1144, 5, 131, 0, 0, 1144, 1145, 3, 118, 59, 0, 1145, 1146, 5, 150, 0, 0, 1146, 1149, 1, 0, 0, 0, 1147, 1149, 3, 160, 80, 0, 1148, 1137, 1, 0, 0, 0, 1148, 1141, 1, 0, 0, 0, 1148, 1147, 1, 0, 0, 0, 1149, 127, 1, 0, 0, 0, 1150, 1155, 3, 130, 65, 0, 1151, 1152, 5, 119, 0, 0, 1152, 1154, 3, 130, 65, 0, 1153, 1151, 1, 0, 0, 0, 1154, 1157, 1, 0, 0, 0, 1155, 1153, 1, 0, 0, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1159, 1, 0, 0, 0, 1157, 1155, 1, 0, 0, 0, 1158, 1160, 5, 119, 0, 0, 1159, 1158, 1, 0, 0, 0, 1159, 1160, 1, 0, 0, 0, 1160, 129, 1, 0, 0, 0, 1161, 1162, 3, 160, 80, 0, 1162, 1163, 5, 6, 0, 0, 1163, 1164, 5, 133, 0, 0, 1164, 1165, 3, 48, 24, 0, 1165, 1166, 5, 152, 0, 0, 1166, 1172, 1, 0, 0, 0, 1167, 1168, 3, 118, 59, 0, 1168, 1169, 5, 6, 0, 0, 1169, 1170, 3, 160, 80, 0, 1170, 1172, 1, 0, 0, 0, 1171, 1161, 1, 0, 0, 0, 1171, 1167, 1, 0, 0, 0, 1172, 131, 1, 0, 0, 0, 1173, 1181, 3, 164, 82, 0, 1174, 1175, 3, 140, 70, 0, 1175, 1176, 5, 123, 0, 0, 1176, 1178, 1, 0, 0, 0, 1177, 1174, 1, 0, 0, 0, 1177, 1178, 1, 0, 0, 0, 1178, 1179, 1, 0, 0, 0, 1179, 1181, 3, 134, 67, 0, 1180, 1173, 1, 0, 0, 0, 1180, 1177, 1, 0, 0, 0, 1181, 133, 1, 0, 0, 0, 1182, 1187, 3, 160, 80, 0, 1183, 1184, 5, 123, 0, 0, 1184, 1186, 3, 160, 80, 0, 1185, 1183, 1, 0, 0, 0, 1186, 1189, 1, 0, 0, 0, 1187, 1185, 1, 0, 0, 0, 1187, 1188, 1, 0, 0, 0, 1188, 135, 1, 0, 0, 0, 1189, 1187, 1, 0, 0, 0, 1190, 1191, 6, 68, -1, 0, 1191, 1200, 3, 140, 70, 0, 1192, 1200, 3, 138, 69, 0, 1193, 1194, 5, 133, 0, 0, 1194, 1195, 3, 48, 24, 0, 1195, 1196, 5, 152, 0, 0, 1196, 1200, 1, 0, 0, 0, 1197, 1200, 3, 124, 62, 0, 1198, 1200, 3, 164, 82, 0, 1199, 1190, 1, 0, 0, 0, 1199, 1192, 1, 0, 0, 0, 1199, 1193, 1, 0, 0, 0, 1199, 1197, 1, 0, 0, 0, 1199, 1198, 1, 0, 0, 0, 1200, 1209, 1, 0, 0, 0, 1201, 1205, 10, 3, 0, 0, 1202, 1206, 3, 158, 79, 0, 1203, 1204, 5, 6, 0, 0, 1204, 1206, 3, 160, 80, 0, 1205, 1202, 1, 0, 0, 0, 1205, 1203, 1, 0, 0, 0, 1206, 1208, 1, 0, 0, 0, 1207, 1201, 1, 0, 0, 0, 1208, 1211, 1, 0, 0, 0, 1209, 1207, 1, 0, 0, 0, 1209, 1210, 1, 0, 0, 0, 1210, 137, 1, 0, 0, 0, 1211, 1209, 1, 0, 0, 0, 1212, 1213, 3, 160, 80, 0, 1213, 1215, 5, 133, 0, 0, 1214, 1216, 3, 142, 71, 0, 1215, 1214, 1, 0, 0, 0, 1215, 1216, 1, 0, 0, 0, 1216, 1217, 1, 0, 0, 0, 1217, 1218, 5, 152, 0, 0, 1218, 139, 1, 0, 0, 0, 1219, 1220, 3, 144, 72, 0, 1220, 1221, 5, 123, 0, 0, 1221, 1223, 1, 0, 0, 0, 1222, 1219, 1, 0, 0, 0, 1222, 1223, 1, 0, 0, 0, 1223, 1224, 1, 0, 0, 0, 1224, 1225, 3, 160, 80, 0, 1225, 141, 1, 0, 0, 0, 1226, 1231, 3, 118, 59, 0, 1227, 1228, 5, 119, 0, 0, 1228, 1230, 3, 118, 59, 0, 1229, 1227, 1, 0, 0, 0, 1230, 1233, 1, 0, 0, 0, 1231, 1229, 1, 0, 0, 0, 1231, 1232, 1, 0, 0, 0, 1232, 1235, 1, 0, 0, 0, 1233, 1231, 1, 0, 0, 0, 1234, 1236, 5, 119, 0, 0, 1235, 1234, 1, 0, 0, 0, 1235, 1236, 1, 0, 0, 0, 1236, 143, 1, 0, 0, 0, 1237, 1238, 3, 160, 80, 0, 1238, 145, 1, 0, 0, 0, 1239, 1248, 5, 109, 0, 0, 1240, 1241, 5, 123, 0, 0, 1241, 1248, 7, 12, 0, 0, 1242, 1243, 5, 111, 0, 0, 1243, 1245, 5, 123, 0, 0, 1244, 1246, 7, 12, 0, 0, 1245, 1244, 1, 0, 0, 0, 1245, 1246, 1, 0, 0, 0, 1246, 1248, 1, 0, 0, 0, 1247, 1239, 1, 0, 0, 0, 1247, 1240, 1, 0, 0, 0, 1247, 1242, 1, 0, 0, 0, 1248, 147, 1, 0, 0, 0, 1249, 1251, 7, 13, 0, 0, 1250, 1249, 1, 0, 0, 0, 1250, 1251, 1, 0, 0, 0, 1251, 1258, 1, 0, 0, 0, 1252, 1259, 3, 146, 73, 0, 1253, 1259, 5, 110, 0, 0, 1254, 1259, 5, 111, 0, 0, 1255, 1259, 5, 112, 0, 0, 1256, 1259, 5, 45, 0, 0, 1257, 1259, 5, 60, 0, 0, 1258, 1252, 1, 0, 0, 0, 1258, 1253, 1, 0, 0, 0, 1258, 1254, 1, 0, 0, 0, 1258, 1255, 1, 0, 0, 0, 1258, 1256, 1, 0, 0, 0, 1258, 1257, 1, 0, 0, 0, 1259, 149, 1, 0, 0, 0, 1260, 1264, 3, 148, 74, 0, 1261, 1264, 5, 113, 0, 0, 1262, 1264, 5, 62, 0, 0, 1263, 1260, 1, 0, 0, 0, 1263, 1261, 1, 0, 0, 0, 1263, 1262, 1, 0, 0, 0, 1264, 151, 1, 0, 0, 0, 1265, 1266, 7, 14, 0, 0, 1266, 153, 1, 0, 0, 0, 1267, 1268, 7, 15, 0, 0, 1268, 155, 1, 0, 0, 0, 1269, 1270, 7, 16, 0, 0, 1270, 157, 1, 0, 0, 0, 1271, 1274, 5, 108, 0, 0, 1272, 1274, 3, 156, 78, 0, 1273, 1271, 1, 0, 0, 0, 1273, 1272, 1, 0, 0, 0, 1274, 159, 1, 0, 0, 0, 1275, 1279, 5, 108, 0, 0, 1276, 1279, 3, 152, 76, 0, 1277, 1279, 3, 154, 77, 0, 1278, 1275, 1, 0, 0, 0, 1278, 1276, 1, 0, 0, 0, 1278, 1277, 1, 0, 0, 0, 1279, 161, 1, 0, 0, 0, 1280, 1281, 3, 166, 83, 0, 1281, 1282, 5, 125, 0, 0, 1282, 1283, 3, 148, 74, 0, 1283, 163, 1, 0, 0, 0, 1284, 1285, 5, 131, 0, 0, 1285, 1286, 3, 118, 59, 0, 1286, 1287, 5, 150, 0, 0, 1287, 165, 1, 0, 0, 0, 1288, 1291, 5, 113, 0, 0, 1289, 1291, 3, 168, 84, 0, 1290, 1288, 1, 0, 0, 0, 1290, 1289, 1, 0, 0, 0, 1291, 167, 1, 0, 0, 0, 1292, 1296, 5, 145, 0, 0, 1293, 1295, 3, 170, 85, 0, 1294, 1293, 1, 0, 0, 0, 1295, 1298, 1, 0, 0, 0, 1296, 1294, 1, 0, 0, 0, 1296, 1297, 1, 0, 0, 0, 1297, 1299, 1, 0, 0, 0, 1298, 1296, 1, 0, 0, 0, 1299, 1300, 5, 147, 0, 0, 1300, 169, 1, 0, 0, 0, 1301, 1302, 5, 160, 0, 0, 1302, 1303, 3, 118, 59, 0, 1303, 1304, 5, 150, 0, 0, 1304, 1307, 1, 0, 0, 0, 1305, 1307, 5, 159, 0, 0, 1306, 1301, 1, 0, 0, 0, 1306, 1305, 1, 0, 0, 0, 1307, 171, 1, 0, 0, 0, 1308, 1312, 5, 146, 0, 0, 1309, 1311, 3, 174, 87, 0, 1310, 1309, 1, 0, 0, 0, 1311, 1314, 1, 0, 0, 0, 1312, 1310, 1, 0, 0, 0, 1312, 1313, 1, 0, 0, 0, 1313, 1315, 1, 0, 0, 0, 1314, 1312, 1, 0, 0, 0, 1315, 1316, 5, 0, 0, 1, 1316, 173, 1, 0, 0, 0, 1317, 1318, 5, 162, 0, 0, 1318, 1319, 3, 118, 59, 0, 1319, 1320, 5, 150, 0, 0, 1320, 1323, 1, 0, 0, 0, 1321, 1323, 5, 161, 0, 0, 1322, 1317, 1, 0, 0, 0, 1322, 1321, 1, 0, 0, 0, 1323, 175, 1, 0, 0, 0, 169, 179, 186, 195, 202, 206, 220, 224, 227, 231, 234, 241, 245, 254, 259, 268, 276, 283, 287, 293, 298, 306, 313, 319, 331, 339, 353, 357, 362, 372, 382, 390, 394, 398, 401, 405, 408, 411, 414, 417, 421, 425, 428, 431, 434, 438, 441, 450, 456, 477, 494, 511, 517, 523, 534, 536, 547, 550, 556, 564, 570, 572, 576, 581, 584, 587, 591, 595, 598, 600, 603, 607, 611, 614, 616, 618, 623, 634, 640, 647, 652, 656, 660, 666, 668, 675, 683, 686, 689, 708, 722, 738, 742, 753, 757, 768, 772, 779, 783, 790, 794, 799, 808, 812, 838, 855, 861, 864, 867, 877, 883, 886, 889, 897, 900, 904, 907, 921, 938, 943, 948, 954, 961, 973, 977, 980, 989, 1003, 1019, 1048, 1056, 1058, 1060, 1069, 1073, 1082, 1086, 1090, 1095, 1102, 1109, 1120, 1127, 1135, 1148, 1155, 1159, 1171, 1177, 1180, 1187, 1199, 1205, 1209, 1215, 1222, 1231, 1235, 1245, 1247, 1250, 1258, 1263, 1273, 1278, 1290, 1296, 1306, 1312, 1322] \ No newline at end of file diff --git a/common/hogql_parser/HogQLParserBaseVisitor.h b/common/hogql_parser/HogQLParserBaseVisitor.h index 2da57d404f5a8..450a987f515c9 100644 --- a/common/hogql_parser/HogQLParserBaseVisitor.h +++ b/common/hogql_parser/HogQLParserBaseVisitor.h @@ -459,6 +459,10 @@ class HogQLParserBaseVisitor : public HogQLParserVisitor { return visitChildren(ctx); } + virtual std::any visitHogqlxChildElement(HogQLParser::HogqlxChildElementContext *ctx) override { + return visitChildren(ctx); + } + virtual std::any visitHogqlxTagElementClosed(HogQLParser::HogqlxTagElementClosedContext *ctx) override { return visitChildren(ctx); } diff --git a/common/hogql_parser/HogQLParserVisitor.h b/common/hogql_parser/HogQLParserVisitor.h index d17febcfbc305..a3ca3f36b128c 100644 --- a/common/hogql_parser/HogQLParserVisitor.h +++ b/common/hogql_parser/HogQLParserVisitor.h @@ -241,6 +241,8 @@ class HogQLParserVisitor : public antlr4::tree::AbstractParseTreeVisitor { virtual std::any visitColumnLambdaExpr(HogQLParser::ColumnLambdaExprContext *context) = 0; + virtual std::any visitHogqlxChildElement(HogQLParser::HogqlxChildElementContext *context) = 0; + virtual std::any visitHogqlxTagElementClosed(HogQLParser::HogqlxTagElementClosedContext *context) = 0; virtual std::any visitHogqlxTagElementNested(HogQLParser::HogqlxTagElementNestedContext *context) = 0; diff --git a/common/hogql_parser/parser.cpp b/common/hogql_parser/parser.cpp index 7337c1ea96b9a..b3df673821f6b 100644 --- a/common/hogql_parser/parser.cpp +++ b/common/hogql_parser/parser.cpp @@ -2536,6 +2536,15 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor { RETURN_NEW_AST_NODE("HogQLXAttribute", "{s:s#,s:N}", "name", name.data(), name.size(), "value", value); } + VISIT(HogqlxChildElement) { + auto tag_element_ctx = ctx->hogqlxTagElement(); + if (tag_element_ctx) { + return visitAsPyObject(tag_element_ctx); + } else { + return visitAsPyObject(ctx->columnExpr()); + } + } + VISIT(HogqlxTagElementClosed) { string kind = visitAsString(ctx->identifier()); RETURN_NEW_AST_NODE( @@ -2545,80 +2554,107 @@ class HogQLParseTreeConverter : public HogQLParserBaseVisitor { } VISIT(HogqlxTagElementNested) { - string opening = visitAsString(ctx->identifier(0)); - string closing = visitAsString(ctx->identifier(1)); + std::string opening = visitAsString(ctx->identifier(0)); + std::string closing = visitAsString(ctx->identifier(1)); if (opening != closing) { throw SyntaxError("Opening and closing HogQLX tags must match. Got " + opening + " and " + closing); } - auto tag_element_ctx = ctx->hogqlxTagElement(); - auto column_expr_ctx = ctx->columnExpr(); - auto tag_attribute_ctx = ctx->hogqlxTagAttribute(); - PyObject* attributes = PyList_New(tag_attribute_ctx.size() + (tag_element_ctx || column_expr_ctx ? 1 : 0)); - if (!attributes) throw PyInternalError(); - bool found_source = false; - for (size_t i = 0; i < tag_attribute_ctx.size(); i++) { - PyObject* object; + auto attribute_ctxs = ctx->hogqlxTagAttribute(); + PyObject *attributes = PyList_New(attribute_ctxs.size()); + if (!attributes) { + throw PyInternalError(); + } + for (size_t i = 0; i < attribute_ctxs.size(); i++) { + PyObject *attr_obj; try { - object = visitAsPyObject(tag_attribute_ctx[i]); + attr_obj = visitAsPyObject(attribute_ctxs[i]); } catch (...) { Py_DECREF(attributes); throw; } - PyList_SET_ITEM(attributes, i, object); + PyList_SET_ITEM(attributes, i, attr_obj); // Steals reference + } - PyObject* name = PyObject_GetAttrString(object, "name"); - if (!name) { - Py_DECREF(attributes); - throw PyInternalError(); - } - PyObject* source_as_str = PyUnicode_FromString("source"); - if (!source_as_str) { - Py_DECREF(name); - Py_DECREF(attributes); - throw PyInternalError(); + auto child_element_ctxs = ctx->hogqlxChildElement(); + if (!child_element_ctxs.empty()) { + for (size_t i = 0; i < attribute_ctxs.size(); i++) { + PyObject *attr = PyList_GetItem(attributes, i); // borrowed + if (!attr) { + Py_DECREF(attributes); + throw PyInternalError(); + } + PyObject *name_obj = PyObject_GetAttrString(attr, "name"); + if (!name_obj) { + Py_DECREF(attributes); + throw PyInternalError(); + } + PyObject *children_str = PyUnicode_FromString("children"); + if (!children_str) { + Py_DECREF(name_obj); + Py_DECREF(attributes); + throw PyInternalError(); + } + int is_children = PyObject_RichCompareBool(name_obj, children_str, Py_EQ); + Py_DECREF(children_str); + Py_DECREF(name_obj); + if (is_children == -1) { + Py_DECREF(attributes); + throw PyInternalError(); + } + if (is_children == 1) { + Py_DECREF(attributes); + throw SyntaxError("Can't have a HogQLX tag with both children and a 'children' attribute"); + } } - int tentative_found_source = PyObject_RichCompareBool(name, source_as_str, Py_EQ); - Py_DECREF(source_as_str); - Py_DECREF(name); - if (tentative_found_source == -1) { + + PyObject *children_list = PyList_New(child_element_ctxs.size()); + if (!children_list) { Py_DECREF(attributes); throw PyInternalError(); } - if (tentative_found_source) { - found_source = true; + for (size_t i = 0; i < child_element_ctxs.size(); i++) { + PyObject *child_ast; + try { + child_ast = visitAsPyObject(child_element_ctxs[i]); + } catch (...) { + Py_DECREF(children_list); + Py_DECREF(attributes); + throw; + } + PyList_SET_ITEM(children_list, i, child_ast); // Steals reference } - } - if (tag_element_ctx) { - if (found_source) { - Py_DECREF(attributes); - throw SyntaxError("Nested HogQLX tags cannot have a source attribute"); - } - PyObject* source_attribute = build_ast_node( - "HogQLXAttribute", "{s:s#,s:N}", "name", "source", 6, "value", visitAsPyObject(ctx->hogqlxTagElement()) + PyObject *children_attr = build_ast_node( + "HogQLXAttribute", + "{s:s#,s:O}", + "name", "children", (Py_ssize_t)8, + "value", children_list ); - if (!source_attribute) { + if (!children_attr) { + Py_DECREF(children_list); Py_DECREF(attributes); throw PyInternalError(); } - PyList_SET_ITEM(attributes, tag_attribute_ctx.size(), source_attribute); - } else if (column_expr_ctx) { - if (found_source) { - Py_DECREF(attributes); - throw SyntaxError("Nested HogQLX tags cannot have a source attribute"); - } - PyObject* source_attribute = build_ast_node( - "HogQLXAttribute", "{s:s#,s:N}", "name", "source", 6, "value", visitAsPyObject(ctx->columnExpr()) - ); - if (!source_attribute) { + int appended = PyList_Append(attributes, children_attr); + Py_DECREF(children_attr); + if (appended == -1) { Py_DECREF(attributes); throw PyInternalError(); } - PyList_SET_ITEM(attributes, tag_attribute_ctx.size(), source_attribute); } - RETURN_NEW_AST_NODE("HogQLXTag", "{s:s#,s:N}", "kind", opening.data(), opening.size(), "attributes", attributes); + PyObject *ret = build_ast_node( + "HogQLXTag", + "{s:s#,s:N}", + "kind", opening.data(), (Py_ssize_t)opening.size(), + "attributes", attributes + ); + if (!ret) { + Py_DECREF(attributes); + throw PyInternalError(); + } + return ret; } VISIT(Placeholder) { diff --git a/common/hogql_parser/setup.py b/common/hogql_parser/setup.py index 9db7837eddf20..d1526839abc80 100644 --- a/common/hogql_parser/setup.py +++ b/common/hogql_parser/setup.py @@ -32,13 +32,13 @@ setup( name="hogql_parser", - version="1.0.50", - url="https://github.com/PostHog/posthog/tree/master/hogql_parser", + version="1.0.454", + url="https://github.com/PostHog/posthog/tree/master/common/hogql_parser", + description="HogQL parser for internal PostHog use", author="PostHog Inc.", author_email="hey@posthog.com", maintainer="PostHog Inc.", maintainer_email="hey@posthog.com", - description="HogQL parser for internal PostHog use", long_description=open("README.md").read(), long_description_content_type="text/markdown", package_data={"hogql_parser": ["__init__.pyi", "py.typed"]}, diff --git a/cypress/e2e/dashboard.cy.ts b/cypress/e2e/dashboard.cy.ts index e130feeedc897..4605d9c756083 100644 --- a/cypress/e2e/dashboard.cy.ts +++ b/cypress/e2e/dashboard.cy.ts @@ -97,7 +97,7 @@ describe('Dashboard', () => { cy.contains('span', 'Last 14 days').click() cy.contains('span', 'Save').click() - cy.contains('span[class="text-primary text-sm font-medium"]', 'Refreshing').should('not.exist') + cy.contains('span[class="text-accent-primary text-sm font-medium"]', 'Refreshing').should('not.exist') cy.get('span').contains('Refreshing').should('not.exist') }) diff --git a/cypress/e2e/featureFlags.cy.ts b/cypress/e2e/featureFlags.cy.ts index df4d740b8ec4b..23b05ee25c989 100644 --- a/cypress/e2e/featureFlags.cy.ts +++ b/cypress/e2e/featureFlags.cy.ts @@ -328,6 +328,15 @@ describe('Feature Flags', () => { cy.get('[data-attr=feature-flag-variant-rollout-percentage-input]').click().type(`4.5`).should('have.value', 4) }) + it('Allows creating remote config flag without setting release conditions', () => { + cy.get('[data-attr=top-bar-name]').should('contain', 'Feature flags') + // Start creating a remote config flag + cy.get('[data-attr=new-feature-flag]').click() + cy.get('[data-attr=feature-flag-key]').click().type(`{moveToEnd}${name}`).should('have.value', name) + cy.get('[data-attr=feature-flag-served-value-segmented-button]').contains('Remote config').click() + cy.get('[data-attr=save-feature-flag]').first().click() + }) + it('Sets URL properly when switching between tabs', () => { cy.get('[data-attr=top-bar-name]').should('contain', 'Feature flags') cy.get('[data-attr=feature-flags-tab-navigation]').contains('History').click() diff --git a/cypress/e2e/trends.cy.ts b/cypress/e2e/trends.cy.ts index 9f6d45236520a..e06ac615fe1cc 100644 --- a/cypress/e2e/trends.cy.ts +++ b/cypress/e2e/trends.cy.ts @@ -152,13 +152,6 @@ describe('Trends', () => { cy.get('[data-attr=trend-line-graph]').should('exist') }) - it('Apply all users cohort breakdown', () => { - cy.get('[data-attr=add-breakdown-button]').click() - cy.get('[data-attr=taxonomic-tab-cohorts_with_all]').click() - cy.contains('All Users*').click() - cy.get('[data-attr=trend-line-graph]').should('exist') - }) - it('Show warning on MAU math in total value insight', () => { cy.get('[data-attr=chart-filter]').click() cy.get('.Popover').find('.LemonButton').contains('Pie').click() diff --git a/dags/definitions.py b/dags/definitions.py index 6b8bdc1d91948..3bc0f923aa1be 100644 --- a/dags/definitions.py +++ b/dags/definitions.py @@ -15,7 +15,9 @@ resources_by_env = { "prod": { "cluster": ClickhouseClusterResource.configure_at_launch(), - "io_manager": s3_pickle_io_manager.configured({"s3_bucket": "posthog-dags", "s3_prefix": "dag-storage"}), + "io_manager": s3_pickle_io_manager.configured( + {"s3_bucket": settings.DAGSTER_S3_BUCKET, "s3_prefix": "dag-storage"} + ), "s3": s3_resource, }, "local": { diff --git a/docker-compose.base.yml b/docker-compose.base.yml index 7e22d9bf3d953..5c70a33d4e30b 100644 --- a/docker-compose.base.yml +++ b/docker-compose.base.yml @@ -53,6 +53,8 @@ services: test: ['CMD-SHELL', 'pg_isready -U posthog'] interval: 5s timeout: 5s + volumes: + - ./docker/postgres-init-scripts:/docker-entrypoint-initdb.d redis: image: redis:6.2.7-alpine @@ -296,3 +298,42 @@ services: environment: <<: *worker_env TEMPORAL_HOST: temporal + + cyclotron-fetch: + image: ghcr.io/posthog/posthog/cyclotron-fetch:master + build: + context: rust/ + args: + BIN: cyclotron-fetch + restart: on-failure + environment: + DATABASE_URL: 'postgres://posthog:posthog@db:5432/cyclotron' + KAFKA_HOSTS: 'kafka:9092' + KAFKA_TOPIC: 'clickhouse_app_metrics2' + WORKER_ID: 'default_worker_id' + JOB_POLL_INTERVAL: '1s' + MAX_CONCURRENT_JOBS: '10' + DEBUG: '1' + RUST_LOG: 'info' + depends_on: + db: + condition: service_healthy + kafka: + condition: service_started + + cyclotron-janitor: + image: ghcr.io/posthog/posthog/cyclotron-janitor:master + build: + context: rust/ + args: + BIN: cyclotron-janitor + restart: on-failure + environment: + DATABASE_URL: 'postgres://posthog:posthog@db:5432/cyclotron' + KAFKA_HOSTS: 'kafka:9092' + KAFKA_TOPIC: 'clickhouse_app_metrics2' + depends_on: + db: + condition: service_healthy + kafka: + condition: service_started diff --git a/docker-compose.dev-full.yml b/docker-compose.dev-full.yml index 7a91c4b6324c9..bf7eb2d7ebe4a 100644 --- a/docker-compose.dev-full.yml +++ b/docker-compose.dev-full.yml @@ -220,3 +220,34 @@ services: - kafka - objectstorage - temporal + + cyclotron-fetch: + extends: + file: docker-compose.base.yml + service: cyclotron-fetch + ports: + - '3304:3304' # Expose the fetch service port + environment: + DATABASE_URL: 'postgres://posthog:posthog@db:5432/cyclotron' + KAFKA_HOSTS: 'kafka:9092' + WORKER_ID: 'default_worker_id' + DEBUG: 1 # Add debug mode like other services + depends_on: + db: + condition: service_healthy + kafka: + condition: service_started + + cyclotron-janitor: + extends: + file: docker-compose.base.yml + service: cyclotron-janitor + environment: + DATABASE_URL: 'postgres://posthog:posthog@db:5432/cyclotron' + KAFKA_HOSTS: 'kafka:9092' + DEBUG: 1 # Add debug mode like other services + depends_on: + db: + condition: service_healthy + kafka: + condition: service_started diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 08e50a4367b73..8984f710f3a69 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -30,7 +30,6 @@ services: # and eventually kills postgres, these settings aim to stop that happening. # They are only for DEV and should not be used in production. command: postgres -c max_connections=1000 -c idle_in_transaction_session_timeout=300000 - redis: extends: file: docker-compose.base.yml @@ -193,3 +192,25 @@ services: condition: service_started db: condition: service_healthy + + cyclotron-fetch: + extends: + file: docker-compose.base.yml + service: cyclotron-fetch + environment: + DATABASE_URL: 'postgres://posthog:posthog@db:5432/cyclotron' + RUST_LOG: 'info' + depends_on: + db: + condition: service_healthy + + cyclotron-janitor: + extends: + file: docker-compose.base.yml + service: cyclotron-janitor + environment: + DATABASE_URL: 'postgres://posthog:posthog@db:5432/cyclotron' + RUST_LOG: 'info' + depends_on: + db: + condition: service_healthy diff --git a/docker-compose.hobby.yml b/docker-compose.hobby.yml index 144561399629f..072a9d7985fb8 100644 --- a/docker-compose.hobby.yml +++ b/docker-compose.hobby.yml @@ -219,6 +219,34 @@ services: - objectstorage - temporal + cyclotron-fetch: + extends: + file: docker-compose.base.yml + service: cyclotron-fetch + environment: + DATABASE_URL: 'postgres://posthog:posthog@db:5432/cyclotron' + KAFKA_HOSTS: 'kafka:9092' + SENTRY_DSN: $SENTRY_DSN + depends_on: + db: + condition: service_healthy + kafka: + condition: service_started + + cyclotron-janitor: + extends: + file: docker-compose.base.yml + service: cyclotron-janitor + environment: + DATABASE_URL: 'postgres://posthog:posthog@db:5432/cyclotron' + KAFKA_HOSTS: 'kafka:9092' + SENTRY_DSN: $SENTRY_DSN + depends_on: + db: + condition: service_healthy + kafka: + condition: service_started + volumes: zookeeper-data: zookeeper-datalog: diff --git a/docker/postgres-init-scripts/create-cyclotron-db.sh b/docker/postgres-init-scripts/create-cyclotron-db.sh new file mode 100644 index 0000000000000..3cdae2021dd7e --- /dev/null +++ b/docker/postgres-init-scripts/create-cyclotron-db.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e +set -u + +echo "Checking if database 'cyclotron' exists..." +DB_EXISTS=$(psql -U "$POSTGRES_USER" -tAc "SELECT 1 FROM pg_database WHERE datname='cyclotron'") + +if [ -z "$DB_EXISTS" ]; then + echo "Creating database 'cyclotron'..." + psql -U "$POSTGRES_USER" -c "CREATE DATABASE cyclotron;" + psql -U "$POSTGRES_USER" -c "GRANT ALL PRIVILEGES ON DATABASE cyclotron TO $POSTGRES_USER;" + echo "Database 'cyclotron' created successfully" +else + echo "Database 'cyclotron' already exists" +fi \ No newline at end of file diff --git a/ee/LICENSE b/ee/LICENSE index 36b2726e5075a..1861ad90df713 100644 --- a/ee/LICENSE +++ b/ee/LICENSE @@ -1,5 +1,5 @@ The PostHog Enterprise license (the “Enterprise License”) -Copyright (c) 2020-2023 PostHog Inc. +Copyright (c) 2020-2025 PostHog Inc. With regard to the PostHog Software: diff --git a/ee/api/conversation.py b/ee/api/conversation.py index 70e314b94039f..7e67035c28e2c 100644 --- a/ee/api/conversation.py +++ b/ee/api/conversation.py @@ -18,6 +18,7 @@ class MessageSerializer(serializers.Serializer): content = serializers.CharField(required=True, max_length=1000) conversation = serializers.UUIDField(required=False) + trace_id = serializers.UUIDField(required=True) def validate(self, data): try: @@ -65,5 +66,6 @@ def create(self, request: Request, *args, **kwargs): serializer.validated_data["message"], user=cast(User, request.user), is_new_conversation=not conversation_id, + trace_id=serializer.validated_data["trace_id"], ) return StreamingHttpResponse(assistant.stream(), content_type=ServerSentEventRenderer.media_type) diff --git a/ee/api/test/test_authentication.py b/ee/api/test/test_authentication.py index d102b7257cd8c..bc9fb26d564c4 100644 --- a/ee/api/test/test_authentication.py +++ b/ee/api/test/test_authentication.py @@ -709,8 +709,8 @@ def test_xmlsec_and_lxml(self): import xmlsec import lxml - assert "1.3.13" == xmlsec.__version__ - assert "4.9.4" == lxml.__version__ + assert "1.3.14" == xmlsec.__version__ + assert "5.2.1" == lxml.__version__ class TestCustomGoogleOAuth2(APILicensedTest): diff --git a/ee/api/test/test_conversation.py b/ee/api/test/test_conversation.py index 6eb466876dc01..d9891cca54ad3 100644 --- a/ee/api/test/test_conversation.py +++ b/ee/api/test/test_conversation.py @@ -1,3 +1,4 @@ +import uuid from unittest.mock import patch from rest_framework import status @@ -27,7 +28,7 @@ def test_create_conversation(self): with patch.object(Assistant, "_stream", return_value=["test response"]) as stream_mock: response = self.client.post( f"/api/environments/{self.team.id}/conversations/", - {"content": "test query"}, + {"content": "test query", "trace_id": str(uuid.uuid4())}, ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(self._get_streaming_content(response), b"test response") @@ -45,6 +46,7 @@ def test_add_message_to_existing_conversation(self): { "conversation": str(conversation.id), "content": "test query", + "trace_id": str(uuid.uuid4()), }, ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -58,7 +60,7 @@ def test_cant_access_other_users_conversation(self): self.client.force_login(self.user) response = self.client.post( f"/api/environments/{self.team.id}/conversations/", - {"conversation": conversation.id, "content": "test query"}, + {"conversation": conversation.id, "content": "test query", "trace_id": str(uuid.uuid4())}, ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -67,7 +69,7 @@ def test_cant_access_other_teams_conversation(self): conversation = Conversation.objects.create(user=self.user, team=self.other_team) response = self.client.post( f"/api/environments/{self.team.id}/conversations/", - {"conversation": conversation.id, "content": "test query"}, + {"conversation": conversation.id, "content": "test query", "trace_id": str(uuid.uuid4())}, ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -81,21 +83,21 @@ def test_rate_limit_burst(self): for _ in range(11): # Assuming burst limit is less than this response = self.client.post( f"/api/environments/{self.team.id}/conversations/", - {"content": "test query"}, + {"content": "test query", "trace_id": str(uuid.uuid4())}, ) self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS) def test_empty_content(self): response = self.client.post( f"/api/environments/{self.team.id}/conversations/", - {"content": ""}, + {"content": "", "trace_id": str(uuid.uuid4())}, ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) def test_content_too_long(self): response = self.client.post( f"/api/environments/{self.team.id}/conversations/", - {"content": "x" * 1001}, # Very long message + {"content": "x" * 1001, "trace_id": str(uuid.uuid4())}, # Very long message ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -105,6 +107,16 @@ def test_invalid_conversation_id(self): { "conversation": "not-a-valid-uuid", "content": "test query", + "trace_id": str(uuid.uuid4()), + }, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_missing_trace_id(self): + response = self.client.post( + f"/api/environments/{self.team.id}/conversations/", + { + "content": "test query", }, ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -115,6 +127,7 @@ def test_nonexistent_conversation(self): { "conversation": "12345678-1234-5678-1234-567812345678", "content": "test query", + "trace_id": str(uuid.uuid4()), }, ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -130,6 +143,7 @@ def test_deleted_conversation(self): { "conversation": str(conversation_id), "content": "test query", + "trace_id": str(uuid.uuid4()), }, ) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) @@ -138,7 +152,7 @@ def test_unauthenticated_request(self): self.client.logout() response = self.client.post( f"/api/environments/{self.team.id}/conversations/", - {"content": "test query"}, + {"content": "test query", "trace_id": str(uuid.uuid4())}, ) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) @@ -150,7 +164,7 @@ def raise_error(): with patch.object(Assistant, "_stream", side_effect=raise_error): response = self.client.post( f"/api/environments/{self.team.id}/conversations/", - {"content": "test query"}, + {"content": "test query", "trace_id": str(uuid.uuid4())}, ) with self.assertRaises(Exception) as context: b"".join(response.streaming_content) diff --git a/ee/clickhouse/models/test/__snapshots__/test_cohort.ambr b/ee/clickhouse/models/test/__snapshots__/test_cohort.ambr index 84888eceb87d3..3a20414b1d99d 100644 --- a/ee/clickhouse/models/test/__snapshots__/test_cohort.ambr +++ b/ee/clickhouse/models/test/__snapshots__/test_cohort.ambr @@ -233,7 +233,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), greaterOrEquals(timestamp, toDateTime64('2023-01-21 00:00:00.000000', 6, 'UTC')), lessOrEquals(timestamp, toDateTime64('2025-01-21 23:59:59.999999', 6, 'UTC')), equals(e.event, '$pageview'))) + WHERE and(equals(e.team_id, 99999), greaterOrEquals(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(timestamp, toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')), equals(e.event, '$pageview'))) GROUP BY actor_id) AS source ORDER BY source.id ASC LIMIT 100 SETTINGS optimize_aggregation_in_order=1, @@ -374,7 +374,7 @@ actor_id AS id FROM (SELECT min(toTimeZone(e.timestamp, 'UTC')) AS min_timestamp, - minIf(toTimeZone(e.timestamp, 'UTC'), greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2025-01-06 00:00:00.000000', 6, 'UTC'))) AS min_timestamp_with_condition, + minIf(toTimeZone(e.timestamp, 'UTC'), greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) AS min_timestamp_with_condition, if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id) AS actor_id, argMin(e.uuid, toTimeZone(e.timestamp, 'UTC')) AS uuid, argMin(e.distinct_id, toTimeZone(e.timestamp, 'UTC')) AS distinct_id @@ -386,7 +386,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2025-01-21 23:59:59.999999', 6, 'UTC')), equals(e.event, 'signup')) + WHERE and(equals(e.team_id, 99999), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')), equals(e.event, 'signup')) GROUP BY if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id) HAVING ifNull(equals(min_timestamp, min_timestamp_with_condition), isNull(min_timestamp) and isNull(min_timestamp_with_condition))) @@ -474,7 +474,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), greaterOrEquals(timestamp, toDateTime64('2023-01-21 00:00:00.000000', 6, 'UTC')), lessOrEquals(timestamp, toDateTime64('2025-01-21 23:59:59.999999', 6, 'UTC')), equals(e.event, '$pageview'))) + WHERE and(equals(e.team_id, 99999), greaterOrEquals(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(timestamp, toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')), equals(e.event, '$pageview'))) GROUP BY actor_id) AS source ORDER BY source.id ASC LIMIT 100 SETTINGS optimize_aggregation_in_order=1, @@ -488,7 +488,7 @@ actor_id AS id FROM (SELECT min(toTimeZone(e.timestamp, 'UTC')) AS min_timestamp, - minIf(toTimeZone(e.timestamp, 'UTC'), greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2025-01-06 00:00:00.000000', 6, 'UTC'))) AS min_timestamp_with_condition, + minIf(toTimeZone(e.timestamp, 'UTC'), greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) AS min_timestamp_with_condition, if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id) AS actor_id, argMin(e.uuid, toTimeZone(e.timestamp, 'UTC')) AS uuid, argMin(e.distinct_id, toTimeZone(e.timestamp, 'UTC')) AS distinct_id @@ -500,7 +500,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2025-01-21 23:59:59.999999', 6, 'UTC')), equals(e.event, 'signup')) + WHERE and(equals(e.team_id, 99999), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')), equals(e.event, 'signup')) GROUP BY if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id) HAVING ifNull(equals(min_timestamp, min_timestamp_with_condition), isNull(min_timestamp) and isNull(min_timestamp_with_condition))) diff --git a/ee/clickhouse/queries/test/__snapshots__/test_cohort_query.ambr b/ee/clickhouse/queries/test/__snapshots__/test_cohort_query.ambr index d86295051e4b7..95fa0e096850e 100644 --- a/ee/clickhouse/queries/test/__snapshots__/test_cohort_query.ambr +++ b/ee/clickhouse/queries/test/__snapshots__/test_cohort_query.ambr @@ -367,7 +367,7 @@ SELECT behavior_query.person_id AS id FROM (SELECT pdi.person_id AS person_id, - countIf(timestamp > 'explicit_timestamp' + countIf(timestamp > 'explicit_redacted_timestamp' AND timestamp < now() AND event = '$pageview' AND (has(['something'], replaceRegexpAll(JSONExtractRaw(properties, '$filter_prop'), '^"|"$', '')))) > 0 AS performed_event_condition_None_level_level_0_level_0_0 diff --git a/ee/clickhouse/views/test/funnel/__snapshots__/test_clickhouse_funnel.ambr b/ee/clickhouse/views/test/funnel/__snapshots__/test_clickhouse_funnel.ambr index 3923f79e01cca..70360a65beea6 100644 --- a/ee/clickhouse/views/test/funnel/__snapshots__/test_clickhouse_funnel.ambr +++ b/ee/clickhouse/views/test/funnel/__snapshots__/test_clickhouse_funnel.ambr @@ -271,7 +271,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -411,7 +411,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__group_0.properties___industry, 'finance'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__group_0.properties___industry, 'finance'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps diff --git a/ee/clickhouse/views/test/funnel/__snapshots__/test_clickhouse_funnel_person.ambr b/ee/clickhouse/views/test/funnel/__snapshots__/test_clickhouse_funnel_person.ambr index 7272f10b35d60..f561791090a1d 100644 --- a/ee/clickhouse/views/test/funnel/__snapshots__/test_clickhouse_funnel_person.ambr +++ b/ee/clickhouse/views/test/funnel/__snapshots__/test_clickhouse_funnel_person.ambr @@ -56,6 +56,7 @@ e."$group_0" as aggregation_target, if(notEmpty(pdi.distinct_id), pdi.person_id, e.person_id) as person_id, person.person_props as person_props, + person.pmat_email as pmat_email, if(event = 'step one', 1, 0) as step_0, if(step_0 = 1, timestamp, null) as latest_0, if(event = 'step two', 1, 0) as step_1, @@ -79,6 +80,7 @@ HAVING argMax(is_deleted, version) = 0) AS pdi ON e.distinct_id = pdi.distinct_id INNER JOIN (SELECT id, + argMax(pmat_email, version) as pmat_email, argMax(properties, version) as person_props FROM person WHERE team_id = 99999 @@ -95,7 +97,7 @@ AND event IN ['step one', 'step three', 'step two'] AND toTimeZone(timestamp, 'UTC') >= toDateTime('2021-05-01 00:00:00', 'UTC') AND toTimeZone(timestamp, 'UTC') <= toDateTime('2021-05-10 23:59:59', 'UTC') - AND ((replaceRegexpAll(JSONExtractRaw(person_props, 'email'), '^"|"$', '') ILIKE '%g0%' + AND (("pmat_email" ILIKE '%g0%' OR replaceRegexpAll(JSONExtractRaw(person_props, 'name'), '^"|"$', '') ILIKE '%g0%' OR replaceRegexpAll(JSONExtractRaw(e.properties, 'distinct_id'), '^"|"$', '') ILIKE '%g0%' OR replaceRegexpAll(JSONExtractRaw(group_properties_0, 'name'), '^"|"$', '') ILIKE '%g0%' diff --git a/ee/hogai/assistant.py b/ee/hogai/assistant.py index b4b6d80029606..d8770433e5fa1 100644 --- a/ee/hogai/assistant.py +++ b/ee/hogai/assistant.py @@ -1,13 +1,13 @@ import json from collections.abc import Generator, Iterator from typing import Any, Optional, cast -from uuid import uuid4 +from uuid import UUID, uuid4 +import posthoganalytics from langchain_core.callbacks.base import BaseCallbackHandler from langchain_core.messages import AIMessageChunk from langchain_core.runnables.config import RunnableConfig from langgraph.graph.state import CompiledStateGraph -import posthoganalytics from posthoganalytics.ai.langchain.callbacks import CallbackHandler from pydantic import BaseModel @@ -79,6 +79,7 @@ def __init__( new_message: HumanMessage, user: Optional[User] = None, is_new_conversation: bool = False, + trace_id: Optional[str | UUID] = None, ): self._team = team self._user = user @@ -96,6 +97,7 @@ def __init__( "conversation_id": str(self._conversation.id), "is_first_conversation": is_new_conversation, }, + trace_id=trace_id, ) if posthoganalytics.default_client else None 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 index e3c36bc7032f9..516b0a5bd017a 100644 --- 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 @@ -48,27 +48,27 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_7)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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_8)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_9)s), person.version), plus(now64(6, %(hogql_val_10)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_11)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_12)s), now64(6, %(hogql_val_13)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_14)s), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_15)s), 0)) - GROUP BY events.`$session_id` - HAVING 1))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_7)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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_8)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_9)s), person.version), plus(now64(6, %(hogql_val_10)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_11)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_12)s), now64(6, %(hogql_val_13)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_14)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_15)s), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -92,27 +92,27 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_7)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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_8)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_9)s), person.version), plus(now64(6, %(hogql_val_10)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_11)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_12)s), now64(6, %(hogql_val_13)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_14)s), toDateTime64('2020-12-28 23:58:00.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_15)s), 0)) - GROUP BY events.`$session_id` - HAVING 1))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_7)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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_8)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_9)s), person.version), plus(now64(6, %(hogql_val_10)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_11)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_12)s), now64(6, %(hogql_val_13)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_14)s), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_15)s), 0)) + GROUP BY events.`$session_id` + HAVING 1))) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -535,30 +535,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -625,30 +625,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -715,30 +715,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -805,30 +805,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -1312,19 +1312,19 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1356,19 +1356,19 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1400,19 +1400,19 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1444,19 +1444,19 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1488,19 +1488,19 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1532,19 +1532,19 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1576,19 +1576,19 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -1620,19 +1620,19 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC diff --git a/frontend/__snapshots__/components-activitylog--insight-activity--dark.png b/frontend/__snapshots__/components-activitylog--insight-activity--dark.png index 9fa6256d0c217..b4d37292ee9a8 100644 Binary files a/frontend/__snapshots__/components-activitylog--insight-activity--dark.png and b/frontend/__snapshots__/components-activitylog--insight-activity--dark.png differ diff --git a/frontend/__snapshots__/components-activitylog--insight-activity--light.png b/frontend/__snapshots__/components-activitylog--insight-activity--light.png index bc40839af4c33..12512e9a25861 100644 Binary files a/frontend/__snapshots__/components-activitylog--insight-activity--light.png and b/frontend/__snapshots__/components-activitylog--insight-activity--light.png differ diff --git a/frontend/__snapshots__/components-errors-error-display--with-cymbal-errors--dark.png b/frontend/__snapshots__/components-errors-error-display--with-cymbal-errors--dark.png index f848b76c806df..f667196910683 100644 Binary files a/frontend/__snapshots__/components-errors-error-display--with-cymbal-errors--dark.png and b/frontend/__snapshots__/components-errors-error-display--with-cymbal-errors--dark.png differ diff --git a/frontend/__snapshots__/components-hogfetti--hogfetti--dark.png b/frontend/__snapshots__/components-hogfetti--hogfetti--dark.png index bb28fb83c7543..49def50ebe875 100644 Binary files a/frontend/__snapshots__/components-hogfetti--hogfetti--dark.png and b/frontend/__snapshots__/components-hogfetti--hogfetti--dark.png differ diff --git a/frontend/__snapshots__/components-html-elements-display--small-with-uniqueness-check--dark.png b/frontend/__snapshots__/components-html-elements-display--small-with-uniqueness-check--dark.png index 7e1f54d60ffb2..501f880f4c9c1 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--small-with-uniqueness-check--dark.png and b/frontend/__snapshots__/components-html-elements-display--small-with-uniqueness-check--dark.png differ diff --git a/frontend/__snapshots__/components-html-elements-display--small-with-uniqueness-check--light.png b/frontend/__snapshots__/components-html-elements-display--small-with-uniqueness-check--light.png index 28cfc2ba09456..e9c25ac96ab2c 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--small-with-uniqueness-check--light.png and b/frontend/__snapshots__/components-html-elements-display--small-with-uniqueness-check--light.png differ diff --git a/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--dark.png b/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--dark.png index 7e1f54d60ffb2..501f880f4c9c1 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--dark.png and b/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--dark.png differ diff --git a/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--light.png b/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--light.png index 28cfc2ba09456..e9c25ac96ab2c 100644 Binary files a/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--light.png and b/frontend/__snapshots__/components-html-elements-display--with-uniqueness-check--light.png differ diff --git a/frontend/__snapshots__/components-playlist--default--dark.png b/frontend/__snapshots__/components-playlist--default--dark.png index c17671656b2c1..d0db141dfb9f8 100644 Binary files a/frontend/__snapshots__/components-playlist--default--dark.png and b/frontend/__snapshots__/components-playlist--default--dark.png differ diff --git a/frontend/__snapshots__/components-playlist--default--light.png b/frontend/__snapshots__/components-playlist--default--light.png index a375e48085c59..733f30b6e0509 100644 Binary files a/frontend/__snapshots__/components-playlist--default--light.png and b/frontend/__snapshots__/components-playlist--default--light.png differ diff --git a/frontend/__snapshots__/components-playlist--multiple-sections--dark.png b/frontend/__snapshots__/components-playlist--multiple-sections--dark.png index c3bc31e0d0c3d..00602c6e33065 100644 Binary files a/frontend/__snapshots__/components-playlist--multiple-sections--dark.png and b/frontend/__snapshots__/components-playlist--multiple-sections--dark.png differ diff --git a/frontend/__snapshots__/components-playlist--multiple-sections--light.png b/frontend/__snapshots__/components-playlist--multiple-sections--light.png index b6142d3873a4b..aba08e2cdb7d5 100644 Binary files a/frontend/__snapshots__/components-playlist--multiple-sections--light.png and b/frontend/__snapshots__/components-playlist--multiple-sections--light.png differ diff --git a/frontend/__snapshots__/components-playlist--with-footer--dark.png b/frontend/__snapshots__/components-playlist--with-footer--dark.png index 4d39d12ab654e..a66600dd71699 100644 Binary files a/frontend/__snapshots__/components-playlist--with-footer--dark.png and b/frontend/__snapshots__/components-playlist--with-footer--dark.png differ diff --git a/frontend/__snapshots__/components-playlist--with-footer--light.png b/frontend/__snapshots__/components-playlist--with-footer--light.png index 25b084434616d..d338b5389100e 100644 Binary files a/frontend/__snapshots__/components-playlist--with-footer--light.png and b/frontend/__snapshots__/components-playlist--with-footer--light.png differ diff --git a/frontend/__snapshots__/components-property-key-info--property-key-info--dark.png b/frontend/__snapshots__/components-property-key-info--property-key-info--dark.png index 0f7a15872752d..c7ceacfd4fd49 100644 Binary files a/frontend/__snapshots__/components-property-key-info--property-key-info--dark.png and b/frontend/__snapshots__/components-property-key-info--property-key-info--dark.png differ diff --git a/frontend/__snapshots__/components-property-key-info--property-key-info--light.png b/frontend/__snapshots__/components-property-key-info--property-key-info--light.png index b9cd979649fc2..649dba618ee27 100644 Binary files a/frontend/__snapshots__/components-property-key-info--property-key-info--light.png and b/frontend/__snapshots__/components-property-key-info--property-key-info--light.png differ diff --git a/frontend/__snapshots__/design-system-colors--brand-colors--dark.png b/frontend/__snapshots__/design-system-colors--brand-colors--dark.png index 61ae47b886556..3d3d266aee519 100644 Binary files a/frontend/__snapshots__/design-system-colors--brand-colors--dark.png and b/frontend/__snapshots__/design-system-colors--brand-colors--dark.png differ diff --git a/frontend/__snapshots__/design-system-colors--brand-colors--light.png b/frontend/__snapshots__/design-system-colors--brand-colors--light.png index 8ab7ed567f6fd..8422fb68f5eca 100644 Binary files a/frontend/__snapshots__/design-system-colors--brand-colors--light.png and b/frontend/__snapshots__/design-system-colors--brand-colors--light.png differ diff --git a/frontend/__snapshots__/design-system-colors--primitive-colors--dark.png b/frontend/__snapshots__/design-system-colors--primitive-colors--dark.png index effa25391f57e..5940ff0dc0b00 100644 Binary files a/frontend/__snapshots__/design-system-colors--primitive-colors--dark.png and b/frontend/__snapshots__/design-system-colors--primitive-colors--dark.png differ diff --git a/frontend/__snapshots__/design-system-colors--primitive-colors--light.png b/frontend/__snapshots__/design-system-colors--primitive-colors--light.png index 3f2b1b17883f9..fd73ac2623ce0 100644 Binary files a/frontend/__snapshots__/design-system-colors--primitive-colors--light.png and b/frontend/__snapshots__/design-system-colors--primitive-colors--light.png differ diff --git a/frontend/__snapshots__/design-system-colors--semantic-colors--dark.png b/frontend/__snapshots__/design-system-colors--semantic-colors--dark.png new file mode 100644 index 0000000000000..2a6a6b13342f5 Binary files /dev/null and b/frontend/__snapshots__/design-system-colors--semantic-colors--dark.png differ diff --git a/frontend/__snapshots__/design-system-colors--semantic-colors--light.png b/frontend/__snapshots__/design-system-colors--semantic-colors--light.png new file mode 100644 index 0000000000000..0ead01d40bfc3 Binary files /dev/null and b/frontend/__snapshots__/design-system-colors--semantic-colors--light.png differ diff --git a/frontend/__snapshots__/exporter-exporter--dashboard--dark.png b/frontend/__snapshots__/exporter-exporter--dashboard--dark.png index fd246c4f284c6..d9e51d1f75bac 100644 Binary files a/frontend/__snapshots__/exporter-exporter--dashboard--dark.png and b/frontend/__snapshots__/exporter-exporter--dashboard--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-badge-lemon-badge--active--dark.png b/frontend/__snapshots__/lemon-ui-lemon-badge-lemon-badge--active--dark.png index 7ec1e19e66e54..0a79b2663fac8 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-badge-lemon-badge--active--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-badge-lemon-badge--active--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--closable--dark.png b/frontend/__snapshots__/lemon-ui-lemon-banner--closable--dark.png index e143dbc600893..46b88e16ba775 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--closable--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--closable--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--closable--light.png b/frontend/__snapshots__/lemon-ui-lemon-banner--closable--light.png index 884c6abcce0a5..29b9b3f574636 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--closable--light.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--closable--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable--dark.png b/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable--dark.png index e0a769a62e6ec..2a862a5b9670a 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable--light.png b/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable--light.png index ee17d48028034..98bea62cccc6c 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable--light.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--error--dark.png b/frontend/__snapshots__/lemon-ui-lemon-banner--error--dark.png index a06f335a66446..2cd25a7065c47 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--error--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--error--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--error--light.png b/frontend/__snapshots__/lemon-ui-lemon-banner--error--light.png index f6347948cb166..a93b6f04f00eb 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--error--light.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--error--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--info--dark.png b/frontend/__snapshots__/lemon-ui-lemon-banner--info--dark.png index 16c1f52ebd2da..e935b43ffde89 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--info--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--info--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--info--light.png b/frontend/__snapshots__/lemon-ui-lemon-banner--info--light.png index e4d448e7f9fef..c6e9d1eca2a11 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--info--light.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--info--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--narrow--dark.png b/frontend/__snapshots__/lemon-ui-lemon-banner--narrow--dark.png index 33f17df67f01c..447bdfffdebfa 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--narrow--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--narrow--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--narrow--light.png b/frontend/__snapshots__/lemon-ui-lemon-banner--narrow--light.png index 7ccf598a882c0..ed41ddd0fbb5e 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--narrow--light.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--narrow--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--narrow-with-buttons--dark.png b/frontend/__snapshots__/lemon-ui-lemon-banner--narrow-with-buttons--dark.png index 188640a767b47..317c100a8d66e 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--narrow-with-buttons--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--narrow-with-buttons--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--success--dark.png b/frontend/__snapshots__/lemon-ui-lemon-banner--success--dark.png index 6717e6e657649..f6902b7eb41ac 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--success--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--success--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--success--light.png b/frontend/__snapshots__/lemon-ui-lemon-banner--success--light.png index 0f14dfa86ac3f..219bce3eca6db 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--success--light.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--success--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--warning--dark.png b/frontend/__snapshots__/lemon-ui-lemon-banner--warning--dark.png index 5d88520becc4c..3623766f9781a 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--warning--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--warning--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-button--as-links--dark.png b/frontend/__snapshots__/lemon-ui-lemon-button--as-links--dark.png index 4a05a07fe66a3..095aa3d3218f4 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-button--as-links--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-button--as-links--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-input--prefix--dark.png b/frontend/__snapshots__/lemon-ui-lemon-input--prefix--dark.png new file mode 100644 index 0000000000000..b0304a1e908a3 Binary files /dev/null and b/frontend/__snapshots__/lemon-ui-lemon-input--prefix--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-input--prefix--light.png b/frontend/__snapshots__/lemon-ui-lemon-input--prefix--light.png new file mode 100644 index 0000000000000..9429baabd334f Binary files /dev/null and b/frontend/__snapshots__/lemon-ui-lemon-input--prefix--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-radio--with-top-position--dark.png b/frontend/__snapshots__/lemon-ui-lemon-radio--with-top-position--dark.png new file mode 100644 index 0000000000000..52603937e925f Binary files /dev/null and b/frontend/__snapshots__/lemon-ui-lemon-radio--with-top-position--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-radio--with-top-position--light.png b/frontend/__snapshots__/lemon-ui-lemon-radio--with-top-position--light.png new file mode 100644 index 0000000000000..5d5f308256da0 Binary files /dev/null and b/frontend/__snapshots__/lemon-ui-lemon-radio--with-top-position--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-tag--lemon-tag--light.png b/frontend/__snapshots__/lemon-ui-lemon-tag--lemon-tag--light.png index 4b0f7e1e10387..317ba0c1e4061 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-tag--lemon-tag--light.png and b/frontend/__snapshots__/lemon-ui-lemon-tag--lemon-tag--light.png differ diff --git a/frontend/__snapshots__/posthog-3000-sidebar--feature-flags--dark.png b/frontend/__snapshots__/posthog-3000-sidebar--feature-flags--dark.png index 7829d129a5824..3a5c14de58b46 100644 Binary files a/frontend/__snapshots__/posthog-3000-sidebar--feature-flags--dark.png and b/frontend/__snapshots__/posthog-3000-sidebar--feature-flags--dark.png differ diff --git a/frontend/__snapshots__/posthog-3000-sidebar--feature-flags--light.png b/frontend/__snapshots__/posthog-3000-sidebar--feature-flags--light.png index 5486d5cbee97e..ddc0a38607472 100644 Binary files a/frontend/__snapshots__/posthog-3000-sidebar--feature-flags--light.png and b/frontend/__snapshots__/posthog-3000-sidebar--feature-flags--light.png differ diff --git a/frontend/__snapshots__/queries-webvitals--web-vitals--dark.png b/frontend/__snapshots__/queries-webvitals--web-vitals--dark.png new file mode 100644 index 0000000000000..a27903d954d40 Binary files /dev/null and b/frontend/__snapshots__/queries-webvitals--web-vitals--dark.png differ diff --git a/frontend/__snapshots__/queries-webvitals--web-vitals--light.png b/frontend/__snapshots__/queries-webvitals--web-vitals--light.png new file mode 100644 index 0000000000000..b13819a0e8b44 Binary files /dev/null and b/frontend/__snapshots__/queries-webvitals--web-vitals--light.png differ diff --git a/frontend/__snapshots__/queries-webvitalspathbreakdown--web-vitals-path-breakdown--dark.png b/frontend/__snapshots__/queries-webvitalspathbreakdown--web-vitals-path-breakdown--dark.png new file mode 100644 index 0000000000000..d994cadd2a7df Binary files /dev/null and b/frontend/__snapshots__/queries-webvitalspathbreakdown--web-vitals-path-breakdown--dark.png differ diff --git a/frontend/__snapshots__/queries-webvitalspathbreakdown--web-vitals-path-breakdown--light.png b/frontend/__snapshots__/queries-webvitalspathbreakdown--web-vitals-path-breakdown--light.png new file mode 100644 index 0000000000000..35077006522e7 Binary files /dev/null and b/frontend/__snapshots__/queries-webvitalspathbreakdown--web-vitals-path-breakdown--light.png differ diff --git a/frontend/__snapshots__/replay-player-success--recent-recordings--dark.png b/frontend/__snapshots__/replay-player-success--recent-recordings--dark.png index 3ef9bb3e14605..5a5e1ef64c9d4 100644 Binary files a/frontend/__snapshots__/replay-player-success--recent-recordings--dark.png and b/frontend/__snapshots__/replay-player-success--recent-recordings--dark.png differ diff --git a/frontend/__snapshots__/replay-player-success--recent-recordings--light.png b/frontend/__snapshots__/replay-player-success--recent-recordings--light.png index 487c471f83e03..a929b624d162a 100644 Binary files a/frontend/__snapshots__/replay-player-success--recent-recordings--light.png and b/frontend/__snapshots__/replay-player-success--recent-recordings--light.png differ diff --git a/frontend/__snapshots__/replay-player-success--second-recording-in-list--dark.png b/frontend/__snapshots__/replay-player-success--second-recording-in-list--dark.png index 485897f412b68..958231f193965 100644 Binary files a/frontend/__snapshots__/replay-player-success--second-recording-in-list--dark.png and b/frontend/__snapshots__/replay-player-success--second-recording-in-list--dark.png differ diff --git a/frontend/__snapshots__/replay-player-success--second-recording-in-list--light.png b/frontend/__snapshots__/replay-player-success--second-recording-in-list--light.png index 4473ec5b7f5a8..c58b6e6c18cb1 100644 Binary files a/frontend/__snapshots__/replay-player-success--second-recording-in-list--light.png and b/frontend/__snapshots__/replay-player-success--second-recording-in-list--light.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--edit-remote-config-feature-flag--dark.png b/frontend/__snapshots__/scenes-app-feature-flags--edit-remote-config-feature-flag--dark.png new file mode 100644 index 0000000000000..e0a1833342f3e Binary files /dev/null and b/frontend/__snapshots__/scenes-app-feature-flags--edit-remote-config-feature-flag--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--edit-remote-config-feature-flag--light.png b/frontend/__snapshots__/scenes-app-feature-flags--edit-remote-config-feature-flag--light.png new file mode 100644 index 0000000000000..a084c0f7e3633 Binary files /dev/null and b/frontend/__snapshots__/scenes-app-feature-flags--edit-remote-config-feature-flag--light.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--feature-flags-list--dark.png b/frontend/__snapshots__/scenes-app-feature-flags--feature-flags-list--dark.png index f802f28fec914..bcf344cbb64ce 100644 Binary files a/frontend/__snapshots__/scenes-app-feature-flags--feature-flags-list--dark.png and b/frontend/__snapshots__/scenes-app-feature-flags--feature-flags-list--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--feature-flags-list--light.png b/frontend/__snapshots__/scenes-app-feature-flags--feature-flags-list--light.png index cf03f291445c5..053737e28c943 100644 Binary files a/frontend/__snapshots__/scenes-app-feature-flags--feature-flags-list--light.png and b/frontend/__snapshots__/scenes-app-feature-flags--feature-flags-list--light.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--new-feature-flag--dark.png b/frontend/__snapshots__/scenes-app-feature-flags--new-feature-flag--dark.png index 835b68b2ea10d..86549f6d80c84 100644 Binary files a/frontend/__snapshots__/scenes-app-feature-flags--new-feature-flag--dark.png and b/frontend/__snapshots__/scenes-app-feature-flags--new-feature-flag--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--new-feature-flag--light.png b/frontend/__snapshots__/scenes-app-feature-flags--new-feature-flag--light.png index 64f704c6a75bc..36378b620ec30 100644 Binary files a/frontend/__snapshots__/scenes-app-feature-flags--new-feature-flag--light.png and b/frontend/__snapshots__/scenes-app-feature-flags--new-feature-flag--light.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown--light.png b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown--light.png index 969049953221d..c52297b7c8ddd 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown--light.png and b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown--light.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--notebooks-list--dark.png b/frontend/__snapshots__/scenes-app-notebooks--notebooks-list--dark.png index c54f8204d23fa..220b881f1fece 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--notebooks-list--dark.png and b/frontend/__snapshots__/scenes-app-notebooks--notebooks-list--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-new-hog-function--dark.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-new-hog-function--dark.png index e2a9211eb848d..53e9883e83ee0 100644 Binary files a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-new-hog-function--dark.png and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-new-hog-function--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-new-hog-function--light.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-new-hog-function--light.png index 9e368a5e1eb05..1eaddf28cc8a7 100644 Binary files a/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-new-hog-function--light.png and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-node-new-hog-function--light.png differ diff --git a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png index 4c898d798b0ba..954ad1f02d6ba 100644 Binary files a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png and b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png index 720fc8ce9e6c3..66bd2187c7462 100644 Binary files a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png and b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png differ diff --git a/frontend/__snapshots__/scenes-app-web-analytics--web-analytics-dashboard--dark.png b/frontend/__snapshots__/scenes-app-web-analytics--web-analytics-dashboard--dark.png new file mode 100644 index 0000000000000..e49e21eb17170 Binary files /dev/null and b/frontend/__snapshots__/scenes-app-web-analytics--web-analytics-dashboard--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-web-analytics--web-analytics-dashboard--light.png b/frontend/__snapshots__/scenes-app-web-analytics--web-analytics-dashboard--light.png new file mode 100644 index 0000000000000..9efe8bd719298 Binary files /dev/null and b/frontend/__snapshots__/scenes-app-web-analytics--web-analytics-dashboard--light.png differ diff --git a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--dark.png b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--dark.png index 4a5327dba9e8d..cc67f683f43ee 100644 Binary files a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--dark.png and b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-billing--billing-with-credit-cta--light.png b/frontend/__snapshots__/scenes-other-billing--billing-with-credit-cta--light.png index 2f16be3127aeb..edbce809e0bcf 100644 Binary files a/frontend/__snapshots__/scenes-other-billing--billing-with-credit-cta--light.png and b/frontend/__snapshots__/scenes-other-billing--billing-with-credit-cta--light.png differ diff --git a/frontend/__snapshots__/scenes-other-billing-product--billing-product-temporarily-free--dark.png b/frontend/__snapshots__/scenes-other-billing-product--billing-product-temporarily-free--dark.png index 0b155eda5c097..65a0a140d0a81 100644 Binary files a/frontend/__snapshots__/scenes-other-billing-product--billing-product-temporarily-free--dark.png and b/frontend/__snapshots__/scenes-other-billing-product--billing-product-temporarily-free--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-admin--dark.png b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-admin--dark.png index 9ac327262fda7..fe4970b399a02 100644 Binary files a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-admin--dark.png and b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-admin--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-admin--light.png b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-admin--light.png index ed6d860d966a8..34e6fe6f7e0b0 100644 Binary files a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-admin--light.png and b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-admin--light.png differ diff --git a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-member--dark.png b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-member--dark.png index 99868c3dcd292..f328b37a5dc11 100644 Binary files a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-member--dark.png and b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-member--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-member--light.png b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-member--light.png index d88c2735d6269..30d56cc6d5653 100644 Binary files a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-member--light.png and b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-member--light.png differ diff --git a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-owner--dark.png b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-owner--dark.png index 9ac327262fda7..fe4970b399a02 100644 Binary files a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-owner--dark.png and b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-owner--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-owner--light.png b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-owner--light.png index ed6d860d966a8..34e6fe6f7e0b0 100644 Binary files a/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-owner--light.png and b/frontend/__snapshots__/scenes-other-org-member-invites--current-user-is-owner--light.png differ diff --git a/frontend/__snapshots__/web-analytics-dashboard--web-analytics-dashboard--dark.png b/frontend/__snapshots__/web-analytics-dashboard--web-analytics-dashboard--dark.png new file mode 100644 index 0000000000000..e49e21eb17170 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-dashboard--web-analytics-dashboard--dark.png differ diff --git a/frontend/__snapshots__/web-analytics-dashboard--web-analytics-dashboard--light.png b/frontend/__snapshots__/web-analytics-dashboard--web-analytics-dashboard--light.png new file mode 100644 index 0000000000000..9efe8bd719298 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-dashboard--web-analytics-dashboard--light.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--browser--dark.png b/frontend/__snapshots__/web-analytics-tiles--browser--dark.png new file mode 100644 index 0000000000000..3188c20285aa9 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--browser--dark.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--browser--light.png b/frontend/__snapshots__/web-analytics-tiles--browser--light.png new file mode 100644 index 0000000000000..11850867528d8 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--browser--light.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--path--dark.png b/frontend/__snapshots__/web-analytics-tiles--path--dark.png new file mode 100644 index 0000000000000..c9e06472e8739 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--path--dark.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--path--light.png b/frontend/__snapshots__/web-analytics-tiles--path--light.png new file mode 100644 index 0000000000000..33e50d2109f64 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--path--light.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--referrer-domain--dark.png b/frontend/__snapshots__/web-analytics-tiles--referrer-domain--dark.png new file mode 100644 index 0000000000000..cef5f08691cf0 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--referrer-domain--dark.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--referrer-domain--light.png b/frontend/__snapshots__/web-analytics-tiles--referrer-domain--light.png new file mode 100644 index 0000000000000..8dd3b9b239aa1 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--referrer-domain--light.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--retention--dark.png b/frontend/__snapshots__/web-analytics-tiles--retention--dark.png new file mode 100644 index 0000000000000..aceedd35ce9b3 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--retention--dark.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--retention--light.png b/frontend/__snapshots__/web-analytics-tiles--retention--light.png new file mode 100644 index 0000000000000..45cf2557b4a16 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--retention--light.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--world-map--dark.png b/frontend/__snapshots__/web-analytics-tiles--world-map--dark.png new file mode 100644 index 0000000000000..1293654e36626 Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--world-map--dark.png differ diff --git a/frontend/__snapshots__/web-analytics-tiles--world-map--light.png b/frontend/__snapshots__/web-analytics-tiles--world-map--light.png new file mode 100644 index 0000000000000..8f9642c00cb4f Binary files /dev/null and b/frontend/__snapshots__/web-analytics-tiles--world-map--light.png differ diff --git a/frontend/src/layout.ejs b/frontend/src/layout.ejs index b9649a9934ad4..ee3198f112350 100644 --- a/frontend/src/layout.ejs +++ b/frontend/src/layout.ejs @@ -110,7 +110,7 @@ input:focus + label { transform: translate(0, 0) scale(1); cursor: pointer; - color: var(--primary); + color: var(--accent-primary); } {% block head %} {% endblock %} diff --git a/frontend/src/layout.html b/frontend/src/layout.html index a07ce3034f1a8..fea0951970735 100644 --- a/frontend/src/layout.html +++ b/frontend/src/layout.html @@ -149,7 +149,7 @@ input:focus+label { transform: translate(0, 0) scale(1); cursor: pointer; - color: var(--primary); + color: var(--accent-primary); } h2 { diff --git a/frontend/src/layout/navigation-3000/navigationLogic.tsx b/frontend/src/layout/navigation-3000/navigationLogic.tsx index 2cdfb214e8790..298544851d049 100644 --- a/frontend/src/layout/navigation-3000/navigationLogic.tsx +++ b/frontend/src/layout/navigation-3000/navigationLogic.tsx @@ -567,7 +567,7 @@ export const navigation3000Logic = kea([ label: 'Error tracking', icon: , to: urls.errorTracking(), - tag: 'alpha' as const, + tag: 'beta' as const, } : null, featureFlags[FEATURE_FLAGS.HEATMAPS_UI] diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx index 7701538ffd36c..957e52372a259 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx @@ -259,7 +259,7 @@ export const SidePanelSupport = (): JSX.Element => { targetBlank className="mt-2" > - Email an engineer + Email our support engineers ) : null} diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/activation/SidePanelActivation.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/activation/SidePanelActivation.tsx index 5aef55b51a523..fa8b6993fd67e 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/activation/SidePanelActivation.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/activation/SidePanelActivation.tsx @@ -23,7 +23,11 @@ export const SidePanelActivation = (): JSX.Element => {

Use our Quick Start guide to learn about everything PostHog can do for you and your product.

- + {activeTasks.length}

still to go

diff --git a/frontend/src/layout/navigation/TopBar/AccountPopover.scss b/frontend/src/layout/navigation/TopBar/AccountPopover.scss index bdf99c5a3739f..af0924404c5dc 100644 --- a/frontend/src/layout/navigation/TopBar/AccountPopover.scss +++ b/frontend/src/layout/navigation/TopBar/AccountPopover.scss @@ -5,12 +5,13 @@ max-width: 22rem; } +// TODO: to remove, this is unused .AccountPopover__side-link { flex-grow: 1; margin-left: 0.5rem; font-size: 0.8125rem; font-weight: 600; - color: var(--primary-3000); + color: var(--accent-primary); text-align: right; } diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 7e755747cf6a7..8d15e128b0374 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -2659,7 +2659,7 @@ const api = { }, conversations: { - async create(data: { content: string; conversation?: string | null }): Promise { + async create(data: { content: string; conversation?: string | null; trace_id: string }): Promise { return api.createResponse(new ApiRequest().conversations().assembleFullUrl(), data) }, }, diff --git a/frontend/src/lib/components/ActivityLog/ActivityLog.scss b/frontend/src/lib/components/ActivityLog/ActivityLog.scss index fb27cb2e79afe..fe160e2d2bcba 100644 --- a/frontend/src/lib/components/ActivityLog/ActivityLog.scss +++ b/frontend/src/lib/components/ActivityLog/ActivityLog.scss @@ -37,7 +37,7 @@ overflow-wrap: anywhere; &--unread { - background-color: var(--primary-highlight); + background-color: var(--accent-primary-highlight); border-radius: var(--radius); } diff --git a/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss b/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss index c61a7a179be63..1a56aa13c8fd4 100644 --- a/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss +++ b/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss @@ -12,8 +12,8 @@ transition: border 200ms ease; &--highlighted { - border-color: var(--primary-3000); - outline: 1px solid var(--primary-3000); + border-color: var(--accent-primary); + outline: 1px solid var(--accent-primary); } .ErrorBoundary { @@ -141,7 +141,7 @@ line-height: 1rem; color: var(--text-3000); vertical-align: middle; - background: var(--primary-highlight); + background: var(--accent-primary-highlight); border-radius: var(--radius); &.SeriesDisplay__raw-name--action, @@ -158,7 +158,7 @@ line-height: 1rem; color: var(--bg-light); text-align: center; - background: var(--primary-3000); + background: var(--accent-primary); border-radius: var(--radius); } } diff --git a/frontend/src/lib/components/Cards/InsightCard/InsightMeta.tsx b/frontend/src/lib/components/Cards/InsightCard/InsightMeta.tsx index 91c331b2f23b6..0adc214a92bfb 100644 --- a/frontend/src/lib/components/Cards/InsightCard/InsightMeta.tsx +++ b/frontend/src/lib/components/Cards/InsightCard/InsightMeta.tsx @@ -289,7 +289,7 @@ export function InsightMetaContent({ title="This insight is queued to check for newer results. It will be updated soon." placement="top-end" > - + Refreshing diff --git a/frontend/src/lib/components/Cards/handles.tsx b/frontend/src/lib/components/Cards/handles.tsx index 68d82e2b59853..6b9e67db1ebff 100644 --- a/frontend/src/lib/components/Cards/handles.tsx +++ b/frontend/src/lib/components/Cards/handles.tsx @@ -6,7 +6,7 @@ export function ResizeHandle1D({ orientation }: { orientation: 'horizontal' | 'v
- + @@ -28,7 +28,7 @@ export function ResizeHandle2D(): JSX.Element {
- + diff --git a/frontend/src/lib/components/CommandBar/ActionResult.tsx b/frontend/src/lib/components/CommandBar/ActionResult.tsx index de97368c3ff66..b9bb9e5b6b5df 100644 --- a/frontend/src/lib/components/CommandBar/ActionResult.tsx +++ b/frontend/src/lib/components/CommandBar/ActionResult.tsx @@ -26,7 +26,7 @@ export const ActionResult = ({ result, focused }: SearchResultProps): JSX.Elemen
{ setActiveTab(tab) @@ -48,7 +48,6 @@ const Count = ({ tab }: CountProps): JSX.Element | null => { return } else if (!isLoading && tabsCount[tab] != null) { return {tabsCount[tab]} - } else { - return null } + return null } diff --git a/frontend/src/lib/components/CommandBar/SearchResult.tsx b/frontend/src/lib/components/CommandBar/SearchResult.tsx index 7335fdebc61bd..26e2d76d769a9 100644 --- a/frontend/src/lib/components/CommandBar/SearchResult.tsx +++ b/frontend/src/lib/components/CommandBar/SearchResult.tsx @@ -49,7 +49,7 @@ export const SearchResult = ({ result, resultIndex, focused }: SearchResultProps
{ if (mobileLayout) { diff --git a/frontend/src/lib/components/CommandBar/index.scss b/frontend/src/lib/components/CommandBar/index.scss index 3150d46ed5ada..65c9679aec9da 100644 --- a/frontend/src/lib/components/CommandBar/index.scss +++ b/frontend/src/lib/components/CommandBar/index.scss @@ -7,12 +7,13 @@ } .SearchBarTab { + // TODO: not working, look to fix &:hover { border-left: 2px solid var(--border-3000); } &.SearchBarTab__active { - border-color: var(--primary-3000); + border-color: var(--accent-primary); } } diff --git a/frontend/src/lib/components/CopyToClipboard.tsx b/frontend/src/lib/components/CopyToClipboard.tsx index 649d29f5b1d7d..b0bfa7dfbc887 100644 --- a/frontend/src/lib/components/CopyToClipboard.tsx +++ b/frontend/src/lib/components/CopyToClipboard.tsx @@ -10,7 +10,7 @@ interface InlinePropsBase { /** Makes text selectable instead of copying on click anywhere */ selectable?: boolean isValueSensitive?: boolean - tooltipMessage?: string | null + tooltipMessage?: React.ReactNode | null iconStyle?: Record /** @default end */ iconPosition?: 'end' | 'start' diff --git a/frontend/src/lib/components/DatabaseTableTree/TreeRow.tsx b/frontend/src/lib/components/DatabaseTableTree/TreeRow.tsx index eb542418f4520..44a39bfcad13e 100644 --- a/frontend/src/lib/components/DatabaseTableTree/TreeRow.tsx +++ b/frontend/src/lib/components/DatabaseTableTree/TreeRow.tsx @@ -112,10 +112,10 @@ export function TreeFolderRow({ item, depth, onClick, selectedRow, dropdownOverl return '' } - const getIconColor = (): 'text-primary' | 'text-danger' | 'text-warning' | 'text-success' => { + const getIconColor = (): 'text-accent-primary' | 'text-danger' | 'text-warning' | 'text-success' => { if (item.table?.type === 'materialized_view') { if (item.table.status === 'Running') { - return 'text-primary' + return 'text-accent-primary' } if (item.table.status === 'Failed') { return 'text-danger' diff --git a/frontend/src/lib/components/DebugNotice.tsx b/frontend/src/lib/components/DebugNotice.tsx index 72fbb460955aa..958f72fa84de6 100644 --- a/frontend/src/lib/components/DebugNotice.tsx +++ b/frontend/src/lib/components/DebugNotice.tsx @@ -38,7 +38,7 @@ export function DebugNotice(): JSX.Element | null { return ( } + icon={} title={
diff --git a/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.scss b/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.scss index a03bd65c54b8e..9715e3c91a0c3 100644 --- a/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.scss +++ b/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.scss @@ -63,7 +63,7 @@ } &.taxonomy-icon-built-in { - color: var(--primary-3000); + color: var(--accent-primary); } } } diff --git a/frontend/src/lib/components/HTMLElementsDisplay/SelectableElement.scss b/frontend/src/lib/components/HTMLElementsDisplay/SelectableElement.scss index 9bb52ae318d07..221187b3ed03f 100644 --- a/frontend/src/lib/components/HTMLElementsDisplay/SelectableElement.scss +++ b/frontend/src/lib/components/HTMLElementsDisplay/SelectableElement.scss @@ -4,7 +4,7 @@ transition: background-color 200ms ease, color 200ms ease, border 200ms ease; &.SelectableElement--selected { - background: var(--primary-3000); + background: var(--accent-primary); } &:hover { diff --git a/frontend/src/lib/components/HedgehogBuddy/HedgehogOptions.tsx b/frontend/src/lib/components/HedgehogBuddy/HedgehogOptions.tsx index 9530847ca69e8..e9a5ec73972d4 100644 --- a/frontend/src/lib/components/HedgehogBuddy/HedgehogOptions.tsx +++ b/frontend/src/lib/components/HedgehogBuddy/HedgehogOptions.tsx @@ -135,7 +135,7 @@ function HedgehogAccessories(): JSX.Element { key={acc} className={clsx( 'border-2', - accessories.includes(acc) ? 'border-primary' : 'border-transparent' + accessories.includes(acc) ? 'border-accent-primary' : 'border-transparent' )} size="small" onClick={() => onClick(acc)} @@ -170,7 +170,7 @@ function HedgehogColor(): JSX.Element { className={clsx( 'border-2', !hedgehogConfig.color && hedgehogConfig.skin === option - ? 'border-primary' + ? 'border-accent-primary' : 'border-transparent' )} size="small" @@ -186,7 +186,7 @@ function HedgehogColor(): JSX.Element { key={option} className={clsx( 'border-2', - hedgehogConfig.color === option ? 'border-primary' : 'border-transparent' + hedgehogConfig.color === option ? 'border-accent-primary' : 'border-transparent' )} size="small" onClick={() => patchHedgehogConfig({ color: option as any, skin: 'default' })} diff --git a/frontend/src/lib/components/HoqQLPropertyInfo.tsx b/frontend/src/lib/components/HoqQLPropertyInfo.tsx index 1c461c8f12245..9d0702cde962a 100644 --- a/frontend/src/lib/components/HoqQLPropertyInfo.tsx +++ b/frontend/src/lib/components/HoqQLPropertyInfo.tsx @@ -6,7 +6,7 @@ type HoqQLPropertyInfoProps = { export const HoqQLPropertyInfo = ({ value }: HoqQLPropertyInfoProps): JSX.Element => { return ( - + {midEllipsis(value, 60)} ) diff --git a/frontend/src/lib/components/JSONViewer.tsx b/frontend/src/lib/components/JSONViewer.tsx index f5b338e14fc46..dfacaf1417a56 100644 --- a/frontend/src/lib/components/JSONViewer.tsx +++ b/frontend/src/lib/components/JSONViewer.tsx @@ -3,15 +3,25 @@ import { useValues } from 'kea' import { themeLogic } from '~/layout/navigation-3000/themeLogic' -export function JSONViewer(props: ReactJsonViewProps): JSX.Element { +export function JSONViewer({ + name = null, // Don't label the root node as "root" by default + displayDataTypes = false, // Reduce visual clutter + displayObjectSize = false, // Reduce visual clutter + ...props +}: ReactJsonViewProps): JSX.Element { const { isDarkModeOn } = useValues(themeLogic) return ( ) } diff --git a/frontend/src/lib/components/Map/Maplibre.scss b/frontend/src/lib/components/Map/Maplibre.scss index d5e468c3860fc..46fa91698c850 100644 --- a/frontend/src/lib/components/Map/Maplibre.scss +++ b/frontend/src/lib/components/Map/Maplibre.scss @@ -1,6 +1,6 @@ .maplibregl-ctrl-attrib-button:focus, .maplibregl-ctrl-group button:focus { - box-shadow: 0 0 2px 2px var(--primary-3000); + box-shadow: 0 0 2px 2px var(--accent-primary); } @media screen { diff --git a/frontend/src/lib/components/Playlist/Playlist.scss b/frontend/src/lib/components/Playlist/Playlist.scss index b140e11412c2f..57fa425c6b87f 100644 --- a/frontend/src/lib/components/Playlist/Playlist.scss +++ b/frontend/src/lib/components/Playlist/Playlist.scss @@ -55,10 +55,10 @@ border-left: 3px solid transparent; &--active { - border-left-color: var(--primary-3000); + border-left-color: var(--accent-primary); } &:hover { - background-color: var(--primary-highlight); + background-color: var(--accent-primary-highlight); } } diff --git a/frontend/src/lib/components/Playlist/Playlist.tsx b/frontend/src/lib/components/Playlist/Playlist.tsx index 11e61203538cd..b00deac2e3733 100644 --- a/frontend/src/lib/components/Playlist/Playlist.tsx +++ b/frontend/src/lib/components/Playlist/Playlist.tsx @@ -97,11 +97,11 @@ export function Playlist({ .find((i) => i.id === activeItemId) || null return ( -
+
(empty string) : valueDisplayText - const recognizedSource: 'posthog' | 'langfuse' | null = coreDefinition - ? 'posthog' - : value.startsWith('langfuse ') - ? 'langfuse' - : null + const recognizedSource: 'posthog' | 'langfuse' | null = + coreDefinition || value.startsWith('$') ? 'posthog' : value.startsWith('langfuse ') ? 'langfuse' : null const innerContent = (
- {!!coreDefinition && } + {!!coreDefinition && ( + + )} {coreDefinition.label}
{coreDefinition.description || coreDefinition.examples ? ( diff --git a/frontend/src/lib/components/TaxonomicFilter/InfiniteList.scss b/frontend/src/lib/components/TaxonomicFilter/InfiniteList.scss index bb12fd2788d46..c378e06590dad 100644 --- a/frontend/src/lib/components/TaxonomicFilter/InfiniteList.scss +++ b/frontend/src/lib/components/TaxonomicFilter/InfiniteList.scss @@ -48,7 +48,7 @@ } &.taxonomy-icon-built-in { - color: var(--primary-3000); + color: var(--accent-primary); } } } @@ -75,7 +75,7 @@ } &.expand-row { - color: var(--primary-3000); + color: var(--accent-primary); } } } diff --git a/frontend/src/lib/components/TaxonomicFilter/cohortFilterUtils.ts b/frontend/src/lib/components/TaxonomicFilter/cohortFilterUtils.ts deleted file mode 100644 index 0ecf87bdc0590..0000000000000 --- a/frontend/src/lib/components/TaxonomicFilter/cohortFilterUtils.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { BehavioralFilterKey } from 'scenes/cohorts/CohortFilters/types' - -import { AnyCohortCriteriaType, CohortCriteriaGroupFilter, CohortType } from '~/types' - -function isCohortCriteriaGroupFilter( - value: AnyCohortCriteriaType | CohortCriteriaGroupFilter -): value is CohortCriteriaGroupFilter { - return (value as CohortCriteriaGroupFilter).type === 'AND' || (value as CohortCriteriaGroupFilter).type === 'OR' -} - -const hasBehavioralFilter = (cohort: CohortType, allCohorts: CohortType[]): boolean => { - const checkCriteriaGroup = (group: CohortCriteriaGroupFilter): boolean => { - return group.values?.some((value) => { - if (isCohortCriteriaGroupFilter(value)) { - return checkCriteriaGroup(value) - } - if (value.type === BehavioralFilterKey.Behavioral) { - return true - } - if (value.type === BehavioralFilterKey.Cohort) { - // the first time we load the page we haven't transformed the cohort data, - // so there's no value_property, and we need to use `value.value` instead. - const cohortId = value.value_property || value.value - const nestedCohort = allCohorts.find((item) => item.id === cohortId) - if (nestedCohort) { - return hasBehavioralFilter(nestedCohort, allCohorts) - } - return false - } - return false - }) - } - - return cohort.filters?.properties ? checkCriteriaGroup(cohort.filters.properties) : false -} - -export const filterOutBehavioralCohorts = (items: CohortType[], hideBehavioralCohorts?: boolean): CohortType[] => { - if (!hideBehavioralCohorts) { - return items - } - - return items.filter((item) => !hasBehavioralFilter(item, items)) -} diff --git a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts index c4547085fbb21..c2f1e3a8605a1 100644 --- a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts +++ b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts @@ -20,7 +20,6 @@ import { CohortType, EventDefinition } from '~/types' import { teamLogic } from '../../../scenes/teamLogic' import { captureTimeToSeeData } from '../../internalMetrics' -import { filterOutBehavioralCohorts } from './cohortFilterUtils' import type { infiniteListLogicType } from './infiniteListLogicType' /* @@ -95,7 +94,7 @@ export const infiniteListLogic = kea([ expand: true, abortAnyRunningQuery: true, }), - loaders(({ actions, values, cache }) => ({ + loaders(({ actions, values, cache, props }) => ({ remoteItems: [ createEmptyListStorage('', true), { @@ -130,6 +129,9 @@ export const infiniteListLogic = kea([ offset, excluded_properties: JSON.stringify(excludedProperties), properties: propertyAllowList ? propertyAllowList.join(',') : undefined, + // TODO: remove this filter once we can support behavioral cohorts for feature flags, it's only + // used in the feature flag property filter UI + ...(props.hideBehavioralCohorts ? { hide_behavioral_cohorts: 'true' } : {}), } const start = performance.now() @@ -247,18 +249,11 @@ export const infiniteListLogic = kea([ if (group?.logic && group?.value) { let items = group.logic.selectors[group.value]?.(state) + // Handle paginated responses for cohorts, which return a CountedPaginatedResponse if (items?.results) { items = items.results } - // TRICKY: Feature flags don't support dynamic behavioral cohorts, - // so we don't want to show them as selectable options in the taxonomic filter - // in the feature flag UI. - // TODO: Once we support dynamic behavioral cohorts, we should show them in the taxonomic filter, - // and remove this kludge. - if (Array.isArray(items) && items.every((item) => 'filters' in item)) { - return filterOutBehavioralCohorts(items, props.hideBehavioralCohorts) - } return items } @@ -327,7 +322,7 @@ export const infiniteListLogic = kea([ } if ('property_type' in n) { - const property_type = n.property_type as string // Data warehouse props dont conformt to PropertyType for some reason + const property_type = n.property_type as string // Data warehouse props dont conform to PropertyType for some reason return property_type === 'Integer' || property_type === 'Float' } diff --git a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx index 979353dc72c21..c34842c8833d6 100644 --- a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx @@ -24,7 +24,6 @@ import { ReplayTaxonomicFilters } from 'scenes/session-recordings/filters/Replay import { teamLogic } from 'scenes/teamLogic' import { actionsModel } from '~/models/actionsModel' -import { cohortsModel } from '~/models/cohortsModel' import { dashboardsModel } from '~/models/dashboardsModel' import { groupPropertiesModel } from '~/models/groupPropertiesModel' import { groupsModel } from '~/models/groupsModel' @@ -368,8 +367,8 @@ export const taxonomicFilterLogic = kea([ name: 'Cohorts', searchPlaceholder: 'cohorts', type: TaxonomicFilterGroupType.CohortsWithAllUsers, - logic: cohortsModel, - value: 'cohortsWithAllUsers', + endpoint: combineUrl(`api/projects/${projectId}/cohorts/`).url, + options: [{ id: 'all', name: 'All Users*' }], getName: (cohort: CohortType) => cohort.name || `Cohort ${cohort.id}`, getValue: (cohort: CohortType) => cohort.id, getPopoverHeader: () => `All Users`, diff --git a/frontend/src/lib/components/TimelineSeekbar/TimelineSeekbar.scss b/frontend/src/lib/components/TimelineSeekbar/TimelineSeekbar.scss index d78733b237d4a..240132d3bdc2b 100644 --- a/frontend/src/lib/components/TimelineSeekbar/TimelineSeekbar.scss +++ b/frontend/src/lib/components/TimelineSeekbar/TimelineSeekbar.scss @@ -26,7 +26,7 @@ font-weight: 500; line-height: 1.25rem; color: var(--bg-light); - background: var(--primary-3000); + background: var(--accent-primary); border-radius: var(--radius); &::selection { @@ -78,7 +78,7 @@ left: 0; width: calc(100% - var(--timeline-seekbar-arrow-width)); height: var(--timeline-seekbar-thickness); - background: var(--primary-3000); + background: var(--accent-primary); } .TimelineSeekbar__line-start, @@ -92,7 +92,7 @@ height: var(--timeline-seekbar-arrow-height); margin: calc(var(--timeline-seekbar-thickness) + 0.125rem) 0; content: ''; - background: var(--primary-3000); + background: var(--accent-primary); } } diff --git a/frontend/src/lib/components/TitledSnack.tsx b/frontend/src/lib/components/TitledSnack.tsx index 50d3d9162e44e..23332ebfaf553 100644 --- a/frontend/src/lib/components/TitledSnack.tsx +++ b/frontend/src/lib/components/TitledSnack.tsx @@ -19,7 +19,7 @@ export function TitledSnack({ 'border-r', 'rounded-l rounded-r-none', 'overflow-hidden text-ellipsis', - type === 'success' ? 'bg-success-highlight' : 'bg-primary-highlight' + type === 'success' ? 'bg-success-highlight' : 'bg-accent-primary-highlight' )} > @@ -32,7 +32,7 @@ export function TitledSnack({ 'pr-1.5 pl-1 py-1 max-w-full', 'rounded-r rounded-l-none', 'overflow-hidden text-ellipsis', - type === 'success' ? 'bg-success-highlight' : 'bg-primary-highlight', + type === 'success' ? 'bg-success-highlight' : 'bg-accent-primary-highlight', 'flex flex-1 items-center' )} > diff --git a/frontend/src/lib/components/UniversalFilters/UniversalFilterButton.tsx b/frontend/src/lib/components/UniversalFilters/UniversalFilterButton.tsx index 02849f2fa24b7..f05119b4e4a81 100644 --- a/frontend/src/lib/components/UniversalFilters/UniversalFilterButton.tsx +++ b/frontend/src/lib/components/UniversalFilters/UniversalFilterButton.tsx @@ -43,7 +43,7 @@ export const UniversalFilterButton = React.forwardRef -
+
{isEvent ? ( ) : isAction ? ( diff --git a/frontend/src/lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip.scss b/frontend/src/lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip.scss index 2d846d456cc08..6ac72e998654a 100644 --- a/frontend/src/lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip.scss +++ b/frontend/src/lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip.scss @@ -26,7 +26,7 @@ align-items: center; width: 1.5rem; height: 1.5rem; - color: var(--primary-3000); + color: var(--accent-primary); > svg { width: 100%; diff --git a/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.scss b/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.scss index ccdb8416fc4f3..9c946eaa92044 100644 --- a/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.scss +++ b/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.scss @@ -1,5 +1,5 @@ .LemonBadge { - --lemon-badge-color: var(--primary-3000); + --lemon-badge-color: var(--accent-primary); --lemon-badge-size: 1.5rem; --lemon-badge-font-size: 0.75rem; --lemon-badge-position-offset: 0.5rem; diff --git a/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.scss b/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.scss index e16b8acf49b5d..27de39e0a82d5 100644 --- a/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.scss +++ b/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.scss @@ -7,7 +7,7 @@ font-weight: 500; color: var(--content-primary); text-align: left; - border: solid 1px var(--border-info); + border: solid 1px var(--border-primary); border-radius: var(--radius); &.LemonBanner--square { @@ -15,24 +15,25 @@ } &.LemonBanner--info { - background-color: var(--fill-info-overlay); + background-color: var(--bg-fill-primary); + border-color: var(--border-primary); } &.LemonBanner--warning { - color: var(--content-on-fill-warning); - background-color: var(--fill-warning-overlay); + color: var(--text-warning-on-bg-fill); + background-color: var(--bg-fill-warning-secondary); border-color: var(--border-warning); } &.LemonBanner--error { - color: var(--content-on-fill-error); - background-color: var(--fill-error-overlay); + color: var(--text-error-on-bg-fill); + background-color: var(--bg-fill-error-secondary); border-color: var(--border-error); } &.LemonBanner--success { - color: var(--content-on-fill-success); - background-color: var(--fill-success-overlay); + color: var(--text-success-on-bg-fill); + background-color: var(--bg-fill-success-secondary); border-color: var(--border-success); } diff --git a/frontend/src/lib/lemon-ui/LemonCard/LemonCard.tsx b/frontend/src/lib/lemon-ui/LemonCard/LemonCard.tsx index c7f86768b3bfe..390919352e7d2 100644 --- a/frontend/src/lib/lemon-ui/LemonCard/LemonCard.tsx +++ b/frontend/src/lib/lemon-ui/LemonCard/LemonCard.tsx @@ -28,7 +28,7 @@ export function LemonCard({ return (
.LemonIcon { - color: var(--primary-3000); + color: var(--accent-primary); } } diff --git a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.stories.tsx b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.stories.tsx index ef9d20dcef901..c83138cefb94e 100644 --- a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.stories.tsx @@ -59,3 +59,6 @@ Numeric.args = { type: 'number', min: 0, step: 1, value: 3 } export const Small: Story = Template.bind({}) Small.args = { allowClear: true, size: 'small' } + +export const Prefix: Story = Template.bind({}) +Prefix.args = { allowClear: true, size: 'small', prefix: } diff --git a/frontend/src/lib/lemon-ui/LemonRadio/LemonRadio.stories.tsx b/frontend/src/lib/lemon-ui/LemonRadio/LemonRadio.stories.tsx index 7410def053da0..f5b780c7f9426 100644 --- a/frontend/src/lib/lemon-ui/LemonRadio/LemonRadio.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonRadio/LemonRadio.stories.tsx @@ -52,3 +52,8 @@ WithDescriptions.args = { }, ], } + +export const WithTopPosition: Story = Template.bind({}) +WithTopPosition.args = { + radioPosition: 'top', +} diff --git a/frontend/src/lib/lemon-ui/LemonRow/LemonRow.scss b/frontend/src/lib/lemon-ui/LemonRow/LemonRow.scss index 0236006d2d047..4b1727fa8c591 100644 --- a/frontend/src/lib/lemon-ui/LemonRow/LemonRow.scss +++ b/frontend/src/lib/lemon-ui/LemonRow/LemonRow.scss @@ -17,10 +17,10 @@ &.LemonRow--status-highlighted { font-weight: 600; color: var(--text-3000); - background: var(--primary-highlight); + background: var(--accent-primary-highlight); .LemonRow__icon { - color: var(--primary-3000); + color: var(--accent-primary); } } diff --git a/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.tsx b/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.tsx index ae3445b71547b..98284b2b21aae 100644 --- a/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.tsx +++ b/frontend/src/lib/lemon-ui/LemonSlider/LemonSlider.tsx @@ -80,13 +80,13 @@ export function LemonSlider({ value = 0, onChange, min, max, step = 1, className
td:not(.LemonTable__cell--sticky) { - background: var(--primary-highlight); + background: var(--accent-primary-highlight); } } @@ -300,7 +300,7 @@ background: var(--lemon-table-background-color); .LemonTable__row--status-highlighted &::before { - background: var(--primary-highlight); + background: var(--accent-primary-highlight); } } diff --git a/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.scss b/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.scss index 9bf9700aa184b..5cc6d9c4137dd 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.scss +++ b/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.scss @@ -6,7 +6,7 @@ height: 0; padding: 0.05rem !important; overflow: hidden; - background: var(--primary-highlight); + background: var(--accent-primary-highlight); border: none !important; transition: height 200ms ease, top 200ms ease; @@ -17,7 +17,7 @@ width: 50%; height: 100%; content: ''; - background: var(--primary-3000); + background: var(--accent-primary); animation: LemonTableLoader__swooping 1.5s linear infinite; } diff --git a/frontend/src/lib/lemon-ui/LemonTable/TableRow.tsx b/frontend/src/lib/lemon-ui/LemonTable/TableRow.tsx index b3339a37aaf9c..688f825b56763 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/TableRow.tsx +++ b/frontend/src/lib/lemon-ui/LemonTable/TableRow.tsx @@ -57,7 +57,9 @@ function TableRowRaw>({ className={clsx( rowClassNameDetermined, rowStatusDetermined && `LemonTable__row--status-${rowStatusDetermined}`, - extraProps?.onClick ? 'hover:underline cursor-pointer hover:bg-primary-highlight' : undefined, + extraProps?.onClick + ? 'hover:underline cursor-pointer hover:bg-accent-primary-highlight' + : undefined, className )} // eslint-disable-next-line react/forbid-dom-props diff --git a/frontend/src/lib/lemon-ui/LemonTag/LemonTag.scss b/frontend/src/lib/lemon-ui/LemonTag/LemonTag.scss index 44d35d3268097..a2a6e29d13b5f 100644 --- a/frontend/src/lib/lemon-ui/LemonTag/LemonTag.scss +++ b/frontend/src/lib/lemon-ui/LemonTag/LemonTag.scss @@ -36,9 +36,9 @@ } &.LemonTag--highlight { - color: var(--highlight); + color: var(--accent-primary); background: none; - border-color: var(--highlight); + border-color: var(--accent-primary); } &.LemonTag--warning { @@ -87,7 +87,7 @@ line-height: 16px; color: var(--primary-alt); vertical-align: bottom; - background-color: var(--primary-highlight); + background-color: var(--accent-primary-highlight); border-radius: 40px; } diff --git a/frontend/src/lib/lemon-ui/LemonWidget/LemonWidget.scss b/frontend/src/lib/lemon-ui/LemonWidget/LemonWidget.scss index 5bbc6dd0ed5aa..b634a485640fc 100644 --- a/frontend/src/lib/lemon-ui/LemonWidget/LemonWidget.scss +++ b/frontend/src/lib/lemon-ui/LemonWidget/LemonWidget.scss @@ -10,6 +10,6 @@ padding: 0.25rem; font-size: 0.875rem; font-weight: 500; - color: var(--muted-alt-3000, var(--primary)); + color: var(--muted-alt-3000, var(--accent-primary)); } } diff --git a/frontend/src/lib/lemon-ui/ProfilePicture/ProfilePicture.scss b/frontend/src/lib/lemon-ui/ProfilePicture/ProfilePicture.scss index af757703d7ad2..1bd0c09c5d958 100644 --- a/frontend/src/lib/lemon-ui/ProfilePicture/ProfilePicture.scss +++ b/frontend/src/lib/lemon-ui/ProfilePicture/ProfilePicture.scss @@ -97,6 +97,6 @@ color: #fff; letter-spacing: -0.05em; user-select: none; - background: var(--primary-3000); + background: var(--accent-primary); border-radius: 50%; } diff --git a/frontend/src/lib/lemon-ui/Spinner/Spinner.tsx b/frontend/src/lib/lemon-ui/Spinner/Spinner.tsx index c2e70d129022f..6da706f087cc2 100644 --- a/frontend/src/lib/lemon-ui/Spinner/Spinner.tsx +++ b/frontend/src/lib/lemon-ui/Spinner/Spinner.tsx @@ -43,7 +43,7 @@ export function SpinnerOverlay({ return (
{mode === 'editing' ? ( - + ) : ( )} diff --git a/frontend/src/lib/lemon-ui/icons/icons.stories.tsx b/frontend/src/lib/lemon-ui/icons/icons.stories.tsx index c4c0d668a5758..c55fafbec88e9 100644 --- a/frontend/src/lib/lemon-ui/icons/icons.stories.tsx +++ b/frontend/src/lib/lemon-ui/icons/icons.stories.tsx @@ -194,7 +194,7 @@ ShelfOther.parameters = { testOptions: { snapshotTargetSelector: '.LemonTable tb export function IconWithCountBubble(): JSX.Element { return ( - + @@ -204,7 +204,7 @@ export function IconWithCountBubble(): JSX.Element { export function IconWithCountHidingZero(): JSX.Element { return ( - + @@ -214,7 +214,7 @@ export function IconWithCountHidingZero(): JSX.Element { export function IconWithCountShowingZero(): JSX.Element { return ( - + @@ -224,7 +224,7 @@ export function IconWithCountShowingZero(): JSX.Element { export function IconWithCountOverflowing(): JSX.Element { return ( - + diff --git a/frontend/src/lib/taxonomy.tsx b/frontend/src/lib/taxonomy.tsx index ce8d14eac7ecb..f339332a36456 100644 --- a/frontend/src/lib/taxonomy.tsx +++ b/frontend/src/lib/taxonomy.tsx @@ -177,7 +177,20 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = { }, $ai_generation: { label: 'AI Generation', - description: 'A call to a generative AI model, e.g. an LLM', + description: 'A call to a generative AI model (LLM)', + }, + $ai_trace: { + label: 'AI Trace', + description: + 'A generative AI trace. Usually a trace tracks a single user interaction and contains one or more AI generation calls', + }, + $ai_metric: { + label: 'AI Metric', + description: 'An evaluation metric for a trace of generative AI models (LLMs)', + }, + $ai_feedback: { + label: 'AI Feedback', + description: 'User-provided feedback for a trace of a generative AI model (LLM)', }, // Mobile SDKs events 'Application Opened': { @@ -1370,9 +1383,9 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = { description: 'The number of tokens in the input prmopt that was sent to the LLM API', examples: [23], }, - $ai_output: { + $ai_output_choices: { label: 'AI Output (LLM)', - description: 'The output JSON that was received from the LLM API', + description: 'The output message choices JSON that was received from the LLM API', examples: [ '{"choices": [{"text": "Quantum computing is a type of computing that harnesses the power of quantum mechanics to perform operations on data."}]}', ], @@ -1412,6 +1425,29 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = { description: 'The parameters used to configure the model in the LLM API, in JSON', examples: ['{"temperature": 0.5, "max_tokens": 50}'], }, + $ai_stream: { + label: 'AI Stream (LLM)', + description: 'Whether the response from the LLM API was streamed', + examples: ['true', 'false'], + }, + $ai_temperature: { + label: 'AI Temperature (LLM)', + description: 'The temperature parameter used in the request to the LLM API', + examples: [0.7, 1.0], + }, + $ai_input_state: { + label: 'AI Input State (LLM)', + description: 'Input state of the LLM agent', + }, + $ai_output_state: { + label: 'AI Output State (LLM)', + description: 'Output state of the LLM agent', + }, + $ai_trace_name: { + label: 'AI Trace Name (LLM)', + description: 'The name given to this trace of LLM API calls', + examples: ['LangGraph'], + }, $ai_provider: { label: 'AI Provider (LLM)', description: 'The provider of the AI model used to generate the output from the LLM API', @@ -1428,6 +1464,21 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = { description: 'The full URL of the request made to the LLM API', examples: ['https://api.openai.com/v1/chat/completions'], }, + $ai_metric_name: { + label: 'AI Metric Name (LLM)', + description: 'The name assigned to the metric used to evaluate the LLM trace', + examples: ['rating', 'accuracy'], + }, + $ai_metric_value: { + label: 'AI Metric Value (LLM)', + description: 'The value assigned to the metric used to evaluate the LLM trace', + examples: ['"negative"', '95'], + }, + $ai_feedback_text: { + label: 'AI Feedback Text (LLM)', + description: 'The text provided by the user for feedback on the LLM trace', + examples: ['"The response was helpful, but it did not use the provided context."'], + }, }, numerical_event_properties: {}, // Same as event properties, see assignment below person_properties: {}, // Currently person properties are the same as event properties, see assignment below diff --git a/frontend/src/lib/ui/Colors/Colors.stories.tsx b/frontend/src/lib/ui/Colors/Colors.stories.tsx index 817284f5c44fa..be2e21be98763 100644 --- a/frontend/src/lib/ui/Colors/Colors.stories.tsx +++ b/frontend/src/lib/ui/Colors/Colors.stories.tsx @@ -1,4 +1,3 @@ -import { LemonCheckbox, Link } from '@posthog/lemon-ui' import { Meta } from '@storybook/react' import { useValues } from 'kea' import { LemonSlider } from 'lib/lemon-ui/LemonSlider' @@ -20,23 +19,31 @@ const meta: Meta = { export default meta const steps3000 = [25, 50, 100, 250, 350, 400, 450, 500] -const stepsLong = [50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950] -const stepsShort = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900] +const steps = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] const primtiveColorMap: Map = new Map([ ['primitive-3000', steps3000], - ['primitive-neutral', stepsLong], - ['primitive-neutral-cool', stepsLong], - ['primitive-blue', stepsShort], - ['primitive-purple', stepsShort], - ['primitive-violet', stepsShort], - ['primitive-red', stepsShort], - ['primitive-orange', stepsShort], - ['primitive-amber', stepsShort], - ['primitive-yellow', stepsShort], - ['primitive-green', stepsShort], - ['primitive-pink', stepsShort], - ['primitive-teal', stepsShort], + ['primitive-neutral-cool', steps], + // ['primitive-gray', steps], + // ['primitive-zinc', steps], + ['primitive-stone', steps], + ['primitive-red', steps], + ['primitive-orange', steps], + ['primitive-amber', steps], + ['primitive-yellow', steps], + ['primitive-lime', steps], + ['primitive-green', steps], + ['primitive-emerald', steps], + ['primitive-teal', steps], + ['primitive-cyan', steps], + ['primitive-sky', steps], + ['primitive-blue', steps], + ['primitive-indigo', steps], + ['primitive-violet', steps], + ['primitive-purple', steps], + ['primitive-fuchsia', steps], + ['primitive-pink', steps], + ['primitive-rose', steps], ]) export function PrimitiveColors(): JSX.Element { @@ -79,13 +86,17 @@ export function BrandColors(): JSX.Element { '--accent-primary', `hsl(${primaryHue}deg ${primarySaturation}% ${primaryLightness}%)` ) + document.body.style.setProperty( + '--accent-secondary', + `hsl(${secondaryHue}deg ${secondarySaturation}% ${secondaryLightness}%)` + ) }, [primaryHue, primarySaturation, primaryLightness, secondaryHue, secondarySaturation, secondaryLightness]) return (
-
- {}} /> - A link example +
+

Accent primary

+

Accent secondary

@@ -139,3 +150,19 @@ export function BrandColors(): JSX.Element {
) } + +export function SemanticColors(): JSX.Element { + const textColors = ['text-primary'] + return ( +
+
+

Texts

+ {textColors.map((color) => ( +
+

{color}

+
+ ))} +
+
+ ) +} diff --git a/frontend/src/lib/utils/apiHost.ts b/frontend/src/lib/utils/apiHost.ts index 52406be14dc26..d8ca0432a2bec 100644 --- a/frontend/src/lib/utils/apiHost.ts +++ b/frontend/src/lib/utils/apiHost.ts @@ -16,6 +16,9 @@ export function liveEventsHostOrigin(): string | null { return 'https://live.eu.posthog.com' } else if (appOrigin === 'https://app.dev.posthog.dev') { return 'https://live.dev.posthog.dev' + } else if (process.env.STORYBOOK) { + return 'http://localhost:6006' } + return 'http://localhost:8666' } diff --git a/frontend/src/lib/utils/cross-sell.ts b/frontend/src/lib/utils/cross-sell.ts index 1687390465fe5..38fc47026ce6a 100644 --- a/frontend/src/lib/utils/cross-sell.ts +++ b/frontend/src/lib/utils/cross-sell.ts @@ -6,6 +6,7 @@ export type ProductCrossSellContext = Record export enum ProductCrossSellLocation { TAXONOMIC_FILTER_EMPTY_STATE = 'taxonomic_filter_empty_state', + WEB_ANALYTICS_INSIGHT = 'web_analytics_insight', } export type ProductCrossSellProperties = { diff --git a/frontend/src/lib/utils/eventUsageLogic.test.ts b/frontend/src/lib/utils/eventUsageLogic.test.ts new file mode 100644 index 0000000000000..7ee4159a62016 --- /dev/null +++ b/frontend/src/lib/utils/eventUsageLogic.test.ts @@ -0,0 +1,46 @@ +import experimentJson from '~/mocks/fixtures/api/experiments/_experiment_launched_with_funnel_and_trends.json' +import { Experiment } from '~/types' + +import { getEventPropertiesForExperiment } from './eventUsageLogic' + +describe('getEventPropertiesForExperiment', () => { + it('returns the correct event properties for an experiment', () => { + // Transform null to undefined where needed + const experiment = { + ...experimentJson, + created_by: { ...experimentJson.created_by, hedgehog_config: undefined }, + holdout: undefined, + } as Experiment + expect(getEventPropertiesForExperiment(experiment)).toEqual({ + id: 90, + name: 'jan-16-running', + type: 'product', + parameters: { + feature_flag_variants: [ + { + key: 'control', + rollout_percentage: 50, + }, + { + key: 'test', + rollout_percentage: 50, + }, + ], + recommended_sample_size: 0, + recommended_running_time: 0, + minimum_detectable_effect: 1, + }, + metrics: [ + { kind: 'ExperimentFunnelsQuery', steps_count: 2, filter_test_accounts: true }, + { kind: 'ExperimentTrendsQuery', series_kind: 'EventsNode', filter_test_accounts: true }, + ], + secondary_metrics: [ + { kind: 'ExperimentTrendsQuery', series_kind: 'EventsNode', filter_test_accounts: true }, + { kind: 'ExperimentTrendsQuery', series_kind: 'EventsNode', filter_test_accounts: true }, + ], + metrics_count: 2, + secondary_metrics_count: 2, + saved_metrics_count: 2, + }) + }) +}) diff --git a/frontend/src/lib/utils/eventUsageLogic.ts b/frontend/src/lib/utils/eventUsageLogic.ts index d23c3ba87eb2c..fda19e0ed62f2 100644 --- a/frontend/src/lib/utils/eventUsageLogic.ts +++ b/frontend/src/lib/utils/eventUsageLogic.ts @@ -1,22 +1,16 @@ import { actions, connect, kea, listeners, path } from 'kea' import { BarStatus, ResultType } from 'lib/components/CommandBar/types' -import { - convertPropertyGroupToProperties, - isGroupPropertyFilter, - isLogEntryPropertyFilter, - isValidPropertyFilter, -} from 'lib/components/PropertyFilters/utils' +import { isLogEntryPropertyFilter, isValidPropertyFilter } from 'lib/components/PropertyFilters/utils' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { isActionFilter, isEventFilter } from 'lib/components/UniversalFilters/utils' import type { Dayjs } from 'lib/dayjs' import { now } from 'lib/dayjs' import { TimeToSeeDataPayload } from 'lib/internalMetrics' -import { isCoreFilter, PROPERTY_KEYS } from 'lib/taxonomy' +import { PROPERTY_KEYS } from 'lib/taxonomy' import { objectClean } from 'lib/utils' import posthog from 'posthog-js' import { Holdout } from 'scenes/experiments/holdoutsLogic' import { SharedMetric } from 'scenes/experiments/SharedMetrics/sharedMetricLogic' -import { isFilterWithDisplay, isFunnelsFilter, isTrendsFilter } from 'scenes/insights/sharedUtils' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { EventIndex } from 'scenes/session-recordings/player/eventIndex' import { MiniFilterKey } from 'scenes/session-recordings/player/inspector/miniFiltersLogic' @@ -26,6 +20,7 @@ import { NewSurvey, SurveyTemplateType } from 'scenes/surveys/constants' import { userLogic } from 'scenes/userLogic' import { ExperimentFunnelsQuery, ExperimentTrendsQuery, Node } from '~/queries/schema' +import { NodeKind } from '~/queries/schema/schema-general' import { getBreakdown, getCompareFilter, @@ -43,8 +38,6 @@ import { } from '~/queries/utils' import { AccessLevel, - AnyPartialFilterType, - AnyPropertyFilter, CohortType, DashboardMode, DashboardType, @@ -58,8 +51,6 @@ import { MultipleSurveyQuestion, PersonType, PropertyFilterType, - PropertyFilterValue, - PropertyGroupFilter, QueryBasedInsightModel, RecordingDurationFilter, RecordingReportLoadTimes, @@ -130,111 +121,41 @@ interface RecordingViewedProps { load_time: number // DEPRECATE: How much time it took to load the session (backend) (milliseconds) } -function flattenProperties(properties: AnyPropertyFilter[]): string[] { - const output = [] - for (const prop of properties || []) { - if (prop.key && isCoreFilter(prop.key)) { - output.push(prop.key) - } else { - output.push('redacted') // Custom property names are not reported +export function getEventPropertiesForMetric(metric: ExperimentTrendsQuery | ExperimentFunnelsQuery): object { + if (metric.kind === NodeKind.ExperimentFunnelsQuery) { + return { + kind: NodeKind.ExperimentFunnelsQuery, + steps_count: metric.funnels_query.series.length, + filter_test_accounts: metric.funnels_query.filterTestAccounts, } } - return output -} - -function hasGroupProperties(properties: AnyPropertyFilter[] | PropertyGroupFilter | undefined): boolean { - const flattenedProperties = convertPropertyGroupToProperties(properties) - return ( - !!flattenedProperties && - flattenedProperties.some( - (property) => isGroupPropertyFilter(property) && property.group_type_index !== undefined - ) - ) -} - -function usedCohortFilterIds(properties: AnyPropertyFilter[] | PropertyGroupFilter | undefined): PropertyFilterValue[] { - const flattenedProperties = convertPropertyGroupToProperties(properties) || [] - const cohortIds = flattenedProperties - .filter((p) => p.type === 'cohort') - .map((p) => p.value) - .filter((a): a is number => !!a) - - return cohortIds || [] + return { + kind: NodeKind.ExperimentTrendsQuery, + series_kind: metric.count_query.series[0].kind, + filter_test_accounts: metric.count_query.filterTestAccounts, + } } -/* - Takes a full list of filters for an insight and sanitizes any potentially sensitive info to report usage -*/ -function sanitizeFilterParams(filters: AnyPartialFilterType): Record { - const { interval, date_from, date_to, filter_test_accounts, insight } = filters - - let properties_local: string[] = [] - - // // If we're aggregating this query by groups - // properties.aggregating_by_groups = filters.aggregation_group_type_index != undefined - // // If groups are being used in this query - // properties.using_groups = - // hasGroupProperties(filters.properties) || filters.breakdown_group_type_index != undefined - - // let totalEventActionFilters = 0 - // const entities = (filters.events || []).concat(filters.actions || []) - // entities.forEach((entity) => { - // if (entity.properties?.length) { - // totalEventActionFilters += entity.properties.length - // properties.using_groups = properties.using_groups || hasGroupProperties(entity.properties) - // } - // if (entity.math_group_type_index != undefined) { - // properties.aggregating_by_groups = true - // } - // }) - // properties.using_groups = properties.using_groups || properties.aggregating_by_groups - - const properties = Array.isArray(filters.properties) ? filters.properties : [] - const events = Array.isArray(filters.events) ? filters.events : [] - const actions = Array.isArray(filters.actions) ? filters.actions : [] - const entities = events.concat(actions) - - // If we're aggregating this query by groups - let aggregating_by_groups = filters.aggregation_group_type_index != undefined - const breakdown_by_groups = filters.breakdown_group_type_index != undefined - // If groups are being used in this query - let using_groups = hasGroupProperties(filters.properties) - const used_cohort_filter_ids = usedCohortFilterIds(filters.properties) - - for (const entity of entities) { - const entityProperties = Array.isArray(entity.properties) ? entity.properties : [] - properties_local = properties_local.concat(flattenProperties(entityProperties)) - - using_groups = using_groups || hasGroupProperties(entityProperties) - if (entity.math_group_type_index != undefined) { - aggregating_by_groups = true - } - } - const properties_global = flattenProperties(properties) +export function getEventPropertiesForExperiment(experiment: Experiment): object { + const allMetrics = [ + ...experiment.metrics, + ...experiment.saved_metrics.filter((m) => m.metadata.type === 'primary').map((m) => m.query), + ] + const allSecondaryMetrics = [ + ...experiment.metrics_secondary, + ...experiment.saved_metrics.filter((m) => m.metadata.type === 'secondary').map((m) => m.query), + ] return { - display: isFilterWithDisplay(filters) ? filters.display : undefined, - interval, - date_from, - date_to, - filter_test_accounts, - formula: isTrendsFilter(filters) ? filters.formula : undefined, - filters_count: properties?.length || 0, - events_count: events?.length || 0, - actions_count: actions?.length || 0, - funnel_viz_type: isFunnelsFilter(filters) ? filters.funnel_viz_type : undefined, - funnel_from_step: isFunnelsFilter(filters) ? filters.funnel_from_step : undefined, - funnel_to_step: isFunnelsFilter(filters) ? filters.funnel_to_step : undefined, - properties_global, - properties_global_custom_count: properties_global.filter((item) => item === 'custom').length, - properties_local, - properties_local_custom_count: properties_local.filter((item) => item === 'custom').length, - properties_all: properties_global.concat(properties_local), - aggregating_by_groups, - breakdown_by_groups, - using_groups: using_groups || aggregating_by_groups || breakdown_by_groups, - used_cohort_filter_ids, - insight, + id: experiment.id, + name: experiment.name, + type: experiment.type, + parameters: experiment.parameters, + metrics: allMetrics.map((m) => getEventPropertiesForMetric(m)), + secondary_metrics: allSecondaryMetrics.map((m) => getEventPropertiesForMetric(m)), + metrics_count: allMetrics.length, + secondary_metrics_count: allSecondaryMetrics.length, + saved_metrics_count: experiment.saved_metrics.length, } } @@ -460,7 +381,7 @@ export const eventUsageLogic = kea([ reportExperimentArchived: (experiment: Experiment) => ({ experiment }), reportExperimentReset: (experiment: Experiment) => ({ experiment }), reportExperimentCreated: (experiment: Experiment) => ({ experiment }), - reportExperimentViewed: (experiment: Experiment) => ({ experiment }), + reportExperimentViewed: (experiment: Experiment, duration: number | null) => ({ experiment, duration }), reportExperimentLaunched: (experiment: Experiment, launchDate: Dayjs) => ({ experiment, launchDate }), reportExperimentStartDateChange: (experiment: Experiment, newStartDate: string) => ({ experiment, @@ -993,64 +914,44 @@ export const eventUsageLogic = kea([ }, reportExperimentArchived: ({ experiment }) => { posthog.capture('experiment archived', { - name: experiment.name, - id: experiment.id, - filters: sanitizeFilterParams(experiment.filters), - parameters: experiment.parameters, + ...getEventPropertiesForExperiment(experiment), }) }, reportExperimentReset: ({ experiment }) => { posthog.capture('experiment reset', { - name: experiment.name, - id: experiment.id, - filters: sanitizeFilterParams(experiment.filters), - parameters: experiment.parameters, + ...getEventPropertiesForExperiment(experiment), }) }, reportExperimentCreated: ({ experiment }) => { posthog.capture('experiment created', { - name: experiment.name, id: experiment.id, + name: experiment.name, type: experiment.type, - filters: sanitizeFilterParams(experiment.filters), parameters: experiment.parameters, - secondary_metrics_count: experiment.secondary_metrics.length, }) }, - reportExperimentViewed: ({ experiment }) => { + reportExperimentViewed: ({ experiment, duration }) => { posthog.capture('experiment viewed', { - name: experiment.name, - id: experiment.id, - filters: sanitizeFilterParams(experiment.filters), - parameters: experiment.parameters, - secondary_metrics_count: experiment.secondary_metrics.length, + ...getEventPropertiesForExperiment(experiment), + duration, }) }, reportExperimentLaunched: ({ experiment, launchDate }) => { posthog.capture('experiment launched', { - name: experiment.name, - id: experiment.id, - filters: sanitizeFilterParams(experiment.filters), - parameters: experiment.parameters, - secondary_metrics_count: experiment.secondary_metrics.length, + ...getEventPropertiesForExperiment(experiment), launch_date: launchDate.toISOString(), }) }, reportExperimentStartDateChange: ({ experiment, newStartDate }) => { posthog.capture('experiment start date changed', { - name: experiment.name, - id: experiment.id, + ...getEventPropertiesForExperiment(experiment), old_start_date: experiment.start_date, new_start_date: newStartDate, }) }, reportExperimentCompleted: ({ experiment, endDate, duration, significant }) => { posthog.capture('experiment completed', { - name: experiment.name, - id: experiment.id, - filters: sanitizeFilterParams(experiment.filters), - parameters: experiment.parameters, - secondary_metrics_count: experiment.secondary_metrics.length, + ...getEventPropertiesForExperiment(experiment), end_date: endDate.toISOString(), duration, significant, @@ -1076,7 +977,6 @@ export const eventUsageLogic = kea([ posthog.capture('experiment variant shipped', { name: experiment.name, id: experiment.id, - filters: sanitizeFilterParams(experiment.filters), parameters: experiment.parameters, secondary_metrics_count: experiment.secondary_metrics.length, }) @@ -1118,15 +1018,15 @@ export const eventUsageLogic = kea([ posthog.capture('experiment shared metric created', { name: sharedMetric.name, id: sharedMetric.id, - kind: sharedMetric.query.kind, + ...getEventPropertiesForMetric(sharedMetric.query as ExperimentTrendsQuery | ExperimentFunnelsQuery), }) }, reportExperimentSharedMetricAssigned: ({ experimentId, sharedMetric }) => { posthog.capture('experiment shared metric assigned', { experiment_id: experimentId, - shared_metric_name: sharedMetric.name, - shared_metric_id: sharedMetric.id, - shared_metric_kind: sharedMetric.query.kind, + name: sharedMetric.name, + id: sharedMetric.id, + ...getEventPropertiesForMetric(sharedMetric.query as ExperimentTrendsQuery | ExperimentFunnelsQuery), }) }, reportExperimentDashboardCreated: ({ experiment, dashboardId }) => { diff --git a/frontend/src/mocks/fixtures/api/experiments/_experiment_launched_with_funnel_and_trends.json b/frontend/src/mocks/fixtures/api/experiments/_experiment_launched_with_funnel_and_trends.json new file mode 100644 index 0000000000000..a675d2d66cfcb --- /dev/null +++ b/frontend/src/mocks/fixtures/api/experiments/_experiment_launched_with_funnel_and_trends.json @@ -0,0 +1,219 @@ +{ + "id": 90, + "name": "jan-16-running", + "description": "Dashboard: [http://localhost:8010/project/1/dashboard/147](http://localhost:8010/project/1/dashboard/147)", + "start_date": "2024-12-30T21:55:00Z", + "end_date": null, + "feature_flag_key": "jan-16-running", + "feature_flag": { + "id": 103, + "team_id": 1, + "name": "Feature Flag for Experiment jan-16-running", + "key": "jan-16-running", + "filters": { + "groups": [ + { + "properties": [], + "rollout_percentage": 100 + } + ], + "multivariate": { + "variants": [ + { + "key": "control", + "rollout_percentage": 50 + }, + { + "key": "test", + "rollout_percentage": 50 + } + ] + }, + "holdout_groups": null, + "aggregation_group_type_index": null, + "payloads": {} + }, + "deleted": false, + "active": true, + "ensure_experience_continuity": false + }, + "holdout": null, + "holdout_id": null, + "parameters": { + "feature_flag_variants": [ + { + "key": "control", + "rollout_percentage": 50 + }, + { + "key": "test", + "rollout_percentage": 50 + } + ], + "recommended_sample_size": 0, + "recommended_running_time": 0, + "minimum_detectable_effect": 1 + }, + "secondary_metrics": [], + "saved_metrics": [ + { + "id": 108, + "experiment": 90, + "saved_metric": 15, + "metadata": { + "type": "primary" + }, + "created_at": "2025-01-22T14:56:12.766068Z", + "query": { + "kind": "ExperimentTrendsQuery", + "count_query": { + "kind": "TrendsQuery", + "series": [ + { + "kind": "EventsNode", + "math": "total", + "name": "[jan-16-running] event one", + "event": "[jan-16-running] event one" + } + ], + "interval": "day", + "dateRange": { + "date_to": "2025-01-20T23:59", + "date_from": "2025-01-06T13:54", + "explicitDate": true + }, + "trendsFilter": { + "display": "ActionsLineGraph" + }, + "filterTestAccounts": true + } + }, + "name": "jan-16-running event one" + }, + { + "id": 109, + "experiment": 90, + "saved_metric": 16, + "metadata": { + "type": "secondary" + }, + "created_at": "2025-01-22T14:56:12.770270Z", + "query": { + "kind": "ExperimentTrendsQuery", + "count_query": { + "kind": "TrendsQuery", + "series": [ + { + "kind": "EventsNode", + "math": "total", + "name": "[jan-16-running] event two", + "event": "[jan-16-running] event two" + } + ], + "interval": "day", + "dateRange": { + "date_to": "2025-01-20T23:59", + "date_from": "2025-01-06T13:54", + "explicitDate": true + }, + "trendsFilter": { + "display": "ActionsLineGraph" + }, + "filterTestAccounts": true + } + }, + "name": "jan-16-running event two" + } + ], + "saved_metrics_ids": [ + { + "id": 108, + "metadata": { + "type": "primary" + } + }, + { + "id": 109, + "metadata": { + "type": "secondary" + } + } + ], + "filters": {}, + "archived": false, + "created_by": { + "id": 1, + "uuid": "0192f783-9ca1-0000-047c-397bd60d6c39", + "distinct_id": "6PMxBkEONKiJoizOIKGkcTICvtOk1smj277H6lqowDG", + "first_name": "Employee 427", + "last_name": "", + "email": "test@posthog.com", + "is_email_verified": null, + "hedgehog_config": null, + "role_at_organization": null + }, + "created_at": "2025-01-16T21:51:09.062768Z", + "updated_at": "2025-01-22T14:56:12.772335Z", + "type": "product", + "metrics": [ + { + "kind": "ExperimentFunnelsQuery", + "funnels_query": { + "kind": "FunnelsQuery", + "series": [ + { + "kind": "EventsNode", + "name": "[jan-16-running] seen", + "event": "[jan-16-running] seen" + }, + { + "kind": "EventsNode", + "name": "[jan-16-running] payment", + "event": "[jan-16-running] payment" + } + ], + "dateRange": { + "date_to": "2025-01-16T23:59", + "date_from": "2025-01-02T13:54", + "explicitDate": true + }, + "funnelsFilter": { + "layout": "horizontal", + "funnelVizType": "steps", + "funnelWindowInterval": 14, + "funnelWindowIntervalUnit": "day" + }, + "filterTestAccounts": true + } + } + ], + "metrics_secondary": [ + { + "kind": "ExperimentTrendsQuery", + "count_query": { + "kind": "TrendsQuery", + "series": [ + { + "kind": "EventsNode", + "math": "total", + "name": "[jan-16-running] event one", + "event": "[jan-16-running] event one" + } + ], + "interval": "day", + "dateRange": { + "date_to": "2025-01-16T23:59", + "date_from": "2025-01-02T13:54", + "explicitDate": true + }, + "trendsFilter": { + "display": "ActionsLineGraph" + }, + "filterTestAccounts": true + } + } + ], + "stats_config": { + "version": 2 + } +} diff --git a/frontend/src/mocks/fixtures/api/experiments/_metric_funnel_events.json b/frontend/src/mocks/fixtures/api/experiments/_metric_funnel_events.json new file mode 100644 index 0000000000000..960e555caae38 --- /dev/null +++ b/frontend/src/mocks/fixtures/api/experiments/_metric_funnel_events.json @@ -0,0 +1,30 @@ +{ + "kind": "ExperimentFunnelsQuery", + "funnels_query": { + "kind": "FunnelsQuery", + "series": [ + { + "kind": "EventsNode", + "name": "[jan-16-running] seen", + "event": "[jan-16-running] seen" + }, + { + "kind": "EventsNode", + "name": "[jan-16-running] payment", + "event": "[jan-16-running] payment" + } + ], + "dateRange": { + "date_to": "2025-01-16T23:59", + "date_from": "2025-01-02T13:54", + "explicitDate": true + }, + "funnelsFilter": { + "layout": "horizontal", + "funnelVizType": "steps", + "funnelWindowInterval": 14, + "funnelWindowIntervalUnit": "day" + }, + "filterTestAccounts": true + } +} diff --git a/frontend/src/mocks/fixtures/api/experiments/_metric_trend_action.json b/frontend/src/mocks/fixtures/api/experiments/_metric_trend_action.json new file mode 100644 index 0000000000000..f8f4a5ef0a1d5 --- /dev/null +++ b/frontend/src/mocks/fixtures/api/experiments/_metric_trend_action.json @@ -0,0 +1,24 @@ +{ + "kind": "ExperimentTrendsQuery", + "count_query": { + "kind": "TrendsQuery", + "series": [ + { + "kind": "ActionsNode", + "id": 8, + "name": "jan-16-running payment action", + "math": "total" + } + ], + "interval": "day", + "dateRange": { + "date_to": "2025-01-16T23:59", + "date_from": "2025-01-02T13:54", + "explicitDate": true + }, + "trendsFilter": { + "display": "ActionsLineGraph" + }, + "filterTestAccounts": true + } +} diff --git a/frontend/src/mocks/fixtures/api/experiments/_metric_trend_custom_exposure.json b/frontend/src/mocks/fixtures/api/experiments/_metric_trend_custom_exposure.json new file mode 100644 index 0000000000000..44ba5736371a5 --- /dev/null +++ b/frontend/src/mocks/fixtures/api/experiments/_metric_trend_custom_exposure.json @@ -0,0 +1,45 @@ +{ + "kind": "ExperimentTrendsQuery", + "count_query": { + "kind": "TrendsQuery", + "series": [ + { + "kind": "EventsNode", + "math": "total", + "name": "[jan-16-running] event one", + "event": "[jan-16-running] event one" + } + ], + "interval": "day", + "dateRange": { + "date_to": "2025-01-16T23:59", + "date_from": "2025-01-02T13:54", + "explicitDate": true + }, + "trendsFilter": { + "display": "ActionsLineGraph" + }, + "filterTestAccounts": true + }, + "exposure_query": { + "kind": "TrendsQuery", + "series": [ + { + "kind": "EventsNode", + "math": "dau", + "name": "[jan-16-running] event zero", + "event": "[jan-16-running] event zero" + } + ], + "interval": "day", + "dateRange": { + "date_to": "2025-01-23T23:59", + "date_from": "2025-01-09T15:10", + "explicitDate": true + }, + "trendsFilter": { + "display": "ActionsLineGraph" + }, + "filterTestAccounts": true + } +} diff --git a/frontend/src/mocks/fixtures/api/experiments/_metric_trend_feature_flag_called.json b/frontend/src/mocks/fixtures/api/experiments/_metric_trend_feature_flag_called.json new file mode 100644 index 0000000000000..a1fd2c3fc8742 --- /dev/null +++ b/frontend/src/mocks/fixtures/api/experiments/_metric_trend_feature_flag_called.json @@ -0,0 +1,24 @@ +{ + "kind": "ExperimentTrendsQuery", + "count_query": { + "kind": "TrendsQuery", + "series": [ + { + "kind": "EventsNode", + "math": "total", + "name": "[jan-16-running] event one", + "event": "[jan-16-running] event one" + } + ], + "interval": "day", + "dateRange": { + "date_to": "2025-01-16T23:59", + "date_from": "2025-01-02T13:54", + "explicitDate": true + }, + "trendsFilter": { + "display": "ActionsLineGraph" + }, + "filterTestAccounts": true + } +} diff --git a/frontend/src/models/cohortsModel.test.ts b/frontend/src/models/cohortsModel.test.ts index b34dd64207c20..9964062c52360 100644 --- a/frontend/src/models/cohortsModel.test.ts +++ b/frontend/src/models/cohortsModel.test.ts @@ -72,7 +72,6 @@ describe('cohortsModel', () => { it('loads cohorts on mount', async () => { await expectLogic(logic).toDispatchActions(['loadCohorts', 'loadCohortsSuccess']) expect(logic.values.cohorts.results).toHaveLength(2) - expect(logic.values.cohortsWithAllUsers).toHaveLength(3) // includes "All Users" }) it('sets polling timeout for calculating cohorts when on cohorts page', async () => { diff --git a/frontend/src/models/cohortsModel.ts b/frontend/src/models/cohortsModel.ts index 3aa1b67cd6f46..caef95b0a58d7 100644 --- a/frontend/src/models/cohortsModel.ts +++ b/frontend/src/models/cohortsModel.ts @@ -177,7 +177,6 @@ export const cohortsModel = kea([ ], }), selectors({ - cohortsWithAllUsers: [(s) => [s.cohorts], (cohorts) => [{ id: 'all', name: 'All Users*' }, ...cohorts.results]], cohortsById: [ (s) => [s.cohorts], (cohorts): Partial> => diff --git a/frontend/src/queries/examples.ts b/frontend/src/queries/examples.ts index 80d85672b549a..046d6c9ba0f89 100644 --- a/frontend/src/queries/examples.ts +++ b/frontend/src/queries/examples.ts @@ -1,4 +1,7 @@ // This file contains example queries, used in storybook and in the /query interface. +import { RETENTION_FIRST_TIME } from 'lib/constants' +import { WEB_VITALS_THRESHOLDS } from 'scenes/web-analytics/webAnalyticsLogic' + import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' import { ActionsNode, @@ -18,13 +21,22 @@ import { RetentionQuery, StickinessQuery, TrendsQuery, + WebStatsBreakdown, + WebVitalsMetric, + WebVitalsPathBreakdownQuery, + WebVitalsPercentile, + WebVitalsQuery, } from '~/queries/schema/schema-general' import { + BaseMathType, ChartDisplayType, FilterLogicalOperator, + InsightType, PropertyFilterType, PropertyGroupFilter, + PropertyMathType, PropertyOperator, + RetentionPeriod, StepOrderValue, } from '~/types' @@ -352,6 +364,150 @@ const Hoggonacci: HogQuery = { } return fibonacci(16);`, } + +const WebVitals: WebVitalsQuery = { + kind: NodeKind.WebVitalsQuery, + properties: [], + dateRange: { + date_from: '-7d', + }, + source: { + kind: NodeKind.TrendsQuery, + dateRange: { + date_from: '-7d', + }, + interval: 'day', + series: (['INP', 'LCP', 'CLS', 'FCP'] as WebVitalsMetric[]).flatMap((name) => + [PropertyMathType.P75, PropertyMathType.P90, PropertyMathType.P99].map((math) => ({ + kind: NodeKind.EventsNode, + event: '$web_vitals', + name: '$web_vitals', + custom_name: name, + math: math, + math_property: `$web_vitals_${name}_value`, + })) + ), + trendsFilter: { display: ChartDisplayType.ActionsLineGraph }, + filterTestAccounts, + }, +} + +const WebVitalsPathBreakdown: WebVitalsPathBreakdownQuery = { + kind: NodeKind.WebVitalsPathBreakdownQuery, + properties: [], + dateRange: { + date_from: '-7d', + }, + filterTestAccounts, + percentile: 'p90' as WebVitalsPercentile, + metric: 'CLS' as WebVitalsMetric, + doPathCleaning: true, + thresholds: [WEB_VITALS_THRESHOLDS['CLS'].good, WEB_VITALS_THRESHOLDS['CLS'].poor], +} + +const WebAnalyticsReferrerDomain: DataTableNode = { + kind: NodeKind.DataTableNode, + source: { + kind: NodeKind.WebStatsTableQuery, + properties: [], + breakdownBy: WebStatsBreakdown.InitialReferringDomain, + dateRange: { + date_from: '-14d', + date_to: null, + }, + compareFilter: { compare: false }, + limit: 10, + filterTestAccounts: false, + conversionGoal: null, + }, +} + +const WebAnalyticsPath: DataTableNode = { + kind: NodeKind.DataTableNode, + source: { + kind: NodeKind.WebStatsTableQuery, + properties: [], + breakdownBy: WebStatsBreakdown.Page, + dateRange: { + date_from: '-14d', + date_to: null, + }, + compareFilter: { compare: false }, + limit: 10, + filterTestAccounts: false, + conversionGoal: null, + }, +} + +const WebAnalyticsBrowser: DataTableNode = { + kind: NodeKind.DataTableNode, + source: { + kind: NodeKind.WebStatsTableQuery, + properties: [], + breakdownBy: WebStatsBreakdown.Browser, + dateRange: { + date_from: '-14d', + date_to: null, + }, + compareFilter: { compare: false }, + limit: 10, + filterTestAccounts: false, + conversionGoal: null, + }, +} + +const WebAnalyticsWorldMap: InsightVizNode = { + kind: NodeKind.InsightVizNode, + source: { + kind: NodeKind.TrendsQuery, + breakdownFilter: { + breakdown: '$geoip_country_code', + breakdown_type: 'event', + }, + dateRange: { + date_from: '-14d', + date_to: null, + }, + series: [ + { + event: '$pageview', + name: 'Pageview', + kind: NodeKind.EventsNode, + math: BaseMathType.MonthlyActiveUsers, // Should be DAU, but it's not supported yet + }, + ], + trendsFilter: { display: ChartDisplayType.WorldMap }, + filterTestAccounts: false, + properties: [], + }, +} + +const WebAnalyticsRetention: InsightVizNode = { + kind: NodeKind.InsightVizNode, + source: { + kind: NodeKind.RetentionQuery, + properties: [], + dateRange: { + date_from: '-14d', + date_to: null, + }, + filterTestAccounts: false, + retentionFilter: { + retentionType: RETENTION_FIRST_TIME, + retentionReference: 'total', + totalIntervals: 8, + period: RetentionPeriod.Week, + }, + }, + vizSpecificOptions: { + [InsightType.RETENTION]: { + hideLineGraph: true, + hideSizeColumn: false, + useSmallLayout: false, + }, + }, +} + /* a subset of examples including only those we can show all users and that don't use HogQL */ export const queryExamples: Record = { Events, @@ -384,6 +540,13 @@ export const queryExamples: Record = { kind: NodeKind.InsightVizNode, source: InsightLifecycleQuery, } as InsightVizNode, + WebVitals, + WebVitalsPathBreakdown, + WebAnalyticsWorldMap, + WebAnalyticsReferrerDomain, + WebAnalyticsPath, + WebAnalyticsBrowser, + WebAnalyticsRetention, } export const stringifiedQueryExamples: Record = Object.fromEntries( diff --git a/frontend/src/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator.scss b/frontend/src/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator.scss index a13edc0963043..8a4ef6880166f 100644 --- a/frontend/src/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator.scss +++ b/frontend/src/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator.scss @@ -28,7 +28,7 @@ padding: 0 0.5rem; margin: calc(var(--radius) / 2) 0; overflow: hidden; - background-color: var(--primary-highlight); + background-color: var(--accent-primary-highlight); border-radius: var(--radius); } diff --git a/frontend/src/queries/nodes/DataTable/renderColumn.tsx b/frontend/src/queries/nodes/DataTable/renderColumn.tsx index 66d643725490d..6e227ee37ee19 100644 --- a/frontend/src/queries/nodes/DataTable/renderColumn.tsx +++ b/frontend/src/queries/nodes/DataTable/renderColumn.tsx @@ -263,7 +263,7 @@ export function renderColumn( return ( {String(value)} diff --git a/frontend/src/queries/nodes/DataVisualization/Components/Charts/LineGraph.tsx b/frontend/src/queries/nodes/DataVisualization/Components/Charts/LineGraph.tsx index 9ae2642378e40..77f54c5628b3a 100644 --- a/frontend/src/queries/nodes/DataVisualization/Components/Charts/LineGraph.tsx +++ b/frontend/src/queries/nodes/DataVisualization/Components/Charts/LineGraph.tsx @@ -188,10 +188,28 @@ export const LineGraph = (): JSX.Element => { }, scaleID: hasLeftYAxis ? 'yLeft' : 'yRight', value: cur.value, + enter: (ctx) => { + if (ctx.chart.options.plugins?.annotation?.annotations) { + const annotations = ctx.chart.options.plugins.annotation.annotations as Record + if (annotations[`line${curIndex}`]) { + annotations[`line${curIndex}`].label.content = `${cur.label}: ${cur.value}` + ctx.chart.update() + } + } + }, + leave: (ctx) => { + if (ctx.chart.options.plugins?.annotation?.annotations) { + const annotations = ctx.chart.options.plugins.annotation.annotations as Record + if (annotations[`line${curIndex}`]) { + annotations[`line${curIndex}`].label.content = cur.label + ctx.chart.update() + } + } + }, } acc.annotations[`line${curIndex}`] = { - type: 'line', + type: 'line' as const, ...line, } diff --git a/frontend/src/queries/nodes/DataVisualization/Components/Variables/variablesLogic.ts b/frontend/src/queries/nodes/DataVisualization/Components/Variables/variablesLogic.ts index d66b5e87c32e1..052537c6f82d6 100644 --- a/frontend/src/queries/nodes/DataVisualization/Components/Variables/variablesLogic.ts +++ b/frontend/src/queries/nodes/DataVisualization/Components/Variables/variablesLogic.ts @@ -60,6 +60,7 @@ export const variablesLogic = kea([ }), setEditorQuery: (query: string) => ({ query }), updateSourceQuery: true, + resetVariables: true, })), propsChanged(({ props, actions }, oldProps) => { if (oldProps.queryInput !== props.queryInput) { @@ -103,6 +104,9 @@ export const variablesLogic = kea([ return stateCopy }, + resetVariables: () => { + return [] + }, }, ], editorQuery: [ @@ -191,6 +195,11 @@ export const variablesLogic = kea([ editorQuery: (query: string) => { const queryVariableMatches = getVariablesFromQuery(query) + if (!queryVariableMatches.length) { + actions.resetVariables() + return + } + queryVariableMatches?.forEach((match) => { if (match === null) { return @@ -212,10 +221,16 @@ export const variablesLogic = kea([ return } + const queryVariableMatches = getVariablesFromQuery(query.source.query) + const variables = Object.values(query.source.variables ?? {}) if (variables.length) { - actions.addVariables(variables) + variables.forEach((variable) => { + if (queryVariableMatches.includes(variable.code_name)) { + actions.addVariable(variable) + } + }) } }, })), diff --git a/frontend/src/queries/nodes/DataVisualization/dataVisualizationLogic.ts b/frontend/src/queries/nodes/DataVisualization/dataVisualizationLogic.ts index e2b0f57d11623..304367fd01e69 100644 --- a/frontend/src/queries/nodes/DataVisualization/dataVisualizationLogic.ts +++ b/frontend/src/queries/nodes/DataVisualization/dataVisualizationLogic.ts @@ -780,7 +780,10 @@ export const dataVisualizationLogic = kea([ dataVisualizationProps: [() => [(_, props) => props], (props): DataVisualizationLogicProps => props], isTableVisualization: [ (state) => [state.visualizationType], - (visualizationType): boolean => visualizationType === ChartDisplayType.ActionsTable, + (visualizationType): boolean => + // BoldNumber relies on yAxis formatting so it's considered a table visualization + visualizationType === ChartDisplayType.ActionsTable || + visualizationType === ChartDisplayType.BoldNumber, ], showTableSettings: [ (state) => [state.visualizationType], diff --git a/frontend/src/queries/nodes/HogQLX/__snapshots__/render.test.tsx.snap b/frontend/src/queries/nodes/HogQLX/__snapshots__/render.test.tsx.snap index 7fc9efa65fd50..5a0c79fc62ce8 100644 --- a/frontend/src/queries/nodes/HogQLX/__snapshots__/render.test.tsx.snap +++ b/frontend/src/queries/nodes/HogQLX/__snapshots__/render.test.tsx.snap @@ -21,17 +21,17 @@ exports[`HogQLX render should render Sparkline 1`] = ` `; exports[`HogQLX render should render array 1`] = ` - + + + 1 + + + 2 + + + 3 + + `; exports[`HogQLX render should render object 1`] = ` diff --git a/frontend/src/queries/nodes/HogQLX/render.tsx b/frontend/src/queries/nodes/HogQLX/render.tsx index 13803d82fcdd0..684f80026e4b8 100644 --- a/frontend/src/queries/nodes/HogQLX/render.tsx +++ b/frontend/src/queries/nodes/HogQLX/render.tsx @@ -26,7 +26,7 @@ export function renderHogQLX(value: any): JSX.Element { if (typeof object === 'object') { if (Array.isArray(object)) { - return 10 ? 0 : 1} /> + return <>{object.map((obj) => renderHogQLX(obj))} } const { __hx_tag: tag, ...rest } = object @@ -56,26 +56,24 @@ export function renderHogQLX(value: any): JSX.Element { ) } else if (tag === 'a') { - const { href, source, target } = rest + const { href, children, source, target } = rest return ( - - {source ? renderHogQLX(source) : href} + + {children ?? source ? renderHogQLX(children ?? source) : href} ) } else if (tag === 'strong') { - const { source } = rest return ( - {renderHogQLX(source)} + {renderHogQLX(rest.children ?? rest.source)} ) } else if (tag === 'em') { - const { source } = rest return ( - {renderHogQLX(source)} + {renderHogQLX(rest.children ?? rest.source)} ) } diff --git a/frontend/src/queries/nodes/InsightViz/InsightViz.scss b/frontend/src/queries/nodes/InsightViz/InsightViz.scss index e815959cccccd..8dd9c8f96a90c 100644 --- a/frontend/src/queries/nodes/InsightViz/InsightViz.scss +++ b/frontend/src/queries/nodes/InsightViz/InsightViz.scss @@ -169,7 +169,7 @@ .funnel-significance-highlight { display: inline-flex; color: var(--bg-light); - background: var(--primary); + background: var(--accent-primary); .LemonIcon { color: var(--bg-light); diff --git a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.tsx b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.tsx index 8f16323bbf8cf..2a6197ac8f0a3 100644 --- a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.tsx +++ b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.tsx @@ -75,7 +75,7 @@ const SelectOption = ({ title, description, value, selectedValue }: SelectOption
{value} diff --git a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx index 0e2373c4a82c4..10e3500bd4361 100644 --- a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx +++ b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx @@ -138,7 +138,7 @@ export function PropertyGroupFilters({ data-attr={`${pageKey}-add-filter-group`} type="secondary" onClick={addFilterGroup} - icon={} + icon={} sideIcon={null} disabledReason={disabledReason} > diff --git a/frontend/src/queries/nodes/WebVitals/WebVitals.stories.tsx b/frontend/src/queries/nodes/WebVitals/WebVitals.stories.tsx new file mode 100644 index 0000000000000..6dc8ee2cf703b --- /dev/null +++ b/frontend/src/queries/nodes/WebVitals/WebVitals.stories.tsx @@ -0,0 +1,50 @@ +import { Meta, StoryFn, StoryObj } from '@storybook/react' + +import { mswDecorator } from '~/mocks/browser' +import { examples } from '~/queries/examples' +import { Query } from '~/queries/Query/Query' + +import webVitals from './__mocks__/WebVitals.json' +import webVitalsTrends from './__mocks__/WebVitalsTrends.json' + +type Story = StoryObj +const meta: Meta = { + title: 'Queries/WebVitals', + component: Query, + parameters: { + layout: 'fullscreen', + viewMode: 'story', + testOptions: { + waitForLoadersToDisappear: true, + waitForSelector: '[data-attr=trend-line-graph] > canvas', + }, + }, + decorators: [ + mswDecorator({ + post: { + '/api/environments/:team_id/query/': (req) => { + if ((req.body as any).query.kind === 'WebVitalsQuery') { + return [200, webVitals] + } else if ((req.body as any).query.kind === 'TrendsQuery') { + return [200, webVitalsTrends] + } + }, + }, + }), + ], +} +export default meta + +// NOTE: See InsightCard.scss to see why we need this wrapper +const QueryTemplate: StoryFn = (args) => { + return ( +
+
+ +
+
+ ) +} + +export const WebVitals: Story = QueryTemplate.bind({}) +WebVitals.args = { query: examples['WebVitals'] } diff --git a/frontend/src/queries/nodes/WebVitals/WebVitalsPathBreakdown.stories.tsx b/frontend/src/queries/nodes/WebVitals/WebVitalsPathBreakdown.stories.tsx new file mode 100644 index 0000000000000..1e616d86ea126 --- /dev/null +++ b/frontend/src/queries/nodes/WebVitals/WebVitalsPathBreakdown.stories.tsx @@ -0,0 +1,34 @@ +import { Meta, StoryFn, StoryObj } from '@storybook/react' + +import { mswDecorator } from '~/mocks/browser' +import { examples } from '~/queries/examples' +import { Query } from '~/queries/Query/Query' + +import webVitalsPathBreakdown from './__mocks__/WebVitalsPathBreakdown.json' + +type Story = StoryObj +const meta: Meta = { + title: 'Queries/WebVitalsPathBreakdown', + component: Query, + parameters: { + layout: 'fullscreen', + viewMode: 'story', + }, + decorators: [ + mswDecorator({ + post: { + '/api/environments/:team_id/query/': (req) => { + if ((req.body as any).query.kind === 'WebVitalsPathBreakdownQuery') { + return [200, webVitalsPathBreakdown] + } + }, + }, + }), + ], +} +export default meta + +const QueryTemplate: StoryFn = (args) => + +export const WebVitalsPathBreakdown: Story = QueryTemplate.bind({}) +WebVitalsPathBreakdown.args = { query: examples['WebVitalsPathBreakdown'] } diff --git a/frontend/src/queries/nodes/WebVitals/WebVitalsPathBreakdown.tsx b/frontend/src/queries/nodes/WebVitals/WebVitalsPathBreakdown.tsx index b2e3a493b77e3..125a5ddb39dee 100644 --- a/frontend/src/queries/nodes/WebVitals/WebVitalsPathBreakdown.tsx +++ b/frontend/src/queries/nodes/WebVitals/WebVitalsPathBreakdown.tsx @@ -39,24 +39,22 @@ export function WebVitalsPathBreakdown(props: { const webVitalsQueryResponse = response as WebVitalsPathBreakdownQueryResponse | undefined return ( -
-
-
-
- -
-
-
- -
-
-
- -
+
+
+
+ +
+
+
+ +
+
+
+
) @@ -157,7 +155,7 @@ const Content = ({
{path} diff --git a/frontend/src/queries/nodes/WebVitals/WebVitalsTab.scss b/frontend/src/queries/nodes/WebVitals/WebVitalsTab.scss index 2ea8fe4dadd30..9d41aa66267a7 100644 --- a/frontend/src/queries/nodes/WebVitals/WebVitalsTab.scss +++ b/frontend/src/queries/nodes/WebVitals/WebVitalsTab.scss @@ -4,5 +4,5 @@ // // To workaround that we need an extremely specific selector div.WebVitals__WebVitalsTab[data-active='true']:not([data-active='false']) { - border-bottom: 2px solid var(--primary) !important; + border-bottom: 2px solid var(--accent-primary) !important; } diff --git a/frontend/src/queries/nodes/WebVitals/__mocks__/WebVitals.json b/frontend/src/queries/nodes/WebVitals/__mocks__/WebVitals.json new file mode 100644 index 0000000000000..1c35a990862e8 --- /dev/null +++ b/frontend/src/queries/nodes/WebVitals/__mocks__/WebVitals.json @@ -0,0 +1,1525 @@ +{ + "cache_key": "cache_4a86df907aa3aae8f01a832303eaffee", + "cache_target_age": "2025-01-20T21:36:14.625758Z", + "calculation_trigger": null, + "error": "", + "hasMore": false, + "hogql": "SELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.75)(properties.$web_vitals_INP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.9)(properties.$web_vitals_INP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.99)(properties.$web_vitals_INP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.75)(properties.$web_vitals_LCP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.9)(properties.$web_vitals_LCP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.99)(properties.$web_vitals_LCP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.75)(properties.$web_vitals_CLS_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.9)(properties.$web_vitals_CLS_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.99)(properties.$web_vitals_CLS_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.75)(properties.$web_vitals_FCP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.9)(properties.$web_vitals_FCP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.99)(properties.$web_vitals_FCP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000", + "is_cached": true, + "last_refresh": "2025-01-20T21:21:14.625758Z", + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "uniq_page_screen_autocaptures", + "customChannelTypeRules": [ + { + "channel_type": "AI", + "combiner": "AND", + "id": "b6332cb7-a32f-4a62-929e-54dc5c37ba1d", + "items": [ + { + "id": "90f32f03-2a30-4278-9869-c1f99da6dbe9", + "key": "referring_domain", + "op": "exact", + "value": ["www.perplexity.ai", "chat.openai.com"] + } + ] + }, + { + "channel_type": "Newsletter", + "combiner": "OR", + "id": "eb1729b9-35a8-4013-bf1b-83bf107feb1f", + "items": [ + { + "id": "40b11644-51bd-49ac-a99e-82fc8f734eda", + "key": "referring_domain", + "op": "icontains", + "value": ["substack.com"] + }, + { + "id": "e048a56a-eba8-4e59-ad20-cda77883594c", + "key": "utm_source", + "op": "exact", + "value": ["substack", "posthog-newsletter"] + } + ] + } + ], + "dataWarehouseEventsModifiers": [], + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_on_events", + "propertyGroupsMode": "optimized", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T21:24:14.625758Z", + "query_status": null, + "results": [ + { + "data": [ + 208, 208, 264, 192, 208, 224, 204, 216, 240, 240, 240, 216, 224, 224, 216, 224, 224, 232, 224, 232, 224, + 232, 248, 224, 256 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 5644, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 0, + "name": "$web_vitals", + "custom_name": "INP", + "math": "p75", + "math_property": "$web_vitals_INP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 448, 404, 511.2000000000003, 356, 431.2000000000003, 489.60000000000036, 377.60000000000036, 416, 440, + 482.40000000000055, 416, 408, 448, 400, 384, 416, 392.8000000000011, 440, 432, 462.39999999999964, 448, + 494.39999999999964, 464, 423.2000000000007, 432 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 11010.400000000003, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 1, + "name": "$web_vitals", + "custom_name": "INP", + "math": "p90", + "math_property": "$web_vitals_INP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 5398.399999999961, 1952, 2316.56, 1652, 1420.7199999999948, 2435.119999999997, 1683.039999999999, + 2231.199999999998, 1550.0800000000017, 3412.4799999999886, 2101.1999999999953, 2379.8399999999965, + 2557.4400000000023, 1847.1199999999808, 1818.3999999999978, 2071.9999999999964, 2112.3199999999924, + 2777.679999999964, 2148.8000000000175, 1759.5200000000004, 2920, 3613.919999999993, 3326.0800000000017, + 1874.000000000011, 9213.120000000003 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 66573.03999999989, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 2, + "name": "$web_vitals", + "custom_name": "INP", + "math": "p99", + "math_property": "$web_vitals_INP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 3568.3499999940395, 3443.7000000029802, 3592.599999964237, 3894, 4329.09999999986, 4734.20000000298, + 4282, 4139.94999996433, 4264.300000190735, 4143.149999976158, 3901.1250000819564, 3526.3000000715256, + 3654.0250000117812, 3266.899999976158, 3353.25, 3507.0999999940395, 3546.674999978393, + 3268.6250000116415, 3200.4250000417233, 3435.550000011921, 3200.699999988079, 3181.5250001102686, + 2914.599999997765, 3018.600000011851, 3276.899999976158 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 90643.65000035858, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 3, + "name": "$web_vitals", + "custom_name": "LCP", + "math": "p75", + "math_property": "$web_vitals_LCP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 6898.620000004768, 6066.000000011923, 6645.500000023842, 6769.779999995231, 7112.830000042919, + 7462.4000000000015, 6677.639999991656, 6898.400000035763, 6752.800000011921, 6705.00000000447, + 6486.459999992977, 5268.1800000095345, 6797.099999913574, 5111.9200000099845, 5309.259999984503, + 5264.180000057258, 5798.550000023795, 5235.170000028617, 5276.9900000005955, 5794.180000010878, + 5524.299999904635, 5508.330000010785, 4782.939999985698, 4954.930000007152, 4180 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 150753.94000006368, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 4, + "name": "$web_vitals", + "custom_name": "LCP", + "math": "p90", + "math_property": "$web_vitals_LCP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 14972.459999999386, 14492.88199998917, 13286.37200002668, 15147.819999982084, 16098.172000016257, + 26791.27199999589, 14126.716000000099, 20172.29000000813, 17852.71999999286, 11359.888000051946, + 12998.47299998641, 14067.57600003465, 52928.860999994795, 13572.531999990822, 14238.465999998729, + 12435.940000006241, 13710.510000000026, 12685.827999962832, 14133.321000098722, 13834.243999979431, + 12811.096000001504, 15165.98199999153, 13037.81200000761, 10969.529999996641, 12637.507999974516 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 403528.27100008697, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 5, + "name": "$web_vitals", + "custom_name": "LCP", + "math": "p99", + "math_property": "$web_vitals_LCP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 0.11738345941473857, 0.1116777414930812, 0.11148180621126402, 0.11360822397057502, 0.12124307699700262, + 0.11431743843387039, 0.13034146384075265, 0.1096454932010398, 0.1032171049025754, 0.10163373737576842, + 0.11356780090728788, 0.11749907891439637, 0.10069782265665575, 0.11132465965808434, 0.11609873675228503, + 0.11423630178971465, 0.11387545309224631, 0.11051331689904792, 0.11757407576451785, 0.11846210741136749, + 0.1174824948671285, 0.11407278675268621, 0.12593172867144742, 0.1107074813984988, 0.10306043138940943 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 2.8396538227654418, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 6, + "name": "$web_vitals", + "custom_name": "CLS", + "math": "p75", + "math_property": "$web_vitals_CLS_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 0.2031501825438871, 0.201093031524413, 0.2095842521407585, 0.19531584873962426, 0.20945469267807423, + 0.1936306164906326, 0.22791121575642423, 0.19295741909908207, 0.18040384491341646, 0.1753472708551133, + 0.20097933723786104, 0.21832177647674206, 0.1938938316000412, 0.20471376133148966, 0.21059146347490743, + 0.228061743698989, 0.2180621190651727, 0.19926843375761277, 0.2047986676284671, 0.21823820057544063, + 0.20621692938858277, 0.2026768616180266, 0.22724847760596295, 0.22080761700372045, 0.08 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 5.183605767439534, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 7, + "name": "$web_vitals", + "custom_name": "CLS", + "math": "p90", + "math_property": "$web_vitals_CLS_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 0.7394310573092746, 0.7269385797236176, 0.5946637560324624, 0.5680860192279006, 0.9944294789929173, + 0.7159124289809393, 0.6872291755897353, 0.4988799234859136, 0.6787006393942361, 0.48761113863982797, + 0.6202096597700668, 0.785384399989555, 0.6679965775660852, 0.6533283073421113, 0.6105931010628081, + 0.6971827577627036, 0.6251693892756585, 0.5720963723483418, 0.6307351475102454, 0.7739021015881435, + 0.7270702309690661, 0.5897405474961678, 0.6549496098731046, 0.723335109961903, 0.6502020234701896 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 16.673777533362976, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 8, + "name": "$web_vitals", + "custom_name": "CLS", + "math": "p99", + "math_property": "$web_vitals_CLS_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 2160.075000000419, 2067.29999999702, 2132.9249999821186, 3711.050000011921, 2681.300000011921, + 2856.5500000864267, 2687.5500000000466, 2552.1999998569954, 2638.7750000022356, 2657.674999985844, 2516, + 2241.875, 2349.2499999701977, 2145.1750000000466, 2267, 2216.325000006705, 2307.699999999997, + 2036.9000000059605, 2043.0999999986961, 2206.900000095367, 2034.2999999988824, 2077, 1792.7999999523165, + 1897.7749999994412, 1894.0500000379977 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 58171.550000000556, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 9, + "name": "$web_vitals", + "custom_name": "FCP", + "math": "p75", + "math_property": "$web_vitals_FCP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 3773.420000006258, 3453.580000007153, 3723.789999967813, 8727.299999982119, 4001.839999990166, + 4394.720000004774, 4071.199999988079, 3910.500000014901, 4185.7499999761585, 4337.560000020264, + 3982.5000000223527, 3558.52000001073, 4610.969999999973, 3321.9600002288844, 3675.659999990464, + 3681.269999994338, 3831.880000014418, 3275.0599999636406, 3335.1900000002324, 3619.7999999999997, + 3387.140000152588, 3392.5600001156336, 2990.020000010729, 3085.5899999588732, 987 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 97416.79000042293, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 10, + "name": "$web_vitals", + "custom_name": "FCP", + "math": "p90", + "math_property": "$web_vitals_FCP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + }, + { + "data": [ + 11201.653999854323, 9744.151999933722, 8339.33600000262, 39768.30000000001, 10892.488000000027, + 15057.085999999925, 14184.98000000121, 17738.50499999999, 17556.822000021937, 9648.784000007943, + 12830.551999999983, 12189.56299999998, 43110.44300001102, 12593.301000013951, 20938.43999999985, + 10767.26799999664, 12188.968000001318, 11859.971999999949, 9838.580999980724, 11323.011999999566, + 10598.827999984396, 14159.835999990933, 11205.70400000571, 7239.854999985324, 12134.85680000143 + ], + "labels": [ + "19-Jan-2025 13:00", + "19-Jan-2025 14:00", + "19-Jan-2025 15:00", + "19-Jan-2025 16:00", + "19-Jan-2025 17:00", + "19-Jan-2025 18:00", + "19-Jan-2025 19:00", + "19-Jan-2025 20:00", + "19-Jan-2025 21:00", + "19-Jan-2025 22:00", + "19-Jan-2025 23:00", + "20-Jan-2025 00:00", + "20-Jan-2025 01:00", + "20-Jan-2025 02:00", + "20-Jan-2025 03:00", + "20-Jan-2025 04:00", + "20-Jan-2025 05:00", + "20-Jan-2025 06:00", + "20-Jan-2025 07:00", + "20-Jan-2025 08:00", + "20-Jan-2025 09:00", + "20-Jan-2025 10:00", + "20-Jan-2025 11:00", + "20-Jan-2025 12:00", + "20-Jan-2025 13:00" + ], + "days": [ + "2025-01-19 13:00:00", + "2025-01-19 14:00:00", + "2025-01-19 15:00:00", + "2025-01-19 16:00:00", + "2025-01-19 17:00:00", + "2025-01-19 18:00:00", + "2025-01-19 19:00:00", + "2025-01-19 20:00:00", + "2025-01-19 21:00:00", + "2025-01-19 22:00:00", + "2025-01-19 23:00:00", + "2025-01-20 00:00:00", + "2025-01-20 01:00:00", + "2025-01-20 02:00:00", + "2025-01-20 03:00:00", + "2025-01-20 04:00:00", + "2025-01-20 05:00:00", + "2025-01-20 06:00:00", + "2025-01-20 07:00:00", + "2025-01-20 08:00:00", + "2025-01-20 09:00:00", + "2025-01-20 10:00:00", + "2025-01-20 11:00:00", + "2025-01-20 12:00:00", + "2025-01-20 13:00:00" + ], + "count": 367111.2867997925, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T13:59:59.999999-08:00", + "date_from": "2025-01-19T13:00:00-08:00", + "entity_type": "events", + "interval": "hour", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-19T13:00:00-08:00", + "2025-01-19T14:00:00-08:00", + "2025-01-19T15:00:00-08:00", + "2025-01-19T16:00:00-08:00", + "2025-01-19T17:00:00-08:00", + "2025-01-19T18:00:00-08:00", + "2025-01-19T19:00:00-08:00", + "2025-01-19T20:00:00-08:00", + "2025-01-19T21:00:00-08:00", + "2025-01-19T22:00:00-08:00", + "2025-01-19T23:00:00-08:00", + "2025-01-20T00:00:00-08:00", + "2025-01-20T01:00:00-08:00", + "2025-01-20T02:00:00-08:00", + "2025-01-20T03:00:00-08:00", + "2025-01-20T04:00:00-08:00", + "2025-01-20T05:00:00-08:00", + "2025-01-20T06:00:00-08:00", + "2025-01-20T07:00:00-08:00", + "2025-01-20T08:00:00-08:00", + "2025-01-20T09:00:00-08:00", + "2025-01-20T10:00:00-08:00", + "2025-01-20T11:00:00-08:00", + "2025-01-20T12:00:00-08:00", + "2025-01-20T13:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 11, + "name": "$web_vitals", + "custom_name": "FCP", + "math": "p99", + "math_property": "$web_vitals_FCP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + } + ], + "timezone": "US/Pacific", + "timings": [] +} diff --git a/frontend/src/queries/nodes/WebVitals/__mocks__/WebVitalsPathBreakdown.json b/frontend/src/queries/nodes/WebVitals/__mocks__/WebVitalsPathBreakdown.json new file mode 100644 index 0000000000000..e0addc0990fe7 --- /dev/null +++ b/frontend/src/queries/nodes/WebVitals/__mocks__/WebVitalsPathBreakdown.json @@ -0,0 +1,65 @@ +{ + "cache_key": "cache_b9ec5efe0975ccf7b4089cdeb9434830_[]", + "cache_target_age": "2025-01-20T19:58:04.187422Z", + "calculation_trigger": null, + "columns": null, + "error": null, + "hasMore": null, + "hogql": "SELECT\n band,\n path,\n value\nFROM\n (SELECT\n multiIf(lessOrEquals(value, 200.0), 'good', lessOrEquals(value, 500.0), 'needs_improvements', 'poor') AS band,\n path,\n value\n FROM\n (SELECT\n events.properties.$pathname AS path,\n quantile(0.9)(toFloat(properties.$web_vitals_INP_value)) AS value\n FROM\n events\n WHERE\n and(equals(event, '$web_vitals'), notEquals(path, NULL), or(and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2024-12-21 00:00:00'))), less(timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59')))), false), 1)\n GROUP BY\n path\n HAVING\n greaterOrEquals(value, 0)))\nORDER BY\n value ASC,\n path ASC\nLIMIT 20\nBY band", + "is_cached": true, + "last_refresh": "2025-01-20T17:58:04.187422Z", + "limit": null, + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "count_pageviews", + "customChannelTypeRules": null, + "dataWarehouseEventsModifiers": null, + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_joined", + "propertyGroupsMode": null, + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T18:13:04.187422Z", + "offset": null, + "query_status": null, + "results": [ + { + "good": [ + { + "path": "/project/1/web/web-vitals", + "value": 8 + }, + { + "path": "/project/1/web", + "value": 115 + }, + { + "path": "/project/1", + "value": 145.8 + } + ], + "needs_improvements": [ + { + "path": "/project/1/product-analytics", + "value": 225 + }, + { + "path": "/project/1/feature-flags", + "value": 480 + } + ], + "poor": [] + } + ], + "samplingRate": null, + "timezone": "UTC", + "timings": [], + "types": null +} diff --git a/frontend/src/queries/nodes/WebVitals/__mocks__/WebVitalsTrends.json b/frontend/src/queries/nodes/WebVitals/__mocks__/WebVitalsTrends.json new file mode 100644 index 0000000000000..b5b834421682d --- /dev/null +++ b/frontend/src/queries/nodes/WebVitals/__mocks__/WebVitalsTrends.json @@ -0,0 +1,166 @@ +{ + "cache_key": "cache_e5302a42eaf932ef92f81374a652990e", + "cache_target_age": "2025-01-20T21:36:36.376082Z", + "calculation_trigger": null, + "error": "", + "hasMore": false, + "hogql": "SELECT\n arrayMap(number -> plus(toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toIntervalHour(number)), range(0, plus(coalesce(dateDiff('hour', toStartOfHour(assumeNotNull(toDateTime('2025-01-19 13:00:00'))), toStartOfHour(assumeNotNull(toDateTime('2025-01-20 13:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n quantile(0.9)(properties.$web_vitals_INP_value) AS total,\n toStartOfHour(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-19 13:00:00'))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 13:59:59'))), equals(event, '$web_vitals'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000", + "is_cached": true, + "last_refresh": "2025-01-20T21:21:36.376082Z", + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "uniq_page_screen_autocaptures", + "customChannelTypeRules": [ + { + "channel_type": "AI", + "combiner": "AND", + "id": "b6332cb7-a32f-4a62-929e-54dc5c37ba1d", + "items": [ + { + "id": "90f32f03-2a30-4278-9869-c1f99da6dbe9", + "key": "referring_domain", + "op": "exact", + "value": ["www.perplexity.ai", "chat.openai.com"] + } + ] + }, + { + "channel_type": "Newsletter", + "combiner": "OR", + "id": "eb1729b9-35a8-4013-bf1b-83bf107feb1f", + "items": [ + { + "id": "40b11644-51bd-49ac-a99e-82fc8f734eda", + "key": "referring_domain", + "op": "icontains", + "value": ["substack.com"] + }, + { + "id": "e048a56a-eba8-4e59-ad20-cda77883594c", + "key": "utm_source", + "op": "exact", + "value": ["substack", "posthog-newsletter"] + } + ] + } + ], + "dataWarehouseEventsModifiers": [], + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_on_events", + "propertyGroupsMode": "optimized", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T21:24:36.376082Z", + "query_status": null, + "results": [ + { + "data": [360, 392, 392, 384, 384, 400, 376, 352, 392, 384, 416, 440, 448, 440, 432], + "labels": [ + "6-Jan-2025", + "7-Jan-2025", + "8-Jan-2025", + "9-Jan-2025", + "10-Jan-2025", + "11-Jan-2025", + "12-Jan-2025", + "13-Jan-2025", + "14-Jan-2025", + "15-Jan-2025", + "16-Jan-2025", + "17-Jan-2025", + "18-Jan-2025", + "19-Jan-2025", + "20-Jan-2025" + ], + "days": [ + "2025-01-06", + "2025-01-07", + "2025-01-08", + "2025-01-09", + "2025-01-10", + "2025-01-11", + "2025-01-12", + "2025-01-13", + "2025-01-14", + "2025-01-15", + "2025-01-16", + "2025-01-17", + "2025-01-18", + "2025-01-19", + "2025-01-20" + ], + "count": 5992, + "label": "$web_vitals", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "duration_ms", + "display": "ActionsLineGraph", + "goalLines": [ + { + "borderColor": "rgb(45, 200, 100)", + "displayLabel": false, + "label": "Good", + "value": 200 + }, + { + "borderColor": "rgb(255, 160, 0)", + "displayLabel": false, + "label": "Poor", + "value": 500 + } + ], + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$web_vitals", + "type": "events", + "order": 0, + "name": "$web_vitals", + "custom_name": "INP", + "math": "p90", + "math_property": "$web_vitals_INP_value", + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + } + } + ], + "timezone": "US/Pacific", + "timings": [] +} diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index 41d31ef077e06..be5de0005bf51 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -8920,12 +8920,14 @@ "inputCost": { "type": "number" }, + "inputState": {}, "inputTokens": { "type": "number" }, "outputCost": { "type": "number" }, + "outputState": {}, "outputTokens": { "type": "number" }, @@ -8937,6 +8939,9 @@ }, "totalLatency": { "type": "number" + }, + "traceName": { + "type": "string" } }, "required": ["id", "createdAt", "person", "events"], diff --git a/frontend/src/queries/schema/schema-general.ts b/frontend/src/queries/schema/schema-general.ts index 561f32fe072ef..c98ee7ed10014 100644 --- a/frontend/src/queries/schema/schema-general.ts +++ b/frontend/src/queries/schema/schema-general.ts @@ -2260,6 +2260,9 @@ export interface LLMTrace { inputCost?: number outputCost?: number totalCost?: number + inputState?: any + outputState?: any + traceName?: string events: LLMTraceEvent[] } diff --git a/frontend/src/scenes/PreflightCheck/PreflightCheck.scss b/frontend/src/scenes/PreflightCheck/PreflightCheck.scss index 26ea6aca9d119..2c2b2e4532d0c 100644 --- a/frontend/src/scenes/PreflightCheck/PreflightCheck.scss +++ b/frontend/src/scenes/PreflightCheck/PreflightCheck.scss @@ -81,7 +81,7 @@ svg, .Preflight__status-text { - color: var(--primary-3000); + color: var(--accent-primary); } } diff --git a/frontend/src/scenes/PreflightCheck/PreflightCheck.tsx b/frontend/src/scenes/PreflightCheck/PreflightCheck.tsx index d290de036dc01..68a0ddc246d14 100644 --- a/frontend/src/scenes/PreflightCheck/PreflightCheck.tsx +++ b/frontend/src/scenes/PreflightCheck/PreflightCheck.tsx @@ -22,7 +22,7 @@ export const scene: SceneExport = { function PreflightCheckIcon({ status, loading }: { status: PreflightCheckStatus; loading?: boolean }): JSX.Element { if (loading) { - return + return } if (status === 'validated') { return diff --git a/frontend/src/scenes/billing/InitialBillingLimitNotice.tsx b/frontend/src/scenes/billing/InitialBillingLimitNotice.tsx index 2a9a6ab048a8b..64d4d789f72cd 100644 --- a/frontend/src/scenes/billing/InitialBillingLimitNotice.tsx +++ b/frontend/src/scenes/billing/InitialBillingLimitNotice.tsx @@ -25,7 +25,7 @@ const InitialBillingLimitNoticeContents = ({ product }: { product: BillingProduc dismissKey={`initial-billing-limit-notice-${product.type}`} >

- Default initial billing limit of ${initialBillingLimit} active. + Default initial billing limit of ${initialBillingLimit} active.

This protects you from accidentally incurring large unexpected charges. Some features may stop working diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaGroups.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaGroups.tsx index 8985f218d40c4..5d271e9450300 100644 --- a/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaGroups.tsx +++ b/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaGroups.tsx @@ -91,7 +91,7 @@ export function CohortCriteriaGroups(logicProps: CohortLogicProps): JSX.Element data-attr="cohort-add-filter-group-criteria" type="secondary" onClick={() => addFilter(groupIndex)} - icon={} + icon={} > Add criteria @@ -110,7 +110,7 @@ export function CohortCriteriaGroups(logicProps: CohortLogicProps): JSX.Element className="mb-4 mt-4" type="secondary" onClick={() => addFilter()} - icon={} + icon={} fullWidth > Add criteria group diff --git a/frontend/src/scenes/cohorts/Cohorts.scss b/frontend/src/scenes/cohorts/Cohorts.scss index 4a33556c6f197..dcf8d5c41d63d 100644 --- a/frontend/src/scenes/cohorts/Cohorts.scss +++ b/frontend/src/scenes/cohorts/Cohorts.scss @@ -8,7 +8,7 @@ .cohort-csv-dragger { height: 155px !important; margin-top: 1rem; - border-color: var(--primary); + border-color: var(--accent-primary); border-style: dashed; border-width: 2px; border-radius: var(--radius); diff --git a/frontend/src/scenes/comments/Comment.tsx b/frontend/src/scenes/comments/Comment.tsx index 8898f1a4f962d..02a81dfa751eb 100644 --- a/frontend/src/scenes/comments/Comment.tsx +++ b/frontend/src/scenes/comments/Comment.tsx @@ -32,7 +32,7 @@ const Comment = ({ comment }: { comment: CommentType }): JSX.Element => { return (

diff --git a/frontend/src/scenes/dashboard/DashboardItems.scss b/frontend/src/scenes/dashboard/DashboardItems.scss index 79c3eb0470b0f..00ed4efa25c98 100644 --- a/frontend/src/scenes/dashboard/DashboardItems.scss +++ b/frontend/src/scenes/dashboard/DashboardItems.scss @@ -45,9 +45,9 @@ z-index: 2; max-width: 100%; user-select: none; - border: 1px solid var(--primary-3000); + border: 1px solid var(--accent-primary); border-radius: var(--radius); - outline: 1px solid var(--primary-3000); + outline: 1px solid var(--accent-primary); transition: 100ms ease; } diff --git a/frontend/src/scenes/data-warehouse/new/dataWarehouseTableLogic.tsx b/frontend/src/scenes/data-warehouse/new/dataWarehouseTableLogic.tsx index 92165933b107f..fb9e96055985b 100644 --- a/frontend/src/scenes/data-warehouse/new/dataWarehouseTableLogic.tsx +++ b/frontend/src/scenes/data-warehouse/new/dataWarehouseTableLogic.tsx @@ -99,6 +99,13 @@ export const dataWarehouseTableLogic = kea([ } } + if (url_pattern?.startsWith('https://storage.cloud.google.com')) { + return { + url_pattern: + 'Google Cloud links must start with "https://storage.googleapis.com". Please update your URL pattern.', + } + } + return { name: !name && 'Please enter a name.', url_pattern: !url_pattern && 'Please enter a url pattern.', diff --git a/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx b/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx index ed17057809093..5419cbd174259 100644 --- a/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx +++ b/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx @@ -75,6 +75,7 @@ export const SchemaTable = ({ schemas, isLoading }: SchemaTableProps): JSX.Eleme return ( updateSchema({ ...schema, sync_frequency: value as DataWarehouseSyncInterval }) diff --git a/frontend/src/scenes/experiments/ExperimentForm.tsx b/frontend/src/scenes/experiments/ExperimentForm.tsx index c0d58cfc5626b..ebf717865a68e 100644 --- a/frontend/src/scenes/experiments/ExperimentForm.tsx +++ b/frontend/src/scenes/experiments/ExperimentForm.tsx @@ -18,9 +18,8 @@ const ExperimentFormFields = (): JSX.Element => { const { experiment, groupTypes, aggregationLabel } = useValues(experimentLogic) const { addVariant, removeExperimentGroup, setExperiment, createExperiment, setExperimentType } = useActions(experimentLogic) - const { webExperimentsAvailable } = useValues(experimentsLogic) + const { webExperimentsAvailable, unavailableFeatureFlagKeys } = useValues(experimentsLogic) const { groupsAccessStatus } = useValues(groupsAccessLogic) - const { takenKeys } = useValues(experimentsLogic) return (
@@ -40,14 +39,14 @@ const ExperimentFormFields = (): JSX.Element => { { setExperiment({ - feature_flag_key: generateFeatureFlagKey(experiment.name, takenKeys), + feature_flag_key: generateFeatureFlagKey( + experiment.name, + unavailableFeatureFlagKeys + ), }) }} > @@ -294,7 +293,7 @@ export function ExperimentForm(): JSX.Element { ) } -const generateFeatureFlagKey = (name: string, takenKeys: string[]): string => { +const generateFeatureFlagKey = (name: string, unavailableFeatureFlagKeys: Set): string => { const baseKey = name .toLowerCase() .replace(/[^A-Za-z0-9-_]+/g, '-') @@ -303,7 +302,8 @@ const generateFeatureFlagKey = (name: string, takenKeys: string[]): string => { let key = baseKey let counter = 1 - while (takenKeys.includes(key)) { + + while (unavailableFeatureFlagKeys.has(key)) { key = `${baseKey}-${counter}` counter++ } diff --git a/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx b/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx index 703b05d0841d9..00417df4f49a9 100644 --- a/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx @@ -19,7 +19,8 @@ import { ReleaseConditionsModal, ReleaseConditionsTable } from './ReleaseConditi import { SummaryTable } from './SummaryTable' const ResultsTab = (): JSX.Element => { - const { experiment, metricResults, primaryMetricsLengthWithSharedMetrics } = useValues(experimentLogic) + const { experiment, metricResults, firstPrimaryMetric, primaryMetricsLengthWithSharedMetrics } = + useValues(experimentLogic) const hasSomeResults = metricResults?.some((result) => result?.insight) const hasSinglePrimaryMetric = primaryMetricsLengthWithSharedMetrics === 1 @@ -43,10 +44,10 @@ const ResultsTab = (): JSX.Element => { )} {/* Show detailed results if there's only a single primary metric */} - {hasSomeResults && hasSinglePrimaryMetric && ( + {hasSomeResults && hasSinglePrimaryMetric && firstPrimaryMetric && (
- +
diff --git a/frontend/src/scenes/experiments/ExperimentView/SummaryTable.tsx b/frontend/src/scenes/experiments/ExperimentView/SummaryTable.tsx index 7030b7d71356a..072e080d89c01 100644 --- a/frontend/src/scenes/experiments/ExperimentView/SummaryTable.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/SummaryTable.tsx @@ -12,15 +12,13 @@ import { ExperimentFunnelsQuery, ExperimentTrendsQuery } from '~/queries/schema' import { FilterLogicalOperator, InsightType, - PropertyFilterType, - PropertyOperator, RecordingUniversalFilters, ReplayTabs, TrendExperimentVariant, - UniversalFiltersGroupValue, } from '~/types' import { experimentLogic } from '../experimentLogic' +import { getViewRecordingFilters } from '../utils' import { VariantTag } from './components' export function SummaryTable({ @@ -309,47 +307,16 @@ export function SummaryTable({ title: '', render: function Key(_, item): JSX.Element { const variantKey = item.key + + const filters = getViewRecordingFilters(metric, experiment.feature_flag_key, variantKey) return ( } tooltip="Watch recordings of people who were exposed to this variant." + disabledReason={filters.length === 0 ? 'Unable to identify recordings for this metric' : undefined} type="secondary" onClick={() => { - const filters: UniversalFiltersGroupValue[] = [ - { - id: '$feature_flag_called', - name: '$feature_flag_called', - type: 'events', - properties: [ - { - key: `$feature/${experiment.feature_flag_key}`, - type: PropertyFilterType.Event, - value: [variantKey], - operator: PropertyOperator.Exact, - }, - { - key: `$feature/${experiment.feature_flag_key}`, - type: PropertyFilterType.Event, - value: 'is_set', - operator: PropertyOperator.IsSet, - }, - { - key: '$feature_flag', - type: PropertyFilterType.Event, - value: experiment.feature_flag_key, - operator: PropertyOperator.Exact, - }, - ], - }, - ] - if (experiment.filters.insight === InsightType.FUNNELS) { - if (experiment.filters?.events?.[0]) { - filters.push(experiment.filters.events[0]) - } else if (experiment.filters?.actions?.[0]) { - filters.push(experiment.filters.actions[0]) - } - } const filterGroup: Partial = { filter_group: { type: FilterLogicalOperator.And, diff --git a/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx b/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx index 81286363c3b6b..596bd65b2d540 100644 --- a/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx @@ -138,7 +138,7 @@ export function VariantScreenshot({ loading={uploading} value={filesToUpload} callToAction={ -
+
+
} diff --git a/frontend/src/scenes/experiments/Metrics/MetricSourceModal.tsx b/frontend/src/scenes/experiments/Metrics/MetricSourceModal.tsx index 3819bcc52f627..c5dfb2976e973 100644 --- a/frontend/src/scenes/experiments/Metrics/MetricSourceModal.tsx +++ b/frontend/src/scenes/experiments/Metrics/MetricSourceModal.tsx @@ -35,7 +35,7 @@ export function MetricSourceModal({
{ closeCurrentModal() @@ -54,7 +54,7 @@ export function MetricSourceModal({
{ closeCurrentModal() openSharedMetricModal(null) diff --git a/frontend/src/scenes/experiments/Metrics/TrendsMetricForm.tsx b/frontend/src/scenes/experiments/Metrics/TrendsMetricForm.tsx index 1f1c694c05fae..5ca16b62dd680 100644 --- a/frontend/src/scenes/experiments/Metrics/TrendsMetricForm.tsx +++ b/frontend/src/scenes/experiments/Metrics/TrendsMetricForm.tsx @@ -145,7 +145,7 @@ export function TrendsMetricForm({ isSecondary = false }: { isSecondary?: boolea { @@ -163,7 +163,7 @@ export function TrendsMetricForm({ isSecondary = false }: { isSecondary?: boolea
Default {!currentMetric.exposure_query && ( - + )}
@@ -176,7 +176,7 @@ export function TrendsMetricForm({ isSecondary = false }: { isSecondary?: boolea Custom {currentMetric.exposure_query && ( - + )}
diff --git a/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx b/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx index 12e01714bea6c..4ff6d3e7fe32d 100644 --- a/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx +++ b/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx @@ -143,7 +143,7 @@ export function DeltaChart({ const { isDarkModeOn } = useValues(themeLogic) const COLORS = { TICK_TEXT_COLOR: 'var(--text-secondary-3000)', - BOUNDARY_LINES: 'var(--border-3000)', + BOUNDARY_LINES: 'var(--border-primary)', ZERO_LINE: 'var(--border-bold)', BAR_NEGATIVE: isDarkModeOn ? '#c32f45' : '#f84257', BAR_POSITIVE: isDarkModeOn ? '#12a461' : '#36cd6f', diff --git a/frontend/src/scenes/experiments/SharedMetrics/SharedMetric.tsx b/frontend/src/scenes/experiments/SharedMetrics/SharedMetric.tsx index 93e17a9fb26c8..03edc624bd397 100644 --- a/frontend/src/scenes/experiments/SharedMetrics/SharedMetric.tsx +++ b/frontend/src/scenes/experiments/SharedMetrics/SharedMetric.tsx @@ -38,7 +38,7 @@ export function SharedMetric(): JSX.Element {
{ @@ -50,7 +50,7 @@ export function SharedMetric(): JSX.Element {
Trend {sharedMetric.query.kind === NodeKind.ExperimentTrendsQuery && ( - + )}
@@ -60,7 +60,7 @@ export function SharedMetric(): JSX.Element {
{ @@ -72,7 +72,7 @@ export function SharedMetric(): JSX.Element {
Funnel {sharedMetric.query.kind === NodeKind.ExperimentFunnelsQuery && ( - + )}
diff --git a/frontend/src/scenes/experiments/SharedMetrics/SharedTrendsMetricForm.tsx b/frontend/src/scenes/experiments/SharedMetrics/SharedTrendsMetricForm.tsx index f59520a9cec4a..d686231fa4ace 100644 --- a/frontend/src/scenes/experiments/SharedMetrics/SharedTrendsMetricForm.tsx +++ b/frontend/src/scenes/experiments/SharedMetrics/SharedTrendsMetricForm.tsx @@ -118,7 +118,7 @@ export function SharedTrendsMetricForm(): JSX.Element {
{ @@ -133,7 +133,7 @@ export function SharedTrendsMetricForm(): JSX.Element {
Default {!sharedMetricQuery.exposure_query && ( - + )}
@@ -146,7 +146,7 @@ export function SharedTrendsMetricForm(): JSX.Element {
{ @@ -183,7 +183,7 @@ export function SharedTrendsMetricForm(): JSX.Element {
Custom {sharedMetricQuery.exposure_query && ( - + )}
diff --git a/frontend/src/scenes/experiments/SharedMetrics/sharedMetricLogic.tsx b/frontend/src/scenes/experiments/SharedMetrics/sharedMetricLogic.tsx index ff6c653a8f7ac..0b06a090873d6 100644 --- a/frontend/src/scenes/experiments/SharedMetrics/sharedMetricLogic.tsx +++ b/frontend/src/scenes/experiments/SharedMetrics/sharedMetricLogic.tsx @@ -64,7 +64,7 @@ export const sharedMetricLogic = kea([ const response = await api.create(`api/projects/@current/experiment_saved_metrics/`, values.sharedMetric) if (response.id) { lemonToast.success('Shared metric created successfully') - actions.reportExperimentSharedMetricCreated(response) + actions.reportExperimentSharedMetricCreated(response as SharedMetric) actions.loadSharedMetrics() router.actions.push('/experiments/shared-metrics') } diff --git a/frontend/src/scenes/experiments/experimentLogic.tsx b/frontend/src/scenes/experiments/experimentLogic.tsx index f60fc6195fb4d..054c2275538d0 100644 --- a/frontend/src/scenes/experiments/experimentLogic.tsx +++ b/frontend/src/scenes/experiments/experimentLogic.tsx @@ -42,6 +42,7 @@ import { import { Breadcrumb, BreakdownAttributionType, + BreakdownType, ChartDisplayType, CohortType, CountPerActorMathType, @@ -666,7 +667,8 @@ export const experimentLogic = kea([ actions.setExperiment({ type: type }) }, loadExperimentSuccess: async ({ experiment }) => { - experiment && actions.reportExperimentViewed(experiment) + const duration = experiment?.start_date ? dayjs().diff(experiment.start_date, 'second') : null + experiment && actions.reportExperimentViewed(experiment, duration) if (experiment?.start_date) { actions.loadMetricResults() @@ -937,6 +939,15 @@ export const experimentLogic = kea([ { name: 'Experiment: ' + values.experiment.name, description: `Dashboard for [${experimentUrl}](${experimentUrl})`, + filters: { + date_from: values.experiment.start_date, + date_to: values.experiment.end_date, + properties: [], + breakdown_filter: { + breakdown: '$feature/' + values.experiment.feature_flag_key, + breakdown_type: 'event' as BreakdownType, + }, + }, } as Partial ) @@ -1427,8 +1438,8 @@ export const experimentLogic = kea([ }, ], credibleIntervalForVariant: [ - (s) => [s.experimentStatsVersion], - (experimentStatsVersion) => + () => [], + () => ( metricResult: CachedExperimentTrendsQueryResponse | CachedExperimentFunnelsQueryResponse | null, variantKey: string, @@ -1460,26 +1471,14 @@ export const experimentLogic = kea([ const controlVariant = (metricResult.variants as TrendExperimentVariant[]).find( ({ key }) => key === 'control' ) as TrendExperimentVariant - const variant = (metricResult.variants as TrendExperimentVariant[]).find( - ({ key }) => key === variantKey - ) as TrendExperimentVariant const controlMean = controlVariant.count / controlVariant.absolute_exposure - const meanLowerBound = - experimentStatsVersion === 2 - ? credibleInterval[0] / variant.absolute_exposure - : credibleInterval[0] - const meanUpperBound = - experimentStatsVersion === 2 - ? credibleInterval[1] / variant.absolute_exposure - : credibleInterval[1] - // Calculate the percentage difference between the credible interval bounds of the variant and the control's mean. // This represents the range in which the true percentage change relative to the control is likely to fall. - const lowerBound = ((meanLowerBound - controlMean) / controlMean) * 100 - const upperBound = ((meanUpperBound - controlMean) / controlMean) * 100 - return [lowerBound, upperBound] + const relativeLowerBound = ((credibleInterval[0] - controlMean) / controlMean) * 100 + const relativeUpperBound = ((credibleInterval[1] - controlMean) / controlMean) * 100 + return [relativeLowerBound, relativeUpperBound] }, ], getIndexForVariant: [ @@ -1736,6 +1735,18 @@ export const experimentLogic = kea([ return primaryMetricsLengthWithSharedMetrics > 0 }, ], + firstPrimaryMetric: [ + (s) => [s.experiment], + (experiment: Experiment): ExperimentTrendsQuery | ExperimentFunnelsQuery | undefined => { + if (experiment.metrics.length) { + return experiment.metrics[0] + } + const primaryMetric = experiment.saved_metrics.find((metric) => metric.metadata.type === 'primary') + if (primaryMetric) { + return primaryMetric.query + } + }, + ], experimentStatsVersion: [ (s) => [s.experiment], (experiment: Experiment): number => { diff --git a/frontend/src/scenes/experiments/experimentsLogic.ts b/frontend/src/scenes/experiments/experimentsLogic.ts index 361414fb2bf64..ff79c0936bf32 100644 --- a/frontend/src/scenes/experiments/experimentsLogic.ts +++ b/frontend/src/scenes/experiments/experimentsLogic.ts @@ -7,6 +7,7 @@ import api from 'lib/api' import { FEATURE_FLAGS } from 'lib/constants' import { lemonToast } from 'lib/lemon-ui/LemonToast/LemonToast' import { featureFlagLogic, FeatureFlagsSet } from 'lib/logic/featureFlagLogic' +import { featureFlagsLogic, type FeatureFlagsResult } from 'scenes/feature-flags/featureFlagsLogic' import { projectLogic } from 'scenes/projectLogic' import { userLogic } from 'scenes/userLogic' @@ -44,6 +45,8 @@ export const experimentsLogic = kea([ ['user', 'hasAvailableFeature'], featureFlagLogic, ['featureFlags'], + featureFlagsLogic, + ['featureFlags'], router, ['location'], ], @@ -161,10 +164,15 @@ export const experimentsLogic = kea([ () => [featureFlagLogic.selectors.featureFlags], (featureFlags: FeatureFlagsSet) => featureFlags[FEATURE_FLAGS.WEB_EXPERIMENTS], ], - // This only checks the first page, which is very large so it's not a big deal - takenKeys: [ - (s) => [s.experiments], - (experiments: Experiment[]) => experiments.map((experiment) => experiment.feature_flag_key), + // TRICKY: we do not load all feature flags here, just the latest ones. + unavailableFeatureFlagKeys: [ + (s) => [featureFlagsLogic.selectors.featureFlags, s.experiments], + (featureFlags: FeatureFlagsResult, experiments: Experiment[]) => { + return new Set([ + ...featureFlags.results.map((flag) => flag.key), + ...experiments.map((experiment) => experiment.feature_flag_key), + ]) + }, ], })), events(({ actions }) => ({ diff --git a/frontend/src/scenes/experiments/utils.test.ts b/frontend/src/scenes/experiments/utils.test.ts index 906841aaec363..f0f9e3f27d380 100644 --- a/frontend/src/scenes/experiments/utils.test.ts +++ b/frontend/src/scenes/experiments/utils.test.ts @@ -1,7 +1,12 @@ -import { EntityType, FeatureFlagFilters, InsightType } from '~/types' +import metricFunnelEventsJson from '~/mocks/fixtures/api/experiments/_metric_funnel_events.json' +import metricTrendActionJson from '~/mocks/fixtures/api/experiments/_metric_trend_action.json' +import metricTrendCustomExposureJson from '~/mocks/fixtures/api/experiments/_metric_trend_custom_exposure.json' +import metricTrendFeatureFlagCalledJson from '~/mocks/fixtures/api/experiments/_metric_trend_feature_flag_called.json' +import { ExperimentFunnelsQuery, ExperimentTrendsQuery } from '~/queries/schema/schema-general' +import { EntityType, FeatureFlagFilters, InsightType, PropertyFilterType, PropertyOperator } from '~/types' import { getNiceTickValues } from './MetricsView/MetricsView' -import { getMinimumDetectableEffect, transformFiltersForWinningVariant } from './utils' +import { getMinimumDetectableEffect, getViewRecordingFilters, transformFiltersForWinningVariant } from './utils' describe('utils', () => { it('Funnel experiment returns correct MDE', async () => { @@ -235,3 +240,148 @@ describe('getNiceTickValues', () => { expect(getNiceTickValues(8.5)).toEqual([-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10]) }) }) + +describe('getViewRecordingFilters', () => { + const featureFlagKey = 'jan-16-running' + + it('returns the correct filters for a funnel metric', () => { + const filters = getViewRecordingFilters( + metricFunnelEventsJson as ExperimentFunnelsQuery, + featureFlagKey, + 'control' + ) + expect(filters).toEqual([ + { + id: '[jan-16-running] seen', + name: '[jan-16-running] seen', + type: 'events', + properties: [ + { + key: `$feature/${featureFlagKey}`, + type: PropertyFilterType.Event, + value: ['control'], + operator: PropertyOperator.Exact, + }, + ], + }, + { + id: '[jan-16-running] payment', + name: '[jan-16-running] payment', + type: 'events', + properties: [ + { + key: `$feature/${featureFlagKey}`, + type: PropertyFilterType.Event, + value: ['control'], + operator: PropertyOperator.Exact, + }, + ], + }, + ]) + }) + it('returns the correct filters for a trend metric', () => { + const filters = getViewRecordingFilters( + metricTrendFeatureFlagCalledJson as ExperimentTrendsQuery, + featureFlagKey, + 'test' + ) + expect(filters).toEqual([ + { + id: '$feature_flag_called', + name: '$feature_flag_called', + type: 'events', + properties: [ + { + key: '$feature_flag_response', + type: PropertyFilterType.Event, + value: ['test'], + operator: PropertyOperator.Exact, + }, + { + key: '$feature_flag', + type: PropertyFilterType.Event, + value: 'jan-16-running', + operator: PropertyOperator.Exact, + }, + ], + }, + { + id: '[jan-16-running] event one', + name: '[jan-16-running] event one', + type: 'events', + properties: [ + { + key: `$feature/${featureFlagKey}`, + type: PropertyFilterType.Event, + value: ['test'], + operator: PropertyOperator.Exact, + }, + ], + }, + ]) + }) + it('returns the correct filters for a trend metric with custom exposure', () => { + const filters = getViewRecordingFilters( + metricTrendCustomExposureJson as ExperimentTrendsQuery, + featureFlagKey, + 'test' + ) + expect(filters).toEqual([ + { + id: '[jan-16-running] event zero', + name: '[jan-16-running] event zero', + type: 'events', + properties: [ + { + key: `$feature/${featureFlagKey}`, + type: PropertyFilterType.Event, + value: ['test'], + operator: PropertyOperator.Exact, + }, + ], + }, + { + id: '[jan-16-running] event one', + name: '[jan-16-running] event one', + type: 'events', + properties: [ + { + key: `$feature/${featureFlagKey}`, + type: PropertyFilterType.Event, + value: ['test'], + operator: PropertyOperator.Exact, + }, + ], + }, + ]) + }) + it('returns the correct filters for a trend metric with an action', () => { + const filters = getViewRecordingFilters(metricTrendActionJson as ExperimentTrendsQuery, featureFlagKey, 'test') + expect(filters).toEqual([ + { + id: '$feature_flag_called', + name: '$feature_flag_called', + type: 'events', + properties: [ + { + key: '$feature_flag_response', + type: PropertyFilterType.Event, + value: ['test'], + operator: PropertyOperator.Exact, + }, + { + key: '$feature_flag', + type: PropertyFilterType.Event, + value: 'jan-16-running', + operator: PropertyOperator.Exact, + }, + ], + }, + { + id: 8, + name: 'jan-16-running payment action', + type: 'actions', + }, + ]) + }) +}) diff --git a/frontend/src/scenes/experiments/utils.ts b/frontend/src/scenes/experiments/utils.ts index b76a68cf50787..e3fe9d268b018 100644 --- a/frontend/src/scenes/experiments/utils.ts +++ b/frontend/src/scenes/experiments/utils.ts @@ -1,6 +1,16 @@ import { getSeriesColor } from 'lib/colors' -import { FeatureFlagFilters, FunnelTimeConversionMetrics, InsightType, TrendResult } from '~/types' +import { ExperimentFunnelsQuery, ExperimentTrendsQuery } from '~/queries/schema' +import { AnyEntityNode, NodeKind } from '~/queries/schema/schema-general' +import { + FeatureFlagFilters, + FunnelTimeConversionMetrics, + InsightType, + PropertyFilterType, + PropertyOperator, + TrendResult, + UniversalFiltersGroupValue, +} from '~/types' export function getExperimentInsightColour(variantIndex: number | null): string { return variantIndex !== null ? getSeriesColor(variantIndex) : 'var(--muted-3000)' @@ -90,3 +100,80 @@ export function transformFiltersForWinningVariant( ], } } + +function seriesToFilter( + series: AnyEntityNode, + featureFlagKey: string, + variantKey: string +): UniversalFiltersGroupValue | null { + if (series.kind === NodeKind.EventsNode) { + return { + id: series.event as string, + name: series.event as string, + type: 'events', + properties: [ + { + key: `$feature/${featureFlagKey}`, + type: PropertyFilterType.Event, + value: [variantKey], + operator: PropertyOperator.Exact, + }, + ], + } + } else if (series.kind === NodeKind.ActionsNode) { + return { + id: series.id, + name: series.name, + type: 'actions', + } + } + return null +} + +export function getViewRecordingFilters( + metric: ExperimentTrendsQuery | ExperimentFunnelsQuery, + featureFlagKey: string, + variantKey: string +): UniversalFiltersGroupValue[] { + const filters: UniversalFiltersGroupValue[] = [] + if (metric.kind === NodeKind.ExperimentTrendsQuery) { + if (metric.exposure_query) { + const exposure_filter = seriesToFilter(metric.exposure_query.series[0], featureFlagKey, variantKey) + if (exposure_filter) { + filters.push(exposure_filter) + } + } else { + filters.push({ + id: '$feature_flag_called', + name: '$feature_flag_called', + type: 'events', + properties: [ + { + key: `$feature_flag_response`, + type: PropertyFilterType.Event, + value: [variantKey], + operator: PropertyOperator.Exact, + }, + { + key: '$feature_flag', + type: PropertyFilterType.Event, + value: featureFlagKey, + operator: PropertyOperator.Exact, + }, + ], + }) + } + const count_filter = seriesToFilter(metric.count_query.series[0], featureFlagKey, variantKey) + if (count_filter) { + filters.push(count_filter) + } + return filters + } + metric.funnels_query.series.forEach((series) => { + const filter = seriesToFilter(series, featureFlagKey, variantKey) + if (filter) { + filters.push(filter) + } + }) + return filters +} diff --git a/frontend/src/scenes/feature-flags/FeatureFlag.tsx b/frontend/src/scenes/feature-flags/FeatureFlag.tsx index c90fad40986c9..4bc62fb585378 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlag.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlag.tsx @@ -150,26 +150,34 @@ export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element {
- {/* TODO: In a follow up, clean up super_groups and combine into regular ReleaseConditions component */} - {featureFlag.filters.super_groups && ( - - )} -
-
- -
-
-

Insights that use this feature flag

- -
-
-
- {featureFlags[FEATURE_FLAGS.AUTO_ROLLBACK_FEATURE_FLAGS] && ( - + {!featureFlag.is_remote_configuration && ( + <> + {/* TODO: In a follow up, clean up super_groups and combine into regular ReleaseConditions component */} + {featureFlag.filters.super_groups && ( + + )} + +
+
+ +
+ +
+

Insights that use this feature flag

+ +
+
+
+ + {featureFlags[FEATURE_FLAGS.AUTO_ROLLBACK_FEATURE_FLAGS] && ( + + )} + + )}
- + ), @@ -363,42 +371,50 @@ export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element {
)} - - {({ value, onChange }) => ( -
- onChange(!value)} - fullWidth - checked={value} - /> -
- If your feature flag is applied before identifying the user, use this to - ensure that the flag value remains consistent for the same user. - Depending on your setup, this option might not always be suitable. This - feature requires creating profiles for anonymous users.{' '} - - Learn more - + {!featureFlag.is_remote_configuration && ( + + {({ value, onChange }) => ( +
+ onChange(!value)} + fullWidth + checked={value} + /> +
+ If your feature flag is applied before identifying the user, use + this to ensure that the flag value remains consistent for the same + user. Depending on your setup, this option might not always be + suitable. This feature requires creating profiles for anonymous + users.{' '} + + Learn more + +
-
- )} - + )} + + )}
- - + {!featureFlag.is_remote_configuration && ( + <> + + + + )} + {isNewFeatureFlag && ( @@ -728,6 +744,7 @@ function FeatureFlagRollout({ readOnly }: { readOnly?: boolean }): JSX.Element { featureFlag, recordingFilterForFlag, flagStatus, + flagType, } = useValues(featureFlagLogic) const { distributeVariantsEqually, @@ -736,6 +753,7 @@ function FeatureFlagRollout({ readOnly }: { readOnly?: boolean }): JSX.Element { setMultivariateEnabled, setFeatureFlag, saveFeatureFlag, + setRemoteConfigEnabled, } = useActions(featureFlagLogic) const filterGroups: FeatureFlagGroupType[] = featureFlag.filters.groups || [] @@ -830,7 +848,9 @@ function FeatureFlagRollout({ readOnly }: { readOnly?: boolean }): JSX.Element { } checked={featureFlag.active} /> - + {!featureFlag.is_remote_configuration && ( + + )} )} @@ -948,30 +968,51 @@ function FeatureFlagRollout({ readOnly }: { readOnly?: boolean }): JSX.Element { label: Multiple variants with rollout percentages (A/B/n test), value: 'multivariate', }, + { + label: Remote config (single payload), + value: 'remote_config', + disabledReason: + featureFlag.experiment_set && featureFlag.experiment_set?.length > 0 + ? 'This feature flag is associated with an experiment.' + : undefined, + }, ]} onChange={(value) => { - if (value === 'boolean' && nonEmptyVariants.length) { + if (['boolean', 'remote_config'].includes(value) && nonEmptyVariants.length) { confirmRevertMultivariateEnabled() } else { setMultivariateEnabled(value === 'multivariate') + setRemoteConfigEnabled(value === 'remote_config') focusVariantKeyField(0) } }} - value={multivariateEnabled ? 'multivariate' : 'boolean'} + value={flagType} />
- {capitalizeFirstLetter(aggregationTargetName)} will be served{' '} - {multivariateEnabled ? ( + {featureFlag.is_remote_configuration ? ( + + Remote config flags provide runtime configuration values in your app. Read more in the{' '} + + remote config flags documentation + + . + + ) : ( <> - a variant key according to the below distribution + {capitalizeFirstLetter(aggregationTargetName)} will be served{' '} + {multivariateEnabled ? ( + <> + a variant key according to the below distribution + + ) : ( + + true + + )}{' '} + if they match one or more release condition groups. - ) : ( - - true - - )}{' '} - if they match one or more release condition groups. + )}
)} @@ -988,10 +1029,16 @@ function FeatureFlagRollout({ readOnly }: { readOnly?: boolean }): JSX.Element { ) : (
- Specify a valid JSON payload to be returned when the served value is{' '} - - true - + {featureFlag.is_remote_configuration ? ( + <>Specify a valid JSON payload to be returned for the config flag + ) : ( + <> + Specify a valid JSON payload to be returned when the served value is{' '} + + true + + + )}
@@ -1001,10 +1048,20 @@ function FeatureFlagRollout({ readOnly }: { readOnly?: boolean }): JSX.Element { /> + {featureFlag.is_remote_configuration && ( +
+ Note: remote config flags must be accessed through payloads, e.g.{' '} + getFeatureFlagPayload. Using + standard SDK methods such as{' '} + getFeatureFlag or{' '} + isFeatureEnabled will always return{' '} + true +
+ )}
)}
- {readOnly && ( + {readOnly && !featureFlag.is_remote_configuration && (

Recordings

Watch recordings of people who have been exposed to the feature flag.

diff --git a/frontend/src/scenes/feature-flags/FeatureFlagCodeInstructions.stories.tsx b/frontend/src/scenes/feature-flags/FeatureFlagCodeInstructions.stories.tsx index 2878e8887e4c8..4d1f0ebec3c24 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlagCodeInstructions.stories.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlagCodeInstructions.stories.tsx @@ -32,6 +32,7 @@ const REGULAR_FEATURE_FLAG: FeatureFlagType = { user_access_level: 'editor', tags: [], surveys: [], + is_remote_configuration: false, } const GROUP_FEATURE_FLAG: FeatureFlagType = { diff --git a/frontend/src/scenes/feature-flags/FeatureFlagInstructions.tsx b/frontend/src/scenes/feature-flags/FeatureFlagInstructions.tsx index a235c1a775e07..9cfa828304b6d 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlagInstructions.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlagInstructions.tsx @@ -39,6 +39,7 @@ export interface CodeInstructionsProps { selectedLanguage?: string featureFlag?: FeatureFlagType dataAttr?: string + showFlagValue?: boolean showLocalEval?: boolean showBootstrap?: boolean showAdvancedOptions?: boolean @@ -58,10 +59,14 @@ export function CodeInstructions({ const [defaultSelectedOption] = options const [selectedOption, setSelectedOption] = useState(defaultSelectedOption) const [bootstrapOption, setBootstrapOption] = useState(BOOTSTRAPPING_OPTIONS[0]) - const [showPayloadCode, setShowPayloadCode] = useState(Object.keys(featureFlag?.filters.payloads || {}).length > 0) + const [showPayloadCode, setShowPayloadCode] = useState( + featureFlag?.is_remote_configuration || Object.keys(featureFlag?.filters.payloads || {}).length > 0 + ) const [showLocalEvalCode, setShowLocalEvalCode] = useState(showLocalEval) const [showBootstrapCode, setShowBootstrapCode] = useState(showBootstrap) + const showFlagValueCode = !featureFlag?.is_remote_configuration + const multivariantFlag = !!featureFlag?.filters.multivariate?.variants const featureFlagKey = featureFlag?.key || 'my-flag' @@ -124,8 +129,9 @@ export function CodeInstructions({ } if ( - Object.keys(featureFlag?.filters.payloads || {}).length > 0 && - Object.values(featureFlag?.filters.payloads || {}).some((value) => value) + featureFlag?.is_remote_configuration || + (Object.keys(featureFlag?.filters.payloads || {}).length > 0 && + Object.values(featureFlag?.filters.payloads || {}).some((value) => value)) ) { setShowPayloadCode(true) } else { @@ -270,15 +276,17 @@ export function CodeInstructions({

Local evaluation

)} - + {showFlagValueCode && ( + + )} {showPayloadCode && ( <>

Payload

diff --git a/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx b/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx index b929e2d203f19..0ea57da679cfa 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlags.stories.tsx @@ -82,6 +82,13 @@ export function EditMultiVariateFeatureFlag(): JSX.Element { return } +export function EditRemoteConfigFeatureFlag(): JSX.Element { + useEffect(() => { + router.actions.push(urls.featureFlag(1738)) + }, []) + return +} + export function FeatureFlagNotFound(): JSX.Element { useEffect(() => { router.actions.push(urls.featureFlag(1111111111111)) diff --git a/frontend/src/scenes/feature-flags/__mocks__/feature_flags.json b/frontend/src/scenes/feature-flags/__mocks__/feature_flags.json index 553b162b849d2..b736d1524b93a 100644 --- a/frontend/src/scenes/feature-flags/__mocks__/feature_flags.json +++ b/frontend/src/scenes/feature-flags/__mocks__/feature_flags.json @@ -131,6 +131,37 @@ "is_simple_flag": false, "rollout_percentage": null }, + { + "id": 1738, + "name": "", + "key": "mock-remote-config", + "filters": { + "groups": [ + { + "variant": null, + "properties": [], + "rollout_percentage": 100 + } + ], + "payloads": { + "true": "{\"foo\":\"bar\"}" + }, + "multivariate": null + }, + "deleted": false, + "active": true, + "created_by": { + "id": 6352, + "uuid": "cf25089d-71f1-4f70-9d0b-7577e47d78c1", + "distinct_id": "8ed346b4-01e9-44e2-b5a1-286cb131a400", + "first_name": "Cameron", + "email": "cameron@posthog.com" + }, + "created_at": "2022-02-08T20:54:04.344072Z", + "is_simple_flag": false, + "is_remote_configuration": true, + "rollout_percentage": null + }, { "id": 1498, "name": "Feature https://github.com/PostHog/posthog/issues/3636.", diff --git a/frontend/src/scenes/feature-flags/activityDescriptions.tsx b/frontend/src/scenes/feature-flags/activityDescriptions.tsx index a85f73cde21ad..aa3190e689fdb 100644 --- a/frontend/src/scenes/feature-flags/activityDescriptions.tsx +++ b/frontend/src/scenes/feature-flags/activityDescriptions.tsx @@ -253,6 +253,7 @@ const featureFlagActionsMapping: Record< has_enriched_analytics: () => null, surveys: () => null, user_access_level: () => null, + is_remote_configuration: () => null, } export function flagActivityDescriber(logItem: ActivityLogItem, asNotification?: boolean): HumanizedChange { diff --git a/frontend/src/scenes/feature-flags/featureFlagLogic.ts b/frontend/src/scenes/feature-flags/featureFlagLogic.ts index f2ec35e853b48..e8520fac59ab4 100644 --- a/frontend/src/scenes/feature-flags/featureFlagLogic.ts +++ b/frontend/src/scenes/feature-flags/featureFlagLogic.ts @@ -102,6 +102,7 @@ const NEW_FLAG: FeatureFlagType = { can_edit: true, user_access_level: 'editor', tags: [], + is_remote_configuration: false, } const NEW_VARIANT = { key: '', @@ -127,6 +128,14 @@ export function validateFeatureFlagKey(key: string): string | undefined { : undefined } +function validatePayloadRequired(payload: JsonType, is_remote_configuration: boolean): string | undefined { + if (!is_remote_configuration) { + return undefined + } + + return payload === undefined ? 'Payload is required for remote configuration flags.' : undefined +} + export interface FeatureFlagLogicProps { id: number | 'new' | 'link' } @@ -267,6 +276,7 @@ export const featureFlagLogic = kea([ removeRollbackCondition: (index: number) => ({ index }), deleteFeatureFlag: (featureFlag: Partial) => ({ featureFlag }), restoreFeatureFlag: (featureFlag: Partial) => ({ featureFlag }), + setRemoteConfigEnabled: (enabled: boolean) => ({ enabled }), setMultivariateEnabled: (enabled: boolean) => ({ enabled }), setMultivariateOptions: (multivariateOptions: MultivariateFlagOptions | null) => ({ multivariateOptions }), addVariant: true, @@ -295,7 +305,7 @@ export const featureFlagLogic = kea([ ...NEW_FLAG, ensure_experience_continuity: values.currentTeam?.flags_persistence_default || false, }, - errors: ({ key, filters }) => { + errors: ({ key, filters, is_remote_configuration }) => { return { key: validateFeatureFlagKey(key), filters: { @@ -310,6 +320,11 @@ export const featureFlagLogic = kea([ FeatureFlagGroupType, ValidationErrorType >[], + payloads: { + true: validatePayloadRequired(filters?.payloads['true'], is_remote_configuration), + } as unknown as DeepPartialMap, ValidationErrorType> | undefined, + // Forced cast necessary to prevent Kea's typechecking from raising "Type instantiation + // is excessively deep and possibly infinite" error }, } }, @@ -351,6 +366,16 @@ export const featureFlagLogic = kea([ } return { ...state, filters: { ...state.filters, multivariate: multivariateOptions } } }, + setRemoteConfigEnabled: (state, { enabled }) => { + if (!state) { + return state + } + + return { + ...state, + is_remote_configuration: enabled, + } + }, addVariant: (state) => { if (!state) { return state @@ -926,12 +951,38 @@ export const featureFlagLogic = kea([ actions.loadScheduledChanges() } }, + setRemoteConfigEnabled: ({ enabled }) => { + if (enabled) { + actions.setFeatureFlagFilters( + { + ...values.featureFlag.filters, + groups: [ + { + variant: null, + properties: [], + rollout_percentage: 100, + }, + ], + }, + {} + ) + } + }, })), selectors({ sentryErrorCount: [(s) => [s.sentryStats], (stats) => stats.total_count], sentryIntegrationEnabled: [(s) => [s.sentryStats], (stats) => !!stats.sentry_integration_enabled], props: [() => [(_, props) => props], (props) => props], multivariateEnabled: [(s) => [s.featureFlag], (featureFlag) => !!featureFlag?.filters.multivariate], + flagType: [ + (s) => [s.featureFlag], + (featureFlag) => + featureFlag?.is_remote_configuration + ? 'remote_config' + : featureFlag?.filters.multivariate + ? 'multivariate' + : 'boolean', + ], roleBasedAccessEnabled: [ (s) => [s.hasAvailableFeature], (hasAvailableFeature) => hasAvailableFeature(AvailableFeature.ROLE_BASED_ACCESS), diff --git a/frontend/src/scenes/funnels/FunnelBarHorizontal/FunnelBarHorizontal.scss b/frontend/src/scenes/funnels/FunnelBarHorizontal/FunnelBarHorizontal.scss index 0341f53d40da1..1bd8516d55d82 100644 --- a/frontend/src/scenes/funnels/FunnelBarHorizontal/FunnelBarHorizontal.scss +++ b/frontend/src/scenes/funnels/FunnelBarHorizontal/FunnelBarHorizontal.scss @@ -121,7 +121,7 @@ $glyph_height: 23px; // Based on .funnel-step-glyph .funnel-bar { position: relative; height: 100%; - background: var(--primary-3000); + background: var(--accent-primary); transition: width 0.2s ease, height 0.2s ease; &.first { diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx index 9784588429372..d5b56098b1a7f 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx @@ -24,7 +24,7 @@ export function TaxonomicBreakdownButton({ } + icon={} data-attr="add-breakdown-button" onClick={() => setOpen(!open)} sideIcon={null} diff --git a/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.scss b/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.scss index 67f91dd9086fd..b9c15bc469c20 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.scss +++ b/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.scss @@ -4,7 +4,7 @@ .edit-icon { font-size: 1rem; - color: var(--primary-3000); + color: var(--accent-primary); cursor: pointer; } @@ -14,7 +14,7 @@ .EntityFilterInfo:not(.text-muted) { font-weight: 500; - color: var(--primary-3000); + color: var(--accent-primary); } } } diff --git a/frontend/src/scenes/max/Thread.tsx b/frontend/src/scenes/max/Thread.tsx index f6bbcca35d851..997ae9678d33f 100644 --- a/frontend/src/scenes/max/Thread.tsx +++ b/frontend/src/scenes/max/Thread.tsx @@ -25,7 +25,6 @@ import { AssistantForm, AssistantMessage, FailureMessage, - HumanMessage, VisualizationMessage, } from '~/queries/schema/schema-assistant-messages' import { InsightVizNode, NodeKind } from '~/queries/schema/schema-general' @@ -58,7 +57,7 @@ interface MessageGroupProps { index: number } -function MessageGroup({ messages, isFinal: isFinalGroup, index: messageGroupIndex }: MessageGroupProps): JSX.Element { +function MessageGroup({ messages, isFinal: isFinalGroup }: MessageGroupProps): JSX.Element { const { user } = useValues(userLogic) const groupType = messages[0].type === 'human' ? 'human' : 'ai' @@ -102,7 +101,6 @@ function MessageGroup({ messages, isFinal: isFinalGroup, index: messageGroupInde message={message} interactable={messageIndex === messages.length - 1} isFinalGroup={isFinalGroup} - messageGroupIndex={messageGroupIndex} /> ) } else if (isVisualizationMessage(message)) { @@ -173,13 +171,12 @@ const MessageTemplate = React.forwardRef(f interface TextAnswerProps { message: (AssistantMessage | FailureMessage) & ThreadMessage - messageGroupIndex: number interactable?: boolean isFinalGroup?: boolean } const TextAnswer = React.forwardRef(function TextAnswer( - { message, messageGroupIndex, interactable, isFinalGroup }, + { message, interactable, isFinalGroup }, ref ) { const retriable = !!(interactable && isFinalGroup) @@ -205,7 +202,7 @@ const TextAnswer = React.forwardRef(function Te } // Show answer actions if the assistant's response is complete at this point - return + return } return null @@ -315,39 +312,23 @@ function RetriableFailureActions(): JSX.Element { ) } -function SuccessActions({ - messageGroupIndex, - retriable, -}: { - messageGroupIndex: number - retriable: boolean -}): JSX.Element { - const { threadGrouped } = useValues(maxLogic) +function SuccessActions({ retriable }: { retriable: boolean }): JSX.Element { + const { traceId } = useValues(maxLogic) const { retryLastMessage } = useActions(maxLogic) const [rating, setRating] = useState<'good' | 'bad' | null>(null) const [feedback, setFeedback] = useState('') const [feedbackInputStatus, setFeedbackInputStatus] = useState<'hidden' | 'pending' | 'submitted'>('hidden') - const [relevantHumanMessage, relevantVisualizationMessage] = useMemo(() => { - // We need to find the relevant visualization message (which might be a message earlier if the most recent one - // is a results summary message), and the human message that triggered it. - const visualizationMessage = threadGrouped[messageGroupIndex].find( - isVisualizationMessage - ) as VisualizationMessage - const humanMessage = threadGrouped[messageGroupIndex - 1][0] as HumanMessage - return [humanMessage, visualizationMessage] - }, [threadGrouped, messageGroupIndex]) - function submitRating(newRating: 'good' | 'bad'): void { if (rating) { return // Already rated } setRating(newRating) - posthog.capture('chat rating', { - question: relevantHumanMessage.content, - answer: JSON.stringify(relevantVisualizationMessage.answer), - answer_rating: rating, + posthog.capture('$ai_metric', { + $ai_metric_name: 'quality', + $ai_metric_value: newRating, + $ai_trace_id: traceId, }) if (newRating === 'bad') { setFeedbackInputStatus('pending') @@ -358,10 +339,9 @@ function SuccessActions({ if (!feedback) { return // Input is empty } - posthog.capture('chat feedback', { - question: relevantHumanMessage.content, - answer: JSON.stringify(relevantVisualizationMessage.answer), - feedback, + posthog.capture('$ai_feedback', { + $ai_feedback_text: feedback, + $ai_trace_id: traceId, }) setFeedbackInputStatus('submitted') } @@ -399,7 +379,7 @@ function SuccessActions({
{feedbackInputStatus !== 'hidden' && ( -
+

{feedbackInputStatus === 'pending' ? 'What disappointed you about the answer?' diff --git a/frontend/src/scenes/max/maxLogic.ts b/frontend/src/scenes/max/maxLogic.ts index d8b6873f1cce6..ac72c5f374814 100644 --- a/frontend/src/scenes/max/maxLogic.ts +++ b/frontend/src/scenes/max/maxLogic.ts @@ -59,6 +59,7 @@ export const maxLogic = kea([ retryLastMessage: true, scrollThreadToBottom: true, setConversation: (conversation: Conversation) => ({ conversation }), + setTraceId: (traceId: string) => ({ traceId }), }), reducers({ question: [ @@ -107,6 +108,7 @@ export const maxLogic = kea([ setVisibleSuggestions: (_, { suggestions }) => suggestions, }, ], + traceId: [null as string | null, { setTraceId: (_, { traceId }) => traceId }], }), loaders({ // TODO: Move question suggestions to `maxGlobalLogic`, which will make this logic `maxThreadLogic` @@ -165,9 +167,14 @@ export const maxLogic = kea([ askMax: async ({ prompt }) => { actions.addMessage({ type: AssistantMessageType.Human, content: prompt, status: 'completed' }) try { + // Generate a trace ID for the conversation run + const traceId = uuid() + actions.setTraceId(traceId) + const response = await api.conversations.create({ content: prompt, conversation: values.conversation?.id, + trace_id: traceId, }) const reader = response.body?.getReader() diff --git a/frontend/src/scenes/notebooks/AddToNotebook/DraggableToNotebook.scss b/frontend/src/scenes/notebooks/AddToNotebook/DraggableToNotebook.scss index 48e5accc597ac..d88baf490e18a 100644 --- a/frontend/src/scenes/notebooks/AddToNotebook/DraggableToNotebook.scss +++ b/frontend/src/scenes/notebooks/AddToNotebook/DraggableToNotebook.scss @@ -3,7 +3,7 @@ &--dragging { background-color: var(--bg-light); - outline: 1px solid var(--primary-3000); + outline: 1px solid var(--accent-primary); // Weird hack - this fixes chrome from not correctly identifying the bounds of the component for the drag preview // https://github.com/react-dnd/react-dnd/issues/832#issuecomment-442071628 diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodeMap.tsx b/frontend/src/scenes/notebooks/Nodes/NotebookNodeMap.tsx index f0e5ef1931c7d..3034396eca18f 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodeMap.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodeMap.tsx @@ -40,7 +40,7 @@ const Component = ({ attributes }: NotebookNodeProps) return ( ) diff --git a/frontend/src/scenes/notebooks/Notebook/Notebook.scss b/frontend/src/scenes/notebooks/Notebook/Notebook.scss index 27f28b77f1418..67d59286a9a3d 100644 --- a/frontend/src/scenes/notebooks/Notebook/Notebook.scss +++ b/frontend/src/scenes/notebooks/Notebook/Notebook.scss @@ -130,13 +130,13 @@ } &.Backlink--selected { - border-color: var(--primary-3000); + border-color: var(--accent-primary); } &.Backlink--active { color: var(--white); - background: var(--primary-3000); - border: 1px solid var(--primary-3000); + background: var(--accent-primary); + border: 1px solid var(--accent-primary); & .Backlink__label, & svg { @@ -292,7 +292,7 @@ inset: 0; z-index: -1; content: ''; - background: var(--primary-3000); + background: var(--accent-primary); opacity: var(--notebook-comment-background-opacity); } } diff --git a/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanel.scss b/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanel.scss index f108dd22ec473..9d084c3766814 100644 --- a/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanel.scss +++ b/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanel.scss @@ -41,7 +41,7 @@ &--active { height: 8rem; - border-color: var(--primary-3000); + border-color: var(--accent-primary); .NotebookPanelDropzone__message { opacity: 1; diff --git a/frontend/src/scenes/onboarding/Onboarding.tsx b/frontend/src/scenes/onboarding/Onboarding.tsx index bf9b3d01171da..e8a880aeed2b3 100644 --- a/frontend/src/scenes/onboarding/Onboarding.tsx +++ b/frontend/src/scenes/onboarding/Onboarding.tsx @@ -120,7 +120,9 @@ const ProductAnalyticsOnboarding = (): JSX.Element => { window.innerWidth > 1000 && combinedSnippetAndLiveEventsHosts.length > 0 - const showSessionReplayStep = useFeatureFlag('ONBOARDING_SESSION_REPLAY_SEPERATE_STEP', 'test') + const showSessionReplayStep = + useFeatureFlag('ONBOARDING_SESSION_REPLAY_SEPERATE_STEP', 'test') && + !selectedProducts.includes(ProductKey.SESSION_REPLAY) const options: ProductConfigOption[] = [ { diff --git a/frontend/src/scenes/persons/PersonDisplay.scss b/frontend/src/scenes/persons/PersonDisplay.scss index 35bd87c28bad9..f9adca124a3fd 100644 --- a/frontend/src/scenes/persons/PersonDisplay.scss +++ b/frontend/src/scenes/persons/PersonDisplay.scss @@ -4,6 +4,10 @@ .ProfilePicture { margin-right: 0.5rem; transition: opacity 200ms ease; + + &.sm { + margin-right: 0.25rem; + } } a:hover { diff --git a/frontend/src/scenes/pipeline/utils.tsx b/frontend/src/scenes/pipeline/utils.tsx index 04737c1cd514c..a2463cfccc94d 100644 --- a/frontend/src/scenes/pipeline/utils.tsx +++ b/frontend/src/scenes/pipeline/utils.tsx @@ -201,7 +201,7 @@ export function LogLevelDisplay(level: LogEntryLevel): JSX.Element { color = 'text-text-3000' break case 'INFO': - color = 'text-primary' + color = 'text-accent-primary' break case 'WARNING': case 'WARN': diff --git a/frontend/src/scenes/project-homepage/ProjectHomepage.scss b/frontend/src/scenes/project-homepage/ProjectHomepage.scss index 1939a08c17fde..54ebd5d9eb956 100644 --- a/frontend/src/scenes/project-homepage/ProjectHomepage.scss +++ b/frontend/src/scenes/project-homepage/ProjectHomepage.scss @@ -16,7 +16,7 @@ color: var(--text-3000); &:hover { - color: var(--primary-3000); + color: var(--accent-primary); } } } diff --git a/frontend/src/scenes/saved-insights/SavedInsights.scss b/frontend/src/scenes/saved-insights/SavedInsights.scss index bc9f2a281daaf..6c8dcebef7afd 100644 --- a/frontend/src/scenes/saved-insights/SavedInsights.scss +++ b/frontend/src/scenes/saved-insights/SavedInsights.scss @@ -6,7 +6,7 @@ padding: 8px 12px 8px 16px; color: white; cursor: pointer; - background-color: var(--primary-3000); + background-color: var(--accent-primary); border: 1px solid var(--border); border-radius: var(--radius); } diff --git a/frontend/src/scenes/session-recordings/apm/playerInspector/ItemPerformanceEvent.tsx b/frontend/src/scenes/session-recordings/apm/playerInspector/ItemPerformanceEvent.tsx index dc118953ea57d..aa714867b00f0 100644 --- a/frontend/src/scenes/session-recordings/apm/playerInspector/ItemPerformanceEvent.tsx +++ b/frontend/src/scenes/session-recordings/apm/playerInspector/ItemPerformanceEvent.tsx @@ -187,7 +187,7 @@ export function ItemPerformanceEvent({ item, finalTimestamp }: ItemPerformanceEv
{ + setSidebarOpen(true) + setTab(SessionRecordingSidebarTab.INSPECTOR) + } + + return ( + } + onClick={handleClick} + > + Activity + + ) +} diff --git a/frontend/src/scenes/session-recordings/player/PlayerMeta.scss b/frontend/src/scenes/session-recordings/player/PlayerMeta.scss index 2f71eab4c8d83..2e8c259c02d4b 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerMeta.scss +++ b/frontend/src/scenes/session-recordings/player/PlayerMeta.scss @@ -82,7 +82,7 @@ color: var(--text-3000); &:hover { - color: var(--primary-3000); + color: var(--accent-primary); } } } diff --git a/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx b/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx index 6e4e69a038f79..cbaeb4748399a 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx +++ b/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx @@ -7,7 +7,7 @@ import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { isObject, percentage } from 'lib/utils' +import { isObject } from 'lib/utils' import { DraggableToNotebook } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' import { IconWindow } from 'scenes/session-recordings/player/icons' import { PlayerMetaLinks } from 'scenes/session-recordings/player/PlayerMetaLinks' @@ -17,6 +17,8 @@ import { urls } from 'scenes/urls' import { getCurrentExporterData } from '~/exporter/exporterViewLogic' import { Logo } from '~/toolbar/assets/Logo' +import { PlayerBottomSettings } from './controller/PlayerController' +import { PlayerPersonMeta } from './PlayerPersonMeta' import { sessionRecordingPlayerLogic, SessionRecordingPlayerMode } from './sessionRecordingPlayerLogic' function URLOrScreen({ lastUrl }: { lastUrl: string | undefined }): JSX.Element | null { @@ -65,19 +67,38 @@ function URLOrScreen({ lastUrl }: { lastUrl: string | undefined }): JSX.Element ) } +export function ResolutionView(): JSX.Element { + const { logicProps } = useValues(sessionRecordingPlayerLogic) + + const { resolutionDisplay, scaleDisplay, loading } = useValues(playerMetaLogic(logicProps)) + + return loading ? ( + + ) : ( + + The resolution of the page as it was captured was {resolutionDisplay} +
+ You are viewing the replay at {scaleDisplay} of the original size + + } + > + + {resolutionDisplay} + ({scaleDisplay}) + +
+ ) +} + export function PlayerMeta({ iconsOnly }: { iconsOnly: boolean }): JSX.Element { const { logicProps, isFullScreen } = useValues(sessionRecordingPlayerLogic) - const { - windowIds, - trackedWindow, - resolution, - lastPageviewEvent, - lastUrl, - scale, - currentWindowIndex, - sessionPlayerMetaDataLoading, - } = useValues(playerMetaLogic(logicProps)) + const { windowIds, trackedWindow, lastPageviewEvent, lastUrl, currentWindowIndex, loading } = useValues( + playerMetaLogic(logicProps) + ) const { setTrackedWindow } = useActions(playerMetaLogic(logicProps)) @@ -91,32 +112,6 @@ export function PlayerMeta({ iconsOnly }: { iconsOnly: boolean }): JSX.Element { const mode = logicProps.mode ?? SessionRecordingPlayerMode.Standard const whitelabel = getCurrentExporterData()?.whitelabel ?? false - const resolutionView = sessionPlayerMetaDataLoading ? ( - - ) : resolution ? ( - - The resolution of the page as it was captured was{' '} - - {resolution.width} x {resolution.height} - -
- You are viewing the replay at {percentage(scale, 1, true)} of the original size - - } - > - - {resolution && ( - <> - {resolution.width} x {resolution.height} {!isSmallPlayer && `(${percentage(scale, 1, true)})`} - - )} - -
- ) : null - if (mode === SessionRecordingPlayerMode.Sharing) { if (whitelabel) { return <> @@ -131,7 +126,7 @@ export function PlayerMeta({ iconsOnly }: { iconsOnly: boolean }): JSX.Element { ) : null} - {resolutionView} +
) @@ -164,8 +159,8 @@ export function PlayerMeta({ iconsOnly }: { iconsOnly: boolean }): JSX.Element { 'PlayerMeta--fullscreen': isFullScreen, })} > -
- {sessionPlayerMetaDataLoading ? ( +
+ {loading ? ( ) : ( <> @@ -190,8 +185,12 @@ export function PlayerMeta({ iconsOnly }: { iconsOnly: boolean }): JSX.Element { )}
- {resolutionView} + +
+ +
+
) diff --git a/frontend/src/scenes/session-recordings/player/PlayerPersonMeta.tsx b/frontend/src/scenes/session-recordings/player/PlayerPersonMeta.tsx index 658060b5c401b..15e359885697a 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerPersonMeta.tsx +++ b/frontend/src/scenes/session-recordings/player/PlayerPersonMeta.tsx @@ -24,7 +24,7 @@ export function PlayerPersonMeta(): JSX.Element { } return ( -
+
{!sessionPerson ? ( ) : ( diff --git a/frontend/src/scenes/session-recordings/player/PlayerSidebar.tsx b/frontend/src/scenes/session-recordings/player/PlayerSidebar.tsx index 315330a437704..bd4deaef83bdd 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerSidebar.tsx +++ b/frontend/src/scenes/session-recordings/player/PlayerSidebar.tsx @@ -11,8 +11,6 @@ import { useRef } from 'react' import { SessionRecordingSidebarStacking, SessionRecordingSidebarTab } from '~/types' -import { TabToIcon } from './inspector/PlayerInspectorControls' -import { PlayerPersonMeta } from './PlayerPersonMeta' import { playerSettingsLogic } from './playerSettingsLogic' import { playerSidebarLogic } from './sidebar/playerSidebarLogic' import { PlayerSidebarTab } from './sidebar/PlayerSidebarTab' @@ -67,7 +65,7 @@ export function PlayerSidebar(): JSX.Element { containerRef={ref} closeThreshold={100} /> - {sidebarOpen ? ( + {sidebarOpen && ( <>
@@ -105,23 +103,6 @@ export function PlayerSidebar(): JSX.Element {
- ) : ( -
- - {Object.values(TabToIcon).map((Icon, idx) => { - return Icon ? ( - } - onClick={() => { - setSidebarOpen(true) - setTab(SessionRecordingSidebarTab.INSPECTOR) - }} - /> - ) : null - })} -
)}
) diff --git a/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx b/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx index d70a3267a2532..0d0915ccafaa7 100644 --- a/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx +++ b/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx @@ -70,6 +70,7 @@ export function SessionRecordingPlayer(props: SessionRecordingPlayerProps): JSX. matchingEventsMatchType, sessionRecordingData, autoPlay, + noInspector, playlistLogic, mode, playerRef, diff --git a/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx b/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx index a3076d9663a99..3a739f6991fc7 100644 --- a/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx +++ b/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx @@ -33,6 +33,7 @@ import { TimestampFormatToLabel } from 'scenes/session-recordings/utils' import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' import { SessionPlayerState } from '~/types' +import { PlayerInspector } from '../PlayerInspector' import { SeekSkip, Timestamp } from './PlayerControllerTime' import { Seekbar } from './Seekbar' @@ -162,7 +163,7 @@ function InspectDOM(): JSX.Element { return ( openExplorer()} @@ -174,16 +175,25 @@ function InspectDOM(): JSX.Element { ) } -function PlayerBottomSettings(): JSX.Element { +export function PlayerBottomSettings(): JSX.Element { + const { + logicProps: { noInspector }, + } = useValues(sessionRecordingPlayerLogic) + return ( - -
- - - - + +
+
+ + + + +
+
+ {noInspector ? null : } + +
-
) } @@ -261,8 +271,6 @@ export function PlayerController(): JSX.Element {
- -
) } diff --git a/frontend/src/scenes/session-recordings/player/controller/Seekbar.scss b/frontend/src/scenes/session-recordings/player/controller/Seekbar.scss index fc4b0e5e20690..0a1676157f9ea 100644 --- a/frontend/src/scenes/session-recordings/player/controller/Seekbar.scss +++ b/frontend/src/scenes/session-recordings/player/controller/Seekbar.scss @@ -47,7 +47,7 @@ .PlayerSeekbar__currentbar { z-index: 3; - background-color: var(--primary-3000); + background-color: var(--accent-primary); border-radius: var(--bar-height) 0 0 var(--bar-height); } @@ -76,7 +76,7 @@ width: var(--thumb-size); height: var(--thumb-size); margin-top: calc(var(--thumb-size) / 2 * -1); - background-color: var(--primary-3000); + background-color: var(--accent-primary); border: 2px solid var(--bg-light); border-radius: 50%; transition: top 150ms ease-in-out; @@ -136,7 +136,7 @@ } &--primary { - --tick-color: var(--primary-3000); + --tick-color: var(--accent-primary); } .PlayerSeekbarTick__line { diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx index 936034bd35dec..a2f6303ba0761 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx @@ -29,13 +29,6 @@ import { FilterableInspectorListItemTypes } from '~/types' import { sessionRecordingPlayerLogic, SessionRecordingPlayerMode } from '../sessionRecordingPlayerLogic' import { InspectorSearchInfo } from './components/InspectorSearchInfo' -export const TabToIcon = { - [FilterableInspectorListItemTypes.EVENTS]: IconUnverifiedEvent, - [FilterableInspectorListItemTypes.CONSOLE]: IconTerminal, - [FilterableInspectorListItemTypes.NETWORK]: IconDashboard, - [FilterableInspectorListItemTypes.DOCTOR]: IconStethoscope, -} - function sideActionForType({ miniFilters, setMiniFilter, diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorList.scss b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorList.scss index c94526e6fab4d..eea4a688ac8fc 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorList.scss +++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorList.scss @@ -7,7 +7,7 @@ height: 0.5rem; margin-top: 0.25rem; pointer-events: none; - background-color: var(--primary-3000); + background-color: var(--accent-primary); border-radius: 0 var(--radius) var(--radius) 0; transition: transform 200ms linear; will-change: transform; diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/PlayerInspectorListItem.tsx b/frontend/src/scenes/session-recordings/player/inspector/components/PlayerInspectorListItem.tsx index 1cd6a13c357ba..88317888d9e8f 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/components/PlayerInspectorListItem.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/components/PlayerInspectorListItem.tsx @@ -275,7 +275,7 @@ export function PlayerInspectorListItem({ ref={ref} className={clsx( 'ml-1 flex flex-col items-center', - isExpanded && 'border border-primary', + isExpanded && 'border border-accent-primary', isExpanded && item.highlightColor && `border border-${item.highlightColor}-dark`, isHovering && 'bg-bg-light' )} diff --git a/frontend/src/scenes/session-recordings/player/playerMetaLogic.tsx b/frontend/src/scenes/session-recordings/player/playerMetaLogic.tsx index ab22e3451ff3b..0e1cc456cc157 100644 --- a/frontend/src/scenes/session-recordings/player/playerMetaLogic.tsx +++ b/frontend/src/scenes/session-recordings/player/playerMetaLogic.tsx @@ -6,7 +6,7 @@ import api from 'lib/api' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { lemonToast } from 'lib/lemon-ui/LemonToast' import { getCoreFilterDefinition } from 'lib/taxonomy' -import { ceilMsToClosestSecond, findLastIndex, humanFriendlyDuration, objectsEqual } from 'lib/utils' +import { ceilMsToClosestSecond, findLastIndex, humanFriendlyDuration, objectsEqual, percentage } from 'lib/utils' import posthog from 'posthog-js' import { countryCodeToName } from 'scenes/insights/views/WorldMap' import { OverviewItem } from 'scenes/session-recordings/components/OverviewGrid' @@ -62,6 +62,7 @@ export const playerMetaLogic = kea([ 'sessionEventsData', 'sessionPlayerMetaData', 'sessionPlayerMetaDataLoading', + 'snapshotsLoading', 'windowIds', 'trackedWindow', ], @@ -105,9 +106,9 @@ export const playerMetaLogic = kea([ })), selectors(() => ({ loading: [ - (s) => [s.sessionPlayerMetaDataLoading, s.recordingPropertiesLoading], - (sessionPlayerMetaDataLoading, recordingPropertiesLoading) => - sessionPlayerMetaDataLoading || recordingPropertiesLoading, + (s) => [s.sessionPlayerMetaDataLoading, s.snapshotsLoading, s.recordingPropertiesLoading], + (sessionPlayerMetaDataLoading, snapshotsLoading, recordingPropertiesLoading) => + sessionPlayerMetaDataLoading || snapshotsLoading || recordingPropertiesLoading, ], sessionPerson: [ (s) => [s.sessionPlayerData], @@ -146,6 +147,18 @@ export const playerMetaLogic = kea([ }, }, ], + resolutionDisplay: [ + (s) => [s.resolution], + (resolution) => { + return `${resolution?.width || '--'} x ${resolution?.height || '--'}` + }, + ], + scaleDisplay: [ + (s) => [s.scale], + (scale) => { + return `${percentage(scale, 1, true)}` + }, + ], startTime: [ (s) => [s.sessionPlayerData], (sessionPlayerData) => { diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts index 5e7e7955f4602..df8bd1914f0c6 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts @@ -86,6 +86,7 @@ export interface SessionRecordingPlayerLogicProps extends SessionRecordingDataLo matchingEventsMatchType?: MatchingEventsMatchType playlistLogic?: BuiltLogic autoPlay?: boolean + noInspector?: boolean mode?: SessionRecordingPlayerMode playerRef?: RefObject pinned?: boolean diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts index a909b3ab61600..772ac08149e7a 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts @@ -16,6 +16,7 @@ import { FEATURE_FLAGS } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { objectClean, objectsEqual } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { getCurrentTeamId } from 'lib/utils/getAppContext' import { NodeKind, RecordingOrder, RecordingsQuery, RecordingsQueryResponse } from '~/queries/schema/schema-general' import { @@ -401,7 +402,7 @@ export const sessionRecordingsPlaylistLogic = kea ({ + reducers(({ props, key }) => ({ unusableEventsInFilter: [ [] as string[], { @@ -414,6 +415,7 @@ export const sessionRecordingsPlaylistLogic = kea { return { diff --git a/frontend/src/scenes/session-recordings/templates/SessionRecordingTemplates.tsx b/frontend/src/scenes/session-recordings/templates/SessionRecordingTemplates.tsx index 588c826bae012..9e69efc1a0224 100644 --- a/frontend/src/scenes/session-recordings/templates/SessionRecordingTemplates.tsx +++ b/frontend/src/scenes/session-recordings/templates/SessionRecordingTemplates.tsx @@ -176,7 +176,7 @@ const RecordingTemplateCard = (props: RecordingTemplateCardProps): JSX.Element =
)}

- showVariables()} className="text-primary"> + showVariables()} className="text-accent-primary"> {props.template.name}

diff --git a/frontend/src/scenes/settings/environment/AutocaptureSettings.tsx b/frontend/src/scenes/settings/environment/AutocaptureSettings.tsx index df1a27da9a580..b02aa1a9c582c 100644 --- a/frontend/src/scenes/settings/environment/AutocaptureSettings.tsx +++ b/frontend/src/scenes/settings/environment/AutocaptureSettings.tsx @@ -1,14 +1,10 @@ -import { LemonDivider, LemonSwitch, LemonTag, LemonTextArea, Link } from '@posthog/lemon-ui' -import clsx from 'clsx' +import { LemonDivider, LemonSwitch, LemonTag, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { SupportedWebVitalsMetrics } from 'posthog-js' import { teamLogic } from 'scenes/teamLogic' import { userLogic } from 'scenes/userLogic' -import { autocaptureExceptionsLogic } from './autocaptureExceptionsLogic' - function WebVitalsAllowedMetricSwitch({ metric }: { metric: SupportedWebVitalsMetrics }): JSX.Element { const { userLoading } = useValues(userLogic) const { currentTeam } = useValues(teamLogic) @@ -104,11 +100,19 @@ export function ExceptionAutocaptureSettings(): JSX.Element { const { updateCurrentTeam } = useActions(teamLogic) const { reportAutocaptureExceptionsToggled } = useActions(eventUsageLogic) - const { errorsToIgnoreRules, rulesCharacters } = useValues(autocaptureExceptionsLogic) - const { setErrorsToIgnoreRules } = useActions(autocaptureExceptionsLogic) - return ( <> +

+ Captures frontend exceptions thrown on a customers using `onError` and `onUnhandledRejection` listeners + in our web JavaScript SDK. +

+

+ Autocapture is also available for our{' '} + + Python SDK + + , where it can be configured directly in code. +

{ @@ -126,27 +130,6 @@ export function ExceptionAutocaptureSettings(): JSX.Element { } bordered /> -

Ignore errors

-

- If you're experiencing a high volume of unhelpful errors, add regular expressions here to ignore them. - This will ignore all errors that match, including those that are not autocaptured. -

-

- You can enter a regular expression that matches values of {' '} - here to ignore them. One per line. For example, if you want to drop all errors that contain the word - "bot", you can enter "bot" here. Or if you want to drop all errors that are exactly "bot", you can enter - "^bot$". -

-

Only up to 300 characters of config are allowed here.

- -
300 ? 'text-danger' : 'text-muted')}> - {rulesCharacters} / 300 characters -
) } diff --git a/frontend/src/scenes/settings/environment/DataColorThemes.tsx b/frontend/src/scenes/settings/environment/DataColorThemes.tsx index d73ed68295f63..c059cfc088131 100644 --- a/frontend/src/scenes/settings/environment/DataColorThemes.tsx +++ b/frontend/src/scenes/settings/environment/DataColorThemes.tsx @@ -1,9 +1,12 @@ import { IconBadge } from '@posthog/icons' import { LemonButton, LemonDialog, LemonLabel, LemonSelect, LemonTable } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' import { LemonTableLink } from 'lib/lemon-ui/LemonTable/LemonTableLink' import { teamLogic } from 'scenes/teamLogic' +import { AvailableFeature } from '~/types' + import { DataColorThemeModal } from './DataColorThemeModal' import { dataColorThemesLogic } from './dataColorThemesLogic' @@ -17,53 +20,55 @@ export function DataColorThemes(): JSX.Element { const themes = _themes || [] return ( -
- ( - selectTheme(theme.id)} title={name as string} /> - ), - }, - { - title: 'Official', - dataIndex: 'is_global', - key: 'is_global', - render: (is_global) => (is_global ? : null), - }, - ]} - /> - selectTheme('new')}> - Add theme - - - Default theme - { - const theme = themes.find((theme) => theme.id === value) - LemonDialog.open({ - title: `Change the default data theme to "${theme!.name}"?`, - description: 'This changes the default colors used when visualizing data in insights.', - primaryButton: { - children: 'Change default theme', - onClick: () => updateCurrentTeam({ default_data_theme: value! }), + +
+ ( + selectTheme(theme.id)} title={name as string} /> + ), }, - secondaryButton: { - children: 'Cancel', + { + title: 'Official', + dataIndex: 'is_global', + key: 'is_global', + render: (is_global) => (is_global ? : null), }, - }) - }} - loading={themesLoading || currentTeamLoading} - options={themes.map((theme) => ({ value: theme.id, label: theme.name }))} - /> + ]} + /> + selectTheme('new')}> + Add theme + + + Default theme + { + const theme = themes.find((theme) => theme.id === value) + LemonDialog.open({ + title: `Change the default data theme to "${theme!.name}"?`, + description: 'This changes the default colors used when visualizing data in insights.', + primaryButton: { + children: 'Change default theme', + onClick: () => updateCurrentTeam({ default_data_theme: value! }), + }, + secondaryButton: { + children: 'Cancel', + }, + }) + }} + loading={themesLoading || currentTeamLoading} + options={themes.map((theme) => ({ value: theme.id, label: theme.name }))} + /> - -
+ +
+ ) } diff --git a/frontend/src/scenes/settings/environment/autocaptureExceptionsLogic.ts b/frontend/src/scenes/settings/environment/autocaptureExceptionsLogic.ts deleted file mode 100644 index b9be34b208c50..0000000000000 --- a/frontend/src/scenes/settings/environment/autocaptureExceptionsLogic.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' -import { teamLogic } from 'scenes/teamLogic' - -import type { autocaptureExceptionsLogicType } from './autocaptureExceptionsLogicType' - -export const autocaptureExceptionsLogic = kea([ - path(['scenes', 'project', 'Settings', 'autocaptureExceptionsLogic']), - connect(() => ({ - values: [teamLogic, ['currentTeam']], - actions: [teamLogic, ['updateCurrentTeam']], - })), - actions({ - setErrorsToIgnoreRules: (newRules: string) => ({ newRules }), - }), - reducers({ - errorsToIgnoreRules: [ - '', - { - setErrorsToIgnoreRules: (_, { newRules }) => newRules, - }, - ], - }), - selectors({ - currentTeamErrorsToIgnoreRules: [ - (s) => [s.currentTeam], - (currentTeam) => (currentTeam?.autocapture_exceptions_errors_to_ignore || []).join('\n'), - ], - rulesCharacters: [(s) => [s.errorsToIgnoreRules], (errorsToIgnoreRules) => errorsToIgnoreRules.length], - }), - listeners(({ actions, values }) => ({ - setErrorsToIgnoreRules: async ({ newRules }, breakpoint) => { - if (values.currentTeamErrorsToIgnoreRules === newRules.trim()) { - return - } - - await breakpoint(300) - - const updateRules = newRules - .trim() - .split('\n') - .map((rule) => rule.trim()) - .filter((rule) => !!rule) - actions.updateCurrentTeam({ - autocapture_exceptions_errors_to_ignore: updateRules, - }) - }, - })), - afterMount(({ actions, values }) => { - actions.setErrorsToIgnoreRules(values.currentTeamErrorsToIgnoreRules) - }), -]) diff --git a/frontend/src/scenes/surveys/SurveyAppearanceUtils.tsx b/frontend/src/scenes/surveys/SurveyAppearanceUtils.tsx index 5d28310cc15b4..d61bbcffa29df 100644 --- a/frontend/src/scenes/surveys/SurveyAppearanceUtils.tsx +++ b/frontend/src/scenes/surveys/SurveyAppearanceUtils.tsx @@ -28,7 +28,7 @@ export function PresentationTypeCard({

{title}

diff --git a/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx b/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx index b52d4ec859d7c..56999193a98bd 100644 --- a/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx +++ b/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx @@ -374,7 +374,7 @@ export function ActorRow({ actor, onOpenRecording, propertiesTimelineFilter }: A {actor.distinct_ids?.[0] && ( diff --git a/frontend/src/scenes/web-analytics/WebAnalyticsDashboard.stories.tsx b/frontend/src/scenes/web-analytics/WebAnalyticsDashboard.stories.tsx new file mode 100644 index 0000000000000..58b309a6ed033 --- /dev/null +++ b/frontend/src/scenes/web-analytics/WebAnalyticsDashboard.stories.tsx @@ -0,0 +1,89 @@ +import { Meta } from '@storybook/react' +import { useActions } from 'kea' +import { router } from 'kea-router' +import { FEATURE_FLAGS } from 'lib/constants' +import { useEffect } from 'react' +import { App } from 'scenes/App' +import { urls } from 'scenes/urls' + +import { mswDecorator } from '~/mocks/browser' + +import uniqueVisitorsMock from './__mocks__/UniqueVisitors.json' +import webOverviewMock from './__mocks__/WebOverview.json' +import browserMock from './tiles/__mocks__/Browser.json' +import pathMock from './tiles/__mocks__/Path.json' +import referringDomainMock from './tiles/__mocks__/ReferringDomain.json' +import retentionMock from './tiles/__mocks__/Retention.json' +import { DeviceTab, SourceTab, webAnalyticsLogic } from './webAnalyticsLogic' + +const meta: Meta = { + title: 'Scenes-App/Web Analytics', + parameters: { + layout: 'fullscreen', + viewMode: 'story', + mockDate: '2023-02-01', + featureFlags: [ + FEATURE_FLAGS.WEB_VITALS, + FEATURE_FLAGS.WEB_ANALYTICS_CONVERSION_GOAL_FILTERS, + FEATURE_FLAGS.WEB_ANALYTICS_PERIOD_COMPARISON, + ], + testOptions: { + includeNavigationInSnapshot: true, + waitForLoadersToDisappear: true, + waitForSelector: '[data-attr=trend-line-graph] > canvas', + }, + }, + decorators: [ + mswDecorator({ + get: { + // Live count of users on product + '/stats': () => [200, { users_on_product: 2387 }], + + // Avoid displaying error of missing $pageview/$pageleave/$web_vitals events + '/api/projects/:team_id/event_definitions': () => [200, { count: 5 }], + }, + post: { + '/api/environments/:team_id/query': (req) => { + const query = (req.body as any).query + const queryKind = query.kind + + if (queryKind === 'DatabaseSchemaQuery') { + return [200, { tables: {} }] // Empty schema, we don't care about this here + } else if (queryKind === 'WebOverviewQuery') { + return [200, webOverviewMock] + } else if (queryKind === 'TrendsQuery') { + return [200, uniqueVisitorsMock] + } else if (queryKind === 'WebStatsTableQuery') { + if (query.breakdownBy === 'Page') { + return [200, pathMock] + } else if (query.breakdownBy === 'InitialReferringDomain') { + return [200, referringDomainMock] + } else if (query.breakdownBy === 'Browser') { + return [200, browserMock] + } + } else if (queryKind === 'RetentionQuery') { + return [200, retentionMock] + } + }, + }, + }), + ], +} +export default meta + +export function WebAnalyticsDashboard(): JSX.Element { + const { setSourceTab, setDeviceTab } = useActions(webAnalyticsLogic) + + useEffect(() => { + // Open the web analytics dashboard page + router.actions.push(urls.webAnalytics()) + + // Set the source tab to referring domain + setSourceTab(SourceTab.REFERRING_DOMAIN) + + // Set the device tab to browsers + setDeviceTab(DeviceTab.BROWSER) + }, []) + + return +} diff --git a/frontend/src/scenes/web-analytics/WebAnalyticsLiveUserCount.tsx b/frontend/src/scenes/web-analytics/WebAnalyticsLiveUserCount.tsx index 18ce5abaed98b..b35eb5f9e8bf0 100644 --- a/frontend/src/scenes/web-analytics/WebAnalyticsLiveUserCount.tsx +++ b/frontend/src/scenes/web-analytics/WebAnalyticsLiveUserCount.tsx @@ -29,7 +29,7 @@ export const WebAnalyticsLiveUserCount = (): JSX.Element | null => { return (
- + {humanFriendlyLargeNumber(liveUserCount)} currently online diff --git a/frontend/src/scenes/web-analytics/__mocks__/UniqueVisitors.json b/frontend/src/scenes/web-analytics/__mocks__/UniqueVisitors.json new file mode 100644 index 0000000000000..d2add98ba0a0d --- /dev/null +++ b/frontend/src/scenes/web-analytics/__mocks__/UniqueVisitors.json @@ -0,0 +1,213 @@ +{ + "cache_key": "cache_b80cb541a5183eaf621d6ea4a1ddaf07", + "cache_target_age": "2025-01-21T01:17:20.311895Z", + "calculation_trigger": null, + "error": "", + "hasMore": false, + "hogql": "SELECT\n arrayMap(number -> plus(toStartOfDay(assumeNotNull(toDateTime('2025-01-06 00:00:00'))), toIntervalDay(number)), range(0, plus(coalesce(dateDiff('day', toStartOfDay(assumeNotNull(toDateTime('2025-01-06 00:00:00'))), toStartOfDay(assumeNotNull(toDateTime('2025-01-20 23:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n count(DISTINCT e.person_id) AS total,\n toStartOfDay(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, toStartOfDay(assumeNotNull(toDateTime('2025-01-06 00:00:00')))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59'))), equals(event, '$pageview'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000\nUNION ALL\nSELECT\n arrayMap(number -> plus(toStartOfDay(assumeNotNull(toDateTime('2024-12-23 00:00:00'))), toIntervalDay(number)), range(0, plus(coalesce(dateDiff('day', toStartOfDay(assumeNotNull(toDateTime('2024-12-23 00:00:00'))), toStartOfDay(assumeNotNull(toDateTime('2025-01-06 23:59:59'))))), 1))) AS date,\n arrayMap(_match_date -> arraySum(arraySlice(groupArray(ifNull(count, 0)), indexOf(groupArray(day_start) AS _days_for_count, _match_date) AS _index, plus(minus(arrayLastIndex(x -> equals(x, _match_date), _days_for_count), _index), 1))), date) AS total\nFROM\n (SELECT\n sum(total) AS count,\n day_start\n FROM\n (SELECT\n count(DISTINCT e.person_id) AS total,\n toStartOfDay(timestamp) AS day_start\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, toStartOfDay(assumeNotNull(toDateTime('2024-12-23 00:00:00')))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-06 23:59:59'))), equals(event, '$pageview'))\n GROUP BY\n day_start)\n GROUP BY\n day_start\n ORDER BY\n day_start ASC)\nORDER BY\n arraySum(total) DESC\nLIMIT 50000", + "is_cached": false, + "last_refresh": "2025-01-20T23:17:20.311895Z", + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "uniq_page_screen_autocaptures", + "customChannelTypeRules": [], + "dataWarehouseEventsModifiers": [], + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_on_events", + "propertyGroupsMode": "optimized", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T23:32:20.311895Z", + "query_status": null, + "results": [ + { + "data": [ + 22938, 24418, 23817, 23910, 20335, 9312, 17801, 25286, 25401, 25335, 25473, 21397, 9582, 18421, 20439 + ], + "labels": [ + "6-Jan-2025", + "7-Jan-2025", + "8-Jan-2025", + "9-Jan-2025", + "10-Jan-2025", + "11-Jan-2025", + "12-Jan-2025", + "13-Jan-2025", + "14-Jan-2025", + "15-Jan-2025", + "16-Jan-2025", + "17-Jan-2025", + "18-Jan-2025", + "19-Jan-2025", + "20-Jan-2025" + ], + "days": [ + "2025-01-06", + "2025-01-07", + "2025-01-08", + "2025-01-09", + "2025-01-10", + "2025-01-11", + "2025-01-12", + "2025-01-13", + "2025-01-14", + "2025-01-15", + "2025-01-16", + "2025-01-17", + "2025-01-18", + "2025-01-19", + "2025-01-20" + ], + "count": 313865, + "label": "$pageview", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": "Unique visitors", + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "compare": true, + "compare_label": "current" + }, + { + "data": [ + 16719, 11526, 8331, 13021, 12876, 7616, 14604, 14756, 9937, 9875, 18814, 16182, 8505, 17010, 22936 + ], + "labels": [ + "23-Dec-2024", + "24-Dec-2024", + "25-Dec-2024", + "26-Dec-2024", + "27-Dec-2024", + "28-Dec-2024", + "29-Dec-2024", + "30-Dec-2024", + "31-Dec-2024", + "1-Jan-2025", + "2-Jan-2025", + "3-Jan-2025", + "4-Jan-2025", + "5-Jan-2025", + "6-Jan-2025" + ], + "days": [ + "2024-12-23", + "2024-12-24", + "2024-12-25", + "2024-12-26", + "2024-12-27", + "2024-12-28", + "2024-12-29", + "2024-12-30", + "2024-12-31", + "2025-01-01", + "2025-01-02", + "2025-01-03", + "2025-01-04", + "2025-01-05", + "2025-01-06" + ], + "count": 202708, + "label": "$pageview", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "ActionsLineGraph", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": "Unique visitors", + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "compare": true, + "compare_label": "previous" + } + ], + "timezone": "US/Pacific", + "timings": [] +} diff --git a/frontend/src/scenes/web-analytics/__mocks__/WebOverview.json b/frontend/src/scenes/web-analytics/__mocks__/WebOverview.json new file mode 100644 index 0000000000000..46960d87a7058 --- /dev/null +++ b/frontend/src/scenes/web-analytics/__mocks__/WebOverview.json @@ -0,0 +1,78 @@ +{ + "cache_key": "cache_2d74908f63413d881a5c3d91a20397b2_[{'alias': '/person/', 'regex': '\\\\/person\\\\/[^\\\\/]+'}, {'alias': 'Any Session Recording Route', 'regex': '.*sessionRecordingId=[a-zA-Z]+'}, {'alias': '/insights/', 'regex': '.*\\\\/insights\\\\/[0-9a-zA-Z]+'}, {'alias': '/project/', 'regex': '\\\\/project\\\\/\\\\d+'}, {'alias': '/dashboard/', 'regex': '\\\\/dashboard\\\\/[0-9]+'}, {'alias': '/feature_flags/', 'regex': '\\\\/feature_flags\\\\/[0-9]+'}, {'alias': '/replay/', 'regex': '\\\\/replay\\\\/[0-9a-f\\\\-]+'}, {'alias': '/cohorts/', 'regex': '\\\\/cohorts\\\\/[0-9]+'}, {'alias': '/experiments/', 'regex': '\\\\/experiments\\\\/[0-9]+'}, {'alias': '/surveys/', 'regex': '\\\\/surveys\\\\/[0-9a-f\\\\-]+'}, {'alias': '/events/', 'regex': '\\\\/events\\\\/[0-9a-f\\\\-]+'}, {'alias': '/verify_email/', 'regex': '\\\\/verify_email\\\\/[0-9a-f\\\\-]+'}, {'alias': '/community/profiles/', 'regex': '\\\\/community\\\\/profiles\\\\/[0-9]+'}, {'alias': '/notebooks/', 'regex': '\\\\/notebooks\\\\/[a-zA-Z0-9]+'}, {'alias': '/groups/', 'regex': '\\\\/groups\\\\/[0-9]+\\\\/[a-zA-Z0-9]+'}, {'alias': '/signup/', 'regex': '\\\\/signup\\\\/[0-9a-f\\\\-]+'}, {'alias': '/playlists/', 'regex': '\\\\/playlists\\\\/[a-zA-Z0-9]+'}, {'alias': '/destinations/', 'regex': '\\\\/destinations?\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/properties/', 'regex': '\\\\/properties\\\\/[0-9a-f\\\\-]+'}, {'alias': '/sources/', 'regex': '\\\\/sources\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/reset', 'regex': '\\\\/reset\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/actions/', 'regex': '\\\\/actions\\\\/[0-9]+'}, {'alias': '/shared-metrics/', 'regex': '\\\\/shared-metrics\\\\/[0-9]+'}]", + "cache_target_age": "2025-01-20T23:41:25.930048Z", + "calculation_trigger": null, + "dateFrom": "2025-01-06 00:00:00", + "dateTo": "2025-01-20 23:59:59", + "error": null, + "hogql": null, + "is_cached": true, + "last_refresh": "2025-01-20T21:41:25.930048Z", + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "uniq_page_screen_autocaptures", + "customChannelTypeRules": [], + "dataWarehouseEventsModifiers": null, + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_on_events", + "propertyGroupsMode": "optimized", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T21:56:25.930048Z", + "query_status": null, + "results": [ + { + "changeFromPreviousPct": 12.4, + "isIncreaseBad": null, + "key": "visitors", + "kind": "unit", + "previous": 160000, + "value": 171861 + }, + { + "changeFromPreviousPct": 15.6, + "isIncreaseBad": null, + "key": "views", + "kind": "unit", + "previous": 4700000, + "value": 4863551 + }, + { + "changeFromPreviousPct": -1.4, + "isIncreaseBad": null, + "key": "sessions", + "kind": "unit", + "previous": 552000, + "value": 547323 + }, + { + "changeFromPreviousPct": 0, + "isIncreaseBad": null, + "key": "session duration", + "kind": "duration_s", + "previous": 724, + "value": 724.9206857358319 + }, + { + "changeFromPreviousPct": -0.5, + "isIncreaseBad": true, + "key": "bounce rate", + "kind": "percentage", + "previous": 10, + "value": 9.921511959952788 + } + ], + "samplingRate": { + "denominator": null, + "numerator": 1 + }, + "timezone": "US/Pacific", + "timings": null +} diff --git a/frontend/src/scenes/web-analytics/tiles/WebAnalyticsRecordings.tsx b/frontend/src/scenes/web-analytics/tiles/WebAnalyticsRecordings.tsx index b04d7cdaa8953..5e43b2ebabba1 100644 --- a/frontend/src/scenes/web-analytics/tiles/WebAnalyticsRecordings.tsx +++ b/frontend/src/scenes/web-analytics/tiles/WebAnalyticsRecordings.tsx @@ -30,7 +30,7 @@ export function WebAnalyticsRecordingsTile({ tile }: { tile: ReplayTile }): JSX. title: 'Recordings are not enabled for this project', description: 'Once recordings are enabled, new recordings will display here.', buttonText: 'Enable recordings', - buttonTo: urls.settings('project-replay'), + buttonTo: urls.settings('project-replay', 'replay'), } : webAnalyticsFilters.length > 0 ? { diff --git a/frontend/src/scenes/web-analytics/tiles/WebAnalyticsTile.stories.tsx b/frontend/src/scenes/web-analytics/tiles/WebAnalyticsTile.stories.tsx new file mode 100644 index 0000000000000..7db0d114995d8 --- /dev/null +++ b/frontend/src/scenes/web-analytics/tiles/WebAnalyticsTile.stories.tsx @@ -0,0 +1,65 @@ +import { Meta, StoryFn, StoryObj } from '@storybook/react' + +import { mswDecorator } from '~/mocks/browser' +import { examples } from '~/queries/examples' +import { Query } from '~/queries/Query/Query' + +import browserMock from './__mocks__/Browser.json' +import pathMock from './__mocks__/Path.json' +import referringDomainMock from './__mocks__/ReferringDomain.json' +import retentionMock from './__mocks__/Retention.json' +import worldMapMock from './__mocks__/WorldMap.json' +import { webAnalyticsDataTableQueryContext } from './WebAnalyticsTile' + +type Story = StoryObj +const meta: Meta = { + title: 'Web Analytics/Tiles', + component: Query, + parameters: { + layout: 'fullscreen', + viewMode: 'story', + }, + decorators: [ + mswDecorator({ + post: { + '/api/environments/:team_id/query/': (req) => { + if ((req.body as any).query.kind === 'WebStatsTableQuery') { + if ((req.body as any).query.breakdownBy === 'InitialReferringDomain') { + return [200, referringDomainMock] + } else if ((req.body as any).query.breakdownBy === 'Page') { + return [200, pathMock] + } else if ((req.body as any).query.breakdownBy === 'Browser') { + return [200, browserMock] + } + } else if ((req.body as any).query.kind === 'TrendsQuery') { + if ((req.body as any).query.trendsFilter?.display === 'WorldMap') { + return [200, worldMapMock] + } + } else if ((req.body as any).query.kind === 'RetentionQuery') { + return [200, retentionMock] + } + }, + }, + }), + ], +} +export default meta + +const Template: StoryFn = (args) => { + return +} + +export const WorldMap: Story = Template.bind({}) +WorldMap.args = { query: examples['WebAnalyticsWorldMap'] } + +export const ReferrerDomain: Story = Template.bind({}) +ReferrerDomain.args = { query: examples['WebAnalyticsReferrerDomain'] } + +export const Path: Story = Template.bind({}) +Path.args = { query: examples['WebAnalyticsPath'] } + +export const Retention: Story = Template.bind({}) +Retention.args = { query: examples['WebAnalyticsRetention'] } + +export const Browser: Story = Template.bind({}) +Browser.args = { query: examples['WebAnalyticsBrowser'] } diff --git a/frontend/src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx b/frontend/src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx index 27e25160d3e96..b33d3e86c63ed 100644 --- a/frontend/src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx +++ b/frontend/src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx @@ -1,4 +1,4 @@ -import { IconTrending } from '@posthog/icons' +import { IconRewindPlay, IconTrending } from '@posthog/icons' import { Tooltip } from '@posthog/lemon-ui' import clsx from 'clsx' import { useActions, useValues } from 'kea' @@ -9,6 +9,7 @@ import { IconOpenInNew, IconTrendingDown, IconTrendingFlat } from 'lib/lemon-ui/ import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch' import { percentage, UnexpectedNeverError } from 'lib/utils' +import { ProductCrossSellLocation, trackProductCrossSell } from 'lib/utils/cross-sell' import { useCallback, useMemo } from 'react' import { NewActionButton } from 'scenes/actions/NewActionButton' import { countryCodeToFlag, countryCodeToName } from 'scenes/insights/views/WorldMap' @@ -21,6 +22,7 @@ import { actionsModel } from '~/models/actionsModel' import { Query } from '~/queries/Query/Query' import { DataTableNode, + DataVisualizationNode, InsightVizNode, NodeKind, QuerySchema, @@ -28,7 +30,15 @@ import { WebVitalsPathBreakdownQuery, } from '~/queries/schema/schema-general' import { QueryContext, QueryContextColumnComponent, QueryContextColumnTitleComponent } from '~/queries/types' -import { ChartDisplayType, InsightLogicProps, ProductKey, PropertyFilterType } from '~/types' +import { + ChartDisplayType, + FilterLogicalOperator, + InsightLogicProps, + ProductKey, + PropertyFilterType, + PropertyOperator, + ReplayTabs, +} from '~/types' const toUtcOffsetFormat = (value: number): string => { if (value === 0) { @@ -367,6 +377,18 @@ export const webAnalyticsDataTableQueryContext: QueryContext = { action_name: { title: 'Action', }, + replay_url: { + title: ' ', + render: ({ record, query }: { record: any; query: DataTableNode | DataVisualizationNode }) => ( + + ), + align: 'right', + }, }, } @@ -661,3 +683,200 @@ export const WebQuery = ({ return } + +/** + * Map breakdown types to their corresponding property filter type + * Outside the renderReplayButton function + */ +const BREAKDOWN_TYPE_MAP: Partial< + Record +> = { + [WebStatsBreakdown.DeviceType]: PropertyFilterType.Person, + [WebStatsBreakdown.InitialPage]: PropertyFilterType.Session, + [WebStatsBreakdown.ExitPage]: PropertyFilterType.Session, + [WebStatsBreakdown.Page]: PropertyFilterType.Event, + [WebStatsBreakdown.Browser]: PropertyFilterType.Person, + [WebStatsBreakdown.OS]: PropertyFilterType.Person, + [WebStatsBreakdown.InitialChannelType]: PropertyFilterType.Session, + [WebStatsBreakdown.InitialReferringDomain]: PropertyFilterType.Session, + [WebStatsBreakdown.InitialUTMSource]: PropertyFilterType.Session, + [WebStatsBreakdown.InitialUTMCampaign]: PropertyFilterType.Session, + [WebStatsBreakdown.InitialUTMMedium]: PropertyFilterType.Session, + [WebStatsBreakdown.InitialUTMContent]: PropertyFilterType.Session, + [WebStatsBreakdown.InitialUTMTerm]: PropertyFilterType.Session, +} + +/** + * Map breakdown types to their corresponding property filter key + * Outside the renderReplayButton function + */ +const BREAKDOWN_KEY_MAP: Partial> = { + [WebStatsBreakdown.DeviceType]: '$device_type', + [WebStatsBreakdown.InitialPage]: '$entry_pathname', + [WebStatsBreakdown.ExitPage]: '$end_pathname', + [WebStatsBreakdown.Page]: '$pathname', + [WebStatsBreakdown.Browser]: '$browser', + [WebStatsBreakdown.OS]: '$os', + [WebStatsBreakdown.InitialChannelType]: '$channel_type', + [WebStatsBreakdown.InitialReferringDomain]: '$entry_referring_domain', + [WebStatsBreakdown.InitialUTMSource]: '$entry_utm_source', + [WebStatsBreakdown.InitialUTMCampaign]: '$entry_utm_campaign', + [WebStatsBreakdown.InitialUTMMedium]: '$entry_utm_medium', + [WebStatsBreakdown.InitialUTMContent]: '$entry_utm_content', + [WebStatsBreakdown.InitialUTMTerm]: '$entry_utm_term', +} + +/** + * Render a button that opens the recordings page with the correct filters + * + * @param date_from + * @param date_to + * @param breakdownBy + * @param value + * @returns JSX.Element + */ +const RenderReplayButton = ({ + date_from, + date_to, + breakdownBy, + value, +}: { + date_from: string + date_to: string + breakdownBy: WebStatsBreakdown + value: string +}): JSX.Element => { + const sharedButtonProps = { + icon: , + type: 'tertiary' as const, + size: 'xsmall' as const, + tooltip: 'View recordings', + className: 'float-right no-underline', + targetBlank: true, + onClick: (e: React.MouseEvent) => { + e.stopPropagation() + trackProductCrossSell({ + from: ProductKey.WEB_ANALYTICS, + to: ProductKey.SESSION_REPLAY, + location: ProductCrossSellLocation.WEB_ANALYTICS_INSIGHT, + context: {}, + }) + }, + } + + /** If value is null - just open session replay home page */ + if (value === null) { + return + } + + /** View port is a unique case, so we need to handle it differently */ + if (breakdownBy === WebStatsBreakdown.Viewport) { + return ( + + ) + } + + /** UTM source, medium, campaign is a unique case, so we need to handle it differently, as combining them with AND */ + if (breakdownBy === WebStatsBreakdown.InitialUTMSourceMediumCampaign) { + const values = value.split(' / ') + return ( + + ) + } + + const type = BREAKDOWN_TYPE_MAP[breakdownBy] || PropertyFilterType.Person + const key = BREAKDOWN_KEY_MAP[breakdownBy] + if (!key || !type) { + /** If the breakdown is not supported, return an empty element */ + return <> + } + + /** Render the button */ + return ( + + ) +} diff --git a/frontend/src/scenes/web-analytics/tiles/__mocks__/Browser.json b/frontend/src/scenes/web-analytics/tiles/__mocks__/Browser.json new file mode 100644 index 0000000000000..45e817d30ba8d --- /dev/null +++ b/frontend/src/scenes/web-analytics/tiles/__mocks__/Browser.json @@ -0,0 +1,85 @@ +{ + "cache_key": "cache_3dbcd9fb0d569f205781b7c151c83fb8_[{'alias': '/person/', 'regex': '\\\\/person\\\\/[^\\\\/]+'}, {'alias': 'Any Session Recording Route', 'regex': '.*sessionRecordingId=[a-zA-Z]+'}, {'alias': '/insights/', 'regex': '.*\\\\/insights\\\\/[0-9a-zA-Z]+'}, {'alias': '/project/', 'regex': '\\\\/project\\\\/\\\\d+'}, {'alias': '/dashboard/', 'regex': '\\\\/dashboard\\\\/[0-9]+'}, {'alias': '/feature_flags/', 'regex': '\\\\/feature_flags\\\\/[0-9]+'}, {'alias': '/replay/', 'regex': '\\\\/replay\\\\/[0-9a-f\\\\-]+'}, {'alias': '/cohorts/', 'regex': '\\\\/cohorts\\\\/[0-9]+'}, {'alias': '/experiments/', 'regex': '\\\\/experiments\\\\/[0-9]+'}, {'alias': '/surveys/', 'regex': '\\\\/surveys\\\\/[0-9a-f\\\\-]+'}, {'alias': '/events/', 'regex': '\\\\/events\\\\/[0-9a-f\\\\-]+'}, {'alias': '/verify_email/', 'regex': '\\\\/verify_email\\\\/[0-9a-f\\\\-]+'}, {'alias': '/community/profiles/', 'regex': '\\\\/community\\\\/profiles\\\\/[0-9]+'}, {'alias': '/notebooks/', 'regex': '\\\\/notebooks\\\\/[a-zA-Z0-9]+'}, {'alias': '/groups/', 'regex': '\\\\/groups\\\\/[0-9]+\\\\/[a-zA-Z0-9]+'}, {'alias': '/signup/', 'regex': '\\\\/signup\\\\/[0-9a-f\\\\-]+'}, {'alias': '/playlists/', 'regex': '\\\\/playlists\\\\/[a-zA-Z0-9]+'}, {'alias': '/destinations/', 'regex': '\\\\/destinations?\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/properties/', 'regex': '\\\\/properties\\\\/[0-9a-f\\\\-]+'}, {'alias': '/sources/', 'regex': '\\\\/sources\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/reset', 'regex': '\\\\/reset\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/actions/', 'regex': '\\\\/actions\\\\/[0-9]+'}, {'alias': '/shared-metrics/', 'regex': '\\\\/shared-metrics\\\\/[0-9]+'}]", + "cache_target_age": "2025-01-21T00:57:40.526794Z", + "calculation_trigger": null, + "columns": ["context.columns.breakdown_value", "context.columns.visitors", "context.columns.views"], + "error": null, + "hasMore": true, + "hogql": "SELECT\n breakdown_value AS `context.columns.breakdown_value`,\n tuple(uniq(filtered_person_id), NULL) AS `context.columns.visitors`,\n tuple(sum(filtered_pageview_count), NULL) AS `context.columns.views`\nFROM\n (SELECT\n any(person_id) AS filtered_person_id,\n count() AS filtered_pageview_count,\n properties.$browser AS breakdown_value,\n session.session_id AS session_id,\n any(session.$is_bounce) AS is_bounce,\n min(session.$start_timestamp) AS start_timestamp\n FROM\n events\n WHERE\n and(or(and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-06 00:00:00'))), less(timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59')))), false), or(equals(event, '$pageview'), equals(event, '$screen')), 1, notEquals(breakdown_value, NULL))\n GROUP BY\n session_id,\n breakdown_value)\nGROUP BY\n `context.columns.breakdown_value`\nORDER BY\n `context.columns.visitors` DESC,\n `context.columns.views` DESC,\n `context.columns.breakdown_value` ASC\nLIMIT 11\nOFFSET 0", + "is_cached": true, + "last_refresh": "2025-01-20T22:57:40.526794Z", + "limit": 10, + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "uniq_page_screen_autocaptures", + "customChannelTypeRules": [ + { + "channel_type": "AI", + "combiner": "AND", + "id": "b6332cb7-a32f-4a62-929e-54dc5c37ba1d", + "items": [ + { + "id": "90f32f03-2a30-4278-9869-c1f99da6dbe9", + "key": "referring_domain", + "op": "exact", + "value": ["www.perplexity.ai", "chat.openai.com"] + } + ] + }, + { + "channel_type": "Newsletter", + "combiner": "OR", + "id": "eb1729b9-35a8-4013-bf1b-83bf107feb1f", + "items": [ + { + "id": "40b11644-51bd-49ac-a99e-82fc8f734eda", + "key": "referring_domain", + "op": "icontains", + "value": ["substack.com"] + }, + { + "id": "e048a56a-eba8-4e59-ad20-cda77883594c", + "key": "utm_source", + "op": "exact", + "value": ["substack", "posthog-newsletter"] + } + ] + } + ], + "dataWarehouseEventsModifiers": null, + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_on_events", + "propertyGroupsMode": "optimized", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T23:12:40.526794Z", + "offset": 0, + "query_status": null, + "results": [ + ["Chrome", [130638, 128392], [4098916, 4150238]], + ["Firefox", [12749, 13105], [279210, 275481]], + ["Mobile Safari", [11677, 12044], [83129, 81893]], + ["Safari", [8284, 8129], [252689, 255104]], + ["Microsoft Edge", [6251, 6482], [170961, 168234]], + ["Chrome iOS", [2993, 2851], [29640, 30128]], + ["Opera", [740, 712], [17802, 18234]], + ["Firefox iOS", [265, 284], [2003, 1892]], + ["Samsung Internet", [246, 231], [1506, 1582]], + ["Android Mobile", [92, 87], [257, 271]] + ], + "samplingRate": null, + "timezone": "US/Pacific", + "timings": [], + "types": [ + ["context.columns.breakdown_value", "Nullable(String)"], + ["context.columns.visitors", "Tuple(UInt64, Nullable(Nothing))"], + ["context.columns.views", "Tuple(UInt64, Nullable(Nothing))"] + ] +} diff --git a/frontend/src/scenes/web-analytics/tiles/__mocks__/Path.json b/frontend/src/scenes/web-analytics/tiles/__mocks__/Path.json new file mode 100644 index 0000000000000..32e889596b1c6 --- /dev/null +++ b/frontend/src/scenes/web-analytics/tiles/__mocks__/Path.json @@ -0,0 +1,58 @@ +{ + "cache_key": "cache_bc7df2389a4b4e9958a291d07ca0894a_[{'alias': '/person/', 'regex': '\\\\/person\\\\/[^\\\\/]+'}, {'alias': 'Any Session Recording Route', 'regex': '.*sessionRecordingId=[a-zA-Z]+'}, {'alias': '/insights/', 'regex': '.*\\\\/insights\\\\/[0-9a-zA-Z]+'}, {'alias': '/project/', 'regex': '\\\\/project\\\\/\\\\d+'}, {'alias': '/dashboard/', 'regex': '\\\\/dashboard\\\\/[0-9]+'}, {'alias': '/feature_flags/', 'regex': '\\\\/feature_flags\\\\/[0-9]+'}, {'alias': '/replay/', 'regex': '\\\\/replay\\\\/[0-9a-f\\\\-]+'}, {'alias': '/cohorts/', 'regex': '\\\\/cohorts\\\\/[0-9]+'}, {'alias': '/experiments/', 'regex': '\\\\/experiments\\\\/[0-9]+'}, {'alias': '/surveys/', 'regex': '\\\\/surveys\\\\/[0-9a-f\\\\-]+'}, {'alias': '/events/', 'regex': '\\\\/events\\\\/[0-9a-f\\\\-]+'}, {'alias': '/verify_email/', 'regex': '\\\\/verify_email\\\\/[0-9a-f\\\\-]+'}, {'alias': '/community/profiles/', 'regex': '\\\\/community\\\\/profiles\\\\/[0-9]+'}, {'alias': '/notebooks/', 'regex': '\\\\/notebooks\\\\/[a-zA-Z0-9]+'}, {'alias': '/groups/', 'regex': '\\\\/groups\\\\/[0-9]+\\\\/[a-zA-Z0-9]+'}, {'alias': '/signup/', 'regex': '\\\\/signup\\\\/[0-9a-f\\\\-]+'}, {'alias': '/playlists/', 'regex': '\\\\/playlists\\\\/[a-zA-Z0-9]+'}, {'alias': '/destinations/', 'regex': '\\\\/destinations?\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/properties/', 'regex': '\\\\/properties\\\\/[0-9a-f\\\\-]+'}, {'alias': '/sources/', 'regex': '\\\\/sources\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/reset', 'regex': '\\\\/reset\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/actions/', 'regex': '\\\\/actions\\\\/[0-9]+'}, {'alias': '/shared-metrics/', 'regex': '\\\\/shared-metrics\\\\/[0-9]+'}]", + "cache_target_age": "2025-01-20T23:43:16.443321Z", + "calculation_trigger": null, + "columns": [ + "context.columns.breakdown_value", + "context.columns.visitors", + "context.columns.views", + "context.columns.bounce_rate" + ], + "error": null, + "hasMore": true, + "hogql": "SELECT\n counts.breakdown_value AS `context.columns.breakdown_value`,\n tuple(counts.visitors, counts.previous_visitors) AS `context.columns.visitors`,\n tuple(counts.views, counts.previous_views) AS `context.columns.views`,\n tuple(bounce.bounce_rate, bounce.previous_bounce_rate) AS `context.columns.bounce_rate`\nFROM\n (SELECT\n breakdown_value,\n uniqIf(filtered_person_id, and(greaterOrEquals(start_timestamp, assumeNotNull(toDateTime('2025-01-06 00:00:00'))), less(start_timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59'))))) AS visitors,\n uniqIf(filtered_person_id, false) AS previous_visitors,\n sumIf(filtered_pageview_count, and(greaterOrEquals(start_timestamp, assumeNotNull(toDateTime('2025-01-06 00:00:00'))), less(start_timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59'))))) AS views,\n sumIf(filtered_pageview_count, false) AS previous_views\n FROM\n (SELECT\n any(person_id) AS filtered_person_id,\n count() AS filtered_pageview_count,\n replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(events.properties.$pathname, '\\\\/person\\\\/[^\\\\/]+', '/person/'), '.*sessionRecordingId=[a-zA-Z]+', 'Any Session Recording Route'), '.*\\\\/insights\\\\/[0-9a-zA-Z]+', '/insights/'), '\\\\/project\\\\/\\\\d+', '/project/'), '\\\\/dashboard\\\\/[0-9]+', '/dashboard/'), '\\\\/feature_flags\\\\/[0-9]+', '/feature_flags/'), '\\\\/replay\\\\/[0-9a-f\\\\-]+', '/replay/'), '\\\\/cohorts\\\\/[0-9]+', '/cohorts/'), '\\\\/experiments\\\\/[0-9]+', '/experiments/'), '\\\\/surveys\\\\/[0-9a-f\\\\-]+', '/surveys/'), '\\\\/events\\\\/[0-9a-f\\\\-]+', '/events/'), '\\\\/verify_email\\\\/[0-9a-f\\\\-]+', '/verify_email/'), '\\\\/community\\\\/profiles\\\\/[0-9]+', '/community/profiles/'), '\\\\/notebooks\\\\/[a-zA-Z0-9]+', '/notebooks/'), '\\\\/groups\\\\/[0-9]+\\\\/[a-zA-Z0-9]+', '/groups/'), '\\\\/signup\\\\/[0-9a-f\\\\-]+', '/signup/'), '\\\\/playlists\\\\/[a-zA-Z0-9]+', '/playlists/'), '\\\\/destinations?\\\\/[0-9a-zA-Z\\\\-]+', '/destinations/'), '\\\\/properties\\\\/[0-9a-f\\\\-]+', '/properties/'), '\\\\/sources\\\\/[0-9a-zA-Z\\\\-]+', '/sources/'), '\\\\/reset\\\\/[0-9a-zA-Z\\\\-]+', '/reset'), '\\\\/actions\\\\/[0-9]+', '/actions/'), '\\\\/shared-metrics\\\\/[0-9]+', '/shared-metrics/') AS breakdown_value,\n session.session_id AS session_id,\n min(session.$start_timestamp) AS start_timestamp\n FROM\n events\n WHERE\n and(or(equals(events.event, '$pageview'), equals(events.event, '$screen')), or(and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-06 00:00:00'))), less(timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59')))), false), 1, 1, notEquals(breakdown_value, NULL))\n GROUP BY\n session_id,\n breakdown_value)\n GROUP BY\n breakdown_value) AS counts\n LEFT JOIN (SELECT\n breakdown_value,\n avgIf(is_bounce, and(greaterOrEquals(start_timestamp, assumeNotNull(toDateTime('2025-01-06 00:00:00'))), less(start_timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59'))))) AS bounce_rate,\n avgIf(is_bounce, false) AS previous_bounce_rate\n FROM\n (SELECT\n replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(replaceRegexpAll(session.$entry_pathname, '\\\\/person\\\\/[^\\\\/]+', '/person/'), '.*sessionRecordingId=[a-zA-Z]+', 'Any Session Recording Route'), '.*\\\\/insights\\\\/[0-9a-zA-Z]+', '/insights/'), '\\\\/project\\\\/\\\\d+', '/project/'), '\\\\/dashboard\\\\/[0-9]+', '/dashboard/'), '\\\\/feature_flags\\\\/[0-9]+', '/feature_flags/'), '\\\\/replay\\\\/[0-9a-f\\\\-]+', '/replay/'), '\\\\/cohorts\\\\/[0-9]+', '/cohorts/'), '\\\\/experiments\\\\/[0-9]+', '/experiments/'), '\\\\/surveys\\\\/[0-9a-f\\\\-]+', '/surveys/'), '\\\\/events\\\\/[0-9a-f\\\\-]+', '/events/'), '\\\\/verify_email\\\\/[0-9a-f\\\\-]+', '/verify_email/'), '\\\\/community\\\\/profiles\\\\/[0-9]+', '/community/profiles/'), '\\\\/notebooks\\\\/[a-zA-Z0-9]+', '/notebooks/'), '\\\\/groups\\\\/[0-9]+\\\\/[a-zA-Z0-9]+', '/groups/'), '\\\\/signup\\\\/[0-9a-f\\\\-]+', '/signup/'), '\\\\/playlists\\\\/[a-zA-Z0-9]+', '/playlists/'), '\\\\/destinations?\\\\/[0-9a-zA-Z\\\\-]+', '/destinations/'), '\\\\/properties\\\\/[0-9a-f\\\\-]+', '/properties/'), '\\\\/sources\\\\/[0-9a-zA-Z\\\\-]+', '/sources/'), '\\\\/reset\\\\/[0-9a-zA-Z\\\\-]+', '/reset'), '\\\\/actions\\\\/[0-9]+', '/actions/'), '\\\\/shared-metrics\\\\/[0-9]+', '/shared-metrics/') AS breakdown_value,\n any(session.$is_bounce) AS is_bounce,\n session.session_id AS session_id,\n min(session.$start_timestamp) AS start_timestamp\n FROM\n events\n WHERE\n and(or(equals(events.event, '$pageview'), equals(events.event, '$screen')), notEquals(breakdown_value, NULL), or(and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-06 00:00:00'))), less(timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59')))), false), 1, 1)\n GROUP BY\n session_id,\n breakdown_value)\n GROUP BY\n breakdown_value) AS bounce ON equals(counts.breakdown_value, bounce.breakdown_value)\nORDER BY\n `context.columns.visitors` DESC,\n `context.columns.views` DESC,\n `context.columns.breakdown_value` ASC\nLIMIT 11\nOFFSET 0", + "is_cached": true, + "last_refresh": "2025-01-20T21:43:16.443321Z", + "limit": 10, + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "uniq_page_screen_autocaptures", + "customChannelTypeRules": [], + "dataWarehouseEventsModifiers": null, + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_on_events", + "propertyGroupsMode": "optimized", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T21:58:16.443321Z", + "offset": 0, + "query_status": null, + "results": [ + ["/", [76095, 72341], [233693, 241023], [0.1182, 0.1162]], + ["/login", [53629, 51234], [192373, 195481], [0.03184, 0.02984]], + ["/project/", [33811, 35102], [331828, 328192], [0.009685, 0.008685]], + ["/project//dashboard", [16314, 15892], [131470, 134829], [0.010872, 0.009872]], + ["/insights/", [16249, 16892], [516739, 509234], [0.007644, 0.008644]], + ["/project//insights", [15902, 15234], [148649, 152938], [0.007896, 0.006896]], + ["/project//web", [15687, 16234], [198440, 195234], [0.007481, 0.008481]], + ["/pricing", [15295, 14892], [24103, 25234], [0.2477, 0.2457]], + ["/project//replay/home", [15189, 15892], [632756, 628192], [0.009924, 0.008924]], + ["/project//dashboard/", [14598, 14129], [259451, 262938], [0.007145, 0.006145]] + ], + "samplingRate": null, + "timezone": "US/Pacific", + "timings": [], + "types": [ + ["context.columns.breakdown_value", "Nullable(String)"], + ["context.columns.visitors", "Tuple(UInt64, UInt64)"], + ["context.columns.views", "Tuple(UInt64, UInt64)"], + ["context.columns.bounce_rate", "Tuple(Nullable(Float64), Nullable(Float64))"] + ] +} diff --git a/frontend/src/scenes/web-analytics/tiles/__mocks__/ReferringDomain.json b/frontend/src/scenes/web-analytics/tiles/__mocks__/ReferringDomain.json new file mode 100644 index 0000000000000..9715e435e9ff9 --- /dev/null +++ b/frontend/src/scenes/web-analytics/tiles/__mocks__/ReferringDomain.json @@ -0,0 +1,52 @@ +{ + "cache_key": "cache_1f01018c3a7bd9ef3f1fe37562a3ad2a_[{'alias': '/person/', 'regex': '\\\\/person\\\\/[^\\\\/]+'}, {'alias': 'Any Session Recording Route', 'regex': '.*sessionRecordingId=[a-zA-Z]+'}, {'alias': '/insights/', 'regex': '.*\\\\/insights\\\\/[0-9a-zA-Z]+'}, {'alias': '/project/', 'regex': '\\\\/project\\\\/\\\\d+'}, {'alias': '/dashboard/', 'regex': '\\\\/dashboard\\\\/[0-9]+'}, {'alias': '/feature_flags/', 'regex': '\\\\/feature_flags\\\\/[0-9]+'}, {'alias': '/replay/', 'regex': '\\\\/replay\\\\/[0-9a-f\\\\-]+'}, {'alias': '/cohorts/', 'regex': '\\\\/cohorts\\\\/[0-9]+'}, {'alias': '/experiments/', 'regex': '\\\\/experiments\\\\/[0-9]+'}, {'alias': '/surveys/', 'regex': '\\\\/surveys\\\\/[0-9a-f\\\\-]+'}, {'alias': '/events/', 'regex': '\\\\/events\\\\/[0-9a-f\\\\-]+'}, {'alias': '/verify_email/', 'regex': '\\\\/verify_email\\\\/[0-9a-f\\\\-]+'}, {'alias': '/community/profiles/', 'regex': '\\\\/community\\\\/profiles\\\\/[0-9]+'}, {'alias': '/notebooks/', 'regex': '\\\\/notebooks\\\\/[a-zA-Z0-9]+'}, {'alias': '/groups/', 'regex': '\\\\/groups\\\\/[0-9]+\\\\/[a-zA-Z0-9]+'}, {'alias': '/signup/', 'regex': '\\\\/signup\\\\/[0-9a-f\\\\-]+'}, {'alias': '/playlists/', 'regex': '\\\\/playlists\\\\/[a-zA-Z0-9]+'}, {'alias': '/destinations/', 'regex': '\\\\/destinations?\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/properties/', 'regex': '\\\\/properties\\\\/[0-9a-f\\\\-]+'}, {'alias': '/sources/', 'regex': '\\\\/sources\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/reset', 'regex': '\\\\/reset\\\\/[0-9a-zA-Z\\\\-]+'}, {'alias': '/actions/', 'regex': '\\\\/actions\\\\/[0-9]+'}, {'alias': '/shared-metrics/', 'regex': '\\\\/shared-metrics\\\\/[0-9]+'}]", + "cache_target_age": "2025-01-21T00:30:47.497494Z", + "calculation_trigger": null, + "columns": ["context.columns.breakdown_value", "context.columns.visitors", "context.columns.views"], + "error": null, + "hasMore": true, + "hogql": "SELECT\n breakdown_value AS `context.columns.breakdown_value`,\n tuple(uniq(filtered_person_id), NULL) AS `context.columns.visitors`,\n tuple(sum(filtered_pageview_count), NULL) AS `context.columns.views`\nFROM\n (SELECT\n any(person_id) AS filtered_person_id,\n count() AS filtered_pageview_count,\n session.$entry_referring_domain AS breakdown_value,\n session.session_id AS session_id,\n any(session.$is_bounce) AS is_bounce,\n min(session.$start_timestamp) AS start_timestamp\n FROM\n events\n WHERE\n and(or(and(greaterOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-06 00:00:00'))), less(timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59')))), false), or(equals(event, '$pageview'), equals(event, '$screen')), 1, notEquals(breakdown_value, NULL))\n GROUP BY\n session_id,\n breakdown_value)\nGROUP BY\n `context.columns.breakdown_value`\nORDER BY\n `context.columns.visitors` DESC,\n `context.columns.views` DESC,\n `context.columns.breakdown_value` ASC\nLIMIT 11\nOFFSET 0", + "is_cached": false, + "last_refresh": "2025-01-20T22:30:47.497494Z", + "limit": 10, + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "uniq_page_screen_autocaptures", + "customChannelTypeRules": [], + "dataWarehouseEventsModifiers": null, + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_on_events", + "propertyGroupsMode": "optimized", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T22:45:47.497494Z", + "offset": 0, + "query_status": null, + "results": [ + ["$direct", [104808, 100000], [4172018, 4200000]], + ["www.google.com", [53556, 48234], [293642, 298481]], + ["posthog.com", [7357, 8912], [24274, 29183]], + ["us.posthog.com", [4149, 3891], [170370, 175293]], + ["eu.posthog.com", [2370, 3142], [105763, 118924]], + ["www.linkedin.com", [2210, 1893], [4626, 4829]], + ["duckduckgo.com", [2134, 2531], [8948, 8237]], + ["github.com", [1605, 1428], [9318, 9475]], + ["www.bing.com", [1217, 1502], [5421, 5234]], + ["t.co", [1060, 892], [2931, 3047]] + ], + "samplingRate": null, + "timezone": "US/Pacific", + "timings": [], + "types": [ + ["context.columns.breakdown_value", "Nullable(String)"], + ["context.columns.visitors", "Tuple(UInt64, Nullable(Nothing))"], + ["context.columns.views", "Tuple(UInt64, Nullable(Nothing))"] + ] +} diff --git a/frontend/src/scenes/web-analytics/tiles/__mocks__/Retention.json b/frontend/src/scenes/web-analytics/tiles/__mocks__/Retention.json new file mode 100644 index 0000000000000..8d727dc084930 --- /dev/null +++ b/frontend/src/scenes/web-analytics/tiles/__mocks__/Retention.json @@ -0,0 +1,188 @@ +{ + "cache_key": "cache_aba8c07511e89a6429bd57b150a564af", + "cache_target_age": "2025-01-21T09:41:32.060989Z", + "calculation_trigger": null, + "error": null, + "hogql": "SELECT\n [actor_activity.breakdown_values] AS breakdown_values,\n actor_activity.intervals_from_base AS intervals_from_base,\n count(DISTINCT actor_activity.actor_id) AS count\nFROM\n (SELECT\n events.person_id AS actor_id,\n if(has(arraySort(groupUniqArrayIf(toStartOfWeek(events.timestamp, 3), and(equals(events.event, '$pageview'), and(greaterOrEquals(events.timestamp, toStartOfWeek(toDateTime('2024-12-02 00:00:00.000000'), 3)), lessOrEquals(events.timestamp, toDateTime('2025-01-27 00:00:00.000000')))))) AS _target_timestamps, toStartOfWeek(minIf(events.timestamp, equals(events.event, '$pageview')))), _target_timestamps, []) AS target_timestamps,\n arraySort(groupUniqArrayIf(toStartOfWeek(events.timestamp, 3), equals(events.event, '$pageview'))) AS returning_timestamps,\n arrayMap(x -> plus(toStartOfWeek(assumeNotNull(toDateTime('2024-12-02 00:00:00'))), toIntervalWeek(x)), range(0, 8)) AS date_range,\n arrayJoin(arrayFilter(x -> greater(x, -1), arrayMap((_breakdown_value, breakdown_value_timestamp) -> if(equals(target_timestamps[1], breakdown_value_timestamp), minus(_breakdown_value, 1), -1), arrayEnumerate(date_range), date_range))) AS breakdown_values,\n arrayJoin(arrayConcat(if(equals(target_timestamps[1], date_range[plus(breakdown_values, 1)]), [0], []), arrayFilter(x -> greater(x, 0), arrayMap(_timestamp -> minus(indexOf(arraySlice(date_range, plus(breakdown_values, 1)), _timestamp), 1), returning_timestamps)))) AS intervals_from_base\n FROM\n events\n WHERE\n in(event, tuple('$pageview', '$pageview'))\n GROUP BY\n actor_id) AS actor_activity\nGROUP BY\n breakdown_values,\n intervals_from_base\nORDER BY\n breakdown_values ASC,\n intervals_from_base ASC\nLIMIT 10000", + "is_cached": true, + "last_refresh": "2025-01-20T21:41:32.060989Z", + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "uniq_page_screen_autocaptures", + "customChannelTypeRules": [], + "dataWarehouseEventsModifiers": null, + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_on_events", + "propertyGroupsMode": "optimized", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T21:56:32.060989Z", + "query_status": null, + "results": [ + { + "date": "2024-12-02T00:00:00-08:00", + "label": "Week 0", + "values": [ + { + "count": 52839 + }, + { + "count": 2501 + }, + { + "count": 1610 + }, + { + "count": 859 + }, + { + "count": 845 + }, + { + "count": 1135 + }, + { + "count": 1081 + }, + { + "count": 287 + } + ] + }, + { + "date": "2024-12-09T00:00:00-08:00", + "label": "Week 1", + "values": [ + { + "count": 51654 + }, + { + "count": 2449 + }, + { + "count": 1044 + }, + { + "count": 1015 + }, + { + "count": 1304 + }, + { + "count": 1193 + }, + { + "count": 304 + } + ] + }, + { + "date": "2024-12-16T00:00:00-08:00", + "label": "Week 2", + "values": [ + { + "count": 46686 + }, + { + "count": 1557 + }, + { + "count": 1205 + }, + { + "count": 1453 + }, + { + "count": 1251 + }, + { + "count": 338 + } + ] + }, + { + "date": "2024-12-23T00:00:00-08:00", + "label": "Week 3", + "values": [ + { + "count": 36137 + }, + { + "count": 1208 + }, + { + "count": 887 + }, + { + "count": 704 + }, + { + "count": 173 + } + ] + }, + { + "date": "2024-12-30T00:00:00-08:00", + "label": "Week 4", + "values": [ + { + "count": 38690 + }, + { + "count": 1751 + }, + { + "count": 1083 + }, + { + "count": 264 + } + ] + }, + { + "date": "2025-01-06T00:00:00-08:00", + "label": "Week 5", + "values": [ + { + "count": 54805 + }, + { + "count": 2665 + }, + { + "count": 543 + } + ] + }, + { + "date": "2025-01-13T00:00:00-08:00", + "label": "Week 6", + "values": [ + { + "count": 58006 + }, + { + "count": 1008 + } + ] + }, + { + "date": "2025-01-20T00:00:00-08:00", + "label": "Week 7", + "values": [ + { + "count": 6656 + } + ] + } + ], + "timezone": "US/Pacific", + "timings": [] +} diff --git a/frontend/src/scenes/web-analytics/tiles/__mocks__/WorldMap.json b/frontend/src/scenes/web-analytics/tiles/__mocks__/WorldMap.json new file mode 100644 index 0000000000000..58a58cc072027 --- /dev/null +++ b/frontend/src/scenes/web-analytics/tiles/__mocks__/WorldMap.json @@ -0,0 +1,11490 @@ +{ + "cache_key": "cache_284659783c7c6e632de63417e564c264", + "cache_target_age": "2025-01-20T23:45:54.521201Z", + "calculation_trigger": null, + "error": "", + "hasMore": false, + "hogql": "SELECT\n sum(total) AS total,\n if(ifNull(greaterOrEquals(row_number, 251), 0), '$$_posthog_breakdown_other_$$', breakdown_value) AS breakdown_value\nFROM\n (SELECT\n count AS total,\n breakdown_value AS breakdown_value,\n row_number() OVER (ORDER BY total DESC) AS row_number\n FROM\n (SELECT\n sum(total) AS count,\n breakdown_value\n FROM\n (SELECT\n count(DISTINCT e.person_id) AS total,\n ifNull(nullIf(toString(properties.$geoip_country_code), ''), '$$_posthog_breakdown_null_$$') AS breakdown_value\n FROM\n events AS e SAMPLE 1\n WHERE\n and(greaterOrEquals(timestamp, toStartOfDay(assumeNotNull(toDateTime('2025-01-06 00:00:00')))), lessOrEquals(timestamp, assumeNotNull(toDateTime('2025-01-20 23:59:59'))), equals(event, '$pageview'))\n GROUP BY\n breakdown_value)\n GROUP BY\n breakdown_value\n ORDER BY\n breakdown_value ASC)\n ORDER BY\n total DESC,\n breakdown_value ASC)\nWHERE\n notEquals(breakdown_value, NULL)\nGROUP BY\n breakdown_value\nORDER BY\n if(equals(breakdown_value, '$$_posthog_breakdown_other_$$'), 2, if(equals(breakdown_value, '$$_posthog_breakdown_null_$$'), 1, 0)) ASC,\n total DESC,\n breakdown_value ASC\nLIMIT 50000", + "is_cached": false, + "last_refresh": "2025-01-20T21:45:54.521201Z", + "modifiers": { + "bounceRateDurationSeconds": null, + "bounceRatePageViewMode": "uniq_page_screen_autocaptures", + "customChannelTypeRules": [], + "dataWarehouseEventsModifiers": [], + "debug": null, + "inCohortVia": "auto", + "materializationMode": "legacy_null_as_null", + "optimizeJoinedFilters": false, + "personsArgMaxVersion": "auto", + "personsJoinMode": null, + "personsOnEventsMode": "person_id_override_properties_on_events", + "propertyGroupsMode": "optimized", + "s3TableUseInvalidColumns": null, + "sessionTableVersion": "auto", + "useMaterializedViews": true + }, + "next_allowed_client_refresh": "2025-01-20T22:00:54.521201Z", + "query_status": null, + "results": [ + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 45319, + "label": "US", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "US" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 18108, + "label": "FR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "FR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 12971, + "label": "IN", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "IN" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 11873, + "label": "GB", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GB" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 10586, + "label": "DE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "DE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 6725, + "label": "CA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 4438, + "label": "BR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 4178, + "label": "ES", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "ES" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 4156, + "label": "NL", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "NL" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 3240, + "label": "AU", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AU" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 3076, + "label": "PL", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PL" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1945, + "label": "CN", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CN" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1913, + "label": "SE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1739, + "label": "SG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1713, + "label": "ID", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "ID" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1629, + "label": "IL", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "IL" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1617, + "label": "IT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "IT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1570, + "label": "PT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1440, + "label": "CH", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CH" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1436, + "label": "RU", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "RU" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1399, + "label": "VN", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "VN" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1368, + "label": "NO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "NO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1347, + "label": "UA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "UA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1310, + "label": "AT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1244, + "label": "DK", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "DK" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1232, + "label": "BE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1217, + "label": "TR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1123, + "label": "FI", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "FI" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1121, + "label": "HK", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "HK" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1107, + "label": "MX", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MX" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1081, + "label": "IE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "IE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1079, + "label": "JP", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "JP" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1078, + "label": "PK", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PK" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1047, + "label": "CZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 993, + "label": "KR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "KR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 945, + "label": "RO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "RO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 943, + "label": "PH", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PH" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 877, + "label": "AR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 824, + "label": "TH", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TH" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 770, + "label": "AE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 718, + "label": "BG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 711, + "label": "CO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 689, + "label": "NZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "NZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 687, + "label": "ZA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "ZA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 655, + "label": "EG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "EG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 637, + "label": "HU", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "HU" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 606, + "label": "LT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 595, + "label": "GR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 594, + "label": "HR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "HR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 585, + "label": "MY", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MY" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 548, + "label": "RS", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "RS" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 536, + "label": "KE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "KE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 527, + "label": "GE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 523, + "label": "TW", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TW" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 510, + "label": "EE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "EE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 499, + "label": "CL", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CL" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 493, + "label": "IR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "IR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 482, + "label": "NG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "NG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 474, + "label": "BD", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BD" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 442, + "label": "MA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 364, + "label": "SI", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SI" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 334, + "label": "SK", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SK" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 309, + "label": "SA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 303, + "label": "LV", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LV" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 220, + "label": "LK", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LK" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 211, + "label": "CY", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CY" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 211, + "label": "NP", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "NP" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 208, + "label": "KZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "KZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 198, + "label": "AM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 195, + "label": "GH", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GH" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 166, + "label": "UY", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "UY" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 158, + "label": "PE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 154, + "label": "CR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 148, + "label": "BY", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BY" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 145, + "label": "DZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "DZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 138, + "label": "TN", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TN" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 137, + "label": "UZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "UZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 115, + "label": "IS", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "IS" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 110, + "label": "MD", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MD" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 104, + "label": "MK", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MK" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 98, + "label": "DO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "DO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 96, + "label": "MT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 93, + "label": "BA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 92, + "label": "JO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "JO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 87, + "label": "IQ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "IQ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 84, + "label": "LB", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LB" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 83, + "label": "LU", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LU" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 76, + "label": "VE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "VE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 75, + "label": "EC", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "EC" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 66, + "label": "ME", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "ME" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 63, + "label": "PS", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PS" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 60, + "label": "GT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 55, + "label": "ET", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "ET" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 54, + "label": "UG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "UG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 52, + "label": "AZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 52, + "label": "BH", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BH" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 52, + "label": "NI", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "NI" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 52, + "label": "PR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 49, + "label": "PY", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PY" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 49, + "label": "QA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "QA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 48, + "label": "KH", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "KH" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 47, + "label": "AL", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AL" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 44, + "label": "KG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "KG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 40, + "label": "PA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 38, + "label": "BO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 38, + "label": "MU", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MU" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 36, + "label": "JM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "JM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 36, + "label": "KW", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "KW" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 34, + "label": "HN", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "HN" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 34, + "label": "SN", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SN" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 33, + "label": "RW", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "RW" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 32, + "label": "XK", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "XK" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 29, + "label": "RE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "RE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 29, + "label": "SV", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SV" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 28, + "label": "ZW", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "ZW" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 27, + "label": "MN", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MN" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 27, + "label": "TZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 25, + "label": "CI", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CI" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 24, + "label": "AD", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AD" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 23, + "label": "OM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "OM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 22, + "label": "MG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 20, + "label": "MV", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MV" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 18, + "label": "BJ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BJ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 17, + "label": "CM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 14, + "label": "MO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 14, + "label": "TT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 14, + "label": "ZM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "ZM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 13, + "label": "JE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "JE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 11, + "label": "BT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 11, + "label": "BZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 11, + "label": "IM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "IM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 11, + "label": "SO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 10, + "label": "BS", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BS" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 10, + "label": "SC", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SC" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 10, + "label": "YE", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "YE" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 9, + "label": "GP", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GP" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 9, + "label": "MC", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MC" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 9, + "label": "ML", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "ML" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 9, + "label": "TG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 8, + "label": "CU", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CU" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 8, + "label": "GF", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GF" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 8, + "label": "KY", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "KY" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 8, + "label": "MM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 7, + "label": "AG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 7, + "label": "BB", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BB" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 7, + "label": "HT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "HT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 7, + "label": "LA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 7, + "label": "LY", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LY" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 6, + "label": "AO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 6, + "label": "BM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 6, + "label": "CW", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CW" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 6, + "label": "GI", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GI" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 6, + "label": "MR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 6, + "label": "MZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 6, + "label": "NA", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "NA" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 6, + "label": "SR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 5, + "label": "CV", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CV" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 5, + "label": "LR", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LR" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 5, + "label": "MQ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MQ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 4, + "label": "BF", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BF" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 4, + "label": "CD", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CD" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 4, + "label": "FO", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "FO" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 4, + "label": "GG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 4, + "label": "LI", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LI" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 4, + "label": "SY", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SY" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 3, + "label": "AF", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AF" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 3, + "label": "AW", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AW" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 3, + "label": "BW", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BW" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 3, + "label": "CG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "CG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 3, + "label": "FJ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "FJ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 3, + "label": "SM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 3, + "label": "TJ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TJ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "AI", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AI" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "AX", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "AX" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "BI", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BI" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "BN", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BN" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "LC", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LC" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "MW", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MW" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "PG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "PG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "SD", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SD" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "SX", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SX" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "SZ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SZ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 2, + "label": "VU", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "VU" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "BQ", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "BQ" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "DM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "DM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "GD", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GD" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "GL", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GL" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "GN", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GN" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "GY", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "GY" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "LS", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "LS" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "MF", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "MF" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "NC", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "NC" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "SB", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SB" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "SL", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SL" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "SS", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "SS" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "TL", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TL" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "TM", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "TM" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "VG", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "VG" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "VI", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "VI" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 1, + "label": "YT", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "YT" + }, + { + "data": [], + "days": [], + "count": 0, + "aggregated_value": 118, + "label": "$$_posthog_breakdown_null_$$", + "filter": { + "insight": "TRENDS", + "properties": [], + "filter_test_accounts": false, + "date_to": "2025-01-20T23:59:59.999999-08:00", + "date_from": "2025-01-06T00:00:00-08:00", + "entity_type": "events", + "interval": "day", + "aggregationAxisFormat": "numeric", + "display": "WorldMap", + "resultCustomizationBy": "value", + "showAlertThresholdLines": false, + "showLegend": false, + "showPercentStackView": false, + "showValuesOnSeries": false, + "smoothingIntervals": 1, + "yAxisScaleType": "linear", + "breakdown": "$geoip_country_code", + "breakdown_type": "event" + }, + "action": { + "days": [ + "2025-01-06T00:00:00-08:00", + "2025-01-07T00:00:00-08:00", + "2025-01-08T00:00:00-08:00", + "2025-01-09T00:00:00-08:00", + "2025-01-10T00:00:00-08:00", + "2025-01-11T00:00:00-08:00", + "2025-01-12T00:00:00-08:00", + "2025-01-13T00:00:00-08:00", + "2025-01-14T00:00:00-08:00", + "2025-01-15T00:00:00-08:00", + "2025-01-16T00:00:00-08:00", + "2025-01-17T00:00:00-08:00", + "2025-01-18T00:00:00-08:00", + "2025-01-19T00:00:00-08:00", + "2025-01-20T00:00:00-08:00" + ], + "id": "$pageview", + "type": "events", + "order": 0, + "name": "$pageview", + "custom_name": null, + "math": "dau", + "math_property": null, + "math_hogql": null, + "math_group_type_index": null, + "properties": {} + }, + "breakdown_value": "$$_posthog_breakdown_null_$$" + } + ], + "timezone": "US/Pacific", + "timings": [] +} diff --git a/frontend/src/scenes/web-analytics/webAnalyticsLogic.tsx b/frontend/src/scenes/web-analytics/webAnalyticsLogic.tsx index 72bf2042b34d0..976465285c0b1 100644 --- a/frontend/src/scenes/web-analytics/webAnalyticsLogic.tsx +++ b/frontend/src/scenes/web-analytics/webAnalyticsLogic.tsx @@ -737,7 +737,7 @@ export const webAnalyticsLogic = kea([ source?: Partial, tab?: Partial ): TabsTileTab => { - const columns = ['breakdown_value', 'visitors', 'views'] + const columns = ['breakdown_value', 'visitors', 'views', 'replay_url'] if (source?.includeBounceRate) { columns.push('bounce_rate') } @@ -1108,7 +1108,7 @@ export const webAnalyticsLogic = kea([ stripQueryParams: shouldStripQueryParams, }, embedded: false, - columns: ['url', 'visitors', 'clicks'], + columns: ['url', 'visitors', 'clicks', 'replay_url'], }, insightProps: createInsightProps(TileId.PATHS, PathTab.END_PATH), canOpenModal: true, @@ -1525,7 +1525,7 @@ export const webAnalyticsLogic = kea([ filterTestAccounts, }, embedded: true, - columns: ['breakdown_value', 'visitors', 'views'], + columns: ['breakdown_value', 'visitors', 'views', 'replay_url'], }, insightProps: createInsightProps(TileId.GOALS), canOpenInsight: false, diff --git a/frontend/src/styles/design-system.scss b/frontend/src/styles/design-system.scss index ac41f4d92ceb4..26c0e2b2f821f 100644 --- a/frontend/src/styles/design-system.scss +++ b/frontend/src/styles/design-system.scss @@ -2,8 +2,8 @@ // We shouldn't use these directly in our app, instead we should use the semantic colors below // Note: We use SCSS -> CSS variables, so we can manipulate these variables later if needed. // ////////////////////////////////////////////////////////// -$primitive-white: hsl(0deg 0% 100%); -$primitive-black: hsl(0deg 0% 0%); +$primitive-white: white; +$primitive-black: black; $primitive-transparent: hsl(0deg 0% 0% / 0%); $primitive-3000: ( 25: hsl(75deg 14% 96.5%), @@ -22,184 +22,280 @@ $primitive-3000: ( 500: hsl(67deg 8% 55%), ); $primitive-neutral: ( - 50: hsl(0deg 0% 98%), - 100: hsl(0deg 0% 96%), - 150: hsl(0deg 0% 92%), - 200: hsl(0deg 0% 88%), - 250: hsl(0deg 0% 84%), - 300: hsl(0deg 0% 80%), - 350: hsl(0deg 0% 75%), - 400: hsl(0deg 0% 70%), - 450: hsl(0deg 0% 65%), - 500: hsl(0deg 0% 60%), - 550: hsl(0deg 0% 55%), - 600: hsl(0deg 0% 50%), - 650: hsl(0deg 0% 45%), - 700: hsl(0deg 0% 40%), - 750: hsl(0deg 0% 35%), - 800: hsl(0deg 0% 30%), - 850: hsl(0deg 0% 25%), - 900: hsl(0deg 0% 20%), - 950: hsl(0deg 0% 15%), + 50: hsl(0deg 0% 95%), + 100: hsl(0deg 0% 90%), + 150: hsl(0deg 0% 85%), + 200: hsl(0deg 0% 80%), + 250: hsl(0deg 0% 75%), + 300: hsl(0deg 0% 70%), + 350: hsl(0deg 0% 65%), + 400: hsl(0deg 0% 60%), + 450: hsl(0deg 0% 55%), + 500: hsl(0deg 0% 50%), + 550: hsl(0deg 0% 45%), + 600: hsl(0deg 0% 40%), + 650: hsl(0deg 0% 35%), + 700: hsl(0deg 0% 30%), + 750: hsl(0deg 0% 25%), + 800: hsl(0deg 0% 20%), + 850: hsl(0deg 0% 15%), + 900: hsl(0deg 0% 10%), + 950: hsl(0deg 0% 5%), ); $primitive-neutral-cool: ( 50: hsl(240deg 8% 95%), - 100: hsl(232deg 8% 91%), - 150: hsl(230deg 8% 86%), - 200: hsl(233deg 8% 81%), - 250: hsl(231deg 8% 76%), - 300: hsl(230deg 8% 72%), - 350: hsl(231deg 8% 67%), - 400: hsl(231deg 8% 62%), - 450: hsl(232deg 8% 58%), - 500: hsl(231deg 8% 53%), - 550: hsl(231deg 8% 53%), - 600: hsl(230deg 8% 48%), - 650: hsl(229deg 8% 42%), - 700: hsl(230deg 8% 37%), - 750: hsl(231deg 8% 32%), - 800: hsl(230deg 8% 26%), - 850: hsl(236deg 8% 19%), - 900: hsl(240deg 8% 15%), - 950: hsl(240deg 8% 10%), + 100: hsl(232deg 8% 90%), + 200: hsl(233deg 8% 80%), + 300: hsl(230deg 8% 70%), + 400: hsl(231deg 8% 60%), + 500: hsl(231deg 8% 50%), + 600: hsl(230deg 8% 40%), + 700: hsl(230deg 8% 30%), + 800: hsl(230deg 8% 20%), + 900: hsl(240deg 8% 10%), + 950: hsl(240deg 8% 5%), ); -$primitive-blue: ( - 50: hsl(215deg 90% 96%), - 100: hsl(217deg 90% 92%), - 200: hsl(216deg 90% 84%), - 300: hsl(217deg 92% 76%), - 400: hsl(217deg 91% 68%), - 500: hsl(217deg 91% 60%), - 600: hsl(217deg 91% 48%), - 700: hsl(217deg 91% 36%), - 800: hsl(217deg 92% 24%), - 900: hsl(218deg 90% 12%), - 950: hsl(216deg 93% 6%), -); -$primitive-purple: ( - 50: hsl(263deg 100% 95%), - 100: hsl(264deg 100% 90%), - 200: hsl(263deg 100% 81%), - 300: hsl(263deg 100% 71%), - 400: hsl(263deg 100% 61%), - 500: hsl(263deg 100% 52%), - 600: hsl(263deg 100% 41%), - 700: hsl(263deg 100% 31%), - 800: hsl(263deg 100% 21%), - 900: hsl(264deg 100% 10%), - 950: hsl(264deg 100% 5%), -); -$primitive-violet: ( - 50: hsl(279deg 100% 96%), - 100: hsl(280deg 100% 92%), - 200: hsl(280deg 100% 84%), - 300: hsl(280deg 100% 76%), - 400: hsl(280deg 100% 68%), - 500: hsl(280deg 100% 60%), - 600: hsl(280deg 100% 48%), - 700: hsl(280deg 100% 36%), - 800: hsl(280deg 100% 24%), - 900: hsl(280deg 100% 12%), - 950: hsl(279deg 100% 6%), +$primitive-stone: ( + 50: hsl(60deg 9% 98%), + 100: hsl(60deg 5% 96%), + 200: hsl(20deg 6% 90%), + 300: hsl(24deg 6% 83%), + 400: hsl(24deg 5% 64%), + 500: hsl(25deg 5% 45%), + 600: hsl(24deg 5% 32%), + 700: hsl(24deg 6% 25%), + 800: hsl(25deg 7% 15%), + 900: hsl(24deg 10% 10%), + 950: hsl(20deg 14% 4%), ); $primitive-red: ( - 50: hsl(8deg 85% 95%), - 100: hsl(8deg 86% 89%), - 200: hsl(8deg 88% 78%), - 300: hsl(8deg 87% 67%), - 400: hsl(8deg 87% 56%), - 500: hsl(8deg 87% 45%), - 600: hsl(8deg 87% 36%), - 700: hsl(8deg 87% 27%), - 800: hsl(8deg 87% 18%), - 900: hsl(8deg 87% 9%), - 950: hsl(8deg 85% 5%), -); -$primitive-amber: ( - 50: hsl(36deg 71% 93%), - 100: hsl(38deg 70% 87%), - 200: hsl(38deg 70% 74%), - 300: hsl(38deg 71% 60%), - 400: hsl(38deg 70% 47%), - 500: hsl(38deg 70% 34%), - 600: hsl(38deg 70% 27%), - 700: hsl(38deg 71% 20%), - 800: hsl(37deg 69% 14%), - 900: hsl(38deg 71% 7%), - 950: hsl(38deg 73% 3%), + 50: hsl(0deg 86% 97%), + 100: hsl(0deg 93% 94%), + 200: hsl(0deg 96% 89%), + 300: hsl(0deg 94% 82%), + 400: hsl(0deg 91% 71%), + 500: hsl(0deg 84% 60%), + 600: hsl(0deg 72% 51%), + 700: hsl(0deg 74% 42%), + 800: hsl(0deg 70% 35%), + 900: hsl(0deg 63% 31%), + 950: hsl(0deg 75% 15%), ); $primitive-orange: ( - 50: hsl(30deg 100% 95%), - 100: hsl(31deg 100% 90%), - 200: hsl(30deg 100% 80%), - 300: hsl(30deg 100% 70%), - 400: hsl(30deg 100% 60%), - 500: hsl(30deg 100% 50%), - 600: hsl(30deg 100% 40%), - 700: hsl(30deg 100% 30%), - 800: hsl(30deg 100% 20%), - 900: hsl(31deg 100% 10%), - 950: hsl(31deg 100% 5%), + 50: hsl(33deg 100% 97%), + 100: hsl(34deg 100% 92%), + 200: hsl(32deg 98% 83%), + 300: hsl(30deg 97% 72%), + 400: hsl(27deg 96% 61%), + 500: hsl(24deg 95% 53%), + 600: hsl(23deg 90% 48%), + 700: hsl(21deg 88% 40%), + 800: hsl(17deg 79% 34%), + 900: hsl(15deg 75% 28%), + 950: hsl(13deg 82% 14%), +); +$primitive-amber: ( + 50: hsl(48deg 100% 96%), + 100: hsl(48deg 96% 89%), + 200: hsl(48deg 97% 77%), + 300: hsl(46deg 97% 65%), + 400: hsl(43deg 96% 56%), + 500: hsl(38deg 92% 50%), + 600: hsl(32deg 95% 44%), + 700: hsl(26deg 90% 37%), + 800: hsl(23deg 83% 31%), + 900: hsl(22deg 78% 26%), + 950: hsl(21deg 92% 14%), ); $primitive-yellow: ( - 50: hsl(39deg 100% 95%), - 100: hsl(39deg 100% 90%), - 200: hsl(39deg 100% 80%), - 300: hsl(39deg 100% 70%), - 400: hsl(39deg 100% 60%), - 500: hsl(39deg 100% 50%), - 600: hsl(39deg 100% 40%), - 700: hsl(39deg 100% 30%), - 800: hsl(39deg 100% 20%), - 900: hsl(39deg 100% 10%), - 950: hsl(41deg 100% 5%), + 50: hsl(55deg 92% 95%), + 100: hsl(55deg 97% 88%), + 200: hsl(53deg 98% 77%), + 300: hsl(54deg 98% 64%), + 400: hsl(50deg 98% 53%), + 500: hsl(48deg 96% 48%), + 600: hsl(45deg 93% 47%), + 700: hsl(35deg 92% 33%), + 800: hsl(32deg 81% 29%), + 900: hsl(28deg 73% 26%), + 950: hsl(26deg 83% 14%), +); +$primitive-lime: ( + 50: hsl(78deg 92% 95%), + 100: hsl(80deg 89% 89%), + 200: hsl(81deg 88% 80%), + 300: hsl(82deg 85% 67%), + 400: hsl(83deg 78% 55%), + 500: hsl(84deg 81% 44%), + 600: hsl(85deg 85% 35%), + 700: hsl(86deg 78% 27%), + 800: hsl(86deg 69% 23%), + 900: hsl(88deg 61% 20%), + 950: hsl(89deg 80% 10%), ); $primitive-green: ( - 50: hsl(100deg 100% 93%), - 100: hsl(101deg 100% 85%), - 200: hsl(101deg 100% 70%), - 300: hsl(101deg 100% 56%), - 400: hsl(101deg 100% 41%), - 500: hsl(101deg 100% 26%), - 600: hsl(101deg 100% 21%), - 700: hsl(101deg 100% 16%), - 800: hsl(101deg 100% 10%), - 900: hsl(101deg 100% 5%), - 950: hsl(100deg 100% 3%), + 50: hsl(138deg 76% 97%), + 100: hsl(141deg 84% 93%), + 200: hsl(141deg 79% 85%), + 300: hsl(142deg 77% 73%), + 400: hsl(142deg 69% 58%), + 500: hsl(142deg 71% 45%), + 600: hsl(142deg 76% 36%), + 700: hsl(142deg 72% 29%), + 800: hsl(142deg 64% 24%), + 900: hsl(143deg 64% 20%), + 950: hsl(144deg 80% 10%), ); -$primitive-pink: ( - 50: hsl(324deg 100% 97%), - 100: hsl(321deg 100% 94%), - 200: hsl(323deg 100% 89%), - 300: hsl(322deg 100% 83%), - 400: hsl(322deg 100% 78%), - 500: hsl(322deg 100% 72%), - 600: hsl(322deg 100% 58%), - 700: hsl(322deg 100% 43%), - 800: hsl(322deg 100% 29%), - 900: hsl(322deg 100% 14%), - 950: hsl(322deg 100% 7%), +$primitive-emerald: ( + 50: hsl(152deg 81% 96%), + 100: hsl(149deg 80% 90%), + 200: hsl(152deg 76% 80%), + 300: hsl(156deg 72% 67%), + 400: hsl(158deg 64% 52%), + 500: hsl(160deg 84% 39%), + 600: hsl(161deg 94% 30%), + 700: hsl(163deg 94% 24%), + 800: hsl(163deg 88% 20%), + 900: hsl(164deg 86% 16%), + 950: hsl(165deg 91% 9%), ); $primitive-teal: ( - 50: hsl(180deg 100% 93%), - 100: hsl(180deg 100% 85%), - 200: hsl(180deg 100% 70%), - 300: hsl(180deg 100% 56%), - 400: hsl(180deg 100% 41%), - 500: hsl(180deg 100% 26%), - 600: hsl(180deg 100% 21%), - 700: hsl(180deg 100% 16%), - 800: hsl(180deg 100% 10%), - 900: hsl(180deg 100% 5%), - 950: hsl(180deg 100% 3%), + 50: hsl(166deg 76% 97%), + 100: hsl(167deg 85% 89%), + 200: hsl(168deg 84% 78%), + 300: hsl(171deg 77% 64%), + 400: hsl(172deg 66% 50%), + 500: hsl(173deg 80% 40%), + 600: hsl(175deg 84% 32%), + 700: hsl(175deg 77% 26%), + 800: hsl(176deg 69% 22%), + 900: hsl(176deg 61% 19%), + 950: hsl(178deg 75% 10%), +); +$primitive-cyan: ( + 50: hsl(183deg 100% 96%), + 100: hsl(185deg 96% 90%), + 200: hsl(186deg 94% 82%), + 300: hsl(187deg 92% 69%), + 400: hsl(188deg 86% 53%), + 500: hsl(188deg 95% 43%), + 600: hsl(192deg 91% 36%), + 700: hsl(193deg 82% 31%), + 800: hsl(194deg 70% 27%), + 900: hsl(196deg 64% 24%), + 950: hsl(197deg 79% 15%), +); +$primitive-sky: ( + 50: hsl(204deg 100% 97%), + 100: hsl(204deg 94% 94%), + 200: hsl(201deg 94% 86%), + 300: hsl(199deg 95% 74%), + 400: hsl(198deg 93% 60%), + 500: hsl(199deg 89% 48%), + 600: hsl(200deg 98% 39%), + 700: hsl(201deg 96% 32%), + 800: hsl(201deg 90% 27%), + 900: hsl(202deg 80% 24%), + 950: hsl(204deg 80% 16%), +); +$primitive-blue: ( + 50: hsl(214deg 100% 97%), + 100: hsl(214deg 95% 93%), + 200: hsl(213deg 97% 87%), + 300: hsl(212deg 96% 78%), + 400: hsl(213deg 94% 68%), + 500: hsl(217deg 91% 60%), + 600: hsl(221deg 83% 53%), + 700: hsl(221deg 83% 48%), + 800: hsl(221deg 83% 41%), + 900: hsl(224deg 76% 33%), + 950: hsl(226deg 57% 21%), +); +$primitive-indigo: ( + 50: hsl(226deg 100% 97%), + 100: hsl(226deg 100% 94%), + 200: hsl(228deg 96% 89%), + 300: hsl(230deg 94% 82%), + 400: hsl(234deg 89% 74%), + 500: hsl(239deg 84% 67%), + 600: hsl(243deg 75% 59%), + 700: hsl(245deg 58% 51%), + 800: hsl(244deg 55% 41%), + 900: hsl(242deg 47% 34%), + 950: hsl(244deg 47% 20%), +); +$primitive-violet: ( + 50: hsl(250deg 100% 97%), + 100: hsl(251deg 91% 95%), + 200: hsl(251deg 95% 92%), + 300: hsl(252deg 95% 85%), + 400: hsl(255deg 92% 76%), + 500: hsl(258deg 90% 66%), + 600: hsl(262deg 83% 58%), + 700: hsl(263deg 70% 50%), + 800: hsl(263deg 69% 42%), + 900: hsl(264deg 67% 35%), + 950: hsl(266deg 66% 22%), +); +$primitive-purple: ( + 50: hsl(270deg 100% 98%), + 100: hsl(268deg 100% 95%), + 200: hsl(268deg 100% 92%), + 300: hsl(269deg 97% 85%), + 400: hsl(270deg 95% 75%), + 500: hsl(271deg 91% 65%), + 600: hsl(271deg 81% 56%), + 700: hsl(272deg 72% 47%), + 800: hsl(273deg 67% 39%), + 900: hsl(274deg 66% 32%), + 950: hsl(276deg 87% 21%), +); +$primitive-fuchsia: ( + 50: hsl(289deg 100% 98%), + 100: hsl(287deg 100% 95%), + 200: hsl(288deg 96% 91%), + 300: hsl(291deg 93% 83%), + 400: hsl(292deg 91% 73%), + 500: hsl(292deg 84% 61%), + 600: hsl(293deg 69% 49%), + 700: hsl(295deg 72% 40%), + 800: hsl(295deg 70% 32%), + 900: hsl(295deg 63% 28%), + 950: hsl(297deg 90% 16%), +); +$primitive-pink: ( + 50: hsl(327deg 73% 97%), + 100: hsl(326deg 78% 95%), + 200: hsl(326deg 85% 90%), + 300: hsl(327deg 87% 82%), + 400: hsl(329deg 86% 70%), + 500: hsl(330deg 81% 60%), + 600: hsl(333deg 71% 51%), + 700: hsl(335deg 78% 42%), + 800: hsl(336deg 74% 35%), + 900: hsl(336deg 69% 30%), + 950: hsl(337deg 75% 16%), +); +$primitive-rose: ( + 50: hsl(355deg 100% 97%), + 100: hsl(355deg 100% 95%), + 200: hsl(352deg 96% 90%), + 300: hsl(352deg 95% 82%), + 400: hsl(351deg 95% 71%), + 500: hsl(349deg 89% 60%), + 600: hsl(346deg 77% 50%), + 700: hsl(345deg 83% 41%), + 800: hsl(343deg 80% 35%), + 900: hsl(343deg 73% 30%), + 950: hsl(343deg 88% 16%), ); :root { // Primitive colors (mapped to css custom properties) // ////////////////////////////////////////////////////////// - --primitive-transparent: $primitive-transparent; - --primitive-white: $primitive-white; - --primitive-black: $primitive-black; + --primitive-transparent: #{$primitive-transparent}; + --primitive-white: #{$primitive-white}; + --primitive-black: #{$primitive-black}; // PostHog brand colors // Split into hue, saturation, lightness to make it easier to change the brand colors @@ -225,13 +321,24 @@ $primitive-teal: ( --brand-secondary-lightness: var(--primitive-posthog-brand-blue-lightness); // Accent colors (accents can be changed to change the brand colors) - // Used for interactive elements like inputs, toggles, charts, etc. + // Used for interactive elements and highlights, inputs, toggles, charts, etc. // ////////////////////////////////////////////////////////// --accent-primary: hsl(var(--brand-primary-hue), var(--brand-primary-saturation), var(--brand-primary-lightness)); --accent-primary-hover: hsl( var(--brand-primary-hue), var(--brand-primary-saturation), - calc(var(--brand-primary-lightness) + 20%) + calc(var(--brand-primary-lightness) + 10%) + ); + --accent-primary-active: hsl( + var(--brand-primary-hue), + var(--brand-primary-saturation), + calc(var(--brand-primary-lightness) + 15%) + ); + --accent-primary-highlight: hsla( + var(--brand-primary-hue), + var(--brand-primary-saturation), + var(--brand-primary-lightness), + 10% ); --accent-secondary: hsl( var(--brand-secondary-hue), @@ -241,7 +348,18 @@ $primitive-teal: ( --accent-secondary-hover: hsl( var(--brand-secondary-hue), var(--brand-secondary-saturation), - calc(var(--brand-secondary-lightness) + 20%) + calc(var(--brand-secondary-lightness) + 10%) + ); + --accent-secondary-active: hsl( + var(--brand-secondary-hue), + var(--brand-secondary-saturation), + calc(var(--brand-secondary-lightness) + 15%) + ); + --accent-secondary-highlight: hsla( + var(--brand-secondary-hue), + var(--brand-secondary-saturation), + var(--brand-secondary-lightness), + 10% ); // Backgrounds @@ -255,29 +373,35 @@ $primitive-teal: ( // --surface-primary: var(--primitive-white); /* Most prominent surface (Cards, tables, etc.) */ // Content (text, icons) - // General text, icons, etc on surfaces - // ////////////////////////////////////////////////////////// - --content-primary: var(--primitive-neutral-950); /* Text on primary surface/background */ + --text-primary: var(--primitive-neutral-950); /* Text on primary surface/background */ // Fills // Small colourful areas: banners, pills, buttons, etc. // ////////////////////////////////////////////////////////// - --fill-info-overlay: var(--primitive-3000-25); /* Fill for info blocks */ - --fill-warning-overlay: var(--primitive-orange-50); /* Fill for warning blocks */ - --fill-error-overlay: var(--primitive-red-50); /* Fill for error blocks */ - --fill-success-overlay: var(--primitive-green-50); /* Fill for success blocks */ + --bg-fill-primary: var(--primitive-white); + --bg-fill-secondary: var(--primitive-3000-25); + --bg-fill-tertiary: var(--primitive-3000-50); + --bg-fill-info-secondary: var(--primitive-blue-100); /* promonent bg for info blocks */ + --bg-fill-info-tertiary: var(--primitive-blue-50); /* subtle bg for info blocks */ + --bg-fill-warning-secondary: var(--primitive-orange-100); /* prominent bg for warning blocks */ + --bg-fill-warning-tertiary: var(--primitive-orange-50); /* subtle bg for warning blocks */ + --bg-fill-error-secondary: var(--primitive-red-100); /* prominent bg for error blocks */ + --bg-fill-error-tertiary: var(--primitive-red-50); /* subtle bg for error blocks */ + --bg-fill-success-secondary: var(--primitive-green-100); /* promonent bg for success blocks */ + --bg-fill-success-tertiary: var(--primitive-green-50); /* subtle bg for success blocks */ // Content on fills // Texts which are on colourful fills, ensures contrast // ////////////////////////////////////////////////////////// - --content-on-fill-warning: var(--primitive-orange-800); /* On-fill text for warning */ - --content-on-fill-error: var(--primitive-red-800); /* On-fill text for error */ - --content-on-fill-success: var(--primitive-green-800); /* On-fill text for success */ + --text-warning-on-bg-fill: var(--primitive-orange-800); /* On-fill text for warning */ + --text-error-on-bg-fill: var(--primitive-red-800); /* On-fill text for error */ + --text-success-on-bg-fill: var(--primitive-green-800); /* On-fill text for success */ // Borders // Borders for surfaces/fills (TODO: perhaps we need to do border-fill-primary, border-fill-warning, etc.) // ////////////////////////////////////////////////////////// - --border-info: var(--primitive-3000-300); /* Most prominent border */ + --border-primary: var(--primitive-3000-200); + --border-info: var(--primitive-blue-400); --border-warning: var(--primitive-orange-400); /* Border for warning */ --border-error: var(--primitive-red-400); /* Border for error */ --border-success: var(--primitive-green-400); /* Border for success */ @@ -286,53 +410,81 @@ $primitive-teal: ( --primitive-3000-#{$name}: #{$value}; } - @each $name, $value in $primitive-neutral { - --primitive-neutral-#{$name}: #{$value}; - } - @each $name, $value in $primitive-neutral-cool { --primitive-neutral-cool-#{$name}: #{$value}; } - @each $name, $value in $primitive-blue { - --primitive-blue-#{$name}: #{$value}; - } - - @each $name, $value in $primitive-purple { - --primitive-purple-#{$name}: #{$value}; - } - - @each $name, $value in $primitive-violet { - --primitive-violet-#{$name}: #{$value}; + @each $name, $value in $primitive-stone { + --primitive-stone-#{$name}: #{$value}; } @each $name, $value in $primitive-red { --primitive-red-#{$name}: #{$value}; } - @each $name, $value in $primitive-amber { - --primitive-amber-#{$name}: #{$value}; - } - @each $name, $value in $primitive-orange { --primitive-orange-#{$name}: #{$value}; } + @each $name, $value in $primitive-amber { + --primitive-amber-#{$name}: #{$value}; + } + @each $name, $value in $primitive-yellow { --primitive-yellow-#{$name}: #{$value}; } + @each $name, $value in $primitive-lime { + --primitive-lime-#{$name}: #{$value}; + } + @each $name, $value in $primitive-green { --primitive-green-#{$name}: #{$value}; } - @each $name, $value in $primitive-pink { - --primitive-pink-#{$name}: #{$value}; + @each $name, $value in $primitive-emerald { + --primitive-emerald-#{$name}: #{$value}; } @each $name, $value in $primitive-teal { --primitive-teal-#{$name}: #{$value}; } + + @each $name, $value in $primitive-cyan { + --primitive-cyan-#{$name}: #{$value}; + } + + @each $name, $value in $primitive-sky { + --primitive-sky-#{$name}: #{$value}; + } + + @each $name, $value in $primitive-blue { + --primitive-blue-#{$name}: #{$value}; + } + + @each $name, $value in $primitive-indigo { + --primitive-indigo-#{$name}: #{$value}; + } + + @each $name, $value in $primitive-violet { + --primitive-violet-#{$name}: #{$value}; + } + + @each $name, $value in $primitive-purple { + --primitive-purple-#{$name}: #{$value}; + } + + @each $name, $value in $primitive-fuchsia { + --primitive-fuchsia-#{$name}: #{$value}; + } + + @each $name, $value in $primitive-pink { + --primitive-pink-#{$name}: #{$value}; + } + + @each $name, $value in $primitive-rose { + --primitive-rose-#{$name}: #{$value}; + } } [theme='dark'] { @@ -346,7 +498,39 @@ $primitive-teal: ( --accent-primary-hover: hsl( var(--brand-primary-hue), var(--brand-primary-saturation), - calc(var(--brand-primary-lightness) + 20%) + calc(var(--brand-primary-lightness) + 10%) + ); + --accent-primary-active: hsl( + var(--brand-primary-hue), + var(--brand-primary-saturation), + calc(var(--brand-primary-lightness) + 15%) + ); + --accent-primary-highlight: hsla( + var(--brand-primary-hue), + var(--brand-primary-saturation), + var(--brand-primary-lightness), + 10% + ); + --accent-secondary: hsl( + var(--brand-secondary-hue), + var(--brand-secondary-saturation), + var(--brand-secondary-lightness) + ); + --accent-secondary-hover: hsl( + var(--brand-secondary-hue), + var(--brand-secondary-saturation), + calc(var(--brand-secondary-lightness) + 10%) + ); + --accent-secondary-active: hsl( + var(--brand-secondary-hue), + var(--brand-secondary-saturation), + calc(var(--brand-secondary-lightness) + 15%) + ); + --accent-secondary-highlight: hsla( + var(--brand-secondary-hue), + var(--brand-secondary-saturation), + var(--brand-secondary-lightness), + 10% ); // Backgrounds @@ -357,29 +541,33 @@ $primitive-teal: ( // Fills // ////////////////////////////////////////////////////////// - --fill-info-overlay: var(--primitive-neutral-cool-900); - --fill-warning-overlay: var(--primitive-amber-800); - --fill-error-overlay: var(--primitive-red-900); - --fill-success-overlay: var(--primitive-green-800); - - // Content + --bg-fill-primary: var(--primitive-neutral-cool-900); + --bg-fill-secondary: var(--primitive-neutral-cool-800); + --bg-fill-tertiary: var(--primitive-neutral-cool-700); + --bg-fill-info-secondary: var(--primitive-blue-950); + --bg-fill-info-tertiary: #{rgba(map.get($primitive-blue, 950), 0.5)}; + --bg-fill-warning-secondary: var(--primitive-yellow-950); + --bg-fill-warning-tertiary: #{rgba(map.get($primitive-yellow, 950), 0.5)}; + --bg-fill-error-secondary: var(--primitive-red-950); + --bg-fill-error-tertiary: #{rgba(map.get($primitive-red, 950), 0.5)}; + --bg-fill-success-secondary: var(--primitive-green-950); + --bg-fill-success-tertiary: #{rgba(map.get($primitive-green, 950), 0.5)}; + + // Contents // ////////////////////////////////////////////////////////// - --content-primary: var(--primitive-neutral-100); + --text-primary: var(--primitive-neutral-100); // Content on fills // ////////////////////////////////////////////////////////// - --content-on-fill-warning: var(--primitive-orange-100); - --content-on-fill-error: var(--primitive-red-100); - --content-on-fill-success: var(--primitive-green-50); + --text-warning-on-bg-fill: var(--primitive-orange-50); + --text-error-on-bg-fill: var(--primitive-red-50); + --text-success-on-bg-fill: var(--primitive-green-50); // Borders // ////////////////////////////////////////////////////////// - --border-info: var(--primitive-neutral-700); - --border-warning: var(--primitive-orange-700); - --border-error: var(--primitive-red-700); - --border-success: var(--primitive-green-700); -} - -.fill-accent-primary { - background-color: var(--accent-primary); + --border-primary: var(--primitive-neutral-cool-800); + --border-info: var(--primitive-blue-900); + --border-warning: var(--primitive-orange-900); + --border-error: var(--primitive-red-900); + --border-success: var(--primitive-green-900); } diff --git a/frontend/src/styles/global.scss b/frontend/src/styles/global.scss index ba19c8afb81fe..987878d27dd0c 100644 --- a/frontend/src/styles/global.scss +++ b/frontend/src/styles/global.scss @@ -438,7 +438,7 @@ body { &:hover { .text-link { - color: var(--primary-3000) !important; + color: var(--accent-primary) !important; } } } diff --git a/frontend/src/styles/vars.scss b/frontend/src/styles/vars.scss index 37113d6370317..0b0de93c96fb8 100644 --- a/frontend/src/styles/vars.scss +++ b/frontend/src/styles/vars.scss @@ -17,7 +17,8 @@ $colors: ( 'warning-highlight': rgba(#f7a501, 0.1), 'warning': #f7a501, 'warning-dark': #e09423, - 'highlight': #e49f2c, + 'highlight': var(--accent-primary), + // #e49f2c, //TODO: remove this 'success-highlight': rgba(#388600, 0.1), 'success-light': #5f9d32, 'success': #388600, @@ -33,7 +34,8 @@ $colors: ( 'border-light': rgb(0 0 0 / 8%), 'border-bold': rgb(0 0 0 / 24%), 'transparent': transparent, - 'link': var(--primary-3000), + 'link': var(--accent-primary), + // #f54e00, //TODO: remove this // Colors of the PostHog logo 'brand-blue': #1d4aff, 'brand-red': #f54e00, @@ -45,11 +47,13 @@ $colors: ( 'text-secondary-3000-light': rgba(#111, 0.7), 'muted-3000-light': rgba(#111, 0.6), 'trace-3000-light': rgba(#111, 0.25), - 'primary-3000-light': #f54e01, + 'primary-3000-light': var(--accent-primary), + // #f54e01, 'primary-highlight-light': rgba(#f54e01, 0.1), - 'primary-3000-hover-light': #f54e01, - 'primary-3000-active-light': #f54e01, - + 'primary-3000-hover-light': var(--accent-primary-hover), + // #f54e01, //TODO: remove this + 'primary-3000-active-light': var(--accent-primary-active), + // #f54e01, //TODO: remove this 'secondary-3000-light': rgba(#cfd1c2, 0.6), 'secondary-3000-hover-light': #cfd1c2, 'accent-3000-light': #eeefe9, @@ -59,7 +63,8 @@ $colors: ( 'glass-bg-3000-light': #e4e5deb3, 'glass-border-3000-light': #e4e5de, - 'link-3000-light': #f54e00, + 'link-3000-light': var(--accent-primary), + // #f54e00, //TODO: remove this 'primary-3000-frame-bg-light': #eb9d2a, 'primary-3000-button-bg-light': #fff, 'primary-3000-button-border-light': #b17816, @@ -82,8 +87,10 @@ $colors: ( 'trace-3000-dark': rgba(#fff, 0.25), 'primary-3000-dark': #f7a503, 'primary-highlight-dark': rgba(#f7a503, 0.1), - 'primary-3000-hover-dark': #f7a503, - 'primary-3000-active-dark': #f7a503, + 'primary-3000-hover-dark': var(--accent-primary-hover), + // #f7a503, //TODO: remove this + 'primary-3000-active-dark': var(--accent-primary-active), + // #f7a503, //TODO: remove this 'primary-alt-highlight-light': #e5e7e0, 'secondary-3000-dark': #1d1f27, @@ -94,8 +101,8 @@ $colors: ( 'border-bold-3000-dark': #3f4046, 'glass-bg-3000-dark': #24262a99, 'glass-border-3000-dark': var(--border-3000-dark), - 'link-3000-dark': #f1a82c, - + 'link-3000-dark': var(--accent-primary), + // #f1a82c, 'primary-3000-frame-bg-dark': #926826, 'primary-3000-button-bg-dark': #e0a045, 'primary-3000-button-border-dark': #b17816, @@ -117,7 +124,7 @@ $colors: ( 'text-3000': var(--text-3000), 'text-secondary-3000': var(--text-secondary-3000), 'muted-3000': var(--muted-3000), - 'primary-3000': var(--primary-3000), + 'primary-3000': var(--accent-primary), 'secondary-3000': var(--secondary-3000), 'secondary-3000-hover': var(--secondary-3000-hover), 'accent-3000': var(--accent-3000), @@ -221,7 +228,7 @@ $_lifecycle_dormant: map.get($colors, 'danger'); // which means they aren't available in the toolbar --toastify-color-dark: var(--accent-3000-dark); --toastify-color-light: var(--bg-light); - --toastify-color-info: var(--primary); + --toastify-color-info: var(--accent-primary); --toastify-color-success: var(--success); --toastify-color-warning: var(--warning); --toastify-color-error: var(--danger); @@ -280,10 +287,10 @@ $_lifecycle_dormant: map.get($colors, 'danger'); --text-secondary-3000: var(--text-secondary-3000-light); --muted-3000: var(--muted-3000-light); --trace-3000: var(--trace-3000-light); - --primary-3000: var(--primary-3000-light); - --primary-highlight: var(--primary-highlight-light); - --primary-3000-hover: var(--primary-3000-hover-light); - --primary-3000-active: var(--primary-3000-active-light); + --primary-3000: var(--accent-primary); // var(--primary-3000-light); // TODO: remove this + --primary-highlight: var(--accent-primary-highlight); // var(--primary-highlight-light); // TODO: remove this + --primary-3000-hover: var(--accent-primary-hover); // var(--primary-3000-hover-light); // TODO: remove this + --primary-3000-active: var(--accent-primary-active); // var(--primary-3000-active-light); // TODO: remove this --secondary-3000: var(--secondary-3000-light); --secondary-3000-hover: var(--secondary-3000-hover-light); --accent-3000: var(--accent-3000-light); @@ -318,10 +325,10 @@ $_lifecycle_dormant: map.get($colors, 'danger'); --text-secondary-3000: var(--text-secondary-3000-dark); --muted-3000: var(--muted-3000-dark); --trace-3000: var(--trace-3000-dark); - --primary-3000: var(--primary-3000-dark); - --primary-highlight: var(--primary-highlight-dark); - --primary-3000-hover: var(--primary-3000-hover-dark); - --primary-3000-active: var(--primary-3000-active-dark); + --primary-3000: var(--accent-primary); // var(--primary-3000-dark); // TODO: remove this + --primary-highlight: var(--accent-primary-highlight); // var(--primary-highlight-dark); // TODO: remove this + --primary-3000-hover: var(--accent-primary-hover); // var(--primary-3000-hover-dark); // TODO: remove this + --primary-3000-active: var(--accent-primary-active); // var(--primary-3000-active-dark); // TODO: remove this --secondary-3000: var(--secondary-3000-dark); --secondary-3000-hover: var(--secondary-3000-hover-dark); --accent-3000: var(--accent-3000-dark); @@ -359,7 +366,7 @@ $_lifecycle_dormant: map.get($colors, 'danger'); // defined here so that they can be shared with the toolbar @mixin common-variables { - --primary: var(--primary-3000); + --primary: var(--primary-3000); // Changed to accent-primary --muted: var(--muted-3000); --default: var(--text-3000); diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 5f7747f05794c..32a84b2a4c527 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -167,6 +167,7 @@ export enum AvailableFeature { AUTOMATIC_PROVISIONING = 'automatic_provisioning', MANAGED_REVERSE_PROXY = 'managed_reverse_proxy', ALERTS = 'alerts', + DATA_COLOR_THEMES = 'data_color_themes', } type AvailableFeatureUnion = `${AvailableFeature}` @@ -2989,6 +2990,7 @@ export interface FeatureFlagType extends Omit { - await this.queueInvocationToKafka(item) - }) - ) - } - - protected async queueInvocationToKafka(invocation: HogFunctionInvocation) { - // NOTE: WE keep the queueParams args as kafka land still needs them - const serializedInvocation: HogFunctionInvocationSerialized = { - ...invocation, - hogFunctionId: invocation.hogFunction.id, - } - - if (invocation.queue === 'fetch') { - // Track a metric purely to say a fetch was attempted (this may be what we bill on in the future) - this.produceAppMetric({ - team_id: invocation.teamId, - app_source_id: invocation.hogFunction.id, - metric_kind: 'other', - metric_name: 'fetch', - count: 1, - }) - } - - delete (serializedInvocation as any).hogFunction - - const request: HogFunctionInvocationSerializedCompressed = { - state: await gzipObject(serializedInvocation), - } - - // NOTE: This is very temporary as it is producing the response. the response will actually be produced by the 3rd party service - // Later this will actually be the _request_ which we will push to the async function topic if we make one - this.messagesToProduce.push({ - topic: KAFKA_CDP_FUNCTION_CALLBACKS, - value: request, - key: `${invocation.hogFunction.id}:${invocation.id}`, - }) - } - protected async processInvocationResults(results: HogFunctionInvocationResult[]): Promise { return await runInstrumentedFunction({ statsKey: `cdpConsumer.handleEachBatch.produceResults`, diff --git a/plugin-server/src/cdp/consumers/cdp-cyclotron-worker.consumer.ts b/plugin-server/src/cdp/consumers/cdp-cyclotron-worker.consumer.ts index 50746972b5c29..89e752d9cb7f3 100644 --- a/plugin-server/src/cdp/consumers/cdp-cyclotron-worker.consumer.ts +++ b/plugin-server/src/cdp/consumers/cdp-cyclotron-worker.consumer.ts @@ -24,13 +24,14 @@ export class CdpCyclotronWorker extends CdpConsumerBase { const invocationResults = await runInstrumentedFunction({ statsKey: `cdpConsumer.handleEachBatch.executeInvocations`, func: async () => { - // NOTE: In the future this service will never do fetching (unless we decide we want to do it in node at some point) - // This is just "for now" to support the transition to cyclotron + // NOTE: this service will never do fetching (unless we decide we want to do it in node at some point, its only used for e2e testing) + // fetchExecutor would use rusty-hook to send a fetch request but thats no longer the case + // we are currentyl going to execute the fetch locally for testing purposes + // as nothing should ever land on the deprecated fetch queue this should be safe. const fetchQueue = invocations.filter((item) => item.queue === 'fetch') const fetchResults = await this.runManyWithHeartbeat(fetchQueue, (item) => this.fetchExecutor.execute(item) ) - const hogQueue = invocations.filter((item) => item.queue === 'hog') const hogResults = await this.runManyWithHeartbeat(hogQueue, (item) => this.hogExecutor.execute(item)) return [...hogResults, ...(fetchResults.filter(Boolean) as HogFunctionInvocationResult[])] diff --git a/plugin-server/src/cdp/consumers/cdp-function-callback.consumer.ts b/plugin-server/src/cdp/consumers/cdp-function-callback.consumer.ts index 520f683a5a09a..e69de29bb2d1d 100644 --- a/plugin-server/src/cdp/consumers/cdp-function-callback.consumer.ts +++ b/plugin-server/src/cdp/consumers/cdp-function-callback.consumer.ts @@ -1,139 +0,0 @@ -import { captureException } from '@sentry/node' -import { Message } from 'node-rdkafka' - -import { - HogFunctionInvocation, - HogFunctionInvocationResult, - HogFunctionInvocationSerialized, - HogFunctionInvocationSerializedCompressed, - HogFunctionTypeType, - HogHooksFetchResponse, -} from '~/src/cdp/types' - -import { KAFKA_CDP_FUNCTION_CALLBACKS } from '../../config/kafka-topics' -import { runInstrumentedFunction } from '../../main/utils' -import { status } from '../../utils/status' -import { unGzipObject } from '../utils' -import { CdpConsumerBase } from './cdp-base.consumer' - -export class CdpFunctionCallbackConsumer extends CdpConsumerBase { - protected name = 'CdpFunctionCallbackConsumer' - protected hogTypes: HogFunctionTypeType[] = ['destination', 'internal_destination'] - - public async processBatch(invocations: HogFunctionInvocation[]): Promise { - if (!invocations.length) { - return - } - - const invocationResults = await runInstrumentedFunction({ - statsKey: `cdpConsumer.handleEachBatch.executeInvocations`, - func: async () => { - // NOTE: In the future this service will never do fetching (unless we decide we want to do it in node at some point) - // This is just "for now" to support the transition to cyclotron - const fetchQueue = invocations.filter((item) => item.queue === 'fetch') - - const fetchResults = await Promise.all( - fetchQueue.map((item) => { - return runInstrumentedFunction({ - statsKey: `cdpConsumer.handleEachBatch.fetchExecutor.execute`, - func: () => this.fetchExecutor.execute(item), - timeout: 1000, - }) - }) - ) - - const hogQueue = invocations.filter((item) => item.queue === 'hog') - const hogResults = await this.runManyWithHeartbeat(hogQueue, (item) => this.hogExecutor.execute(item)) - return [...hogResults, ...(fetchResults.filter(Boolean) as HogFunctionInvocationResult[])] - }, - }) - - await this.processInvocationResults(invocationResults) - const newInvocations = invocationResults.filter((r) => !r.finished).map((r) => r.invocation) - await this.queueInvocationsToKafka(newInvocations) - await this.produceQueuedMessages() - } - - public async _parseKafkaBatch(messages: Message[]): Promise { - return await this.runWithHeartbeat(() => - runInstrumentedFunction({ - statsKey: `cdpConsumer.handleEachBatch.parseKafkaMessages`, - func: async () => { - // TRICKY: In the future we won't use kafka. For now though we need to parse messages as Cyclotron style jobs - // or hoghooks async callbacks - - const invocations: HogFunctionInvocation[] = [] - - // Parse the base message value - const entries: (HogHooksFetchResponse | HogFunctionInvocationSerializedCompressed)[] = messages - .map((message) => { - try { - return JSON.parse(message.value!.toString()) - } catch (e) { - status.error('Error parsing message', e) - } - - return undefined - }) - .filter(Boolean) - - // Deserialize the compressed data - await Promise.all( - entries.map(async (item) => { - try { - const invocationSerialized = await unGzipObject( - item.state - ) - - if ('asyncFunctionResponse' in item) { - // This means it is a callback from hoghooks so we need to add the response to the invocation - invocationSerialized.queue = 'hog' - invocationSerialized.queueParameters = item.asyncFunctionResponse - } - - const hogFunctionId = - invocationSerialized.hogFunctionId ?? invocationSerialized.hogFunction?.id - const hogFunction = hogFunctionId - ? this.hogFunctionManager.getHogFunction(hogFunctionId) - : undefined - - if (!hogFunction) { - status.error('Error finding hog function', { - id: invocationSerialized.hogFunctionId, - }) - return - } - - const invocation: HogFunctionInvocation = { - ...invocationSerialized, - hogFunction, - } - - delete (invocation as any).hogFunctionId - - invocations.push(invocation) - } catch (e) { - status.error('Error unzipping message', e, item.state) - captureException(e) - } - }) - ) - - return invocations - }, - }) - ) - } - - public async start(): Promise { - await super.start() - await this.startKafkaConsumer({ - topic: KAFKA_CDP_FUNCTION_CALLBACKS, - groupId: 'cdp-function-callback-consumer', - handleBatch: async (messages) => { - const invocations = await this._parseKafkaBatch(messages) - await this.processBatch(invocations) - }, - }) - } -} diff --git a/plugin-server/src/cdp/consumers/cdp-processed-events.consumer.ts b/plugin-server/src/cdp/consumers/cdp-processed-events.consumer.ts index 6e62511872a98..52b6e558c41e6 100644 --- a/plugin-server/src/cdp/consumers/cdp-processed-events.consumer.ts +++ b/plugin-server/src/cdp/consumers/cdp-processed-events.consumer.ts @@ -1,14 +1,13 @@ import { CyclotronManager } from '@posthog/cyclotron' import { Message } from 'node-rdkafka' -import { Hub, RawClickHouseEvent, ValueMatcher } from '~/src/types' +import { Hub, RawClickHouseEvent } from '~/src/types' import { convertToHogFunctionInvocationGlobals, fixLogDeduplication, serializeHogFunctionInvocation, } from '../../cdp/utils' -import { buildIntegerMatcher } from '../../config/config' import { KAFKA_EVENTS_JSON, KAFKA_LOG_ENTRIES } from '../../config/kafka-topics' import { runInstrumentedFunction } from '../../main/utils' import { status } from '../../utils/status' @@ -22,16 +21,10 @@ export class CdpProcessedEventsConsumer extends CdpConsumerBase { protected groupId = 'cdp-processed-events-consumer' protected hogTypes: HogFunctionTypeType[] = ['destination'] - private cyclotronMatcher: ValueMatcher private cyclotronManager?: CyclotronManager constructor(hub: Hub) { super(hub) - this.cyclotronMatcher = buildIntegerMatcher(hub.CDP_CYCLOTRON_ENABLED_TEAMS, true) - } - - private cyclotronEnabled(invocation: HogFunctionInvocation): boolean { - return !!(this.cyclotronManager && this.cyclotronMatcher(invocation.globals.project.id)) } public async processBatch(invocationGlobals: HogFunctionInvocationGlobals[]): Promise { @@ -43,22 +36,8 @@ export class CdpProcessedEventsConsumer extends CdpConsumerBase { this.createHogFunctionInvocations(invocationGlobals) ) - // Split out the cyclotron invocations - const [cyclotronInvocations, kafkaInvocations] = invocationsToBeQueued.reduce( - (acc, item) => { - if (this.cyclotronEnabled(item)) { - acc[0].push(item) - } else { - acc[1].push(item) - } - - return acc - }, - [[], []] as [HogFunctionInvocation[], HogFunctionInvocation[]] - ) - // For the cyclotron ones we simply create the jobs - const cyclotronJobs = cyclotronInvocations.map((item) => { + const cyclotronJobs = invocationsToBeQueued.map((item) => { return { teamId: item.globals.project.id, functionId: item.hogFunction.id, @@ -75,23 +54,6 @@ export class CdpProcessedEventsConsumer extends CdpConsumerBase { throw e } - if (kafkaInvocations.length) { - // As we don't want to over-produce to kafka we invoke the hog functions and then queue the results - const invocationResults = await runInstrumentedFunction({ - statsKey: `cdpConsumer.handleEachBatch.executeInvocations`, - func: async () => { - const hogResults = await this.runManyWithHeartbeat(kafkaInvocations, (item) => - this.hogExecutor.execute(item) - ) - return [...hogResults] - }, - }) - - await this.processInvocationResults(invocationResults) - const newInvocations = invocationResults.filter((r) => !r.finished).map((r) => r.invocation) - await this.queueInvocationsToKafka(newInvocations) - } - await this.produceQueuedMessages() return invocationsToBeQueued diff --git a/plugin-server/src/cdp/services/fetch-executor.service.ts b/plugin-server/src/cdp/services/fetch-executor.service.ts index 1dbe07e4d603e..0847ff393788a 100644 --- a/plugin-server/src/cdp/services/fetch-executor.service.ts +++ b/plugin-server/src/cdp/services/fetch-executor.service.ts @@ -1,87 +1,25 @@ import { DateTime } from 'luxon' -import { Histogram } from 'prom-client' -import { buildIntegerMatcher } from '../../config/config' -import { PluginsServerConfig, ValueMatcher } from '../../types' +import { PluginsServerConfig } from '../../types' import { trackedFetch } from '../../utils/fetch' import { status } from '../../utils/status' -import { RustyHook } from '../../worker/rusty-hook' import { HogFunctionInvocation, - HogFunctionInvocationAsyncRequest, HogFunctionInvocationResult, HogFunctionQueueParametersFetchRequest, HogFunctionQueueParametersFetchResponse, } from '../types' -import { gzipObject, serializeHogFunctionInvocation } from '../utils' - -export const BUCKETS_KB_WRITTEN = [0, 128, 512, 1024, 2024, 4096, 10240, Infinity] - -const histogramFetchPayloadSize = new Histogram({ - name: 'cdp_async_function_fetch_payload_size_kb', - help: 'The size in kb of the batches we are receiving from Kafka', - buckets: BUCKETS_KB_WRITTEN, -}) - -const histogramHogHooksPayloadSize = new Histogram({ - name: 'cdp_async_function_hoghooks_payload_size_kb', - help: 'The size in kb of the batches we are receiving from Kafka', - buckets: BUCKETS_KB_WRITTEN, -}) - /** * This class is only used by the kafka based queuing system. For the Cyclotron system there is no need for this. */ export class FetchExecutorService { - hogHookEnabledForTeams: ValueMatcher - - constructor(private serverConfig: PluginsServerConfig, private rustyHook: RustyHook) { - this.hogHookEnabledForTeams = buildIntegerMatcher(serverConfig.CDP_ASYNC_FUNCTIONS_RUSTY_HOOK_TEAMS, true) - } + constructor(private serverConfig: PluginsServerConfig) {} async execute(invocation: HogFunctionInvocation): Promise { if (invocation.queue !== 'fetch' || !invocation.queueParameters) { status.error('🦔', `[HogExecutor] Bad invocation`, { invocation }) return } - - const params = invocation.queueParameters as HogFunctionQueueParametersFetchRequest - - if (params.body) { - histogramFetchPayloadSize.observe(params.body.length / 1024) - } - - try { - if (this.hogHookEnabledForTeams(invocation.teamId)) { - // This is very temporary until we are commited to Cyclotron - const payload: HogFunctionInvocationAsyncRequest = { - state: await gzipObject(serializeHogFunctionInvocation(invocation)), - teamId: invocation.teamId, - hogFunctionId: invocation.hogFunction.id, - asyncFunctionRequest: { - name: 'fetch', - args: [ - params.url, - { - ...params, - }, - ], - }, - } - const hoghooksPayload = JSON.stringify(payload) - histogramHogHooksPayloadSize.observe(hoghooksPayload.length / 1024) - const enqueued = await this.rustyHook.enqueueForHog(hoghooksPayload) - if (enqueued) { - // We return nothing here hoghooks will take care of that - return - } - } - - status.info('🦔', `[HogExecutor] Webhook not sent via rustyhook, sending directly instead`) - } catch (err) { - status.error('🦔', `[HogExecutor] Error during fetch`, { error: String(err) }) - } - return await this.executeLocally(invocation) } diff --git a/plugin-server/src/cdp/types.ts b/plugin-server/src/cdp/types.ts index c712d13c3a216..bd154a2c2d29c 100644 --- a/plugin-server/src/cdp/types.ts +++ b/plugin-server/src/cdp/types.ts @@ -256,10 +256,6 @@ export type HogFunctionInvocationSerialized = Omit","token":"THIS IS NOT A TOKEN FOR TEAM 2","ip":"127.0.0.1","site_url":"us.posthog.com","now":"2025-01-01T00:00:00.000Z","event":"$pageview","properties":{"$current_url":"http://localhost:8000"}}", + "distinct_id": "user-1", + "ip": "127.0.0.1", + "now": "2025-01-01T00:00:00.000Z", + "token": "THIS IS NOT A TOKEN FOR TEAM 2", + "uuid": "", + }, + }, +] +`; + +exports[`IngestionConsumer general overflow should allow some events to pass 1`] = ` +[ + { + "key": null, + "topic": "events_plugin_ingestion_overflow_test", + "value": { + "data": "{"distinct_id":"overflow-distinct-id","uuid":"","token":"THIS IS NOT A TOKEN FOR TEAM 2","ip":"127.0.0.1","site_url":"us.posthog.com","now":"2025-01-01T00:00:00.000Z","event":"$pageview","properties":{"$current_url":"http://localhost:8000"}}", + "distinct_id": "overflow-distinct-id", + "ip": "127.0.0.1", + "now": "2025-01-01T00:00:00.000Z", + "token": "THIS IS NOT A TOKEN FOR TEAM 2", + "uuid": "", + }, + }, + { + "key": null, + "topic": "events_plugin_ingestion_overflow_test", + "value": { + "data": "{"distinct_id":"overflow-distinct-id","uuid":"","token":"THIS IS NOT A TOKEN FOR TEAM 2","ip":"127.0.0.1","site_url":"us.posthog.com","now":"2025-01-01T00:00:00.000Z","event":"$pageview","properties":{"$current_url":"http://localhost:8000"}}", + "distinct_id": "overflow-distinct-id", + "ip": "127.0.0.1", + "now": "2025-01-01T00:00:00.000Z", + "token": "THIS IS NOT A TOKEN FOR TEAM 2", + "uuid": "", + }, + }, +] +`; + +exports[`IngestionConsumer general overflow should emit to overflow if token and distinct_id are overflowed 1`] = ` +[ + { + "key": null, + "topic": "events_plugin_ingestion_overflow_test", + "value": { + "data": "{"distinct_id":"overflow-distinct-id","uuid":"","token":"THIS IS NOT A TOKEN FOR TEAM 2","ip":"127.0.0.1","site_url":"us.posthog.com","now":"2025-01-01T00:00:00.000Z","event":"$pageview","properties":{"$current_url":"http://localhost:8000"}}", + "distinct_id": "overflow-distinct-id", + "ip": "127.0.0.1", + "now": "2025-01-01T00:00:00.000Z", + "token": "THIS IS NOT A TOKEN FOR TEAM 2", + "uuid": "", + }, + }, +] +`; + +exports[`IngestionConsumer general should process a standard event 1`] = ` +[ + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "user-1", + "elements_chain": "", + "event": "$pageview", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"$current_url":"http://localhost:8000","$creator_event_uuid":"","$initial_current_url":"http://localhost:8000"}", + "project_id": 2, + "properties": "{"$current_url":"http://localhost:8000","$ip":"127.0.0.1","$set":{"$current_url":"http://localhost:8000"},"$set_once":{"$initial_current_url":"http://localhost:8000"}}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"$current_url":"http://localhost:8000","$creator_event_uuid":"","$initial_current_url":"http://localhost:8000"}", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_distinct_id_test", + "value": { + "distinct_id": "user-1", + "is_deleted": 0, + "person_id": "", + "team_id": 2, + "version": 0, + }, + }, +] +`; + +exports[`IngestionConsumer typical event processing $identify event 1`] = ` +[ + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "user-1", + "elements_chain": "", + "event": "$identify", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"$email":"test@test.com","$creator_event_uuid":""}", + "project_id": 2, + "properties": "{"$set":{"$email":"test@test.com"},"$ip":"127.0.0.1"}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"$email":"test@test.com","$creator_event_uuid":""}", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_distinct_id_test", + "value": { + "distinct_id": "user-1", + "is_deleted": 0, + "person_id": "", + "team_id": 2, + "version": 0, + }, + }, +] +`; + +exports[`IngestionConsumer typical event processing ai event 1`] = ` +[ + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "user-1", + "elements_chain": "", + "event": "$ai_generation", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"$creator_event_uuid":""}", + "project_id": 2, + "properties": "{"$ai_model":"gpt-4","$ai_provider":"openai","$ai_input_tokens":100,"$ai_output_tokens":50,"$ip":"127.0.0.1","$ai_input_cost_usd":0.003,"$ai_output_cost_usd":0.003,"$ai_total_cost_usd":0.006}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"$creator_event_uuid":""}", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_distinct_id_test", + "value": { + "distinct_id": "user-1", + "is_deleted": 0, + "person_id": "", + "team_id": 2, + "version": 0, + }, + }, +] +`; + +exports[`IngestionConsumer typical event processing client ingestion warning 1`] = ` +[ + { + "key": null, + "topic": "clickhouse_ingestion_warnings_test", + "value": { + "details": "{"eventUuid":"","event":"$$client_ingestion_warning","distinctId":"user-1","message":"test"}", + "source": "plugin-server", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "type": "client_ingestion_warning", + }, + }, +] +`; + +exports[`IngestionConsumer typical event processing event with common distinct_id that gets dropped 1`] = ` +[ + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "distinct_id", + "elements_chain": "", + "event": "$pageview", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"$current_url":"http://localhost:8000","$creator_event_uuid":"","$initial_current_url":"http://localhost:8000"}", + "project_id": 2, + "properties": "{"$current_url":"http://localhost:8000","$ip":"127.0.0.1","$set":{"$current_url":"http://localhost:8000"},"$set_once":{"$initial_current_url":"http://localhost:8000"}}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"$current_url":"http://localhost:8000","$creator_event_uuid":"","$initial_current_url":"http://localhost:8000"}", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_distinct_id_test", + "value": { + "distinct_id": "distinct_id", + "is_deleted": 0, + "person_id": "", + "team_id": 2, + "version": 0, + }, + }, +] +`; + +exports[`IngestionConsumer typical event processing malformed event 1`] = ` +[ + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "user-1", + "elements_chain": "", + "event": "", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"$current_url":"http://localhost:8000","$creator_event_uuid":"","$initial_current_url":"http://localhost:8000"}", + "project_id": 2, + "properties": "{"$current_url":"http://localhost:8000","$ip":"127.0.0.1","$set":{"$current_url":"http://localhost:8000"},"$set_once":{"$initial_current_url":"http://localhost:8000"}}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"$current_url":"http://localhost:8000","$creator_event_uuid":"","$initial_current_url":"http://localhost:8000"}", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_distinct_id_test", + "value": { + "distinct_id": "user-1", + "is_deleted": 0, + "person_id": "", + "team_id": 2, + "version": 0, + }, + }, +] +`; + +exports[`IngestionConsumer typical event processing malformed person information 1`] = ` +[ + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "distinct-id-1", + "elements_chain": "", + "event": "$pageview", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"0":"I","1":"N","2":"V","3":"A","4":"L","5":"I","6":"D","$creator_event_uuid":""}", + "project_id": 2, + "properties": "{"$set":"INVALID","$unset":[[[["definitel invalid"]]]],"$ip":"127.0.0.1"}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"0":"I","1":"N","2":"V","3":"A","4":"L","5":"I","6":"D","$creator_event_uuid":""}", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_distinct_id_test", + "value": { + "distinct_id": "distinct-id-1", + "is_deleted": 0, + "person_id": "", + "team_id": 2, + "version": 0, + }, + }, +] +`; + +exports[`IngestionConsumer typical event processing multiple events 1`] = ` +[ + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "anonymous-id-1", + "elements_chain": "", + "event": "$pageview", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"$current_url":"https://example.com/page1","$creator_event_uuid":"","$initial_current_url":"https://example.com/page1"}", + "project_id": 2, + "properties": "{"$current_url":"https://example.com/page1","$ip":"127.0.0.1","$set":{"$current_url":"https://example.com/page1"},"$set_once":{"$initial_current_url":"https://example.com/page1"}}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "identified-id-1", + "elements_chain": "", + "event": "$identify", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"$email":"test@test.com","$creator_event_uuid":""}", + "project_id": 2, + "properties": "{"$set":{"$email":"test@test.com"},"$anonymous_distinct_id":"anonymous-id-1","$ip":"127.0.0.1"}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "identified-id-1", + "elements_chain": "", + "event": "$pageview", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"$email":"test@test.com","$current_url":"https://example.com/page2","$creator_event_uuid":"","$initial_current_url":"https://example.com/page2"}", + "project_id": 2, + "properties": "{"$current_url":"https://example.com/page2","$ip":"127.0.0.1","$set":{"$current_url":"https://example.com/page2"},"$set_once":{"$initial_current_url":"https://example.com/page2"}}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"$current_url":"https://example.com/page1","$creator_event_uuid":"","$initial_current_url":"https://example.com/page1"}", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_distinct_id_test", + "value": { + "distinct_id": "anonymous-id-1", + "is_deleted": 0, + "person_id": "", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"$email":"test@test.com","$creator_event_uuid":""}", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_distinct_id_test", + "value": { + "distinct_id": "identified-id-1", + "is_deleted": 0, + "person_id": "", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"$email":"test@test.com","$current_url":"https://example.com/page2","$creator_event_uuid":"","$initial_current_url":"https://example.com/page2"}", + "team_id": 2, + "version": 1, + }, + }, +] +`; + +exports[`IngestionConsumer typical event processing normal event 1`] = ` +[ + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "user-1", + "elements_chain": "", + "event": "$pageview", + "person_created_at": "2025-01-01 00:00:00", + "person_id": "", + "person_mode": "full", + "person_properties": "{"$current_url":"http://localhost:8000","$creator_event_uuid":"","$initial_current_url":"http://localhost:8000"}", + "project_id": 2, + "properties": "{"$current_url":"http://localhost:8000","$ip":"127.0.0.1","$set":{"$current_url":"http://localhost:8000"},"$set_once":{"$initial_current_url":"http://localhost:8000"}}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, + { + "key": null, + "topic": "clickhouse_person_test", + "value": { + "created_at": "2025-01-01 00:00:00", + "id": "", + "is_deleted": 0, + "is_identified": 0, + "properties": "{"$current_url":"http://localhost:8000","$creator_event_uuid":"","$initial_current_url":"http://localhost:8000"}", + "team_id": 2, + "version": 0, + }, + }, + { + "key": null, + "topic": "clickhouse_person_distinct_id_test", + "value": { + "distinct_id": "user-1", + "is_deleted": 0, + "person_id": "", + "team_id": 2, + "version": 0, + }, + }, +] +`; + +exports[`IngestionConsumer typical event processing person processing off 1`] = ` +[ + { + "key": "", + "topic": "clickhouse_events_json_test", + "value": { + "created_at": "2025-01-01 00:00:00.000", + "distinct_id": "user-1", + "elements_chain": "", + "event": "$pageview", + "person_created_at": "1970-01-01 00:00:05", + "person_id": "", + "person_mode": "propertyless", + "person_properties": "{}", + "project_id": 2, + "properties": "{"$process_person_profile":false,"$ip":"127.0.0.1"}", + "team_id": 2, + "timestamp": "2025-01-01 00:00:00.000", + "uuid": "", + }, + }, +] +`; diff --git a/plugin-server/src/ingestion/ingestion-consumer.test.ts b/plugin-server/src/ingestion/ingestion-consumer.test.ts new file mode 100644 index 0000000000000..eaf7a55cb0f27 --- /dev/null +++ b/plugin-server/src/ingestion/ingestion-consumer.test.ts @@ -0,0 +1,483 @@ +import { DateTime } from 'luxon' +import { Message } from 'node-rdkafka' + +import { UUIDT } from '~/src/utils/utils' +import { + getProducedKafkaMessages, + getProducedKafkaMessagesForTopic, + mockProducer, +} from '~/tests/helpers/mocks/producer.mock' +import { forSnapshot } from '~/tests/helpers/snapshots' +import { createTeam, getFirstTeam, resetTestDatabase } from '~/tests/helpers/sql' + +import { Hub, PipelineEvent, Team } from '../../src/types' +import { closeHub, createHub } from '../../src/utils/db/hub' +import { status } from '../utils/status' +import { IngestionConsumer } from './ingestion-consumer' + +const mockConsumer = { + on: jest.fn(), + commitSync: jest.fn(), + commit: jest.fn(), + queryWatermarkOffsets: jest.fn(), + committed: jest.fn(), + assignments: jest.fn(), + isConnected: jest.fn(() => true), + getMetadata: jest.fn(), +} + +jest.mock('../../src/kafka/batch-consumer', () => { + return { + startBatchConsumer: jest.fn(() => + Promise.resolve({ + join: () => ({ + finally: jest.fn(), + }), + stop: jest.fn(), + consumer: mockConsumer, + }) + ), + } +}) + +jest.setTimeout(1000) + +let offsetIncrementer = 0 + +const createKafkaMessages: (events: PipelineEvent[]) => Message[] = (events) => { + return events.map((event) => { + // TRICKY: This is the slightly different format that capture sends + const captureEvent = { + uuid: event.uuid, + distinct_id: event.distinct_id, + ip: event.ip, + now: event.now, + token: event.token, + data: JSON.stringify(event), + } + return { + value: Buffer.from(JSON.stringify(captureEvent)), + size: 1, + topic: 'test', + offset: offsetIncrementer++, + timestamp: DateTime.now().toMillis(), + partition: 1, + } + }) +} + +describe('IngestionConsumer', () => { + let ingester: IngestionConsumer + let hub: Hub + let team: Team + let team2: Team + let fixedTime: DateTime + + const createEvent = (event?: Partial): PipelineEvent => ({ + distinct_id: 'user-1', + uuid: new UUIDT().toString(), + token: team.api_token, + ip: '127.0.0.1', + site_url: 'us.posthog.com', + now: fixedTime.toISO()!, + event: '$pageview', + properties: { + $current_url: 'http://localhost:8000', + }, + ...event, + }) + + beforeEach(async () => { + fixedTime = DateTime.fromObject({ year: 2025, month: 1, day: 1 }, { zone: 'UTC' }) + jest.spyOn(Date, 'now').mockReturnValue(fixedTime.toMillis()) + + offsetIncrementer = 0 + await resetTestDatabase() + hub = await createHub() + + hub.kafkaProducer = mockProducer + team = await getFirstTeam(hub) + const team2Id = await createTeam(hub.db.postgres, team.organization_id) + team2 = (await hub.db.fetchTeam(team2Id)) as Team + }) + + afterEach(async () => { + jest.restoreAllMocks() + jest.setTimeout(10000) + if (ingester) { + await ingester.stop() + } + await closeHub(hub) + }) + + afterAll(() => { + jest.useRealTimers() + }) + + describe('general', () => { + beforeEach(async () => { + ingester = new IngestionConsumer(hub) + await ingester.start() + }) + + it('should have the correct config', () => { + expect(ingester['name']).toMatchInlineSnapshot(`"ingestion-consumer-events_plugin_ingestion_test"`) + expect(ingester['groupId']).toMatchInlineSnapshot(`"events-ingestion-consumer"`) + expect(ingester['topic']).toMatchInlineSnapshot(`"events_plugin_ingestion_test"`) + expect(ingester['dlqTopic']).toMatchInlineSnapshot(`"events_plugin_ingestion_dlq_test"`) + expect(ingester['overflowTopic']).toMatchInlineSnapshot(`"events_plugin_ingestion_overflow_test"`) + }) + + it('should process a standard event', async () => { + await ingester.handleKafkaBatch(createKafkaMessages([createEvent()])) + + expect(forSnapshot(getProducedKafkaMessages())).toMatchSnapshot() + }) + + describe('overflow', () => { + const now = () => DateTime.now().toMillis() + beforeEach(() => { + // Just to make it easy to see what is configured + expect(hub.EVENT_OVERFLOW_BUCKET_CAPACITY).toEqual(1000) + }) + + it('should emit to overflow if token and distinct_id are overflowed', async () => { + ingester['overflowRateLimiter'].consume(`${team.api_token}:overflow-distinct-id`, 1000, now()) + const overflowMessages = createKafkaMessages([createEvent({ distinct_id: 'overflow-distinct-id' })]) + await ingester.handleKafkaBatch(overflowMessages) + expect(getProducedKafkaMessagesForTopic('clickhouse_events_json_test')).toHaveLength(0) + expect(getProducedKafkaMessagesForTopic('events_plugin_ingestion_overflow_test')).toHaveLength(1) + expect( + forSnapshot(getProducedKafkaMessagesForTopic('events_plugin_ingestion_overflow_test')) + ).toMatchSnapshot() + }) + + it('should allow some events to pass', async () => { + const manyOverflowedMessages = createKafkaMessages([ + createEvent({ distinct_id: 'overflow-distinct-id' }), + createEvent({ distinct_id: 'overflow-distinct-id' }), + createEvent({ distinct_id: 'overflow-distinct-id' }), + ]) + ingester['overflowRateLimiter'].consume(`${team.api_token}:overflow-distinct-id`, 999, now()) + await ingester.handleKafkaBatch(manyOverflowedMessages) + expect(getProducedKafkaMessagesForTopic('events_plugin_ingestion_overflow_test')).toHaveLength(2) + + expect( + forSnapshot(getProducedKafkaMessagesForTopic('events_plugin_ingestion_overflow_test')) + ).toMatchSnapshot() + }) + + it('does not overflow if it is consuming from the overflow topic', async () => { + ingester['topic'] = 'events_plugin_ingestion_overflow_test' + ingester['overflowRateLimiter'].consume(`${team.api_token}:overflow-distinct-id`, 1000, now()) + + const overflowMessages = createKafkaMessages([createEvent({ distinct_id: 'overflow-distinct-id' })]) + await ingester.handleKafkaBatch(overflowMessages) + + expect(getProducedKafkaMessagesForTopic('events_plugin_ingestion_overflow_test')).toHaveLength(0) + expect(getProducedKafkaMessagesForTopic('clickhouse_events_json_test')).toHaveLength(1) + }) + }) + }) + + describe('dropping events', () => { + describe.each(['headers', 'payload'] as const)('via %s', (kind) => { + const addMessageHeaders = (message: Message, token?: string, distinctId?: string) => { + if (kind !== 'headers') { + return + } + message.headers = [] + + if (distinctId) { + message.headers.push({ + key: 'distinct_id', + value: Buffer.from(distinctId), + }) + } + if (token) { + message.headers.push({ + key: 'token', + value: Buffer.from(token), + }) + } + } + + beforeEach(() => { + jest.spyOn(status, 'debug') + }) + + const expectDropLogs = (pairs: [string, string | undefined][]) => { + for (const [token, distinctId] of pairs) { + expect(jest.mocked(status.debug)).toHaveBeenCalledWith('🔁', 'Dropped event', { + distinctId, + token, + }) + } + } + + describe('with DROP_EVENTS_BY_TOKEN', () => { + beforeEach(async () => { + hub.DROP_EVENTS_BY_TOKEN = `${team.api_token},phc_other` + ingester = new IngestionConsumer(hub) + await ingester.start() + }) + + it('should drop events with matching token', async () => { + const messages = createKafkaMessages([createEvent({}), createEvent({})]) + addMessageHeaders(messages[0], team.api_token) + await ingester.handleKafkaBatch(messages) + expect(getProducedKafkaMessagesForTopic('clickhouse_events_json_test')).toHaveLength(0) + expectDropLogs([ + [team.api_token, 'user-1'], + [team.api_token, 'user-1'], + ]) + }) + + it('should not drop events for a different team token', async () => { + const messages = createKafkaMessages([createEvent({ token: team2.api_token })]) + addMessageHeaders(messages[0], team2.api_token) + await ingester.handleKafkaBatch(messages) + expect(getProducedKafkaMessagesForTopic('clickhouse_events_json_test')).not.toHaveLength(0) + expectDropLogs([]) + }) + + it('should only drop events in batch matching', async () => { + const messages = createKafkaMessages([ + createEvent({ token: team.api_token }), + createEvent({ token: team2.api_token, distinct_id: 'team2-distinct-id' }), + createEvent({ token: team.api_token }), + ]) + addMessageHeaders(messages[0], team.api_token) + addMessageHeaders(messages[1], team2.api_token) + addMessageHeaders(messages[2], team.api_token) + await ingester.handleKafkaBatch(messages) + const eventsMessages = getProducedKafkaMessagesForTopic('clickhouse_events_json_test') + expect(eventsMessages).toHaveLength(1) + expect(eventsMessages[0].value).toMatchObject({ + team_id: team2.id, + distinct_id: 'team2-distinct-id', + }) + expectDropLogs([ + [team.api_token, kind === 'headers' ? undefined : 'user-1'], + [team.api_token, kind === 'headers' ? undefined : 'user-1'], + ]) + }) + }) + + describe('with DROP_EVENTS_BY_TOKEN_DISTINCT_ID', () => { + beforeEach(async () => { + hub.DROP_EVENTS_BY_TOKEN_DISTINCT_ID = `${team.api_token}:distinct-id-to-ignore,phc_other:distinct-id-to-ignore` + ingester = new IngestionConsumer(hub) + await ingester.start() + }) + it('should drop events with matching token and distinct_id', async () => { + const messages = createKafkaMessages([ + createEvent({ + distinct_id: 'distinct-id-to-ignore', + }), + ]) + addMessageHeaders(messages[0], team.api_token, 'distinct-id-to-ignore') + await ingester.handleKafkaBatch(messages) + expect(getProducedKafkaMessagesForTopic('clickhouse_events_json_test')).toHaveLength(0) + expectDropLogs([[team.api_token, 'distinct-id-to-ignore']]) + }) + + it('should not drop events for a different team token', async () => { + const messages = createKafkaMessages([ + createEvent({ + token: team2.api_token, + distinct_id: 'distinct-id-to-ignore', + }), + ]) + addMessageHeaders(messages[0], team2.api_token, 'distinct-id-to-ignore') + await ingester.handleKafkaBatch(messages) + expect(getProducedKafkaMessagesForTopic('clickhouse_events_json_test')).not.toHaveLength(0) + expectDropLogs([]) + }) + + it('should not drop events for a different distinct_id', async () => { + const messages = createKafkaMessages([ + createEvent({ + distinct_id: 'other-id', + }), + ]) + addMessageHeaders(messages[0], team.api_token, 'not-ignored') + await ingester.handleKafkaBatch(messages) + expect(getProducedKafkaMessagesForTopic('clickhouse_events_json_test')).not.toHaveLength(0) + expectDropLogs([]) + }) + }) + }) + }) + + describe('event batching', () => { + it('should batch events based on the distinct_id', async () => { + const messages = createKafkaMessages([ + createEvent({ distinct_id: 'distinct-id-1' }), + createEvent({ distinct_id: 'distinct-id-1' }), + createEvent({ distinct_id: 'distinct-id-2' }), + createEvent({ distinct_id: 'distinct-id-1' }), + createEvent({ token: team2.api_token, distinct_id: 'distinct-id-1' }), + ]) + + const batches = await ingester['parseKafkaBatch'](messages) + + expect(Object.keys(batches)).toHaveLength(3) + expect(batches[`${team.api_token}:distinct-id-1`]).toHaveLength(3) + expect(batches[`${team.api_token}:distinct-id-2`]).toHaveLength(1) + expect(batches[`${team2.api_token}:distinct-id-1`]).toHaveLength(1) + }) + }) + + describe('error handling', () => { + let messages: Message[] + + beforeEach(async () => { + ingester = new IngestionConsumer(hub) + await ingester.start() + // Simulate some sort of error happening by mocking out the runner + messages = createKafkaMessages([createEvent()]) + jest.spyOn(status, 'error').mockImplementation(() => {}) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('should handle explicitly non retriable errors by sending to DLQ', async () => { + // NOTE: I don't think this makes a lot of sense but currently is just mimicing existing behavior for the migration + // We should figure this out better and have more explictly named errors + + const error: any = new Error('test') + error.isRetriable = false + jest.spyOn(ingester as any, 'runEventPipeline').mockRejectedValue(error) + + await ingester.handleKafkaBatch(messages) + + expect(jest.mocked(status.error)).toHaveBeenCalledWith('🔥', 'Error processing message', expect.any(Object)) + + expect(forSnapshot(getProducedKafkaMessages())).toMatchSnapshot() + }) + + it.each([undefined, true])('should throw if isRetriable is set to %s', async (isRetriable) => { + const error: any = new Error('test') + error.isRetriable = isRetriable + jest.spyOn(ingester as any, 'runEventPipeline').mockRejectedValue(error) + + await expect(ingester.handleKafkaBatch(messages)).rejects.toThrow() + }) + }) + + describe('typical event processing', () => { + /** + * NOTE: The majority of these tests should be done in the event pipeline runner but + * this is a good place to have some high level happy paths + */ + + beforeEach(async () => { + ingester = new IngestionConsumer(hub) + await ingester.start() + }) + + const eventTests: [string, () => PipelineEvent[]][] = [ + ['normal event', () => [createEvent()]], + [ + '$identify event', + () => [createEvent({ event: '$identify', properties: { $set: { $email: 'test@test.com' } } })], + ], + [ + 'multiple events', + () => [ + createEvent({ + event: '$pageview', + distinct_id: 'anonymous-id-1', + properties: { $current_url: 'https://example.com/page1' }, + }), + createEvent({ + event: '$identify', + distinct_id: 'identified-id-1', + properties: { $set: { $email: 'test@test.com' }, $anonymous_distinct_id: 'anonymous-id-1' }, + }), + createEvent({ + event: '$pageview', + distinct_id: 'identified-id-1', + properties: { $current_url: 'https://example.com/page2' }, + }), + ], + ], + // [ + // 'heatmap event', + // () => [ + // createEvent({ + // distinct_id: 'distinct-id-1', + // event: '$$heatmap', + // properties: { + // $heatmap_data: { + // 'http://localhost:3000/': [ + // { + // x: 1020, + // y: 363, + // target_fixed: false, + // type: 'mousemove', + // }, + // { + // x: 634, + // y: 460, + // target_fixed: false, + // type: 'click', + // }, + // ], + // }, + // }, + // }), + // ], + // ], + [ + // NOTE: This currently returns as is - for now we keep this broken but once we release the new ingester we should fix this + 'malformed person information', + () => [ + createEvent({ + distinct_id: 'distinct-id-1', + properties: { $set: 'INVALID', $unset: [[[['definitel invalid']]]] }, + }), + ], + ], + ['malformed event', () => [createEvent({ event: '' })]], + ['event with common distinct_id that gets dropped', () => [createEvent({ distinct_id: 'distinct_id' })]], + [ + 'ai event', + () => [ + createEvent({ + event: '$ai_generation', + properties: { + $ai_model: 'gpt-4', + $ai_provider: 'openai', + $ai_input_tokens: 100, + $ai_output_tokens: 50, + }, + }), + ], + ], + [ + 'person processing off', + () => [createEvent({ event: '$pageview', properties: { $process_person_profile: false } })], + ], + [ + 'client ingestion warning', + () => [ + createEvent({ + event: '$$client_ingestion_warning', + properties: { $$client_ingestion_warning_message: 'test' }, + }), + ], + ], + ] + + it.each(eventTests)('%s', async (_, createEvents) => { + const messages = createKafkaMessages(createEvents()) + await ingester.handleKafkaBatch(messages) + + expect(forSnapshot(getProducedKafkaMessages())).toMatchSnapshot() + }) + }) +}) diff --git a/plugin-server/src/ingestion/ingestion-consumer.ts b/plugin-server/src/ingestion/ingestion-consumer.ts new file mode 100644 index 0000000000000..699ab17f2c912 --- /dev/null +++ b/plugin-server/src/ingestion/ingestion-consumer.ts @@ -0,0 +1,439 @@ +import * as Sentry from '@sentry/node' +import { Message, MessageHeader } from 'node-rdkafka' +import { Histogram } from 'prom-client' + +import { BatchConsumer, startBatchConsumer } from '../kafka/batch-consumer' +import { createRdConnectionConfigFromEnvVars } from '../kafka/config' +import { KafkaProducerWrapper } from '../kafka/producer' +import { IngestionOverflowMode } from '../main/ingestion-queues/batch-processing/each-batch-ingestion' +import { ingestionOverflowingMessagesTotal } from '../main/ingestion-queues/batch-processing/metrics' +import { addSentryBreadcrumbsEventListeners } from '../main/ingestion-queues/kafka-metrics' +import { + eventDroppedCounter, + ingestionPartitionKeyOverflowed, + latestOffsetTimestampGauge, + setUsageInNonPersonEventsCounter, +} from '../main/ingestion-queues/metrics' +import { runInstrumentedFunction } from '../main/utils' +import { Hub, PipelineEvent, PluginServerService } from '../types' +import { normalizeEvent } from '../utils/event' +import { retryIfRetriable } from '../utils/retries' +import { status } from '../utils/status' +import { EventPipelineResult, EventPipelineRunner } from '../worker/ingestion/event-pipeline/runner' +import { MemoryRateLimiter } from './utils/overflow-detector' + +// Must require as `tsc` strips unused `import` statements and just requiring this seems to init some globals +require('@sentry/tracing') + +const histogramKafkaBatchSize = new Histogram({ + name: 'ingestion_batch_size', + help: 'The size of the batches we are receiving from Kafka', + buckets: [0, 50, 100, 250, 500, 750, 1000, 1500, 2000, 3000, Infinity], +}) + +const histogramKafkaBatchSizeKb = new Histogram({ + name: 'ingestion_batch_size_kb', + help: 'The size in kb of the batches we are receiving from Kafka', + buckets: [0, 128, 512, 1024, 5120, 10240, 20480, 51200, 102400, 204800, Infinity], +}) + +type GroupedIncomingEvents = { + [key: string]: { message: Message; event: PipelineEvent }[] +} + +const PERSON_EVENTS = new Set(['$set', '$identify', '$create_alias', '$merge_dangerously', '$groupidentify']) +const KNOWN_SET_EVENTS = new Set([ + '$feature_interaction', + '$feature_enrollment_update', + 'survey dismissed', + 'survey sent', +]) + +export class IngestionConsumer { + protected name = 'ingestion-consumer' + protected groupId: string + protected topic: string + protected dlqTopic: string + protected overflowTopic?: string + + batchConsumer?: BatchConsumer + isStopping = false + protected heartbeat = () => {} + protected promises: Set> = new Set() + protected kafkaProducer?: KafkaProducerWrapper + + private overflowRateLimiter: MemoryRateLimiter + private ingestionWarningLimiter: MemoryRateLimiter + private tokensToDrop: string[] = [] + private tokenDistinctIdsToDrop: string[] = [] + + constructor(private hub: Hub) { + // The group and topic are configurable allowing for multiple ingestion consumers to be run in parallel + this.groupId = hub.INGESTION_CONSUMER_GROUP_ID + this.topic = hub.INGESTION_CONSUMER_CONSUME_TOPIC + this.overflowTopic = hub.INGESTION_CONSUMER_OVERFLOW_TOPIC + this.dlqTopic = hub.INGESTION_CONSUMER_DLQ_TOPIC + this.tokensToDrop = hub.DROP_EVENTS_BY_TOKEN.split(',').filter((x) => !!x) + this.tokenDistinctIdsToDrop = hub.DROP_EVENTS_BY_TOKEN_DISTINCT_ID.split(',').filter((x) => !!x) + + this.name = `ingestion-consumer-${this.topic}` + this.overflowRateLimiter = new MemoryRateLimiter( + this.hub.EVENT_OVERFLOW_BUCKET_CAPACITY, + this.hub.EVENT_OVERFLOW_BUCKET_REPLENISH_RATE + ) + + this.ingestionWarningLimiter = new MemoryRateLimiter(1, 1.0 / 3600) + } + + public get service(): PluginServerService { + return { + id: this.name, + onShutdown: async () => await this.stop(), + healthcheck: () => this.isHealthy() ?? false, + batchConsumer: this.batchConsumer, + } + } + + public async start(): Promise { + await Promise.all([ + KafkaProducerWrapper.create(this.hub).then((producer) => { + this.kafkaProducer = producer + this.kafkaProducer.producer.connect() + }), + this.startKafkaConsumer({ + topic: this.topic, + groupId: this.groupId, + handleBatch: async (messages) => this.handleKafkaBatch(messages), + }), + ]) + } + + public async stop(): Promise { + status.info('🔁', `${this.name} - stopping`) + this.isStopping = true + + // Mark as stopping so that we don't actually process any more incoming messages, but still keep the process alive + status.info('🔁', `${this.name} - stopping batch consumer`) + await this.batchConsumer?.stop() + status.info('🔁', `${this.name} - stopping kafka producer`) + await this.kafkaProducer?.disconnect() + + status.info('👍', `${this.name} - stopped!`) + } + + public isHealthy() { + return this.batchConsumer?.isHealthy() + } + + private scheduleWork(promise: Promise): Promise { + this.promises.add(promise) + void promise.finally(() => this.promises.delete(promise)) + return promise + } + + public async handleKafkaBatch(messages: Message[]) { + const parsedMessages = await this.parseKafkaBatch(messages) + await this.processBatch(parsedMessages) + for (const message of messages) { + if (message.timestamp) { + latestOffsetTimestampGauge + .labels({ partition: message.partition, topic: message.topic, groupId: this.groupId }) + .set(message.timestamp) + } + } + } + + public async processBatch(groupedIncomingEvents: GroupedIncomingEvents): Promise { + await this.runManyWithHeartbeat(Object.values(groupedIncomingEvents), async (eventsForDistinctId) => { + // Process every message sequentially, stash promises to await on later + for (const { message, event } of eventsForDistinctId) { + // Track $set usage in events that aren't known to use it, before ingestion adds anything there + if ( + event.properties && + !PERSON_EVENTS.has(event.event) && + !KNOWN_SET_EVENTS.has(event.event) && + ('$set' in event.properties || '$set_once' in event.properties || '$unset' in event.properties) + ) { + setUsageInNonPersonEventsCounter.inc() + } + + try { + status.debug('🔁', `Processing event`, { + event, + }) + const eventKey = `${event.token}:${event.distinct_id}` + // Check the rate limiter and emit to overflow if necessary + const isBelowRateLimit = this.overflowRateLimiter.consume(eventKey, 1, message.timestamp) + if (this.overflowEnabled() && !isBelowRateLimit) { + status.debug('🔁', `Sending to overflow`, { + event, + }) + ingestionPartitionKeyOverflowed.labels(`${event.team_id ?? event.token}`).inc() + if (this.ingestionWarningLimiter.consume(eventKey, 1)) { + status.warn('🪣', `Local overflow detection triggered on key ${eventKey}`) + } + + void this.scheduleWork(this.emitToOverflow([message])) + continue + } + + const result = await this.runEventPipeline(event) + + status.debug('🔁', `Processed event`, { + event, + }) + + result.ackPromises?.forEach((promise) => { + void this.scheduleWork( + promise.catch(async (error) => { + await this.handleProcessingError(error, message, event) + }) + ) + }) + } catch (error) { + await this.handleProcessingError(error, message, event) + } + } + }) + + status.debug('🔁', `Waiting for promises`, { + promises: this.promises.size, + }) + await Promise.all(this.promises) + status.debug('🔁', `Processed batch`) + } + + private async runEventPipeline(event: PipelineEvent): Promise { + return await retryIfRetriable(async () => { + const runner = new EventPipelineRunner(this.hub, event) + return await runner.runEventPipeline(event) + }) + } + + private parseKafkaBatch(messages: Message[]): Promise { + return runInstrumentedFunction({ + statsKey: `ingestionConsumer.handleEachBatch.parseKafkaMessages`, + func: () => { + const batches: GroupedIncomingEvents = {} + + for (const message of messages) { + let distinctId: string | undefined + let token: string | undefined + + // Parse the headers so we can early exit if found and should be dropped + message.headers?.forEach((header) => { + if (header.key === 'distinct_id') { + distinctId = header.value.toString() + } + if (header.key === 'token') { + token = header.value.toString() + } + }) + + if (this.shouldDropEvent(token, distinctId)) { + this.logDroppedEvent(token, distinctId) + continue + } + + // Parse the message payload into the event object + const { data: dataStr, ...rawEvent } = JSON.parse(message.value!.toString()) + const combinedEvent: PipelineEvent = { ...JSON.parse(dataStr), ...rawEvent } + const event: PipelineEvent = normalizeEvent({ + ...combinedEvent, + }) + + // In case the headers were not set we check the parsed message now + if (this.shouldDropEvent(combinedEvent.token, combinedEvent.distinct_id)) { + this.logDroppedEvent(combinedEvent.token, combinedEvent.distinct_id) + continue + } + + const eventKey = `${event.token}:${event.distinct_id}` + + // We collect the events grouped by token and distinct_id so that we can process batches in parallel whilst keeping the order of events + // for a given distinct_id + if (!batches[eventKey]) { + batches[eventKey] = [] + } + + batches[eventKey].push({ message, event }) + } + + return Promise.resolve(batches) + }, + }) + } + + private async runWithHeartbeat(func: () => Promise | T): Promise { + // Helper function to ensure that looping over lots of hog functions doesn't block up the thread, killing the consumer + const res = await func() + this.heartbeat() + await new Promise((resolve) => process.nextTick(resolve)) + + return res + } + + private async runManyWithHeartbeat(items: T[], func: (item: T) => Promise | R): Promise { + // Helper function to ensure that looping over lots of hog functions doesn't block up the event loop, leading to healthcheck failures + const results = [] + + for (const item of items) { + results.push(await this.runWithHeartbeat(() => func(item))) + } + return results + } + + private async startKafkaConsumer(options: { + topic: string + groupId: string + handleBatch: (messages: Message[]) => Promise + }): Promise { + this.batchConsumer = await startBatchConsumer({ + ...options, + connectionConfig: createRdConnectionConfigFromEnvVars(this.hub, 'consumer'), + autoCommit: true, + sessionTimeout: this.hub.KAFKA_CONSUMPTION_SESSION_TIMEOUT_MS, + maxPollIntervalMs: this.hub.KAFKA_CONSUMPTION_MAX_POLL_INTERVAL_MS, + consumerMaxBytes: this.hub.KAFKA_CONSUMPTION_MAX_BYTES, + consumerMaxBytesPerPartition: this.hub.KAFKA_CONSUMPTION_MAX_BYTES_PER_PARTITION, + consumerMaxWaitMs: this.hub.KAFKA_CONSUMPTION_MAX_WAIT_MS, + consumerErrorBackoffMs: this.hub.KAFKA_CONSUMPTION_ERROR_BACKOFF_MS, + fetchBatchSize: this.hub.INGESTION_BATCH_SIZE, + batchingTimeoutMs: this.hub.KAFKA_CONSUMPTION_BATCHING_TIMEOUT_MS, + topicCreationTimeoutMs: this.hub.KAFKA_TOPIC_CREATION_TIMEOUT_MS, + topicMetadataRefreshInterval: this.hub.KAFKA_TOPIC_METADATA_REFRESH_INTERVAL_MS, + eachBatch: async (messages, { heartbeat }) => { + status.info('🔁', `${this.name} - handling batch`, { + size: messages.length, + }) + + this.heartbeat = heartbeat + + histogramKafkaBatchSize.observe(messages.length) + histogramKafkaBatchSizeKb.observe(messages.reduce((acc, m) => (m.value?.length ?? 0) + acc, 0) / 1024) + + return await runInstrumentedFunction({ + statsKey: `ingestionConsumer.handleEachBatch`, + sendTimeoutGuardToSentry: false, + func: async () => { + await options.handleBatch(messages) + }, + }) + }, + callEachBatchWhenEmpty: false, + }) + + addSentryBreadcrumbsEventListeners(this.batchConsumer.consumer) + + this.batchConsumer.consumer.on('disconnected', async (err) => { + if (!this.isStopping) { + return + } + // since we can't be guaranteed that the consumer will be stopped before some other code calls disconnect + // we need to listen to disconnect and make sure we're stopped + status.info('🔁', `${this.name} batch consumer disconnected, cleaning up`, { err }) + await this.stop() + }) + } + + private async handleProcessingError(error: any, message: Message, event: PipelineEvent) { + status.error('🔥', `Error processing message`, { + stack: error.stack, + error: error, + }) + + // If the error is a non-retriable error, push to the dlq and commit the offset. Else raise the + // error. + // + // NOTE: there is behavior to push to a DLQ at the moment within EventPipelineRunner. This + // doesn't work so well with e.g. messages that when sent to the DLQ is it's self too large. + // Here we explicitly do _not_ add any additional metadata to the message. We might want to add + // some metadata to the message e.g. in the header or reference e.g. the sentry event id. + // + // TODO: property abstract out this `isRetriable` error logic. This is currently relying on the + // fact that node-rdkafka adheres to the `isRetriable` interface. + + if (error?.isRetriable === false) { + const sentryEventId = Sentry.captureException(error) + const headers: MessageHeader[] = message.headers ?? [] + headers.push({ ['sentry-event-id']: sentryEventId }) + headers.push({ ['event-id']: event.uuid }) + try { + await this.kafkaProducer!.produce({ + topic: this.dlqTopic, + value: message.value, + key: message.key ?? null, // avoid undefined, just to be safe + headers: headers, + }) + } catch (error) { + // If we can't send to the DLQ and it's not retriable, just continue. We'll commit the + // offset and move on. + if (error?.isRetriable === false) { + status.error('🔥', `Error pushing to DLQ`, { + stack: error.stack, + error: error, + }) + return + } + + // If we can't send to the DLQ and it is retriable, raise the error. + throw error + } + } else { + throw error + } + } + + private logDroppedEvent(token?: string, distinctId?: string) { + status.debug('🔁', `Dropped event`, { + token, + distinctId, + }) + eventDroppedCounter + .labels({ + event_type: 'analytics', + drop_cause: 'blocked_token', + }) + .inc() + } + + private shouldDropEvent(token?: string, distinctId?: string) { + return ( + (token && this.tokensToDrop.includes(token)) || + (token && distinctId && this.tokenDistinctIdsToDrop.includes(`${token}:${distinctId}`)) + ) + } + + private overflowEnabled() { + return !!this.hub.INGESTION_CONSUMER_OVERFLOW_TOPIC && this.hub.INGESTION_CONSUMER_OVERFLOW_TOPIC !== this.topic + } + + private async emitToOverflow(kafkaMessages: Message[]) { + const overflowTopic = this.hub.INGESTION_CONSUMER_OVERFLOW_TOPIC + if (!overflowTopic) { + throw new Error('No overflow topic configured') + } + + ingestionOverflowingMessagesTotal.inc(kafkaMessages.length) + + const overflowMode = this.hub.INGESTION_OVERFLOW_PRESERVE_PARTITION_LOCALITY + ? IngestionOverflowMode.Reroute + : IngestionOverflowMode.RerouteRandomly + + const useRandomPartitioning = overflowMode === IngestionOverflowMode.RerouteRandomly + + await Promise.all( + kafkaMessages.map((message) => + this.kafkaProducer!.produce({ + topic: this.overflowTopic!, + value: message.value, + // ``message.key`` should not be undefined here, but in the + // (extremely) unlikely event that it is, set it to ``null`` + // instead as that behavior is safer. + key: useRandomPartitioning ? null : message.key ?? null, + headers: message.headers, + }) + ) + ) + } +} diff --git a/plugin-server/src/ingestion/utils/overflow-detector.test.ts b/plugin-server/src/ingestion/utils/overflow-detector.test.ts new file mode 100644 index 0000000000000..2b0c3a5d2aab0 --- /dev/null +++ b/plugin-server/src/ingestion/utils/overflow-detector.test.ts @@ -0,0 +1,49 @@ +import { MemoryRateLimiter } from './overflow-detector' + +describe('MemoryRateLimiter', () => { + describe('consume()', () => { + const key = 'test' + let limiter: MemoryRateLimiter + let now = new Date('2025-01-01T01:00:00') + + const advanceTime = (seconds: number) => { + now = new Date(now.valueOf() + seconds * 1000) + jest.useFakeTimers().setSystemTime(now) + } + + beforeEach(() => { + limiter = new MemoryRateLimiter(10, 2) + jest.useFakeTimers().setSystemTime(now) + }) + + afterEach(() => { + jest.useRealTimers() + }) + + it('consumes and returns true when tokens available', () => { + expect(limiter.consume(key, 9)).toEqual(true) + }) + + it('returns false when tokens run out', () => { + expect(limiter.consume(key, 10)).toEqual(true) + expect(limiter.consume(key, 1)).toEqual(false) + }) + + it('consumes when tokens have been replenished', () => { + limiter.consume(key, 10) + + expect(limiter.consume(key, 1)).toEqual(false) + + advanceTime(1) + // Now that we have advanced 1 second, we can consume 1 token + expect(limiter.consume(key, 1)).toEqual(true) + }) + + it('consumes when tokens have been replenished with now argument', () => { + limiter.consume(key, 10, now.valueOf()) + expect(limiter.consume(key, 1)).toEqual(false) + // Even though we are not advancing time, we are passing the time to use with now + expect(limiter.consume(key, 1, now.valueOf() + 1000)).toEqual(true) + }) + }) +}) diff --git a/plugin-server/src/ingestion/utils/overflow-detector.ts b/plugin-server/src/ingestion/utils/overflow-detector.ts new file mode 100644 index 0000000000000..2595e38fe1c3f --- /dev/null +++ b/plugin-server/src/ingestion/utils/overflow-detector.ts @@ -0,0 +1,47 @@ +type Bucket = [tokens: number, lastReplenishedTimestamp: number] + +export class MemoryRateLimiter { + public buckets: Map + public replenishRate: number + public bucketCapacity: number + + constructor(bucketCapacity: number, replenishRate: number) { + this.buckets = new Map() + this.bucketCapacity = bucketCapacity + this.replenishRate = replenishRate + } + + private getBucket(key: string): Bucket { + let bucket = this.buckets.get(key) + if (bucket === undefined) { + bucket = [this.bucketCapacity, Date.now()] + this.buckets.set(key, bucket) + } + return bucket + } + + private replenish(bucket: Bucket, now?: number): void { + const replenish_timestamp: number = now ?? Date.now() + + // Replenish the bucket if replenish_timestamp is higher than lastReplenishedTimestamp + const secondsToReplenish = (replenish_timestamp - bucket[1]) / 1000 + if (secondsToReplenish > 0) { + bucket[0] += this.replenishRate * secondsToReplenish + bucket[0] = Math.min(bucket[0], this.bucketCapacity) + bucket[1] = replenish_timestamp + } + } + + consume(key: string, tokens: number, now?: number): boolean { + const bucket = this.getBucket(key) + this.replenish(bucket, now) + + bucket[0] -= tokens + + if (bucket[0] < 0) { + return false + } + + return true + } +} diff --git a/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-consumer.ts b/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-consumer.ts index 259d231e9d01a..4d953f61deaeb 100644 --- a/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-consumer.ts +++ b/plugin-server/src/main/ingestion-queues/analytics-events-ingestion-consumer.ts @@ -1,5 +1,4 @@ import { Message } from 'node-rdkafka' -import { Counter } from 'prom-client' import { buildStringMatcher } from '../../config/config' import { KAFKA_EVENTS_PLUGIN_INGESTION, prefix as KAFKA_PREFIX } from '../../config/kafka-topics' @@ -8,12 +7,6 @@ import { status } from '../../utils/status' import { eachBatchParallelIngestion, IngestionOverflowMode } from './batch-processing/each-batch-ingestion' import { IngestionConsumer } from './kafka-queue' -export const ingestionPartitionKeyOverflowed = new Counter({ - name: 'ingestion_partition_key_overflowed', - help: 'Indicates that a given key has overflowed capacity and been redirected to a different topic. Value incremented once a minute.', - labelNames: ['partition_key'], -}) - export const startAnalyticsEventsIngestionConsumer = async ({ hub, // TODO: remove needing to pass in the whole hub and be more selective on dependency injection. }: { diff --git a/plugin-server/src/main/ingestion-queues/batch-processing/each-batch-ingestion.ts b/plugin-server/src/main/ingestion-queues/batch-processing/each-batch-ingestion.ts index 15e33b9accbc8..37828a4b159c3 100644 --- a/plugin-server/src/main/ingestion-queues/batch-processing/each-batch-ingestion.ts +++ b/plugin-server/src/main/ingestion-queues/batch-processing/each-batch-ingestion.ts @@ -9,9 +9,8 @@ import { status } from '../../../utils/status' import { ConfiguredLimiter, LoggingLimiter } from '../../../utils/token-bucket' import { EventPipelineRunner } from '../../../worker/ingestion/event-pipeline/runner' import { captureIngestionWarning } from '../../../worker/ingestion/utils' -import { ingestionPartitionKeyOverflowed } from '../analytics-events-ingestion-consumer' import { IngestionConsumer } from '../kafka-queue' -import { eventDroppedCounter, latestOffsetTimestampGauge } from '../metrics' +import { eventDroppedCounter, ingestionPartitionKeyOverflowed, latestOffsetTimestampGauge } from '../metrics' import { ingestEventBatchingBatchCountSummary, ingestEventBatchingDistinctIdBatchLengthSummary, @@ -169,11 +168,7 @@ export async function eachBatchParallelIngestion( for (const { message, pluginEvent } of currentBatch) { try { const result = (await retryIfRetriable(async () => { - const runner = new EventPipelineRunner( - queue.pluginsServer, - pluginEvent, - queue.eventsProcessor - ) + const runner = new EventPipelineRunner(queue.pluginsServer, pluginEvent) return await runner.runEventPipeline(pluginEvent) })) as IngestResult diff --git a/plugin-server/src/main/ingestion-queues/metrics.ts b/plugin-server/src/main/ingestion-queues/metrics.ts index abeb3e56237d0..83b8ff61b952b 100644 --- a/plugin-server/src/main/ingestion-queues/metrics.ts +++ b/plugin-server/src/main/ingestion-queues/metrics.ts @@ -49,3 +49,9 @@ export const scheduledTaskCounter = new Counter({ help: 'Scheduled task status change', labelNames: ['status', 'task'], }) + +export const ingestionPartitionKeyOverflowed = new Counter({ + name: 'ingestion_partition_key_overflowed', + help: 'Indicates that a given key has overflowed capacity and been redirected to a different topic. Value incremented once a minute.', + labelNames: ['partition_key'], +}) diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/batch-consumer-factory.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/batch-consumer-factory.ts index b9afcee0613fc..9df26b6a213fd 100644 --- a/plugin-server/src/main/ingestion-queues/session-recording-v2/batch-consumer-factory.ts +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/batch-consumer-factory.ts @@ -36,9 +36,9 @@ export class DefaultBatchConsumerFactory implements BatchConsumerFactory { groupId, topic, eachBatch, - callEachBatchWhenEmpty: true, // Useful as we will still want to account for flushing sessions - autoCommit: true, - autoOffsetStore: true, // TODO: remove this once we implement our own offset store logic + callEachBatchWhenEmpty: true, // Required, as we want to flush session batches periodically + autoCommit: false, + autoOffsetStore: false, sessionTimeout: KAFKA_CONSUMER_SESSION_TIMEOUT_MS, maxPollIntervalMs: this.serverConfig.KAFKA_CONSUMPTION_MAX_POLL_INTERVAL_MS, // the largest size of a message that can be fetched by the consumer. diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/consumer.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/consumer.ts index 183060b449372..c6db2d56fe1d9 100644 --- a/plugin-server/src/main/ingestion-queues/session-recording-v2/consumer.ts +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/consumer.ts @@ -2,6 +2,7 @@ import { captureException } from '@sentry/node' import { CODES, features, KafkaConsumer, librdkafkaVersion, Message, TopicPartition } from 'node-rdkafka' import { KafkaProducerWrapper } from '~/src/kafka/producer' +import { PostgresRouter } from '~/src/utils/db/postgres' import { buildIntegerMatcher } from '../../../config/config' import { BatchConsumer } from '../../../kafka/batch-consumer' @@ -17,14 +18,17 @@ import { KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_EVENTS, KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_OVERFLOW, } from './constants' +import { KafkaMessageParser } from './kafka/message-parser' import { KafkaMetrics } from './kafka/metrics' -import { KafkaParser } from './kafka/parser' +import { KafkaOffsetManager } from './kafka/offset-manager' import { SessionRecordingMetrics } from './metrics' import { PromiseScheduler } from './promise-scheduler' +import { BlackholeSessionBatchWriter } from './sessions/blackhole-session-batch-writer' +import { SessionBatchManager } from './sessions/session-batch-manager' +import { SessionBatchRecorder, SessionBatchRecorderInterface } from './sessions/session-batch-recorder' import { TeamFilter } from './teams/team-filter' import { TeamService } from './teams/team-service' import { MessageWithTeam } from './teams/types' -import { BatchMessageProcessor } from './types' import { CaptureIngestionWarningFn } from './types' import { getPartitionsForTopic } from './utils' import { LibVersionMonitor } from './versions/lib-version-monitor' @@ -41,43 +45,58 @@ export class SessionRecordingIngester { isStopping = false private isDebugLoggingEnabled: ValueMatcher - private readonly messageProcessor: BatchMessageProcessor private readonly metrics: SessionRecordingMetrics private readonly promiseScheduler: PromiseScheduler private readonly batchConsumerFactory: BatchConsumerFactory + private readonly sessionBatchManager: SessionBatchManager + private readonly kafkaParser: KafkaMessageParser + private readonly teamFilter: TeamFilter + private readonly libVersionMonitor?: LibVersionMonitor constructor( private config: PluginsServerConfig, private consumeOverflow: boolean, + private postgres: PostgresRouter, batchConsumerFactory: BatchConsumerFactory, ingestionWarningProducer?: KafkaProducerWrapper ) { - this.isDebugLoggingEnabled = buildIntegerMatcher(config.SESSION_RECORDING_DEBUG_PARTITION, true) - const kafkaMetrics = KafkaMetrics.getInstance() - const kafkaParser = new KafkaParser(kafkaMetrics) - const teamService = new TeamService() - this.metrics = SessionRecordingMetrics.getInstance() - this.promiseScheduler = new PromiseScheduler() + this.topic = consumeOverflow + ? KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_OVERFLOW + : KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_EVENTS this.batchConsumerFactory = batchConsumerFactory - const teamFilter = new TeamFilter(teamService, kafkaParser) - this.messageProcessor = teamFilter + this.isDebugLoggingEnabled = buildIntegerMatcher(config.SESSION_RECORDING_DEBUG_PARTITION, true) + + this.promiseScheduler = new PromiseScheduler() + this.kafkaParser = new KafkaMessageParser(KafkaMetrics.getInstance()) + this.teamFilter = new TeamFilter(new TeamService(postgres)) if (ingestionWarningProducer) { const captureWarning: CaptureIngestionWarningFn = async (teamId, type, details, debounce) => { await captureIngestionWarning(ingestionWarningProducer, teamId, type, details, debounce) } - - this.messageProcessor = new LibVersionMonitor( - teamFilter, - captureWarning, - VersionMetrics.getInstance() - ) + this.libVersionMonitor = new LibVersionMonitor(captureWarning, VersionMetrics.getInstance()) } - this.topic = consumeOverflow - ? KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_OVERFLOW - : KAFKA_SESSION_RECORDING_SNAPSHOT_ITEM_EVENTS + this.metrics = SessionRecordingMetrics.getInstance() + + const offsetManager = new KafkaOffsetManager(async (offsets) => { + await new Promise((resolve, reject) => { + try { + this.batchConsumer!.consumer.commitSync(offsets) + resolve() + } catch (error) { + reject(error) + } + }) + }, this.topic) + this.sessionBatchManager = new SessionBatchManager({ + maxBatchSizeBytes: (config.SESSION_RECORDING_MAX_BATCH_SIZE_KB ?? 0) * 1024, + maxBatchAgeMs: config.SESSION_RECORDING_MAX_BATCH_AGE_MS ?? 1000, + createBatch: () => new SessionBatchRecorder(new BlackholeSessionBatchWriter()), + offsetManager, + }) + this.consumerGroupId = this.consumeOverflow ? KAFKA_CONSUMER_GROUP_ID_OVERFLOW : KAFKA_CONSUMER_GROUP_ID } @@ -90,6 +109,24 @@ export class SessionRecordingIngester { } } + public async handleEachBatch(messages: Message[], context: { heartbeat: () => void }): Promise { + context.heartbeat() + + if (messages.length > 0) { + logger.info('🔁', `blob_ingester_consumer_v2 - handling batch`, { + size: messages.length, + partitionsInBatch: [...new Set(messages.map((x) => x.partition))], + assignedPartitions: this.assignedPartitions, + }) + } + + await runInstrumentedFunction({ + statsKey: `recordingingesterv2.handleEachBatch`, + sendTimeoutGuardToSentry: false, + func: async () => this.processBatchMessages(messages, context), + }) + } + private async processBatchMessages(messages: Message[], context: { heartbeat: () => void }): Promise { // Increment message received counter for each message messages.forEach((message) => { @@ -98,50 +135,76 @@ export class SessionRecordingIngester { const batchSize = messages.length const batchSizeKb = messages.reduce((acc, m) => (m.value?.length ?? 0) + acc, 0) / 1024 - this.metrics.observeKafkaBatchSize(batchSize) this.metrics.observeKafkaBatchSizeKb(batchSizeKb) - const parsedMessages = await runInstrumentedFunction({ + const processedMessages = await runInstrumentedFunction({ statsKey: `recordingingesterv2.handleEachBatch.parseBatch`, func: async () => { - return this.messageProcessor.parseBatch(messages) + const parsedMessages = await this.kafkaParser.parseBatch(messages) + const messagesWithTeam = await this.teamFilter.filterBatch(parsedMessages) + const processedMessages = this.libVersionMonitor + ? await this.libVersionMonitor.processBatch(messagesWithTeam) + : messagesWithTeam + return processedMessages }, }) + context.heartbeat() await runInstrumentedFunction({ statsKey: `recordingingesterv2.handleEachBatch.processMessages`, - func: async () => this.processMessages(parsedMessages), + func: async () => this.processMessages(processedMessages), }) + + context.heartbeat() + + if (this.sessionBatchManager.shouldFlush()) { + await runInstrumentedFunction({ + statsKey: `recordingingesterv2.handleEachBatch.flush`, + func: async () => this.sessionBatchManager.flush(), + }) + } } - private async processMessages(parsedMessages: MessageWithTeam[]): Promise { - if (this.config.SESSION_RECORDING_PARALLEL_CONSUMPTION) { - await Promise.all(parsedMessages.map((m) => this.consume(m))) - } else { + private async processMessages(parsedMessages: MessageWithTeam[]) { + await this.sessionBatchManager.withBatch(async (batch) => { for (const message of parsedMessages) { - await this.consume(message) + this.consume(message, batch) } - } + return Promise.resolve() + }) } - public async handleEachBatch(messages: Message[], context: { heartbeat: () => void }): Promise { - context.heartbeat() + private consume(message: MessageWithTeam, batch: SessionBatchRecorderInterface) { + // we have to reset this counter once we're consuming messages since then we know we're not re-balancing + // otherwise the consumer continues to report however many sessions were revoked at the last re-balance forever + this.metrics.resetSessionsRevoked() + const { team, message: parsedMessage } = message + const debugEnabled = this.isDebugLoggingEnabled(parsedMessage.metadata.partition) - if (messages.length > 0) { - logger.info('🔁', `blob_ingester_consumer_v2 - handling batch`, { - size: messages.length, - partitionsInBatch: [...new Set(messages.map((x) => x.partition))], - assignedPartitions: this.assignedPartitions, + if (debugEnabled) { + logger.debug('🔄', 'processing_session_recording', { + partition: parsedMessage.metadata.partition, + offset: parsedMessage.metadata.offset, + distinct_id: parsedMessage.distinct_id, + session_id: parsedMessage.session_id, + raw_size: parsedMessage.metadata.rawSize, }) } - await runInstrumentedFunction({ - statsKey: `recordingingesterv2.handleEachBatch`, - sendTimeoutGuardToSentry: false, - func: async () => this.processBatchMessages(messages, context), - }) + const { partition } = parsedMessage.metadata + const isDebug = this.isDebugLoggingEnabled(partition) + if (isDebug) { + logger.info('🔁', '[blob_ingester_consumer_v2] - [PARTITION DEBUG] - consuming event', { + ...parsedMessage.metadata, + team_id: team.teamId, + session_id: parsedMessage.session_id, + }) + } + + this.metrics.observeSessionInfo(parsedMessage.metadata.rawSize) + batch.record(message) } public async start(): Promise { @@ -233,38 +296,6 @@ export class SessionRecordingIngester { return this.assignedTopicPartitions.map((x) => x.partition) } - private async consume(messageWithTeam: MessageWithTeam): Promise { - // we have to reset this counter once we're consuming messages since then we know we're not re-balancing - // otherwise the consumer continues to report however many sessions were revoked at the last re-balance forever - this.metrics.resetSessionsRevoked() - const { team, message } = messageWithTeam - const debugEnabled = this.isDebugLoggingEnabled(message.metadata.partition) - - if (debugEnabled) { - logger.debug('🔄', 'processing_session_recording', { - partition: message.metadata.partition, - offset: message.metadata.offset, - distinct_id: message.distinct_id, - session_id: message.session_id, - raw_size: message.metadata.rawSize, - }) - } - - const { partition } = message.metadata - const isDebug = this.isDebugLoggingEnabled(partition) - if (isDebug) { - logger.info('🔁', '[blob_ingester_consumer_v2] - [PARTITION DEBUG] - consuming event', { - ...message.metadata, - team_id: team.teamId, - session_id: message.session_id, - }) - } - - this.metrics.observeSessionInfo(message.metadata.rawSize) - - return Promise.resolve() - } - private async onRevokePartitions(topicPartitions: TopicPartition[]): Promise { /** * The revoke_partitions indicates that the consumer group has had partitions revoked. @@ -277,7 +308,6 @@ export class SessionRecordingIngester { } this.metrics.resetSessionsHandled() - - return Promise.resolve() + await this.sessionBatchManager.discardPartitions(revokedPartitions) } } diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/kafka/parser.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/kafka/message-parser.ts similarity index 88% rename from plugin-server/src/main/ingestion-queues/session-recording-v2/kafka/parser.ts rename to plugin-server/src/main/ingestion-queues/session-recording-v2/kafka/message-parser.ts index 6fc0d4f5fc514..25a29bb89c316 100644 --- a/plugin-server/src/main/ingestion-queues/session-recording-v2/kafka/parser.ts +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/kafka/message-parser.ts @@ -10,10 +10,15 @@ import { ParsedMessageData } from './types' const GZIP_HEADER = Uint8Array.from([0x1f, 0x8b, 0x08, 0x00]) const decompressWithGzip = promisify(gunzip) -export class KafkaParser { +export class KafkaMessageParser { constructor(private readonly metrics: KafkaMetrics) {} - public async parseMessage(message: Message): Promise { + public async parseBatch(messages: Message[]): Promise { + const parsedMessages = await Promise.all(messages.map((message) => this.parseMessage(message))) + return parsedMessages.filter((msg) => msg !== null) as ParsedMessageData[] + } + + private async parseMessage(message: Message): Promise { const dropMessage = (reason: string, extra?: Record) => { this.metrics.incrementMessageDropped('session_recordings_blob_ingestion', reason) diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/kafka/offset-manager.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/kafka/offset-manager.ts new file mode 100644 index 0000000000000..a43c09e90c3fe --- /dev/null +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/kafka/offset-manager.ts @@ -0,0 +1,73 @@ +import { TopicPartitionOffset } from 'node-rdkafka' + +import { SessionBatchRecorderInterface } from '../sessions/session-batch-recorder' +import { MessageWithTeam } from '../teams/types' + +interface PartitionOffset { + partition: number + offset: number +} + +type CommitOffsetsCallback = (offsets: TopicPartitionOffset[]) => Promise + +class OffsetTrackingSessionBatchRecorderWrapper implements SessionBatchRecorderInterface { + constructor( + private readonly recorder: SessionBatchRecorderInterface, + private readonly offsetManager: KafkaOffsetManager + ) {} + + public record(message: MessageWithTeam): number { + const bytesWritten = this.recorder.record(message) + this.offsetManager.trackOffset(message.message.metadata) + return bytesWritten + } + + public async flush(): Promise { + await this.recorder.flush() + } + + public discardPartition(partition: number): void { + this.recorder.discardPartition(partition) + this.offsetManager.discardPartition(partition) + } + + public get size(): number { + return this.recorder.size + } +} + +export class KafkaOffsetManager { + private partitionOffsets: Map = new Map() + + constructor(private readonly commitOffsets: CommitOffsetsCallback, private readonly topic: string) {} + + public wrapBatch(recorder: SessionBatchRecorderInterface): SessionBatchRecorderInterface { + return new OffsetTrackingSessionBatchRecorderWrapper(recorder, this) + } + + public trackOffset({ partition, offset }: PartitionOffset): void { + // We track the next offset to process + this.partitionOffsets.set(partition, offset + 1) + } + + public discardPartition(partition: number): void { + this.partitionOffsets.delete(partition) + } + + public async commit(): Promise { + const topicPartitionOffsets: TopicPartitionOffset[] = [] + + for (const [partition, offset] of this.partitionOffsets.entries()) { + topicPartitionOffsets.push({ + topic: this.topic, + partition, + offset, + }) + } + + if (topicPartitionOffsets.length > 0) { + await this.commitOffsets(topicPartitionOffsets) + this.partitionOffsets.clear() + } + } +} diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/blackhole-session-batch-writer.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/blackhole-session-batch-writer.ts new file mode 100644 index 0000000000000..69ccffad3a66d --- /dev/null +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/blackhole-session-batch-writer.ts @@ -0,0 +1,12 @@ +import { PassThrough } from 'stream' + +import { SessionBatchWriter, StreamWithFinish } from './session-batch-recorder' + +export class BlackholeSessionBatchWriter implements SessionBatchWriter { + public async open(): Promise { + return Promise.resolve({ + stream: new PassThrough(), + finish: async () => Promise.resolve(), + }) + } +} diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/metrics.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/metrics.ts new file mode 100644 index 0000000000000..9cac0db1516a0 --- /dev/null +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/metrics.ts @@ -0,0 +1,39 @@ +import { Counter } from 'prom-client' + +export class SessionBatchMetrics { + private static readonly batchesFlushed = new Counter({ + name: 'recording_blob_ingestion_v2_batches_flushed_total', + help: 'Number of session recording batches that have been flushed', + }) + + private static readonly sessionsFlushed = new Counter({ + name: 'recording_blob_ingestion_v2_sessions_flushed_total', + help: 'Number of individual sessions that have been flushed', + }) + + private static readonly eventsFlushed = new Counter({ + name: 'recording_blob_ingestion_v2_events_flushed_total', + help: 'Number of individual events that have been flushed', + }) + + private static readonly bytesWritten = new Counter({ + name: 'recording_blob_ingestion_v2_bytes_written_total', + help: 'Number of bytes written to storage', + }) + + public static incrementBatchesFlushed(): void { + this.batchesFlushed.inc() + } + + public static incrementSessionsFlushed(count: number = 1): void { + this.sessionsFlushed.inc(count) + } + + public static incrementEventsFlushed(count: number = 1): void { + this.eventsFlushed.inc(count) + } + + public static incrementBytesWritten(bytes: number): void { + this.bytesWritten.inc(bytes) + } +} diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/promise-queue.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/promise-queue.ts new file mode 100644 index 0000000000000..8093f22905f75 --- /dev/null +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/promise-queue.ts @@ -0,0 +1,40 @@ +type QueuedCallback = { + callback: () => Promise + resolve: (value: R) => void + reject: (error: unknown) => void +} + +export class PromiseQueue { + private callbackQueue: QueuedCallback[] = [] + private isExecuting = false + + constructor() {} + + public async add(callback: () => Promise): Promise { + return new Promise((resolve, reject) => { + this.callbackQueue.push({ callback, resolve, reject }) + process.nextTick(() => this.processNextCallback()) + }) + } + + private async processNextCallback(): Promise { + if (this.isExecuting || this.callbackQueue.length === 0) { + return + } + + this.isExecuting = true + const { callback, resolve, reject } = this.callbackQueue.shift()! + + try { + const result = await callback() + resolve(result) + } catch (error) { + reject(error) + } finally { + this.isExecuting = false + if (this.callbackQueue.length > 0) { + process.nextTick(() => this.processNextCallback()) + } + } + } +} diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/recorder.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/recorder.ts new file mode 100644 index 0000000000000..a591faa17928a --- /dev/null +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/recorder.ts @@ -0,0 +1,44 @@ +import { Writable } from 'stream' + +import { ParsedMessageData } from '../kafka/types' + +interface WriteResult { + eventCount: number + bytesWritten: number +} + +export class SessionRecorder { + private chunks: string[] = [] + private size: number = 0 + + public recordMessage(message: ParsedMessageData): number { + let bytesWritten = 0 + + Object.entries(message.eventsByWindowId).forEach(([windowId, events]) => { + events.forEach((event) => { + const serializedLine = JSON.stringify([windowId, event]) + '\n' + this.chunks.push(serializedLine) + bytesWritten += Buffer.byteLength(serializedLine) + }) + }) + + this.size += bytesWritten + return bytesWritten + } + + public async write(stream: Writable): Promise { + let eventCount = 0 + let bytesWritten = 0 + + for (const chunk of this.chunks) { + if (!stream.write(chunk)) { + // Handle backpressure + await new Promise((resolve) => stream.once('drain', resolve)) + } + eventCount++ + bytesWritten += Buffer.byteLength(chunk) + } + + return { eventCount, bytesWritten } + } +} diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/session-batch-manager.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/session-batch-manager.ts new file mode 100644 index 0000000000000..8aa7687b6999b --- /dev/null +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/session-batch-manager.ts @@ -0,0 +1,62 @@ +import { KafkaOffsetManager } from '../kafka/offset-manager' +import { PromiseQueue } from './promise-queue' +import { SessionBatchRecorderInterface } from './session-batch-recorder' + +export interface SessionBatchManagerConfig { + maxBatchSizeBytes: number + maxBatchAgeMs: number + createBatch: () => SessionBatchRecorderInterface + offsetManager: KafkaOffsetManager +} + +export class SessionBatchManager { + private currentBatch: SessionBatchRecorderInterface + private queue: PromiseQueue + private readonly maxBatchSizeBytes: number + private readonly maxBatchAgeMs: number + private readonly createBatch: () => SessionBatchRecorderInterface + private readonly offsetManager: KafkaOffsetManager + private lastFlushTime: number + + constructor(config: SessionBatchManagerConfig) { + this.maxBatchSizeBytes = config.maxBatchSizeBytes + this.maxBatchAgeMs = config.maxBatchAgeMs + this.createBatch = config.createBatch + this.offsetManager = config.offsetManager + this.currentBatch = this.offsetManager.wrapBatch(this.createBatch()) + this.queue = new PromiseQueue() + this.lastFlushTime = Date.now() + } + + public async withBatch(callback: (batch: SessionBatchRecorderInterface) => Promise): Promise { + return this.queue.add(() => callback(this.currentBatch)) + } + + public async flush(): Promise { + return this.queue.add(async () => { + await this.rotateBatch() + }) + } + + public shouldFlush(): boolean { + const batchSize = this.currentBatch.size + const batchAge = Date.now() - this.lastFlushTime + return batchSize >= this.maxBatchSizeBytes || batchAge >= this.maxBatchAgeMs + } + + public async discardPartitions(partitions: number[]): Promise { + return this.queue.add(async () => { + for (const partition of partitions) { + this.currentBatch.discardPartition(partition) + } + return Promise.resolve() + }) + } + + private async rotateBatch(): Promise { + await this.currentBatch.flush() + await this.offsetManager.commit() + this.currentBatch = this.offsetManager.wrapBatch(this.createBatch()) + this.lastFlushTime = Date.now() + } +} diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/session-batch-recorder.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/session-batch-recorder.ts new file mode 100644 index 0000000000000..e44c722921382 --- /dev/null +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/sessions/session-batch-recorder.ts @@ -0,0 +1,99 @@ +import { Writable } from 'stream' + +import { MessageWithTeam } from '../teams/types' +import { SessionBatchMetrics } from './metrics' +import { SessionRecorder } from './recorder' + +export interface StreamWithFinish { + stream: Writable + finish: () => Promise +} + +export interface SessionBatchWriter { + open(): Promise +} + +export interface SessionBatchRecorderInterface { + record(message: MessageWithTeam): number + flush(): Promise + discardPartition(partition: number): void + readonly size: number +} + +export class SessionBatchRecorder implements SessionBatchRecorderInterface { + private readonly partitionSessions = new Map>() + private readonly partitionSizes = new Map() + private _size: number = 0 + + constructor(private readonly writer: SessionBatchWriter) {} + + public record(message: MessageWithTeam): number { + const { partition } = message.message.metadata + const sessionId = message.message.session_id + + if (!this.partitionSessions.has(partition)) { + this.partitionSessions.set(partition, new Map()) + this.partitionSizes.set(partition, 0) + } + + const sessions = this.partitionSessions.get(partition)! + if (!sessions.has(sessionId)) { + sessions.set(sessionId, new SessionRecorder()) + } + + const recorder = sessions.get(sessionId)! + const bytesWritten = recorder.recordMessage(message.message) + + // Update both partition size and total size + const currentPartitionSize = this.partitionSizes.get(partition)! + this.partitionSizes.set(partition, currentPartitionSize + bytesWritten) + this._size += bytesWritten + + return bytesWritten + } + + public discardPartition(partition: number): void { + const partitionSize = this.partitionSizes.get(partition) + if (partitionSize) { + this._size -= partitionSize + this.partitionSizes.delete(partition) + this.partitionSessions.delete(partition) + } + } + + public async flush(): Promise { + const { stream, finish } = await this.writer.open() + + let totalEvents = 0 + let totalSessions = 0 + let totalBytes = 0 + + // Flush sessions grouped by partition + for (const sessions of this.partitionSessions.values()) { + for (const recorder of sessions.values()) { + const { eventCount, bytesWritten } = await recorder.write(stream) + totalEvents += eventCount + totalBytes += bytesWritten + } + totalSessions += sessions.size + } + + stream.end() + await finish() + + // Update metrics + SessionBatchMetrics.incrementBatchesFlushed() + SessionBatchMetrics.incrementSessionsFlushed(totalSessions) + SessionBatchMetrics.incrementEventsFlushed(totalEvents) + SessionBatchMetrics.incrementBytesWritten(totalBytes) + + // Clear sessions, partition sizes, and total size after successful flush + this.partitionSessions.clear() + this.partitionSizes.clear() + this._size = 0 + } + + public get size(): number { + return this._size + } +} diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/teams/team-filter.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/teams/team-filter.ts index feb210cfbd765..d678a7f84e4ff 100644 --- a/plugin-server/src/main/ingestion-queues/session-recording-v2/teams/team-filter.ts +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/teams/team-filter.ts @@ -1,46 +1,31 @@ -import { Message, MessageHeader } from 'node-rdkafka' +import { MessageHeader } from 'node-rdkafka' import { status } from '../../../../utils/status' import { eventDroppedCounter } from '../../metrics' -import { KafkaParser } from '../kafka/parser' -import { BatchMessageProcessor } from '../types' +import { ParsedMessageData } from '../kafka/types' import { TeamService } from './team-service' import { MessageWithTeam, Team } from './types' -export class TeamFilter implements BatchMessageProcessor { - constructor(private readonly teamService: TeamService, private readonly parser: KafkaParser) {} +export class TeamFilter { + constructor(private readonly teamService: TeamService) {} - public async parseMessage(message: Message): Promise { - const team = await this.validateTeamToken(message, message.headers) - if (!team) { - return null - } - - const parsedMessage = await this.parser.parseMessage(message) - if (!parsedMessage) { - return null - } - - return { - team, - message: parsedMessage, - } - } - - public async parseBatch(messages: Message[]): Promise { - const parsedMessages: MessageWithTeam[] = [] + public async filterBatch(messages: ParsedMessageData[]): Promise { + const messagesWithTeam: MessageWithTeam[] = [] for (const message of messages) { - const messageWithTeam = await this.parseMessage(message) - if (messageWithTeam) { - parsedMessages.push(messageWithTeam) + const team = await this.validateTeam(message) + if (team) { + messagesWithTeam.push({ + team, + message, + }) } } - return parsedMessages + return messagesWithTeam } - private async validateTeamToken(message: Message, headers: MessageHeader[] | undefined): Promise { + private async validateTeam(message: ParsedMessageData): Promise { const dropMessage = (reason: string, extra?: Record) => { // TODO refactor eventDroppedCounter @@ -52,13 +37,13 @@ export class TeamFilter implements BatchMessageProcessor> - public async getTeamByToken(_token: string): Promise { - // For now, just return null as we'll implement the actual team lookup later - return Promise.resolve(null) + constructor(postgres: PostgresRouter) { + this.teamRefresher = new BackgroundRefresher( + () => fetchTeamTokensWithRecordings(postgres), + 5 * 60 * 1000, // 5 minutes + (e) => { + // We ignore the error and wait for postgres to recover + logger.error('Error refreshing team tokens', e) + } + ) + } + + public async getTeamByToken(token: string): Promise { + const teams = await this.teamRefresher.get() + const teamConfig = teams[token] + + if (!teamConfig?.teamId) { + return null + } + + return { + teamId: teamConfig.teamId, + consoleLogIngestionEnabled: teamConfig.consoleLogIngestionEnabled, + } } } diff --git a/plugin-server/src/main/ingestion-queues/session-recording-v2/versions/lib-version-monitor.ts b/plugin-server/src/main/ingestion-queues/session-recording-v2/versions/lib-version-monitor.ts index f4ce2695e86c6..348590c8010b1 100644 --- a/plugin-server/src/main/ingestion-queues/session-recording-v2/versions/lib-version-monitor.ts +++ b/plugin-server/src/main/ingestion-queues/session-recording-v2/versions/lib-version-monitor.ts @@ -2,20 +2,15 @@ import { MessageHeader } from 'node-rdkafka' import { status } from '../../../../utils/status' import { MessageWithTeam } from '../teams/types' -import { BatchMessageProcessor, CaptureIngestionWarningFn } from '../types' +import { CaptureIngestionWarningFn } from '../types' import { VersionMetrics } from './version-metrics' -export class LibVersionMonitor implements BatchMessageProcessor { - constructor( - private readonly sourceProcessor: BatchMessageProcessor, - private readonly captureWarning: CaptureIngestionWarningFn, - private readonly metrics: VersionMetrics - ) {} +export class LibVersionMonitor { + constructor(private readonly captureWarning: CaptureIngestionWarningFn, private readonly metrics: VersionMetrics) {} - public async parseBatch(messages: TInput[]): Promise { - const processedMessages = await this.sourceProcessor.parseBatch(messages) - await Promise.all(processedMessages.map((message) => this.checkLibVersion(message))) - return processedMessages + public async processBatch(messages: MessageWithTeam[]): Promise { + await Promise.all(messages.map((message) => this.checkLibVersion(message))) + return messages } private async checkLibVersion(message: MessageWithTeam): Promise { diff --git a/plugin-server/src/main/pluginsServer.ts b/plugin-server/src/main/pluginsServer.ts index ac7f43f66ca85..d6c8251b294c5 100644 --- a/plugin-server/src/main/pluginsServer.ts +++ b/plugin-server/src/main/pluginsServer.ts @@ -12,10 +12,15 @@ import v8Profiler from 'v8-profiler-next' import { getPluginServerCapabilities } from '../capabilities' import { CdpApi } from '../cdp/cdp-api' import { CdpCyclotronWorker, CdpCyclotronWorkerFetch } from '../cdp/consumers/cdp-cyclotron-worker.consumer' -import { CdpFunctionCallbackConsumer } from '../cdp/consumers/cdp-function-callback.consumer' import { CdpInternalEventsConsumer } from '../cdp/consumers/cdp-internal-event.consumer' import { CdpProcessedEventsConsumer } from '../cdp/consumers/cdp-processed-events.consumer' import { defaultConfig } from '../config/config' +import { + KAFKA_EVENTS_PLUGIN_INGESTION, + KAFKA_EVENTS_PLUGIN_INGESTION_HISTORICAL, + KAFKA_EVENTS_PLUGIN_INGESTION_OVERFLOW, +} from '../config/kafka-topics' +import { IngestionConsumer } from '../ingestion/ingestion-consumer' import { KafkaProducerWrapper } from '../kafka/producer' import { Hub, PluginServerCapabilities, PluginServerService, PluginsServerConfig } from '../types' import { closeHub, createHub, createKafkaClient } from '../utils/db/hub' @@ -266,56 +271,97 @@ export async function startPluginsServer( } } - if (capabilities.ingestion) { + if (capabilities.ingestionV2Combined) { + // NOTE: This is for single process deployments like local dev and hobby - it runs all possible consumers + // in a single process. In production these are each separate Deployments of the standard ingestion consumer const hub = await setupHub() - piscina = piscina ?? (await makePiscina(serverConfig, hub)) - services.push( - await startAnalyticsEventsIngestionConsumer({ - hub: hub, - }) - ) - } - if (capabilities.ingestionHistorical) { - const hub = await setupHub() - piscina = piscina ?? (await makePiscina(serverConfig, hub)) - services.push( - await startAnalyticsEventsIngestionHistoricalConsumer({ - hub: hub, - }) - ) - } + const consumersOptions = [ + { + topic: KAFKA_EVENTS_PLUGIN_INGESTION, + group_id: `clickhouse-ingestion`, + }, + { + topic: KAFKA_EVENTS_PLUGIN_INGESTION_HISTORICAL, + group_id: `clickhouse-ingestion-historical`, + }, + { topic: KAFKA_EVENTS_PLUGIN_INGESTION_OVERFLOW, group_id: 'clickhouse-ingestion-overflow' }, + { topic: 'client_iwarnings_ingestion', group_id: 'client_iwarnings_ingestion' }, + { topic: 'heatmaps_ingestion', group_id: 'heatmaps_ingestion' }, + { topic: 'exceptions_ingestion', group_id: 'exceptions_ingestion' }, + ] + + for (const consumerOption of consumersOptions) { + const modifiedHub: Hub = { + ...hub, + INGESTION_CONSUMER_CONSUME_TOPIC: consumerOption.topic, + INGESTION_CONSUMER_GROUP_ID: consumerOption.group_id, + } + const consumer = new IngestionConsumer(modifiedHub) + await consumer.start() + services.push(consumer.service) + } + } else { + if (capabilities.ingestionV2) { + const hub = await setupHub() + const consumer = new IngestionConsumer(hub) + await consumer.start() + services.push(consumer.service) + } - if (capabilities.eventsIngestionPipelines) { - const pipelinesToRun = - serverConfig.PLUGIN_SERVER_EVENTS_INGESTION_PIPELINE === null - ? Object.keys(PIPELINES) - : [serverConfig.PLUGIN_SERVER_EVENTS_INGESTION_PIPELINE] + // Below are all legacy consumers that will be replaced by the new ingestion consumer that covers all cases - for (const pipelineKey of pipelinesToRun) { - if (pipelineKey === null || !PIPELINES[pipelineKey]) { - throw new Error(`Invalid events ingestion pipeline: ${pipelineKey}`) - } + if (capabilities.ingestion) { + const hub = await setupHub() + piscina = piscina ?? (await makePiscina(serverConfig, hub)) + services.push( + await startAnalyticsEventsIngestionConsumer({ + hub: hub, + }) + ) + } + if (capabilities.ingestionHistorical) { const hub = await setupHub() piscina = piscina ?? (await makePiscina(serverConfig, hub)) services.push( - await startEventsIngestionPipelineConsumer({ + await startAnalyticsEventsIngestionHistoricalConsumer({ hub: hub, - pipelineKey: pipelineKey, }) ) } - } - if (capabilities.ingestionOverflow) { - const hub = await setupHub() - piscina = piscina ?? (await makePiscina(serverConfig, hub)) - services.push( - await startAnalyticsEventsIngestionOverflowConsumer({ - hub: hub, - }) - ) + if (capabilities.eventsIngestionPipelines) { + const pipelinesToRun = + serverConfig.PLUGIN_SERVER_EVENTS_INGESTION_PIPELINE === null + ? Object.keys(PIPELINES) + : [serverConfig.PLUGIN_SERVER_EVENTS_INGESTION_PIPELINE] + + for (const pipelineKey of pipelinesToRun) { + if (pipelineKey === null || !PIPELINES[pipelineKey]) { + throw new Error(`Invalid events ingestion pipeline: ${pipelineKey}`) + } + + const hub = await setupHub() + piscina = piscina ?? (await makePiscina(serverConfig, hub)) + services.push( + await startEventsIngestionPipelineConsumer({ + hub: hub, + pipelineKey: pipelineKey, + }) + ) + } + } + + if (capabilities.ingestionOverflow) { + const hub = await setupHub() + piscina = piscina ?? (await makePiscina(serverConfig, hub)) + services.push( + await startAnalyticsEventsIngestionOverflowConsumer({ + hub: hub, + }) + ) + } } if (capabilities.processAsyncOnEventHandlers) { @@ -446,15 +492,19 @@ export async function startPluginsServer( } if (capabilities.sessionRecordingBlobIngestionV2) { + const hub = await setupHub() + const postgres = hub?.postgres ?? new PostgresRouter(serverConfig) const batchConsumerFactory = new DefaultBatchConsumerFactory(serverConfig) - const ingester = new SessionRecordingIngesterV2(serverConfig, false, batchConsumerFactory) + const ingester = new SessionRecordingIngesterV2(serverConfig, false, postgres, batchConsumerFactory) await ingester.start() services.push(ingester.service) } if (capabilities.sessionRecordingBlobIngestionV2Overflow) { + const hub = await setupHub() + const postgres = hub?.postgres ?? new PostgresRouter(serverConfig) const batchConsumerFactory = new DefaultBatchConsumerFactory(serverConfig) - const ingester = new SessionRecordingIngesterV2(serverConfig, true, batchConsumerFactory) + const ingester = new SessionRecordingIngesterV2(serverConfig, true, postgres, batchConsumerFactory) await ingester.start() services.push(ingester.service) } @@ -471,15 +521,8 @@ export async function startPluginsServer( const consumer = new CdpInternalEventsConsumer(hub) await consumer.start() services.push(consumer.service) - } - - if (capabilities.cdpFunctionCallbacks) { - const hub = await setupHub() - const consumer = new CdpFunctionCallbackConsumer(hub) - await consumer.start() - services.push(consumer.service) - // NOTE: The function callback service is more idle so can handle http requests as well + // NOTE: This processor is generally very idle so doubles as our api if (capabilities.http) { const api = new CdpApi(hub, consumer) expressApp.use('/', api.router()) diff --git a/plugin-server/src/types.ts b/plugin-server/src/types.ts index 1d05425f96e86..9cf3d1bdf7f92 100644 --- a/plugin-server/src/types.ts +++ b/plugin-server/src/types.ts @@ -72,7 +72,9 @@ export enum KafkaSaslMechanism { } export enum PluginServerMode { + all_v2 = 'all-v2', ingestion = 'ingestion', + ingestion_v2 = 'ingestion-v2', ingestion_overflow = 'ingestion-overflow', ingestion_historical = 'ingestion-historical', events_ingestion = 'events-ingestion', @@ -87,7 +89,6 @@ export enum PluginServerMode { recordings_blob_ingestion_v2_overflow = 'recordings-blob-ingestion-v2-overflow', cdp_processed_events = 'cdp-processed-events', cdp_internal_events = 'cdp-internal-events', - cdp_function_callbacks = 'cdp-function-callbacks', cdp_cyclotron_worker = 'cdp-cyclotron-worker', functional_tests = 'functional-tests', } @@ -119,7 +120,6 @@ export type CdpConfig = { CDP_WATCHER_DISABLED_TEMPORARY_MAX_COUNT: number // How many times a function can be disabled before it is disabled permanently CDP_ASYNC_FUNCTIONS_RUSTY_HOOK_TEAMS: string CDP_HOG_FILTERS_TELEMETRY_TEAMS: string - CDP_CYCLOTRON_ENABLED_TEAMS: string CDP_CYCLOTRON_BATCH_SIZE: number CDP_CYCLOTRON_BATCH_DELAY_MS: number CDP_REDIS_HOST: string @@ -129,7 +129,16 @@ export type CdpConfig = { CDP_GOOGLE_ADWORDS_DEVELOPER_TOKEN: string } -export interface PluginsServerConfig extends CdpConfig { +export type IngestionConsumerConfig = { + // New config variables used by the new IngestionConsumer + INGESTION_CONSUMER_GROUP_ID: string + INGESTION_CONSUMER_CONSUME_TOPIC: string + INGESTION_CONSUMER_DLQ_TOPIC: string + /** If set then overflow routing is enabled and the topic is used for overflow events */ + INGESTION_CONSUMER_OVERFLOW_TOPIC?: string +} + +export interface PluginsServerConfig extends CdpConfig, IngestionConsumerConfig { TASKS_PER_WORKER: number // number of parallel tasks per worker thread INGESTION_CONCURRENCY: number // number of parallel event ingestion queues per batch INGESTION_BATCH_SIZE: number // kafka consumer batch size @@ -309,6 +318,9 @@ export interface PluginsServerConfig extends CdpConfig { CYCLOTRON_DATABASE_URL: string CYCLOTRON_SHARD_DEPTH_LIMIT: number + + SESSION_RECORDING_MAX_BATCH_SIZE_KB: number | undefined + SESSION_RECORDING_MAX_BATCH_AGE_MS: number | undefined } export interface Hub extends PluginsServerConfig { @@ -361,6 +373,8 @@ export interface PluginServerCapabilities { ingestionOverflow?: boolean ingestionHistorical?: boolean eventsIngestionPipelines?: boolean + ingestionV2Combined?: boolean + ingestionV2?: boolean pluginScheduledTasks?: boolean processPluginJobs?: boolean processAsyncOnEventHandlers?: boolean @@ -371,7 +385,6 @@ export interface PluginServerCapabilities { sessionRecordingBlobIngestionV2Overflow?: boolean cdpProcessedEvents?: boolean cdpInternalEvents?: boolean - cdpFunctionCallbacks?: boolean cdpCyclotronWorker?: boolean appManagementSingleton?: boolean preflightSchedules?: boolean // Used for instance health checks on hobby deploy, not useful on cloud @@ -1178,8 +1191,6 @@ export interface EventPropertyType { project_id: number | null } -export type PluginFunction = 'onEvent' | 'processEvent' | 'pluginTask' - export type GroupTypeToColumnIndex = Record export enum PropertyUpdateOperation { diff --git a/plugin-server/src/utils/background-refresher.ts b/plugin-server/src/utils/background-refresher.ts index c2645d8ba548d..b10a1bdaf4db2 100644 --- a/plugin-server/src/utils/background-refresher.ts +++ b/plugin-server/src/utils/background-refresher.ts @@ -7,7 +7,13 @@ export class BackgroundRefresher { private cachedValuePromise: Promise | null = null private lastRefreshTime = 0 - constructor(private readonly refreshFunction: () => Promise, private readonly maxAgeMs: number = 1000 * 60) {} + constructor( + private readonly refreshFunction: () => Promise, + private readonly maxAgeMs: number = 1000 * 60, + private readonly errorHandler: (e: unknown) => void = (e) => { + throw e + } + ) {} public async refresh(): Promise { if (this.cachedValuePromise) { @@ -34,7 +40,7 @@ export class BackgroundRefresher { if (Date.now() - this.lastRefreshTime > this.maxAgeMs) { // We trigger the refresh but we don't use it - void this.refresh() + void this.refresh().catch(this.errorHandler) } return this.cachedValue! diff --git a/plugin-server/src/utils/db/db.ts b/plugin-server/src/utils/db/db.ts index eeee1c49b1072..fbeb322ccb504 100644 --- a/plugin-server/src/utils/db/db.ts +++ b/plugin-server/src/utils/db/db.ts @@ -63,6 +63,7 @@ import { UUIDT, } from '../utils' import { OrganizationPluginsAccessLevel } from './../../types' +import { RedisOperationError } from './error' import { personUpdateVersionMismatchCounter, pluginLogEntryCounter } from './metrics' import { PostgresRouter, PostgresUse, TransactionClient } from './postgres' import { @@ -193,6 +194,56 @@ export class DB { // Redis + private instrumentRedisQuery( + operationName: string, + tag: string | undefined, + logContext: Record, + runQuery: (client: Redis.Redis) => Promise + ): Promise { + return instrumentQuery(operationName, tag, async () => { + let client: Redis.Redis + const timeout = timeoutGuard(`${operationName} delayed. Waiting over 30 sec.`, logContext) + try { + client = await this.redisPool.acquire() + } catch (error) { + throw new RedisOperationError('Failed to acquire redis client from pool', error) + } + + // Don't use a single try/catch/finally for this, as there are 2 potential errors that could be thrown + // (error and cleanup) and we want to be explicit about which one we choose, rather than relying on + // "what happens when you throw in a finally block". + // We explicitly want to throw the error from the operation if there is one, prioritising it over any errors + // from the cleanup + let operationResult: { value: T } | { error: Error } + let cleanupError: Error | undefined + + try { + operationResult = { value: await runQuery(client) } + } catch (error) { + operationResult = { error } + } + + try { + clearTimeout(timeout) + await this.redisPool.release(client) + } catch (error) { + cleanupError = error + } + + if ('error' in operationResult) { + throw new RedisOperationError( + `${operationName} failed for ${JSON.stringify(logContext)}`, + operationResult.error, + logContext + ) + } + if (cleanupError) { + throw new RedisOperationError('Failed to release redis client from pool', cleanupError) + } + return operationResult.value + }) + } + public redisGet( key: string, defaultValue: T, @@ -200,10 +251,7 @@ export class DB { options: CacheOptions = {} ): Promise { const { jsonSerialize = true } = options - - return instrumentQuery('query.redisGet', tag, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('Getting redis key delayed. Waiting over 30 sec to get key.', { key }) + return this.instrumentRedisQuery('query.redisGet', tag, { key }, async (client) => { try { const value = await tryTwice( async () => await client.get(key), @@ -220,26 +268,16 @@ export class DB { } else { throw error } - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) } }) } public redisGetBuffer(key: string, tag: string): Promise { - return instrumentQuery('query.redisGetBuffer', tag, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('Getting redis key delayed. Waiting over 30 sec to get key.', { key }) - try { - return await tryTwice( - async () => await client.getBuffer(key), - `Waited 5 sec to get redis key: ${key}, retrying once!` - ) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) - } + return this.instrumentRedisQuery('query.redisGetBuffer', tag, { key }, async (client) => { + return await tryTwice( + async () => await client.getBuffer(key), + `Waited 5 sec to get redis key: ${key}, retrying once!` + ) }) } @@ -252,36 +290,22 @@ export class DB { ): Promise { const { jsonSerialize = true } = options - return instrumentQuery('query.redisSet', tag, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('Setting redis key delayed. Waiting over 30 sec to set key', { key }) - try { - const serializedValue = jsonSerialize ? JSON.stringify(value) : (value as string) - if (ttlSeconds) { - await client.set(key, serializedValue, 'EX', ttlSeconds) - } else { - await client.set(key, serializedValue) - } - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) + return this.instrumentRedisQuery('query.redisSet', tag, { key }, async (client) => { + const serializedValue = jsonSerialize ? JSON.stringify(value) : (value as string) + if (ttlSeconds) { + await client.set(key, serializedValue, 'EX', ttlSeconds) + } else { + await client.set(key, serializedValue) } }) } public redisSetBuffer(key: string, value: Buffer, tag: string, ttlSeconds?: number): Promise { - return instrumentQuery('query.redisSetBuffer', tag, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('Setting redis key delayed. Waiting over 30 sec to set key', { key }) - try { - if (ttlSeconds) { - await client.setBuffer(key, value, 'EX', ttlSeconds) - } else { - await client.setBuffer(key, value) - } - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) + return this.instrumentRedisQuery('query.redisSetBuffer', tag, { key }, async (client) => { + if (ttlSeconds) { + await client.setBuffer(key, value, 'EX', ttlSeconds) + } else { + await client.setBuffer(key, value) } }) } @@ -295,19 +319,12 @@ export class DB { ): Promise<'OK' | null> { const { jsonSerialize = true } = options - return instrumentQuery('query.redisSetNX', tag, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('Setting redis key delayed. Waiting over 30 sec to set key (NX)', { key }) - try { - const serializedValue = jsonSerialize ? JSON.stringify(value) : (value as string) - if (ttlSeconds) { - return await client.set(key, serializedValue, 'EX', ttlSeconds, 'NX') - } else { - return await client.set(key, serializedValue, 'NX') - } - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) + return this.instrumentRedisQuery('query.redisSetNX', tag, { key }, async (client) => { + const serializedValue = jsonSerialize ? JSON.stringify(value) : (value as string) + if (ttlSeconds) { + return await client.set(key, serializedValue, 'EX', ttlSeconds, 'NX') + } else { + return await client.set(key, serializedValue, 'NX') } }) } @@ -315,205 +332,127 @@ export class DB { public redisSetMulti(kv: Array<[string, unknown]>, ttlSeconds?: number, options: CacheOptions = {}): Promise { const { jsonSerialize = true } = options - return instrumentQuery('query.redisSet', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('Setting redis key delayed. Waiting over 30 sec to set keys', { - keys: kv.map((x) => x[0]), - }) - try { - let pipeline = client.multi() - for (const [key, value] of kv) { - const serializedValue = jsonSerialize ? JSON.stringify(value) : (value as string) - if (ttlSeconds) { - pipeline = pipeline.set(key, serializedValue, 'EX', ttlSeconds) - } else { - pipeline = pipeline.set(key, serializedValue) - } + return this.instrumentRedisQuery('query.redisSet', undefined, { keys: kv.map((x) => x[0]) }, async (client) => { + let pipeline = client.multi() + for (const [key, value] of kv) { + const serializedValue = jsonSerialize ? JSON.stringify(value) : (value as string) + if (ttlSeconds) { + pipeline = pipeline.set(key, serializedValue, 'EX', ttlSeconds) + } else { + pipeline = pipeline.set(key, serializedValue) } - await pipeline.exec() - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) } + await pipeline.exec() }) } public redisIncr(key: string): Promise { - return instrumentQuery('query.redisIncr', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('Incrementing redis key delayed. Waiting over 30 sec to incr key', { key }) - try { - return await client.incr(key) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) - } + return this.instrumentRedisQuery('query.redisIncr', undefined, { key }, async (client) => { + return await client.incr(key) }) } public redisExpire(key: string, ttlSeconds: number): Promise { - return instrumentQuery('query.redisExpire', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('Expiring redis key delayed. Waiting over 30 sec to expire key', { key }) - try { - return (await client.expire(key, ttlSeconds)) === 1 - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) - } + return this.instrumentRedisQuery('query.redisExpire', undefined, { key }, async (client) => { + return (await client.expire(key, ttlSeconds)) === 1 }) } public redisLPush(key: string, value: unknown, options: CacheOptions = {}): Promise { const { jsonSerialize = true } = options - return instrumentQuery('query.redisLPush', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('LPushing redis key delayed. Waiting over 30 sec to lpush key', { key }) - try { - const serializedValue = jsonSerialize ? JSON.stringify(value) : (value as string | string[]) - return await client.lpush(key, serializedValue) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) - } + return this.instrumentRedisQuery('query.redisLPush', undefined, { key }, async (client) => { + const serializedValue = jsonSerialize ? JSON.stringify(value) : (value as string | string[]) + return await client.lpush(key, serializedValue) }) } - public redisLRange(key: string, startIndex: number, endIndex: number): Promise { - return instrumentQuery('query.redisLRange', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('LRANGE delayed. Waiting over 30 sec to perform LRANGE', { - key, - startIndex, - endIndex, - }) - try { - return await client.lrange(key, startIndex, endIndex) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) - } + public redisLRange(key: string, startIndex: number, endIndex: number, tag?: string): Promise { + return this.instrumentRedisQuery('query.redisLRange', tag, { key, startIndex, endIndex }, async (client) => { + return await client.lrange(key, startIndex, endIndex) }) } public redisLLen(key: string): Promise { - return instrumentQuery('query.redisLLen', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('LLEN delayed. Waiting over 30 sec to perform LLEN', { - key, - }) - try { - return await client.llen(key) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) - } + return this.instrumentRedisQuery('query.redisLLen', undefined, { key }, async (client) => { + return await client.llen(key) }) } public redisBRPop(key1: string, key2: string): Promise<[string, string]> { - return instrumentQuery('query.redisBRPop', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('BRPoping redis key delayed. Waiting over 30 sec to brpop keys', { - key1, - key2, - }) - try { - return await client.brpop(key1, key2) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) - } + return this.instrumentRedisQuery('query.redisBRPop', undefined, { key1, key2 }, async (client) => { + return await client.brpop(key1, key2) }) } public redisLRem(key: string, count: number, elementKey: string): Promise { - return instrumentQuery('query.redisLRem', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('LREM delayed. Waiting over 30 sec to perform LREM', { + return this.instrumentRedisQuery( + 'query.redisLRem', + undefined, + { key, count, elementKey, - }) - try { + }, + async (client) => { return await client.lrem(key, count, elementKey) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) } - }) + ) } public redisLPop(key: string, count: number): Promise { - return instrumentQuery('query.redisLPop', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('LPOP delayed. Waiting over 30 sec to perform LPOP', { + return this.instrumentRedisQuery( + 'query.redisLPop', + undefined, + { key, count, - }) - try { + }, + async (client) => { return await client.lpop(key, count) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) } - }) + ) } public redisSAddAndSCard(key: string, value: Redis.ValueType, ttlSeconds?: number): Promise { - return instrumentQuery('query.redisSAddAndSCard', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('SADD+SCARD delayed. Waiting over 30 sec to perform SADD+SCARD', { - key, - value, - }) - try { - const multi = client.multi() - multi.sadd(key, value) - if (ttlSeconds) { - multi.expire(key, ttlSeconds) - } - multi.scard(key) - const results = await multi.exec() - const scardResult = ttlSeconds ? results[2] : results[1] - return scardResult[1] - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) + return this.instrumentRedisQuery('query.redisSAddAndSCard', undefined, { key }, async (client) => { + const multi = client.multi() + multi.sadd(key, value) + if (ttlSeconds) { + multi.expire(key, ttlSeconds) } + multi.scard(key) + const results = await multi.exec() + const scardResult = ttlSeconds ? results[2] : results[1] + return scardResult[1] }) } public redisSCard(key: string): Promise { - return instrumentQuery('query.redisSCard', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('SCARD delayed. Waiting over 30 sec to perform SCARD', { + return this.instrumentRedisQuery( + 'query.redisSCard', + undefined, + { key, - }) - try { + }, + async (client) => { return await client.scard(key) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) } - }) + ) } public redisPublish(channel: string, message: string): Promise { - return instrumentQuery('query.redisPublish', undefined, async () => { - const client = await this.redisPool.acquire() - const timeout = timeoutGuard('Publish delayed. Waiting over 30 sec to perform Publish', { + return this.instrumentRedisQuery( + 'query.redisPublish', + undefined, + { channel, message, - }) - try { + }, + async (client) => { return await client.publish(channel, message) - } finally { - clearTimeout(timeout) - await this.redisPool.release(client) } - }) + ) } private toPerson(row: RawPerson): InternalPerson { diff --git a/plugin-server/src/utils/db/error.ts b/plugin-server/src/utils/db/error.ts index 8bd2d4f8f5f5d..a1e6f5e4ed675 100644 --- a/plugin-server/src/utils/db/error.ts +++ b/plugin-server/src/utils/db/error.ts @@ -25,6 +25,17 @@ export class MessageSizeTooLarge extends Error { readonly isRetriable = false } +export class RedisOperationError extends Error { + constructor(message: string, error: Error, logContext?: Record) { + super(message) + this.name = 'RedisOperationError' + this.error = error + this.logContext = logContext + } + readonly error: Error + readonly logContext?: Record +} + export async function processError( server: Hub, pluginConfig: PluginConfig | null, diff --git a/plugin-server/tests/utils/token-bucket.test.ts b/plugin-server/src/utils/token-bucket.test.ts similarity index 100% rename from plugin-server/tests/utils/token-bucket.test.ts rename to plugin-server/src/utils/token-bucket.test.ts diff --git a/plugin-server/src/worker/ingestion/event-pipeline/runner.ts b/plugin-server/src/worker/ingestion/event-pipeline/runner.ts index f2d66346323ec..da9eb616021ee 100644 --- a/plugin-server/src/worker/ingestion/event-pipeline/runner.ts +++ b/plugin-server/src/worker/ingestion/event-pipeline/runner.ts @@ -54,10 +54,10 @@ export class EventPipelineRunner { originalEvent: PipelineEvent eventsProcessor: EventsProcessor - constructor(hub: Hub, event: PipelineEvent, eventProcessor: EventsProcessor) { + constructor(hub: Hub, event: PipelineEvent) { this.hub = hub this.originalEvent = event - this.eventsProcessor = eventProcessor + this.eventsProcessor = new EventsProcessor(hub) } isEventDisallowed(event: PipelineEvent): boolean { diff --git a/plugin-server/tests/cdp/cdp-api.test.ts b/plugin-server/tests/cdp/cdp-api.test.ts index 77224fd719929..7fa020cb6aba1 100644 --- a/plugin-server/tests/cdp/cdp-api.test.ts +++ b/plugin-server/tests/cdp/cdp-api.test.ts @@ -4,7 +4,7 @@ import express from 'express' import supertest from 'supertest' import { CdpApi } from '../../src/cdp/cdp-api' -import { CdpFunctionCallbackConsumer } from '../../src/cdp/consumers/cdp-function-callback.consumer' +import { CdpInternalEventsConsumer } from '../../src/cdp/consumers/cdp-internal-event.consumer' import { HogFunctionInvocationGlobals, HogFunctionType } from '../../src/cdp/types' import { Hub, Team } from '../../src/types' import { closeHub, createHub } from '../../src/utils/db/hub' @@ -54,7 +54,7 @@ const mockFetch: jest.Mock = require('../../src/utils/fetch').trackedFetch jest.setTimeout(1000) describe('CDP API', () => { - let processor: CdpFunctionCallbackConsumer + let processor: CdpInternalEventsConsumer let hub: Hub let team: Team @@ -72,7 +72,7 @@ describe('CDP API', () => { hub.CDP_GOOGLE_ADWORDS_DEVELOPER_TOKEN = 'ADWORDS_TOKEN' - processor = new CdpFunctionCallbackConsumer(hub) + processor = new CdpInternalEventsConsumer(hub) await processor.start() diff --git a/plugin-server/tests/cdp/cdp-e2e.test.ts b/plugin-server/tests/cdp/cdp-e2e.test.ts index 3b55f16f5c310..cce755643caa9 100644 --- a/plugin-server/tests/cdp/cdp-e2e.test.ts +++ b/plugin-server/tests/cdp/cdp-e2e.test.ts @@ -1,5 +1,7 @@ +// eslint-disable-next-line simple-import-sort/imports +import { getProducedKafkaMessages, getProducedKafkaMessagesForTopic } from '../helpers/mocks/producer.mock' + import { CdpCyclotronWorker, CdpCyclotronWorkerFetch } from '../../src/cdp/consumers/cdp-cyclotron-worker.consumer' -import { CdpFunctionCallbackConsumer } from '../../src/cdp/consumers/cdp-function-callback.consumer' import { CdpProcessedEventsConsumer } from '../../src/cdp/consumers/cdp-processed-events.consumer' import { HogFunctionInvocationGlobals, HogFunctionType } from '../../src/cdp/types' import { KAFKA_APP_METRICS_2, KAFKA_LOG_ENTRIES } from '../../src/config/kafka-topics' @@ -9,7 +11,6 @@ import { waitForExpect } from '../helpers/expectations' import { getFirstTeam, resetTestDatabase } from '../helpers/sql' import { HOG_EXAMPLES, HOG_FILTERS_EXAMPLES, HOG_INPUTS_EXAMPLES } from './examples' import { createHogExecutionGlobals, insertHogFunction as _insertHogFunction } from './fixtures' -import { createKafkaObserver, TestKafkaObserver } from './helpers/kafka-observer' jest.mock('../../src/utils/fetch', () => { return { @@ -26,16 +27,15 @@ jest.mock('../../src/utils/fetch', () => { const mockFetch: jest.Mock = require('../../src/utils/fetch').trackedFetch -describe('CDP E2E', () => { +describe('CDP Consumer loop', () => { jest.setTimeout(10000) - describe.each(['kafka', 'cyclotron'])('e2e fetch call: %s', (mode) => { + + describe('e2e fetch call', () => { let processedEventsConsumer: CdpProcessedEventsConsumer - let functionProcessor: CdpFunctionCallbackConsumer let cyclotronWorker: CdpCyclotronWorker | undefined let cyclotronFetchWorker: CdpCyclotronWorkerFetch | undefined let hub: Hub let team: Team - let kafkaObserver: TestKafkaObserver let fnFetchNoFilters: HogFunctionType let globals: HogFunctionInvocationGlobals @@ -55,24 +55,15 @@ describe('CDP E2E', () => { ...HOG_FILTERS_EXAMPLES.no_filters, }) - if (mode === 'cyclotron') { - hub.CDP_CYCLOTRON_ENABLED_TEAMS = '*' - hub.CYCLOTRON_DATABASE_URL = 'postgres://posthog:posthog@localhost:5432/test_cyclotron' - } - - kafkaObserver = await createKafkaObserver(hub, [KAFKA_APP_METRICS_2, KAFKA_LOG_ENTRIES]) + hub.CYCLOTRON_DATABASE_URL = 'postgres://posthog:posthog@localhost:5432/test_cyclotron' processedEventsConsumer = new CdpProcessedEventsConsumer(hub) await processedEventsConsumer.start() - functionProcessor = new CdpFunctionCallbackConsumer(hub) - await functionProcessor.start() - if (mode === 'cyclotron') { - cyclotronWorker = new CdpCyclotronWorker(hub) - await cyclotronWorker.start() - cyclotronFetchWorker = new CdpCyclotronWorkerFetch(hub) - await cyclotronFetchWorker.start() - } + cyclotronWorker = new CdpCyclotronWorker(hub) + await cyclotronWorker.start() + cyclotronFetchWorker = new CdpCyclotronWorkerFetch(hub) + await cyclotronFetchWorker.start() globals = createHogExecutionGlobals({ project: { @@ -95,8 +86,6 @@ describe('CDP E2E', () => { afterEach(async () => { const stoppers = [ processedEventsConsumer?.stop().then(() => console.log('Stopped processedEventsConsumer')), - functionProcessor?.stop().then(() => console.log('Stopped functionProcessor')), - kafkaObserver?.stop().then(() => console.log('Stopped kafkaObserver')), cyclotronWorker?.stop().then(() => console.log('Stopped cyclotronWorker')), cyclotronFetchWorker?.stop().then(() => console.log('Stopped cyclotronFetchWorker')), ] @@ -115,12 +104,11 @@ describe('CDP E2E', () => { */ it('should invoke a function in the worker loop until completed', async () => { - // NOTE: We can skip kafka as the entry point const invocations = await processedEventsConsumer.processBatch([globals]) expect(invocations).toHaveLength(1) await waitForExpect(() => { - expect(kafkaObserver.messages).toHaveLength(7) + expect(getProducedKafkaMessages()).toHaveLength(7) }, 5000) expect(mockFetch).toHaveBeenCalledTimes(1) @@ -139,8 +127,8 @@ describe('CDP E2E', () => { ] `) - const logMessages = kafkaObserver.messages.filter((m) => m.topic === KAFKA_LOG_ENTRIES) - const metricsMessages = kafkaObserver.messages.filter((m) => m.topic === KAFKA_APP_METRICS_2) + const logMessages = getProducedKafkaMessagesForTopic(KAFKA_LOG_ENTRIES) + const metricsMessages = getProducedKafkaMessagesForTopic(KAFKA_APP_METRICS_2) expect(metricsMessages).toMatchObject([ { diff --git a/plugin-server/tests/cdp/consumers/cdp-events-consumer.test.ts b/plugin-server/tests/cdp/consumers/cdp-events-consumer.test.ts index 56ea715373ca9..c0be1d89b03e4 100644 --- a/plugin-server/tests/cdp/consumers/cdp-events-consumer.test.ts +++ b/plugin-server/tests/cdp/consumers/cdp-events-consumer.test.ts @@ -1,5 +1,9 @@ // eslint-disable-next-line simple-import-sort/imports -import { getParsedQueuedMessages, mockProducer } from '../../helpers/mocks/producer.mock' +import { + getProducedKafkaMessages, + getProducedKafkaMessagesForTopic, + mockProducer, +} from '../../../tests/helpers/mocks/producer.mock' import { HogWatcherState } from '../../../src/cdp/services/hog-watcher.service' import { HogFunctionInvocationGlobals, HogFunctionType } from '../../../src/cdp/types' @@ -52,29 +56,14 @@ const mockFetch: jest.Mock = require('../../../src/utils/fetch').trackedFetch jest.setTimeout(1000) -type DecodedKafkaMessage = { - topic: string - key?: any - value: Record -} - -const decodeAllKafkaMessages = (): DecodedKafkaMessage[] => { - const queuedMessages = getParsedQueuedMessages() - - const result: DecodedKafkaMessage[] = [] - - for (const topicMessage of queuedMessages) { - for (const message of topicMessage.messages) { - result.push({ - topic: topicMessage.topic, - key: message.key, - value: message.value ?? {}, - }) - } - } - - return result -} +// Add mock for CyclotronManager +const mockBulkCreateJobs = jest.fn() +jest.mock('@posthog/cyclotron', () => ({ + CyclotronManager: jest.fn().mockImplementation(() => ({ + connect: jest.fn(), + bulkCreateJobs: mockBulkCreateJobs, + })), +})) /** * NOTE: The internal and normal events consumers are very similar so we can test them together @@ -100,7 +89,6 @@ describe.each([ beforeEach(async () => { await resetTestDatabase() hub = await createHub() - hub.kafkaProducer = mockProducer team = await getFirstTeam(hub) @@ -108,6 +96,7 @@ describe.each([ await processor.start() mockFetch.mockClear() + mockBulkCreateJobs.mockClear() }) afterEach(async () => { @@ -174,100 +163,35 @@ describe.each([ matchInvocation(fnPrinterPageviewFilters, globals), ]) - expect(decodeAllKafkaMessages()).toMatchObject([ - { - topic: 'log_entries_test', - value: { - message: 'Executing function', - log_source_id: fnFetchNoFilters.id, - }, - }, - { - topic: 'log_entries_test', - value: { - message: - "Suspending function due to async function call 'fetch'. Payload: 2035 bytes. Event: b3a1fe86-b10c-43cc-acaf-d208977608d0", - log_source_id: fnFetchNoFilters.id, - }, - }, - { - topic: 'clickhouse_app_metrics2_test', - value: { - app_source: 'hog_function', - team_id: 2, - app_source_id: fnPrinterPageviewFilters.id, - metric_kind: 'success', - metric_name: 'succeeded', - count: 1, - }, - }, - { - topic: 'log_entries_test', - value: { - message: 'Executing function', - log_source_id: fnPrinterPageviewFilters.id, - }, - }, - { - topic: 'log_entries_test', - value: { - message: 'test', - log_source_id: fnPrinterPageviewFilters.id, - }, - }, - { - topic: 'log_entries_test', - value: { - message: '{"nested":{"foo":"***REDACTED***","bool":false,"null":null}}', - log_source_id: fnPrinterPageviewFilters.id, - }, - }, - { - topic: 'log_entries_test', - value: { - message: '{"foo":"***REDACTED***","bool":false,"null":null}', - log_source_id: fnPrinterPageviewFilters.id, - }, - }, - { - topic: 'log_entries_test', - value: { - message: 'substring: ***REDACTED***', - log_source_id: fnPrinterPageviewFilters.id, - }, - }, - { - topic: 'log_entries_test', - value: { - message: - '{"input_1":"test","secret_input_2":{"foo":"***REDACTED***","bool":false,"null":null},"secret_input_3":"***REDACTED***"}', - log_source_id: fnPrinterPageviewFilters.id, - }, - }, - { - topic: 'log_entries_test', - value: { - message: expect.stringContaining('Function completed'), - log_source_id: fnPrinterPageviewFilters.id, - }, - }, - { - topic: 'clickhouse_app_metrics2_test', - value: { - app_source: 'hog_function', - count: 1, - metric_kind: 'other', - metric_name: 'fetch', - }, - }, - { - topic: 'cdp_function_callbacks_test', - value: { - state: expect.any(String), - }, - key: expect.stringContaining(fnFetchNoFilters.id.toString()), - }, - ]) + // Verify Cyclotron jobs + expect(mockBulkCreateJobs).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + teamId: team.id, + functionId: fnFetchNoFilters.id, + queueName: 'hog', + priority: 1, + vmState: expect.objectContaining({ + hogFunctionId: fnFetchNoFilters.id, + teamId: team.id, + queue: 'hog', + globals: expect.any(Object), + }), + }), + expect.objectContaining({ + teamId: team.id, + functionId: fnPrinterPageviewFilters.id, + queueName: 'hog', + priority: 1, + vmState: expect.objectContaining({ + hogFunctionId: fnPrinterPageviewFilters.id, + teamId: team.id, + queue: 'hog', + globals: expect.any(Object), + }), + }), + ]) + ) }) it("should filter out functions that don't match the filter", async () => { @@ -278,7 +202,26 @@ describe.each([ expect(invocations).toHaveLength(1) expect(invocations).toMatchObject([matchInvocation(fnFetchNoFilters, globals)]) - expect(decodeAllKafkaMessages()).toMatchObject([ + // Verify only one Cyclotron job is created (for fnFetchNoFilters) + expect(mockBulkCreateJobs).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + teamId: team.id, + functionId: fnFetchNoFilters.id, + queueName: 'hog', + priority: 1, + vmState: expect.objectContaining({ + hogFunctionId: fnFetchNoFilters.id, + teamId: team.id, + queue: 'hog', + globals: expect.any(Object), + }), + }), + ]) + ) + + // Still verify the metric for the filtered function + expect(getProducedKafkaMessagesForTopic('clickhouse_app_metrics2_test')).toMatchObject([ { key: expect.any(String), topic: 'clickhouse_app_metrics2_test', @@ -292,18 +235,6 @@ describe.each([ timestamp: expect.any(String), }, }, - { - topic: 'log_entries_test', - }, - { - topic: 'log_entries_test', - }, - { - topic: 'clickhouse_app_metrics2_test', - }, - { - topic: 'cdp_function_callbacks_test', - }, ]) }) @@ -319,7 +250,7 @@ describe.each([ expect(invocations).toHaveLength(0) expect(mockProducer.queueMessages).toHaveBeenCalledTimes(1) - expect(decodeAllKafkaMessages()).toMatchObject([ + expect(getProducedKafkaMessages()).toMatchObject([ { topic: 'clickhouse_app_metrics2_test', value: { @@ -372,7 +303,7 @@ describe.each([ ...HOG_FILTERS_EXAMPLES.broken_filters, }) await processor.processBatch([globals]) - expect(decodeAllKafkaMessages()).toMatchObject([ + expect(getProducedKafkaMessages()).toMatchObject([ { key: expect.any(String), topic: 'clickhouse_app_metrics2_test', diff --git a/plugin-server/tests/helpers/mocks/producer.mock.ts b/plugin-server/tests/helpers/mocks/producer.mock.ts index a452d6693b239..04c211e98fbbb 100644 --- a/plugin-server/tests/helpers/mocks/producer.mock.ts +++ b/plugin-server/tests/helpers/mocks/producer.mock.ts @@ -8,6 +8,12 @@ export type ParsedTopicMessage = { }[] } +export type DecodedKafkaMessage = { + topic: string + key?: any + value: Record +} + jest.mock('../../../src/kafka/producer', () => { const mockKafkaProducer: jest.Mocked = { producer: { @@ -36,8 +42,25 @@ export const getQueuedMessages = (): TopicMessage[] => { }, [] as TopicMessage[]) } +export const getProducedMessages = (): TopicMessage[] => { + return jest.mocked(mockProducer).produce.mock.calls.reduce((acc, call) => { + return acc.concat([ + { + topic: call[0].topic, + messages: [ + { + key: call[0].key, + value: call[0].value, + }, + ], + }, + ]) + }, [] as TopicMessage[]) +} + export const getParsedQueuedMessages = (): ParsedTopicMessage[] => { - return getQueuedMessages().map((topicMessage) => ({ + const allMessages = getProducedMessages().concat(getQueuedMessages()) + return allMessages.map((topicMessage) => ({ topic: topicMessage.topic, messages: topicMessage.messages.map((message) => ({ key: typeof message.key === 'string' ? message.key : null, @@ -45,3 +68,25 @@ export const getParsedQueuedMessages = (): ParsedTopicMessage[] => { })), })) } + +export const getProducedKafkaMessages = (): DecodedKafkaMessage[] => { + const queuedMessages = getParsedQueuedMessages() + + const result: DecodedKafkaMessage[] = [] + + for (const topicMessage of queuedMessages) { + for (const message of topicMessage.messages) { + result.push({ + topic: topicMessage.topic, + key: message.key, + value: message.value ?? {}, + }) + } + } + + return result +} + +export const getProducedKafkaMessagesForTopic = (topic: string): DecodedKafkaMessage[] => { + return getProducedKafkaMessages().filter((x) => x.topic === topic) +} diff --git a/plugin-server/tests/helpers/snapshots.ts b/plugin-server/tests/helpers/snapshots.ts new file mode 100644 index 0000000000000..6943a6802974a --- /dev/null +++ b/plugin-server/tests/helpers/snapshots.ts @@ -0,0 +1,18 @@ +const UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi + +/** + * Helper method that takes an object and replaces all UUIDs with placeholders + */ +export const forSnapshot = (obj: any) => { + const idMap: Record = {} + let strData = JSON.stringify(obj) + + const matches = strData.match(UUID_REGEX) + + for (const match of matches ?? []) { + idMap[match] = `` + strData = strData.replace(match, idMap[match]) + } + + return JSON.parse(strData) +} diff --git a/plugin-server/tests/main/db.test.ts b/plugin-server/tests/main/db.test.ts index 0d7d4c5e118e9..543ce6341258d 100644 --- a/plugin-server/tests/main/db.test.ts +++ b/plugin-server/tests/main/db.test.ts @@ -4,7 +4,7 @@ import { Pool } from 'pg' import { defaultConfig } from '../../src/config/config' import { Hub, Person, PropertyOperator, PropertyUpdateOperation, RawAction, Team } from '../../src/types' import { DB } from '../../src/utils/db/db' -import { DependencyUnavailableError } from '../../src/utils/db/error' +import { DependencyUnavailableError, RedisOperationError } from '../../src/utils/db/error' import { closeHub, createHub } from '../../src/utils/db/hub' import { PostgresRouter, PostgresUse } from '../../src/utils/db/postgres' import { generateKafkaPersonUpdateMessage } from '../../src/utils/db/utils' @@ -903,6 +903,61 @@ describe('DB', () => { }) describe('redis', () => { + describe('instrumentRedisQuery', () => { + const otherErrorType = new Error('other error type') + + it('should only throw Redis errors for operations', async () => { + hub.redisPool.acquire = jest.fn().mockImplementation(() => ({ + get: jest.fn().mockImplementation(() => { + throw otherErrorType + }), + })) + hub.redisPool.release = jest.fn() + await expect(hub.db.redisGet('testKey', 'testDefaultValue', 'testTag')).rejects.toBeInstanceOf( + RedisOperationError + ) + }) + it('should only throw Redis errors for pool acquire', async () => { + hub.redisPool.acquire = jest.fn().mockImplementation(() => { + throw otherErrorType + }) + hub.redisPool.release = jest.fn() + await expect(hub.db.redisGet('testKey', 'testDefaultValue', 'testTag')).rejects.toBeInstanceOf( + RedisOperationError + ) + }) + + it('should only throw Redis errors for pool release', async () => { + hub.redisPool.acquire = jest.fn().mockImplementation(() => ({ + get: jest.fn().mockImplementation(() => { + return 'testValue' + }), + })) + hub.redisPool.release = jest.fn().mockImplementation(() => { + throw otherErrorType + }) + await expect(hub.db.redisGet('testKey', 'testDefaultValue', 'testTag')).rejects.toBeInstanceOf( + RedisOperationError + ) + }) + }) + + describe('get', () => { + const defaultValue = 'testDefaultValue' + const value = 'testValue' + const key = 'testKey' + const tag = 'testTag' + it('should get a value that was previously set', async () => { + await hub.db.redisSet(key, value, tag) + const result = await hub.db.redisGet(key, defaultValue, tag) + expect(result).toEqual(value) + }) + it('should return the default value if there is no value already set', async () => { + const result = await hub.db.redisGet(key, defaultValue, tag) + expect(result).toEqual(defaultValue) + }) + }) + describe('buffer operations', () => { it('writes and reads buffers', async () => { const buffer = Buffer.from('test') diff --git a/plugin-server/tests/main/ingestion-queues/run-ingestion-pipeline.test.ts b/plugin-server/tests/main/ingestion-queues/run-ingestion-pipeline.test.ts index 28d18b2cdd1c3..02675bf99dafc 100644 --- a/plugin-server/tests/main/ingestion-queues/run-ingestion-pipeline.test.ts +++ b/plugin-server/tests/main/ingestion-queues/run-ingestion-pipeline.test.ts @@ -7,7 +7,6 @@ import { closeHub, createHub } from '../../../src/utils/db/hub' import { PostgresUse } from '../../../src/utils/db/postgres' import { UUIDT } from '../../../src/utils/utils' import { EventPipelineRunner } from '../../../src/worker/ingestion/event-pipeline/runner' -import { EventsProcessor } from '../../../src/worker/ingestion/process-event' import { createOrganization, createTeam, POSTGRES_DELETE_TABLES_QUERY } from '../../helpers/sql' describe('workerTasks.runEventPipeline()', () => { @@ -59,9 +58,9 @@ describe('workerTasks.runEventPipeline()', () => { now: new Date().toISOString(), uuid: new UUIDT().toString(), } - await expect( - new EventPipelineRunner(hub, event, new EventsProcessor(hub)).runEventPipeline(event) - ).rejects.toEqual(new DependencyUnavailableError(errorMessage, 'Postgres', new Error(errorMessage))) + await expect(new EventPipelineRunner(hub, event).runEventPipeline(event)).rejects.toEqual( + new DependencyUnavailableError(errorMessage, 'Postgres', new Error(errorMessage)) + ) pgQueryMock.mockRestore() }) }) diff --git a/plugin-server/tests/main/ingestion-queues/session-recording-v2/kafka/message-parser.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording-v2/kafka/message-parser.test.ts new file mode 100644 index 0000000000000..0c1fc3bed8516 --- /dev/null +++ b/plugin-server/tests/main/ingestion-queues/session-recording-v2/kafka/message-parser.test.ts @@ -0,0 +1,219 @@ +import { promisify } from 'node:util' +import { Message } from 'node-rdkafka' +import { gzip } from 'zlib' + +import { KafkaMessageParser } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/message-parser' +import { KafkaMetrics } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/metrics' + +const compressWithGzip = promisify(gzip) + +describe('KafkaMessageParser', () => { + let parser: KafkaMessageParser + let mockKafkaMetrics: jest.Mocked + + beforeEach(() => { + mockKafkaMetrics = { + incrementMessageDropped: jest.fn(), + } as jest.Mocked + parser = new KafkaMessageParser(mockKafkaMetrics) + }) + + const createMessage = (data: any, overrides: Partial = {}): Message => ({ + value: Buffer.from(JSON.stringify(data)), + size: 100, + topic: 'test-topic', + offset: 0, + partition: 0, + timestamp: 1234567890, + ...overrides, + }) + + describe('parseBatch', () => { + it('handles valid snapshot message', async () => { + const snapshotItems = [ + { type: 1, timestamp: 1234567890 }, + { type: 2, timestamp: 1234567891 }, + ] + const messages = [ + createMessage({ + data: JSON.stringify({ + event: '$snapshot_items', + properties: { + $session_id: 'session1', + $window_id: 'window1', + $snapshot_items: snapshotItems, + }, + }), + distinct_id: 'user123', + }), + ] + + const results = await parser.parseBatch(messages) + + expect(results).toHaveLength(1) + expect(results[0]).toMatchObject({ + metadata: { + partition: 0, + topic: 'test-topic', + rawSize: 100, + offset: 0, + timestamp: 1234567890, + }, + headers: undefined, + distinct_id: 'user123', + session_id: 'session1', + eventsByWindowId: { + window1: snapshotItems, + }, + eventsRange: { + start: 1234567890, + end: 1234567891, + }, + snapshot_source: undefined, + }) + expect(mockKafkaMetrics.incrementMessageDropped).not.toHaveBeenCalled() + }) + + it('handles gzipped message', async () => { + const snapshotItems = [ + { type: 1, timestamp: 1234567890 }, + { type: 2, timestamp: 1234567891 }, + ] + const data = { + data: JSON.stringify({ + event: '$snapshot_items', + properties: { + $session_id: 'session1', + $window_id: 'window1', + $snapshot_items: snapshotItems, + }, + }), + distinct_id: 'user123', + } + + const gzippedData = await compressWithGzip(JSON.stringify(data)) + const messages = [createMessage(data, { value: gzippedData })] + + const results = await parser.parseBatch(messages) + + expect(results).toHaveLength(1) + expect(results[0]).toMatchObject({ + session_id: 'session1', + distinct_id: 'user123', + eventsByWindowId: { + window1: snapshotItems, + }, + eventsRange: { + start: 1234567890, + end: 1234567891, + }, + }) + expect(mockKafkaMetrics.incrementMessageDropped).not.toHaveBeenCalled() + }) + + it('filters out message with missing value', async () => { + const messages = [createMessage({}, { value: null })] + + const results = await parser.parseBatch(messages) + + expect(results).toHaveLength(0) + expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( + 'session_recordings_blob_ingestion', + 'message_value_or_timestamp_is_empty' + ) + }) + + it('filters out message with missing timestamp', async () => { + const messages = [createMessage({}, { timestamp: undefined })] + + const results = await parser.parseBatch(messages) + + expect(results).toHaveLength(0) + expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( + 'session_recordings_blob_ingestion', + 'message_value_or_timestamp_is_empty' + ) + }) + + it('filters out message with invalid gzip data', async () => { + const messages = [createMessage({}, { value: Buffer.from([0x1f, 0x8b, 0x08, 0x00, 0x00]) })] + + const results = await parser.parseBatch(messages) + + expect(results).toHaveLength(0) + expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( + 'session_recordings_blob_ingestion', + 'invalid_gzip_data' + ) + }) + + it('filters out message with invalid json', async () => { + const messages = [createMessage({}, { value: Buffer.from('invalid json') })] + + const results = await parser.parseBatch(messages) + + expect(results).toHaveLength(0) + expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( + 'session_recordings_blob_ingestion', + 'invalid_json' + ) + }) + + it('filters out non-snapshot message', async () => { + const messages = [ + createMessage({ + data: JSON.stringify({ + event: 'not_a_snapshot', + properties: { + $session_id: 'session1', + }, + }), + }), + ] + + const results = await parser.parseBatch(messages) + + expect(results).toHaveLength(0) + expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( + 'session_recordings_blob_ingestion', + 'received_non_snapshot_message' + ) + }) + + it('handles empty batch', async () => { + const results = await parser.parseBatch([]) + expect(results).toEqual([]) + }) + + it('processes multiple messages in parallel', async () => { + const messages = [ + createMessage({ + data: JSON.stringify({ + event: '$snapshot_items', + properties: { + $session_id: 'session1', + $window_id: 'window1', + $snapshot_items: [{ timestamp: 1, type: 2 }], + }, + }), + }), + createMessage({ + data: JSON.stringify({ + event: '$snapshot_items', + properties: { + $session_id: 'session2', + $window_id: 'window2', + $snapshot_items: [{ timestamp: 2, type: 2 }], + }, + }), + }), + ] + + const results = await parser.parseBatch(messages) + + expect(results).toHaveLength(2) + expect(results[0]?.session_id).toBe('session1') + expect(results[1]?.session_id).toBe('session2') + }) + }) +}) diff --git a/plugin-server/tests/main/ingestion-queues/session-recording-v2/kafka/offset-manager.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording-v2/kafka/offset-manager.test.ts new file mode 100644 index 0000000000000..f199990535c61 --- /dev/null +++ b/plugin-server/tests/main/ingestion-queues/session-recording-v2/kafka/offset-manager.test.ts @@ -0,0 +1,198 @@ +import { KafkaOffsetManager } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/offset-manager' +import { SessionBatchRecorder } from '../../../../../src/main/ingestion-queues/session-recording-v2/sessions/session-batch-recorder' +import { MessageWithTeam } from '../../../../../src/main/ingestion-queues/session-recording-v2/teams/types' + +describe('KafkaOffsetManager', () => { + let offsetManager: KafkaOffsetManager + let mockCommitOffsets: jest.Mock> + let mockRecorder: jest.Mocked + const TEST_TOPIC = 'test_topic' + + beforeEach(() => { + mockCommitOffsets = jest.fn().mockResolvedValue(undefined) + mockRecorder = { + record: jest.fn().mockReturnValue(100), + flush: jest.fn().mockResolvedValue(undefined), + size: 0, + discardPartition: jest.fn(), + } as unknown as jest.Mocked + + offsetManager = new KafkaOffsetManager(mockCommitOffsets, TEST_TOPIC) + }) + + const createMessage = (metadata: { partition: number; offset: number }): MessageWithTeam => ({ + team: { + teamId: 1, + consoleLogIngestionEnabled: false, + }, + message: { + distinct_id: 'distinct_id', + session_id: 'session1', + eventsByWindowId: { window1: [] }, + eventsRange: { start: 0, end: 0 }, + metadata: { + partition: metadata.partition, + offset: metadata.offset, + topic: 'test_topic', + timestamp: 0, + rawSize: 0, + }, + }, + }) + + it('should track offsets when recording messages', async () => { + const wrapper = offsetManager.wrapBatch(mockRecorder) + const message: MessageWithTeam = { + team: { teamId: 1, consoleLogIngestionEnabled: false }, + message: { + metadata: { partition: 1, offset: 100 }, + }, + } as MessageWithTeam + + wrapper.record(message) + + await wrapper.flush() + await offsetManager.commit() + + expect(mockCommitOffsets).toHaveBeenCalledWith([{ topic: TEST_TOPIC, partition: 1, offset: 101 }]) + }) + + it('should commit offsets for multiple partitions', async () => { + const wrapper = offsetManager.wrapBatch(mockRecorder) + const messages = [ + { partition: 1, offset: 100 }, + { partition: 1, offset: 101 }, + { partition: 2, offset: 200 }, + ] + + for (const metadata of messages) { + wrapper.record({ + team: { teamId: 1, consoleLogIngestionEnabled: false }, + message: { metadata }, + } as MessageWithTeam) + } + + await wrapper.flush() + await offsetManager.commit() + + expect(mockCommitOffsets).toHaveBeenCalledWith([ + { topic: TEST_TOPIC, partition: 1, offset: 102 }, // Last offset + 1 + { topic: TEST_TOPIC, partition: 2, offset: 201 }, // Last offset + 1 + ]) + }) + + it('should clear offsets after commit', async () => { + const wrapper = offsetManager.wrapBatch(mockRecorder) + const message: MessageWithTeam = { + team: { teamId: 1, consoleLogIngestionEnabled: false }, + message: { + metadata: { partition: 1, offset: 100 }, + }, + } as MessageWithTeam + + wrapper.record(message) + await wrapper.flush() + await offsetManager.commit() + + // Second commit should not commit anything + await offsetManager.commit() + + expect(mockCommitOffsets).toHaveBeenCalledTimes(1) + }) + + it('should handle commit failures', async () => { + const error = new Error('Commit failed') + mockCommitOffsets.mockRejectedValueOnce(error) + + const wrapper = offsetManager.wrapBatch(mockRecorder) + wrapper.record({ + team: { teamId: 1, consoleLogIngestionEnabled: false }, + message: { + metadata: { partition: 1, offset: 100 }, + }, + } as MessageWithTeam) + + await wrapper.flush() + await expect(offsetManager.commit()).rejects.toThrow(error) + }) + + describe('partition handling', () => { + it('should delegate discardPartition to inner recorder', () => { + const wrappedBatch = offsetManager.wrapBatch(mockRecorder) + wrappedBatch.discardPartition(1) + + expect(mockRecorder.discardPartition).toHaveBeenCalledWith(1) + }) + + it('should not commit offsets for discarded partitions', async () => { + const wrappedBatch = offsetManager.wrapBatch(mockRecorder) + + // Record messages for two partitions + wrappedBatch.record(createMessage({ partition: 1, offset: 100 })) + wrappedBatch.record(createMessage({ partition: 2, offset: 200 })) + + // Discard partition 1 + wrappedBatch.discardPartition(1) + + await offsetManager.commit() + + // Should only commit offset for partition 2 + expect(mockCommitOffsets).toHaveBeenCalledWith([ + { + topic: 'test_topic', + partition: 2, + offset: 201, + }, + ]) + }) + + it('should handle discarding already committed partitions', async () => { + const wrappedBatch = offsetManager.wrapBatch(mockRecorder) + + // Record and commit a message + wrappedBatch.record(createMessage({ partition: 1, offset: 100 })) + await offsetManager.commit() + + // Discard the partition after commit + wrappedBatch.discardPartition(1) + + // Record new message for same partition + wrappedBatch.record(createMessage({ partition: 1, offset: 101 })) + await offsetManager.commit() + + expect(mockCommitOffsets).toHaveBeenCalledTimes(2) + expect(mockCommitOffsets).toHaveBeenLastCalledWith([ + { + topic: 'test_topic', + partition: 1, + offset: 102, + }, + ]) + }) + + it('should handle discarding non-existent partitions', () => { + const wrappedBatch = offsetManager.wrapBatch(mockRecorder) + wrappedBatch.discardPartition(999) + expect(mockRecorder.discardPartition).toHaveBeenCalledWith(999) + }) + + it('should maintain highest offset when recording multiple messages', async () => { + const wrappedBatch = offsetManager.wrapBatch(mockRecorder) + + // Record messages in non-sequential order + wrappedBatch.record(createMessage({ partition: 1, offset: 100 })) + wrappedBatch.record(createMessage({ partition: 1, offset: 99 })) + wrappedBatch.record(createMessage({ partition: 1, offset: 101 })) + + await offsetManager.commit() + + expect(mockCommitOffsets).toHaveBeenCalledWith([ + { + topic: 'test_topic', + partition: 1, + offset: 102, + }, + ]) + }) + }) +}) diff --git a/plugin-server/tests/main/ingestion-queues/session-recording-v2/kafka/parser.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording-v2/kafka/parser.test.ts deleted file mode 100644 index 7ca6ad52a2569..0000000000000 --- a/plugin-server/tests/main/ingestion-queues/session-recording-v2/kafka/parser.test.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { Message } from 'node-rdkafka' -import { promisify } from 'util' -import { gzip } from 'zlib' - -import { KafkaMetrics } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/metrics' -import { KafkaParser } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/parser' - -const compressWithGzip = promisify(gzip) - -describe('KafkaParser', () => { - let parser: KafkaParser - let mockKafkaMetrics: jest.Mocked - - beforeEach(() => { - mockKafkaMetrics = { - incrementMessageDropped: jest.fn(), - } as jest.Mocked - parser = new KafkaParser(mockKafkaMetrics) - }) - - const createMessage = (data: any, overrides: Partial = {}): Message => ({ - value: Buffer.from(JSON.stringify(data)), - size: 100, - topic: 'test-topic', - offset: 0, - partition: 0, - timestamp: 1234567890, - ...overrides, - }) - - describe('parseMessage', () => { - it('successfully parses a valid message', async () => { - const snapshotItems = [ - { type: 1, timestamp: 1234567890 }, - { type: 2, timestamp: 1234567891 }, - ] - const message = createMessage({ - data: JSON.stringify({ - event: '$snapshot_items', - properties: { - $session_id: 'session1', - $window_id: 'window1', - $snapshot_items: snapshotItems, - }, - }), - distinct_id: 'user123', - }) - - const result = await parser.parseMessage(message) - - expect(result).toEqual({ - metadata: { - partition: 0, - topic: 'test-topic', - rawSize: 100, - offset: 0, - timestamp: 1234567890, - }, - headers: undefined, - distinct_id: 'user123', - session_id: 'session1', - eventsByWindowId: { - window1: snapshotItems, - }, - eventsRange: { - start: 1234567890, - end: 1234567891, - }, - snapshot_source: undefined, - }) - expect(mockKafkaMetrics.incrementMessageDropped).not.toHaveBeenCalled() - }) - - it('successfully parses a gzipped message', async () => { - const snapshotItems = [ - { type: 1, timestamp: 1234567890 }, - { type: 2, timestamp: 1234567891 }, - ] - const data = { - data: JSON.stringify({ - event: '$snapshot_items', - properties: { - $session_id: 'session1', - $window_id: 'window1', - $snapshot_items: snapshotItems, - }, - }), - distinct_id: 'user123', - } - - const gzippedData = await compressWithGzip(JSON.stringify(data)) - const message = createMessage(data, { value: gzippedData }) - - const result = await parser.parseMessage(message) - - expect(result).toBeTruthy() - expect(result?.session_id).toBe('session1') - expect(mockKafkaMetrics.incrementMessageDropped).not.toHaveBeenCalled() - }) - it('drops message with missing value', async () => { - const message = createMessage({}, { value: undefined }) - const result = await parser.parseMessage(message) - - expect(result).toBeNull() - expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( - 'session_recordings_blob_ingestion', - 'message_value_or_timestamp_is_empty' - ) - }) - - it('drops message with missing timestamp', async () => { - const message = createMessage({}, { timestamp: undefined }) - const result = await parser.parseMessage(message) - - expect(result).toBeNull() - expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( - 'session_recordings_blob_ingestion', - 'message_value_or_timestamp_is_empty' - ) - }) - - it('drops message with invalid gzip data', async () => { - const invalidGzip = Buffer.from([0x1f, 0x8b, 0x08, 0x00, 0x00]) // Invalid gzip data - const message = createMessage({}, { value: invalidGzip }) - - const result = await parser.parseMessage(message) - - expect(result).toBeNull() - expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( - 'session_recordings_blob_ingestion', - 'invalid_gzip_data' - ) - }) - - it('drops message with invalid JSON', async () => { - const message = createMessage({}, { value: Buffer.from('invalid json') }) - const result = await parser.parseMessage(message) - - expect(result).toBeNull() - expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( - 'session_recordings_blob_ingestion', - 'invalid_json' - ) - }) - - it('drops non-snapshot messages', async () => { - const message = createMessage({ - data: JSON.stringify({ - event: 'not_a_snapshot', - properties: { - $session_id: 'session1', - }, - }), - }) - - const result = await parser.parseMessage(message) - - expect(result).toBeNull() - expect(mockKafkaMetrics.incrementMessageDropped).toHaveBeenCalledWith( - 'session_recordings_blob_ingestion', - 'received_non_snapshot_message' - ) - }) - }) -}) diff --git a/plugin-server/tests/main/ingestion-queues/session-recording-v2/sessions/recorder.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording-v2/sessions/recorder.test.ts new file mode 100644 index 0000000000000..0bc39a656f746 --- /dev/null +++ b/plugin-server/tests/main/ingestion-queues/session-recording-v2/sessions/recorder.test.ts @@ -0,0 +1,275 @@ +import { PassThrough } from 'stream' + +import { ParsedMessageData } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/types' +import { SessionRecorder } from '../../../../../src/main/ingestion-queues/session-recording-v2/sessions/recorder' + +// RRWeb event type constants +const enum EventType { + DomContentLoaded = 0, + Load = 1, + FullSnapshot = 2, + IncrementalSnapshot = 3, + Meta = 4, + Custom = 5, +} + +describe('SessionRecorder', () => { + let recorder: SessionRecorder + + beforeEach(() => { + recorder = new SessionRecorder() + }) + + const createMessage = (windowId: string, events: any[]): ParsedMessageData => ({ + distinct_id: 'distinct_id', + session_id: 'session_id', + eventsByWindowId: { + [windowId]: events, + }, + eventsRange: { + start: events[0]?.timestamp || 0, + end: events[events.length - 1]?.timestamp || 0, + }, + metadata: { + partition: 1, + topic: 'test', + offset: 0, + timestamp: 0, + rawSize: 0, + }, + }) + + const parseLines = (data: string): Array<[string, any]> => { + return data + .trim() + .split('\n') + .map((line) => JSON.parse(line)) + } + + describe('recordMessage', () => { + it('should record events in JSONL format', async () => { + const events = [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { + source: 1, + adds: [{ parentId: 1, nextId: 2, node: { tag: 'div', attrs: { class: 'test' } } }], + }, + }, + { + type: EventType.IncrementalSnapshot, + timestamp: 2000, + data: { source: 2, texts: [{ id: 1, value: 'Updated text' }] }, + }, + ] + const message = createMessage('window1', events) + + const bytesWritten = recorder.recordMessage(message) + + const stream = new PassThrough() + let streamData = '' + stream.on('data', (chunk) => { + streamData += chunk + }) + + const result = await recorder.write(stream) + const lines = parseLines(streamData) + + expect(lines).toEqual([ + ['window1', events[0]], + ['window1', events[1]], + ]) + expect(bytesWritten).toBeGreaterThan(0) + expect(result.eventCount).toBe(2) + expect(result.bytesWritten).toBe(bytesWritten) + }) + + it('should handle multiple windows with multiple events', async () => { + const events = { + window1: [ + { + type: EventType.Meta, + timestamp: 1000, + data: { href: 'https://example.com', width: 1024, height: 768 }, + }, + { + type: EventType.FullSnapshot, + timestamp: 1500, + data: { + source: 1, + adds: [{ parentId: 1, nextId: null, node: { tag: 'h1', attrs: { id: 'title' } } }], + }, + }, + ], + window2: [ + { + type: EventType.Custom, + timestamp: 2000, + data: { tag: 'user-interaction', payload: { type: 'click', target: '#submit-btn' } }, + }, + { + type: EventType.IncrementalSnapshot, + timestamp: 2500, + data: { source: 3, mousemove: [{ x: 100, y: 200, id: 1 }] }, + }, + ], + } + const message: ParsedMessageData = { + ...createMessage('', []), + eventsByWindowId: events, + } + + recorder.recordMessage(message) + + const stream = new PassThrough() + let streamData = '' + stream.on('data', (chunk) => { + streamData += chunk + }) + + const result = await recorder.write(stream) + const lines = parseLines(streamData) + + expect(lines).toEqual([ + ['window1', events.window1[0]], + ['window1', events.window1[1]], + ['window2', events.window2[0]], + ['window2', events.window2[1]], + ]) + expect(result.eventCount).toBe(4) + expect(result.bytesWritten).toBeGreaterThan(0) + }) + + it('should handle empty events array', async () => { + const message = createMessage('window1', []) + const bytesWritten = recorder.recordMessage(message) + + const stream = new PassThrough() + let streamData = '' + stream.on('data', (chunk) => { + streamData += chunk + }) + + const result = await recorder.write(stream) + expect(streamData).toBe('') + expect(bytesWritten).toBe(0) + expect(result.eventCount).toBe(0) + expect(result.bytesWritten).toBe(0) + }) + + it('should correctly count bytes for multi-byte characters', async () => { + let bytesWritten = 0 + + const events1 = { + window1: [{ type: EventType.Custom, timestamp: 1000, data: { message: 'Testowanie z jeżem 🦔' } }], + } + const message1: ParsedMessageData = { + ...createMessage('', []), + eventsByWindowId: events1, + } + bytesWritten += recorder.recordMessage(message1) + + const events2 = { + window1: [ + { + type: EventType.Custom, + timestamp: 1500, + data: { message: '🦔 What do you call a hedgehog in the desert? A cactus impersonator!' }, + }, + ], + } + const message2: ParsedMessageData = { + ...createMessage('', []), + eventsByWindowId: events2, + } + bytesWritten += recorder.recordMessage(message2) + + const events3 = { + window2: [ + { + type: EventType.Custom, + timestamp: 2000, + data: { message: "🦔 What's a hedgehog's favorite exercise? Spike jumps!" }, + }, + ], + } + const message3: ParsedMessageData = { + ...createMessage('', []), + eventsByWindowId: events3, + } + bytesWritten += recorder.recordMessage(message3) + + const stream = new PassThrough() + let bytesReceived = 0 + stream.on('data', (chunk) => { + bytesReceived += Buffer.byteLength(chunk) + }) + + const result = await recorder.write(stream) + expect(bytesReceived).toBe(bytesWritten) + expect(result.bytesWritten).toBe(bytesWritten) + expect(result.eventCount).toBe(3) + }) + }) + + describe('write', () => { + it('should ensure last line ends with newline', async () => { + const events = [ + { type: EventType.FullSnapshot, timestamp: 1000, data: {} }, + { type: EventType.IncrementalSnapshot, timestamp: 2000, data: {} }, + ] + const message = createMessage('window1', events) + recorder.recordMessage(message) + + const stream = new PassThrough() + let streamData = '' + stream.on('data', (chunk) => { + streamData += chunk + }) + + const result = await recorder.write(stream) + expect(streamData.endsWith('\n')).toBe(true) + expect(result.eventCount).toBe(2) + expect(result.bytesWritten).toBeGreaterThan(0) + }) + + it('should handle backpressure', async () => { + const events = Array.from({ length: 100 }, (_, i) => ({ + type: EventType.Custom, + timestamp: i * 1000, + data: { large: 'x'.repeat(1000) }, // Create large events + })) + const message = createMessage('window1', events) + recorder.recordMessage(message) + + const stream = new PassThrough({ highWaterMark: 100 }) // Small buffer to trigger backpressure + let bytesWrittenBeforeDrain = 0 + let drainOccurred = false + + stream.on('data', (chunk) => { + if (!drainOccurred) { + bytesWrittenBeforeDrain += Buffer.byteLength(chunk) + } + }) + + const writePromise = recorder.write(stream) + + // Wait a tick to allow some data to be written + await new Promise((resolve) => process.nextTick(resolve)) + + // Verify that not all data was written before drain + expect(bytesWrittenBeforeDrain).toBeGreaterThan(0) + expect(bytesWrittenBeforeDrain).toBeLessThan(100000) + + // Now let the stream drain + drainOccurred = true + stream.resume() + + const result = await writePromise + expect(result.eventCount).toBe(100) + expect(result.bytesWritten).toBeGreaterThan(100000) // Should be large due to the event size + expect(result.bytesWritten).toBeGreaterThan(bytesWrittenBeforeDrain) // More data written after drain + }) + }) +}) diff --git a/plugin-server/tests/main/ingestion-queues/session-recording-v2/sessions/session-batch-manager.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording-v2/sessions/session-batch-manager.test.ts new file mode 100644 index 0000000000000..aa5be4bfb273d --- /dev/null +++ b/plugin-server/tests/main/ingestion-queues/session-recording-v2/sessions/session-batch-manager.test.ts @@ -0,0 +1,309 @@ +import { KafkaOffsetManager } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/offset-manager' +import { SessionBatchManager } from '../../../../../src/main/ingestion-queues/session-recording-v2/sessions/session-batch-manager' +import { SessionBatchRecorderInterface } from '../../../../../src/main/ingestion-queues/session-recording-v2/sessions/session-batch-recorder' + +jest.setTimeout(1000) + +const createMockBatch = (): jest.Mocked => { + return { + record: jest.fn(), + flush: jest.fn().mockResolvedValue(undefined), + get size() { + return 0 + }, + discardPartition: jest.fn(), + } as unknown as jest.Mocked +} + +describe('SessionBatchManager', () => { + let manager: SessionBatchManager + let executionOrder: number[] + let createBatchMock: jest.Mock + let currentBatch: jest.Mocked + let mockOffsetManager: jest.Mocked + + beforeEach(() => { + currentBatch = createMockBatch() + createBatchMock = jest.fn().mockImplementation(() => { + currentBatch = createMockBatch() + return currentBatch + }) + + mockOffsetManager = { + wrapBatch: jest.fn().mockImplementation((batch) => batch), + commit: jest.fn().mockResolvedValue(undefined), + trackOffset: jest.fn(), + } as unknown as jest.Mocked + + manager = new SessionBatchManager({ + maxBatchSizeBytes: 100, + maxBatchAgeMs: 1000, + createBatch: createBatchMock, + offsetManager: mockOffsetManager, + }) + executionOrder = [] + }) + + const waitForNextTick = () => new Promise((resolve) => process.nextTick(resolve)) + + const waitFor = async (condition: () => boolean) => { + while (!condition()) { + await waitForNextTick() + } + } + + const waitForValue = async (array: number[], value: number) => { + await waitFor(() => array.includes(value)) + } + + it('should execute callbacks sequentially', async () => { + const promise1 = manager.withBatch(async () => { + executionOrder.push(1) + await waitForValue(executionOrder, 1) + executionOrder.push(2) + }) + + const promise2 = manager.withBatch(async () => { + executionOrder.push(3) + await waitForValue(executionOrder, 3) + executionOrder.push(4) + }) + + const promise3 = manager.withBatch(async () => { + executionOrder.push(5) + executionOrder.push(6) + return Promise.resolve() + }) + + await Promise.all([promise1, promise2, promise3]) + + // Should execute in order despite different delays + expect(executionOrder).toEqual([1, 2, 3, 4, 5, 6]) + }) + + it('should handle errors without breaking the queue', async () => { + const errorSpy = jest.fn() + + const promise1 = manager + .withBatch(async () => { + executionOrder.push(1) + throw new Error('test error') + return Promise.resolve() + }) + .catch(errorSpy) + + const promise2 = manager.withBatch(async () => { + executionOrder.push(2) + return Promise.resolve() + }) + + await Promise.all([promise1, promise2]) + + expect(executionOrder).toEqual([1, 2]) + expect(errorSpy).toHaveBeenCalled() + }) + + it('should maintain order even with immediate callbacks', async () => { + const results: number[] = [] + const promises: Promise[] = [] + + // Queue up 10 immediate callbacks + for (let i = 0; i < 10; i++) { + promises.push( + manager.withBatch(async () => { + results.push(i) + return Promise.resolve() + }) + ) + } + + await Promise.all(promises) + + // Should execute in order 0-9 + expect(results).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + }) + + it('should process new callbacks added during execution', async () => { + const results: number[] = [] + let nestedPromise: Promise | null = null + let promise2: Promise | null = null + const promise1 = manager.withBatch(async () => { + results.push(1) + // Add a new callback while this one is executing + nestedPromise = manager.withBatch(async () => { + await waitFor(() => promise2 !== null) + results.push(2) + return Promise.resolve() + }) + return Promise.resolve() + }) + + await waitFor(() => nestedPromise !== null) + promise2 = manager.withBatch(async () => { + results.push(3) + return Promise.resolve() + }) + + await Promise.all([promise1, promise2, nestedPromise!]) + + expect(results).toEqual([1, 2, 3]) + }) + + it('should create new batch on flush', async () => { + let firstBatch: SessionBatchRecorderInterface | null = null + + await manager.withBatch(async (batch) => { + firstBatch = batch + return Promise.resolve() + }) + + await manager.flush() + + await manager.withBatch(async (batch) => { + expect(batch).not.toBe(firstBatch) + return Promise.resolve() + }) + }) + + it('should create new batch and commit offsets on flush', async () => { + const firstBatch = currentBatch + + await manager.flush() + + expect(firstBatch.flush).toHaveBeenCalled() + expect(mockOffsetManager.commit).toHaveBeenCalled() + expect(createBatchMock).toHaveBeenCalledTimes(2) + }) + + describe('size-based flushing', () => { + it('should indicate flush needed when buffer is full', () => { + jest.spyOn(currentBatch, 'size', 'get').mockReturnValue(150) + expect(manager.shouldFlush()).toBe(true) + }) + + it('should not indicate flush needed when buffer is under limit', () => { + jest.spyOn(currentBatch, 'size', 'get').mockReturnValue(50) + expect(manager.shouldFlush()).toBe(false) + }) + }) + + describe('time-based flushing', () => { + beforeEach(() => { + jest.useFakeTimers() + }) + + afterEach(() => { + jest.useRealTimers() + }) + + it('should not indicate flush needed when buffer is under limit and timeout not reached', () => { + jest.spyOn(currentBatch, 'size', 'get').mockReturnValue(50) + jest.advanceTimersByTime(500) // Advance time by 500ms (less than timeout) + expect(manager.shouldFlush()).toBe(false) + }) + + it('should indicate flush needed when timeout is reached', () => { + jest.spyOn(currentBatch, 'size', 'get').mockReturnValue(50) + jest.advanceTimersByTime(1500) // Advance time by 1.5s (more than timeout) + expect(manager.shouldFlush()).toBe(true) + }) + + it('should indicate flush needed when buffer is full', () => { + jest.spyOn(currentBatch, 'size', 'get').mockReturnValue(150) + expect(manager.shouldFlush()).toBe(true) + }) + + it('should not indicate flush needed immediately after flushing', async () => { + const firstBatch = currentBatch + jest.spyOn(firstBatch, 'size', 'get').mockReturnValue(50) + + // First flush due to timeout + jest.advanceTimersByTime(1500) + expect(manager.shouldFlush()).toBe(true) + const firstFlushPromise = manager.flush() + jest.runAllTimers() + await firstFlushPromise + expect(firstBatch.flush).toHaveBeenCalled() + + expect(manager.shouldFlush()).toBe(false) + }) + }) + + it('should execute callbacks sequentially including flushes', async () => { + const firstBatch = currentBatch + + const promise1 = manager.withBatch(async () => { + executionOrder.push(1) + return Promise.resolve() + }) + + const flushPromise = manager.flush() + + const promise2 = manager.withBatch(async () => { + executionOrder.push(2) + return Promise.resolve() + }) + + await Promise.all([promise1, flushPromise, promise2]) + + expect(executionOrder).toEqual([1, 2]) + expect(firstBatch.flush).toHaveBeenCalled() + expect(mockOffsetManager.commit).toHaveBeenCalled() + }) + + describe('partition handling', () => { + it('should discard partitions on new batch after flush', async () => { + const firstBatch = currentBatch + + // Flush to create a new batch + await manager.flush() + const secondBatch = currentBatch + + // Verify we have a new batch + expect(secondBatch).not.toBe(firstBatch) + + // Discard partitions + await manager.discardPartitions([1, 2]) + + // Verify discards happened on the new batch only + expect(firstBatch.discardPartition).not.toHaveBeenCalled() + expect(secondBatch.discardPartition).toHaveBeenCalledWith(1) + expect(secondBatch.discardPartition).toHaveBeenCalledWith(2) + }) + + it('should discard multiple partitions on current batch', async () => { + await manager.discardPartitions([1, 2]) + expect(currentBatch.discardPartition).toHaveBeenCalledWith(1) + expect(currentBatch.discardPartition).toHaveBeenCalledWith(2) + expect(currentBatch.discardPartition).toHaveBeenCalledTimes(2) + }) + + it('should maintain operation order when discarding partitions', async () => { + const executionOrder: number[] = [] + + // Start a long-running batch operation + const batchPromise = manager.withBatch(async () => { + await new Promise((resolve) => setTimeout(resolve, 100)) + executionOrder.push(1) + return Promise.resolve() + }) + + // Queue up a partition discard + const discardPromise = manager.discardPartitions([1]).then(() => { + executionOrder.push(2) + }) + + // Wait for both operations to complete + await Promise.all([batchPromise, discardPromise]) + + // Verify operations happened in the correct order + expect(executionOrder).toEqual([1, 2]) + expect(currentBatch.discardPartition).toHaveBeenCalledWith(1) + }) + + it('should handle empty partition array', async () => { + await manager.discardPartitions([]) + expect(currentBatch.discardPartition).not.toHaveBeenCalled() + }) + }) +}) diff --git a/plugin-server/tests/main/ingestion-queues/session-recording-v2/sessions/session-batch-recorder.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording-v2/sessions/session-batch-recorder.test.ts new file mode 100644 index 0000000000000..3e62e9b840ae9 --- /dev/null +++ b/plugin-server/tests/main/ingestion-queues/session-recording-v2/sessions/session-batch-recorder.test.ts @@ -0,0 +1,657 @@ +import { PassThrough } from 'stream' + +import { SessionBatchMetrics } from '../../../../../src/main/ingestion-queues/session-recording-v2/sessions/metrics' +import { + SessionBatchRecorder, + SessionBatchRecorderInterface, + SessionBatchWriter, +} from '../../../../../src/main/ingestion-queues/session-recording-v2/sessions/session-batch-recorder' +import { MessageWithTeam } from '../../../../../src/main/ingestion-queues/session-recording-v2/teams/types' + +// RRWeb event type constants +const enum EventType { + DomContentLoaded = 0, + Load = 1, + FullSnapshot = 2, + IncrementalSnapshot = 3, + Meta = 4, + Custom = 5, +} + +interface RRWebEvent { + type: EventType + timestamp: number + data: Record +} + +interface MessageMetadata { + partition?: number + topic?: string + offset?: number + timestamp?: number + rawSize?: number +} + +// Add to the top of the file, after other mocks +jest.mock('../../../../../src/main/ingestion-queues/session-recording-v2/sessions/metrics', () => ({ + SessionBatchMetrics: { + incrementBatchesFlushed: jest.fn(), + incrementSessionsFlushed: jest.fn(), + incrementEventsFlushed: jest.fn(), + incrementBytesWritten: jest.fn(), + }, +})) + +describe('SessionBatchRecorder', () => { + let recorder: SessionBatchRecorderInterface + let mockWriter: jest.Mocked + let mockStream: PassThrough + let mockFinish: () => Promise + + beforeEach(() => { + mockStream = new PassThrough() + mockFinish = jest.fn().mockResolvedValue(undefined) + mockWriter = { + open: jest.fn().mockImplementation(() => + Promise.resolve({ + stream: mockStream, + finish: mockFinish, + }) + ), + } + recorder = new SessionBatchRecorder(mockWriter) + + // Reset metrics mocks + jest.mocked(SessionBatchMetrics.incrementBatchesFlushed).mockClear() + jest.mocked(SessionBatchMetrics.incrementSessionsFlushed).mockClear() + jest.mocked(SessionBatchMetrics.incrementEventsFlushed).mockClear() + jest.mocked(SessionBatchMetrics.incrementBytesWritten).mockClear() + }) + + const createMessage = ( + sessionId: string, + events: RRWebEvent[], + metadata: MessageMetadata = {} + ): MessageWithTeam => ({ + team: { + teamId: 1, + consoleLogIngestionEnabled: false, + }, + message: { + distinct_id: 'distinct_id', + session_id: sessionId, + eventsByWindowId: { + window1: events, + }, + eventsRange: { + start: events[0]?.timestamp || 0, + end: events[events.length - 1]?.timestamp || 0, + }, + metadata: { + partition: 1, + topic: 'test', + offset: 0, + timestamp: 0, + rawSize: 0, + ...metadata, + }, + }, + }) + + const parseLines = (output: string): [string, RRWebEvent][] => { + return output + .trim() + .split('\n') + .map((line) => { + const [windowId, event] = JSON.parse(line) + return [windowId, event] + }) + } + + const captureOutput = (stream: PassThrough): Promise => { + return new Promise((resolve) => { + let streamData = '' + stream.on('data', (chunk) => { + streamData += chunk + }) + stream.on('end', () => { + resolve(streamData) + }) + }) + } + + describe('recording and writing', () => { + it('should process and flush a single session', async () => { + const message = createMessage('session1', [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1, adds: [{ parentId: 1, nextId: 2, node: { tag: 'div' } }] }, + }, + ]) + + recorder.record(message) + const outputPromise = captureOutput(mockStream) + await recorder.flush() + + expect(mockWriter.open).toHaveBeenCalled() + expect(mockFinish).toHaveBeenCalled() + + const output = await outputPromise + const lines = parseLines(output) + expect(lines).toEqual([['window1', message.message.eventsByWindowId.window1[0]]]) + expect(output.endsWith('\n')).toBe(true) + }) + + it('should handle multiple sessions in parallel', async () => { + const messages = [ + createMessage('session1', [ + { + type: EventType.Meta, + timestamp: 1000, + data: { href: 'https://example.com' }, + }, + ]), + createMessage('session2', [ + { + type: EventType.Custom, + timestamp: 2000, + data: { tag: 'user-interaction' }, + }, + ]), + ] + + messages.forEach((message) => recorder.record(message)) + const outputPromise = captureOutput(mockStream) + await recorder.flush() + + expect(mockWriter.open).toHaveBeenCalled() + expect(mockFinish).toHaveBeenCalled() + + const output = await outputPromise + const lines = parseLines(output) + expect(lines).toEqual([ + ['window1', messages[0].message.eventsByWindowId.window1[0]], + ['window1', messages[1].message.eventsByWindowId.window1[0]], + ]) + expect(output.endsWith('\n')).toBe(true) + }) + + it('should accumulate events for the same session', async () => { + const messages = [ + createMessage('session1', [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1, adds: [{ parentId: 1, nextId: 2, node: { tag: 'div' } }] }, + }, + ]), + createMessage('session1', [ + { + type: EventType.IncrementalSnapshot, + timestamp: 2000, + data: { source: 2, texts: [{ id: 1, value: 'Updated text' }] }, + }, + ]), + ] + + messages.forEach((message) => recorder.record(message)) + const outputPromise = captureOutput(mockStream) + await recorder.flush() + + expect(mockWriter.open).toHaveBeenCalled() + expect(mockFinish).toHaveBeenCalled() + + const output = await outputPromise + const lines = parseLines(output) + expect(lines).toEqual([ + ['window1', messages[0].message.eventsByWindowId.window1[0]], + ['window1', messages[1].message.eventsByWindowId.window1[0]], + ]) + expect(output.endsWith('\n')).toBe(true) + }) + + it('should handle empty events array', async () => { + const message = createMessage('session1', []) + const bytesWritten = recorder.record(message) + + const outputPromise = captureOutput(mockStream) + await recorder.flush() + + expect(mockWriter.open).toHaveBeenCalled() + expect(mockFinish).toHaveBeenCalled() + + const output = await outputPromise + expect(output).toBe('') + expect(bytesWritten).toBe(0) + }) + + it('should group events by session when interleaved', async () => { + const messages = [ + createMessage('session1', [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1, adds: [{ parentId: 1, nextId: 2, node: { tag: 'div' } }] }, + }, + ]), + createMessage('session2', [ + { + type: EventType.Meta, + timestamp: 1100, + data: { href: 'https://example.com' }, + }, + ]), + createMessage('session1', [ + { + type: EventType.IncrementalSnapshot, + timestamp: 2000, + data: { source: 2, texts: [{ id: 1, value: 'Updated text' }] }, + }, + ]), + createMessage('session2', [ + { + type: EventType.Custom, + timestamp: 2100, + data: { tag: 'user-interaction' }, + }, + ]), + ] + + messages.forEach((message) => recorder.record(message)) + const outputPromise = captureOutput(mockStream) + await recorder.flush() + + expect(mockWriter.open).toHaveBeenCalled() + expect(mockFinish).toHaveBeenCalled() + + const output = await outputPromise + const lines = parseLines(output) + + // Events should be grouped by session, maintaining chronological order within each session + expect(lines).toEqual([ + // All session1 events + ['window1', messages[0].message.eventsByWindowId.window1[0]], + ['window1', messages[2].message.eventsByWindowId.window1[0]], + // All session2 events + ['window1', messages[1].message.eventsByWindowId.window1[0]], + ['window1', messages[3].message.eventsByWindowId.window1[0]], + ]) + expect(output.endsWith('\n')).toBe(true) + }) + }) + + describe('flushing behavior', () => { + it('should clear sessions after flush', async () => { + const stream1 = new PassThrough() + const stream2 = new PassThrough() + const finish1 = jest.fn().mockResolvedValue(undefined) + const finish2 = jest.fn().mockResolvedValue(undefined) + + mockWriter.open + .mockResolvedValueOnce({ stream: stream1, finish: finish1 }) + .mockResolvedValueOnce({ stream: stream2, finish: finish2 }) + + const message1 = createMessage('session1', [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1, adds: [{ parentId: 1, nextId: 2, node: { tag: 'div' } }] }, + }, + ]) + + const message2 = createMessage('session1', [ + { + type: EventType.IncrementalSnapshot, + timestamp: 2000, + data: { source: 2, texts: [{ id: 1, value: 'Updated text' }] }, + }, + ]) + + recorder.record(message1) + const outputPromise1 = captureOutput(stream1) + await recorder.flush() + + expect(mockWriter.open).toHaveBeenCalledTimes(1) + expect(finish1).toHaveBeenCalledTimes(1) + expect(finish2).not.toHaveBeenCalled() + const output1 = await outputPromise1 + + // Record another message after flush + recorder.record(message2) + const outputPromise2 = captureOutput(stream2) + await recorder.flush() + + expect(mockWriter.open).toHaveBeenCalledTimes(2) + expect(finish1).toHaveBeenCalledTimes(1) + expect(finish2).toHaveBeenCalledTimes(1) + const output2 = await outputPromise2 + + // Each output should only contain the events from its own batch + const lines1 = parseLines(output1) + const lines2 = parseLines(output2) + expect(lines1).toEqual([['window1', message1.message.eventsByWindowId.window1[0]]]) + expect(lines2).toEqual([['window1', message2.message.eventsByWindowId.window1[0]]]) + }) + + it('should not output anything on second flush if no new events', async () => { + const stream1 = new PassThrough() + const stream2 = new PassThrough() + const finish1 = jest.fn().mockResolvedValue(undefined) + const finish2 = jest.fn().mockResolvedValue(undefined) + + mockWriter.open + .mockResolvedValueOnce({ stream: stream1, finish: finish1 }) + .mockResolvedValueOnce({ stream: stream2, finish: finish2 }) + + const message = createMessage('session1', [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1 }, + }, + ]) + + recorder.record(message) + await recorder.flush() + expect(mockWriter.open).toHaveBeenCalledTimes(1) + expect(finish1).toHaveBeenCalledTimes(1) + expect(finish2).not.toHaveBeenCalled() + + const outputPromise = captureOutput(stream2) + await recorder.flush() + const output = await outputPromise + + expect(output).toBe('') + expect(mockWriter.open).toHaveBeenCalledTimes(2) + expect(finish1).toHaveBeenCalledTimes(1) + expect(finish2).toHaveBeenCalledTimes(1) + }) + }) + + describe('partition handling', () => { + it('should flush all partitions', async () => { + const messages = [ + createMessage( + 'session1', + [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1 }, + }, + ], + { partition: 1 } + ), + createMessage( + 'session2', + [ + { + type: EventType.IncrementalSnapshot, + timestamp: 2000, + data: { source: 2 }, + }, + ], + { partition: 2 } + ), + ] + + messages.forEach((message) => recorder.record(message)) + const outputPromise = captureOutput(mockStream) + await recorder.flush() + + const output = await outputPromise + const lines = parseLines(output) + + expect(lines).toEqual([ + ['window1', messages[0].message.eventsByWindowId.window1[0]], + ['window1', messages[1].message.eventsByWindowId.window1[0]], + ]) + expect(mockWriter.open).toHaveBeenCalledTimes(1) + expect(mockFinish).toHaveBeenCalledTimes(1) + }) + + it('should not flush discarded partitions', async () => { + const messages = [ + createMessage( + 'session1', + [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1 }, + }, + ], + { partition: 1 } + ), + createMessage( + 'session2', + [ + { + type: EventType.IncrementalSnapshot, + timestamp: 2000, + data: { source: 2 }, + }, + ], + { partition: 2 } + ), + ] + + messages.forEach((message) => recorder.record(message)) + recorder.discardPartition(1) // Discard partition 1 + + const outputPromise = captureOutput(mockStream) + await recorder.flush() + + const output = await outputPromise + const lines = parseLines(output) + + // Should only contain message from partition 2 + expect(lines).toEqual([['window1', messages[1].message.eventsByWindowId.window1[0]]]) + }) + + it('should correctly update size when discarding partitions', () => { + const message1 = createMessage( + 'session1', + [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1 }, + }, + ], + { partition: 1 } + ) + const message2 = createMessage( + 'session2', + [ + { + type: EventType.IncrementalSnapshot, + timestamp: 2000, + data: { source: 2 }, + }, + ], + { partition: 2 } + ) + + const size1 = recorder.record(message1) + const size2 = recorder.record(message2) + const totalSize = size1 + size2 + + expect(recorder.size).toBe(totalSize) + + recorder.discardPartition(1) + expect(recorder.size).toBe(size2) + + recorder.discardPartition(2) + expect(recorder.size).toBe(0) + }) + + it('should handle discarding non-existent partitions', () => { + const message = createMessage('session1', [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1 }, + }, + ]) + + const bytesWritten = recorder.record(message) + expect(recorder.size).toBe(bytesWritten) + + // Should not throw or change size + recorder.discardPartition(999) + expect(recorder.size).toBe(bytesWritten) + }) + }) + + describe('metrics', () => { + it('should increment metrics on flush', async () => { + const messages = [ + createMessage('session1', [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1 }, + }, + { + type: EventType.IncrementalSnapshot, + timestamp: 2000, + data: { source: 2 }, + }, + ]), + createMessage('session2', [ + { + type: EventType.Meta, + timestamp: 1500, + data: { href: 'https://example.com' }, + }, + ]), + ] + + messages.forEach((message) => recorder.record(message)) + await recorder.flush() + + expect(SessionBatchMetrics.incrementBatchesFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenLastCalledWith(2) // Two sessions + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenLastCalledWith(3) // Three events total + }) + + it('should not increment metrics when no events are flushed', async () => { + await recorder.flush() + + expect(SessionBatchMetrics.incrementBatchesFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenLastCalledWith(0) + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenLastCalledWith(0) + }) + + it('should not count events from discarded partitions', async () => { + const messages = [ + createMessage( + 'session1', + [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1 }, + }, + ], + { partition: 1 } + ), + createMessage( + 'session2', + [ + { + type: EventType.IncrementalSnapshot, + timestamp: 2000, + data: { source: 2 }, + }, + ], + { partition: 2 } + ), + ] + + messages.forEach((message) => recorder.record(message)) + recorder.discardPartition(1) + await recorder.flush() + + expect(SessionBatchMetrics.incrementBatchesFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenLastCalledWith(1) // Only session from partition 2 + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenLastCalledWith(1) // Only event from partition 2 + }) + + it('should not count sessions again on subsequent flushes', async () => { + const stream1 = new PassThrough() + const stream2 = new PassThrough() + const stream3 = new PassThrough() + const finish1 = jest.fn().mockResolvedValue(undefined) + const finish2 = jest.fn().mockResolvedValue(undefined) + const finish3 = jest.fn().mockResolvedValue(undefined) + + mockWriter.open + .mockResolvedValueOnce({ stream: stream1, finish: finish1 }) + .mockResolvedValueOnce({ stream: stream2, finish: finish2 }) + .mockResolvedValueOnce({ stream: stream3, finish: finish3 }) + + const messages = [ + createMessage('session1', [ + { + type: EventType.FullSnapshot, + timestamp: 1000, + data: { source: 1 }, + }, + ]), + createMessage('session2', [ + { + type: EventType.Meta, + timestamp: 1500, + data: { href: 'https://example.com' }, + }, + ]), + ] + + // First flush + messages.forEach((message) => recorder.record(message)) + await recorder.flush() + + expect(SessionBatchMetrics.incrementBatchesFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenCalledTimes(1) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenLastCalledWith(2) // Two sessions + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenLastCalledWith(2) // Two events + + // Second flush without new messages + await recorder.flush() + + expect(SessionBatchMetrics.incrementBatchesFlushed).toHaveBeenCalledTimes(2) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenCalledTimes(2) + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenCalledTimes(2) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenLastCalledWith(0) // No sessions + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenLastCalledWith(0) // No events + + // Add new message and flush again + recorder.record( + createMessage('session3', [ + { + type: EventType.Custom, + timestamp: 2000, + data: { custom: 'data' }, + }, + ]) + ) + await recorder.flush() + + expect(SessionBatchMetrics.incrementBatchesFlushed).toHaveBeenCalledTimes(3) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenCalledTimes(3) + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenCalledTimes(3) + expect(SessionBatchMetrics.incrementSessionsFlushed).toHaveBeenLastCalledWith(1) // Only the new session + expect(SessionBatchMetrics.incrementEventsFlushed).toHaveBeenLastCalledWith(1) // Only the new event + }) + }) +}) diff --git a/plugin-server/tests/main/ingestion-queues/session-recording-v2/teams/team-filter.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording-v2/teams/team-filter.test.ts index aa00599e9a570..91e9c7f76eacf 100644 --- a/plugin-server/tests/main/ingestion-queues/session-recording-v2/teams/team-filter.test.ts +++ b/plugin-server/tests/main/ingestion-queues/session-recording-v2/teams/team-filter.test.ts @@ -1,193 +1,135 @@ -import { Message } from 'node-rdkafka' - -import { KafkaMetrics } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/metrics' -import { KafkaParser } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/parser' +import { ParsedMessageData } from '../../../../../src/main/ingestion-queues/session-recording-v2/kafka/types' import { TeamFilter } from '../../../../../src/main/ingestion-queues/session-recording-v2/teams/team-filter' import { TeamService } from '../../../../../src/main/ingestion-queues/session-recording-v2/teams/team-service' import { Team } from '../../../../../src/main/ingestion-queues/session-recording-v2/teams/types' jest.mock('../../../../../src/main/ingestion-queues/session-recording-v2/teams/team-service') -jest.mock('../../../../../src/main/ingestion-queues/session-recording-v2/kafka/parser') const validTeam: Team = { teamId: 1, consoleLogIngestionEnabled: true, } -const createSessionRecordingMessage = (token?: string, timestamp = Date.now()): Message => ({ - value: Buffer.from('test'), - size: 4, - topic: 'test', - offset: 0, - partition: 0, - timestamp, - headers: token ? [{ token }] : undefined, -}) - -const createParsedMessage = (offset = 0, timestamp = Date.now()) => ({ - distinct_id: 'distinct_id', - session_id: 'session_id', +const createMessage = (token?: string, overrides = {}): ParsedMessageData => ({ metadata: { partition: 0, topic: 'test', - offset, - timestamp, + offset: 0, + timestamp: Date.now(), rawSize: 100, }, + headers: token ? [{ token }] : undefined, + distinct_id: 'distinct_id', + session_id: 'session_id', eventsByWindowId: {}, eventsRange: { start: 0, end: 0 }, + ...overrides, }) describe('TeamFilter', () => { let teamFilter: TeamFilter - let teamService: jest.Mocked - let kafkaMetrics: jest.Mocked - let kafkaParser: jest.Mocked + let mockTeamService: jest.Mocked beforeEach(() => { jest.clearAllMocks() - teamService = new TeamService() as jest.Mocked - kafkaMetrics = new KafkaMetrics() as jest.Mocked - kafkaParser = new KafkaParser(kafkaMetrics) as jest.Mocked - teamFilter = new TeamFilter(teamService, kafkaParser) + mockTeamService = { + getTeamByToken: jest.fn(), + } as jest.Mocked + teamFilter = new TeamFilter(mockTeamService) }) describe('team token validation', () => { it('processes messages with valid team token', async () => { - const message = createSessionRecordingMessage('valid-token') - const parsedMessage = createParsedMessage() - - teamService.getTeamByToken.mockResolvedValueOnce(validTeam) - kafkaParser.parseMessage.mockResolvedValueOnce(parsedMessage) + const message = createMessage('valid-token') + mockTeamService.getTeamByToken.mockResolvedValueOnce(validTeam) - const result = await teamFilter.parseBatch([message]) + const result = await teamFilter.filterBatch([message]) - expect(result).toEqual([{ team: validTeam, message: parsedMessage }]) - expect(teamService.getTeamByToken).toHaveBeenCalledWith('valid-token') - expect(teamService.getTeamByToken).toHaveBeenCalledTimes(1) + expect(result).toEqual([{ team: validTeam, message }]) + expect(mockTeamService.getTeamByToken).toHaveBeenCalledWith('valid-token') + expect(mockTeamService.getTeamByToken).toHaveBeenCalledTimes(1) }) it('drops messages with no token in header', async () => { - const message = createSessionRecordingMessage() - const result = await teamFilter.parseBatch([message]) + const message = createMessage() + const result = await teamFilter.filterBatch([message]) expect(result).toEqual([]) - expect(teamService.getTeamByToken).not.toHaveBeenCalled() - expect(kafkaParser.parseMessage).not.toHaveBeenCalled() + expect(mockTeamService.getTeamByToken).not.toHaveBeenCalled() }) it('drops messages with invalid team tokens', async () => { - const message = createSessionRecordingMessage('invalid-token') - teamService.getTeamByToken.mockResolvedValueOnce(null) - - const result = await teamFilter.parseBatch([message]) - - expect(result).toEqual([]) - expect(teamService.getTeamByToken).toHaveBeenCalledWith('invalid-token') - expect(teamService.getTeamByToken).toHaveBeenCalledTimes(1) - expect(kafkaParser.parseMessage).not.toHaveBeenCalled() - }) - }) + const message = createMessage('invalid-token') + mockTeamService.getTeamByToken.mockResolvedValueOnce(null) - describe('message parsing', () => { - beforeEach(() => { - teamService.getTeamByToken.mockResolvedValue(validTeam) - }) - - it('processes valid parsed messages', async () => { - const message = createSessionRecordingMessage('token') - const parsedMessage = createParsedMessage() - kafkaParser.parseMessage.mockResolvedValueOnce(parsedMessage) - - const result = await teamFilter.parseBatch([message]) - - expect(result).toEqual([{ team: validTeam, message: parsedMessage }]) - expect(teamService.getTeamByToken).toHaveBeenCalledWith('token') - expect(teamService.getTeamByToken).toHaveBeenCalledTimes(1) - }) - - it('drops messages that fail parsing', async () => { - const message = createSessionRecordingMessage('token') - kafkaParser.parseMessage.mockResolvedValueOnce(null) - - const result = await teamFilter.parseBatch([message]) + const result = await teamFilter.filterBatch([message]) expect(result).toEqual([]) - expect(teamService.getTeamByToken).toHaveBeenCalledWith('token') - expect(teamService.getTeamByToken).toHaveBeenCalledTimes(1) + expect(mockTeamService.getTeamByToken).toHaveBeenCalledWith('invalid-token') + expect(mockTeamService.getTeamByToken).toHaveBeenCalledTimes(1) }) }) describe('batch processing', () => { - beforeEach(() => { - teamService.getTeamByToken.mockResolvedValue(validTeam) - }) - it('processes multiple messages in order', async () => { const timestamp = Date.now() const messages = [ - createSessionRecordingMessage('token1', timestamp), - createSessionRecordingMessage('token2', timestamp + 1), + createMessage('token1', { metadata: { timestamp } }), + createMessage('token2', { metadata: { timestamp: timestamp + 1 } }), ] - const parsedMessages = [createParsedMessage(0, timestamp), createParsedMessage(1, timestamp + 1)] - - kafkaParser.parseMessage.mockResolvedValueOnce(parsedMessages[0]).mockResolvedValueOnce(parsedMessages[1]) + mockTeamService.getTeamByToken.mockResolvedValue(validTeam) - const result = await teamFilter.parseBatch(messages) + const result = await teamFilter.filterBatch(messages) expect(result).toEqual([ - { team: validTeam, message: parsedMessages[0] }, - { team: validTeam, message: parsedMessages[1] }, + { team: validTeam, message: messages[0] }, + { team: validTeam, message: messages[1] }, ]) - expect(teamService.getTeamByToken).toHaveBeenCalledWith('token1') - expect(teamService.getTeamByToken).toHaveBeenCalledWith('token2') - expect(teamService.getTeamByToken).toHaveBeenCalledTimes(2) + expect(mockTeamService.getTeamByToken).toHaveBeenCalledWith('token1') + expect(mockTeamService.getTeamByToken).toHaveBeenCalledWith('token2') + expect(mockTeamService.getTeamByToken).toHaveBeenCalledTimes(2) }) it('processes messages with different teams', async () => { const timestamp = Date.now() const messages = [ - createSessionRecordingMessage('token1', timestamp), - createSessionRecordingMessage('token2', timestamp + 1), + createMessage('token1', { metadata: { timestamp } }), + createMessage('token2', { metadata: { timestamp: timestamp + 1 } }), ] - const parsedMessages = [createParsedMessage(0, timestamp), createParsedMessage(1, timestamp + 1)] - const team2 = { ...validTeam, teamId: 2 } + mockTeamService.getTeamByToken.mockResolvedValueOnce(validTeam).mockResolvedValueOnce(team2) - teamService.getTeamByToken.mockResolvedValueOnce(validTeam).mockResolvedValueOnce(team2) - - kafkaParser.parseMessage.mockResolvedValueOnce(parsedMessages[0]).mockResolvedValueOnce(parsedMessages[1]) - - const result = await teamFilter.parseBatch(messages) + const result = await teamFilter.filterBatch(messages) expect(result).toEqual([ - { team: validTeam, message: parsedMessages[0] }, - { team: team2, message: parsedMessages[1] }, + { team: validTeam, message: messages[0] }, + { team: team2, message: messages[1] }, ]) - expect(teamService.getTeamByToken).toHaveBeenCalledWith('token1') - expect(teamService.getTeamByToken).toHaveBeenCalledWith('token2') - expect(teamService.getTeamByToken).toHaveBeenCalledTimes(2) + expect(mockTeamService.getTeamByToken).toHaveBeenCalledWith('token1') + expect(mockTeamService.getTeamByToken).toHaveBeenCalledWith('token2') + expect(mockTeamService.getTeamByToken).toHaveBeenCalledTimes(2) }) it('handles mixed valid and invalid messages in batch', async () => { const messages = [ - createSessionRecordingMessage('token1'), - createSessionRecordingMessage(), // No token - createSessionRecordingMessage('token2'), + createMessage('token1'), + createMessage(), // No token + createMessage('token2'), ] - kafkaParser.parseMessage - .mockResolvedValueOnce(createParsedMessage(0)) - .mockResolvedValueOnce(createParsedMessage(2)) + mockTeamService.getTeamByToken.mockResolvedValue(validTeam) - const result = await teamFilter.parseBatch(messages) + const result = await teamFilter.filterBatch(messages) - expect(result).toHaveLength(2) - expect(teamService.getTeamByToken).toHaveBeenCalledWith('token1') - expect(teamService.getTeamByToken).toHaveBeenCalledWith('token2') - expect(teamService.getTeamByToken).toHaveBeenCalledTimes(2) + expect(result).toEqual([ + { team: validTeam, message: messages[0] }, + { team: validTeam, message: messages[2] }, + ]) + expect(mockTeamService.getTeamByToken).toHaveBeenCalledWith('token1') + expect(mockTeamService.getTeamByToken).toHaveBeenCalledWith('token2') + expect(mockTeamService.getTeamByToken).toHaveBeenCalledTimes(2) }) }) }) diff --git a/plugin-server/tests/main/ingestion-queues/session-recording-v2/teams/team-service.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording-v2/teams/team-service.test.ts new file mode 100644 index 0000000000000..0efaa630f6ec0 --- /dev/null +++ b/plugin-server/tests/main/ingestion-queues/session-recording-v2/teams/team-service.test.ts @@ -0,0 +1,142 @@ +import { TeamService } from '../../../../../src/main/ingestion-queues/session-recording-v2/teams/team-service' +import { PostgresRouter } from '../../../../../src/utils/db/postgres' +import { fetchTeamTokensWithRecordings } from '../../../../../src/worker/ingestion/team-manager' + +jest.mock('../../../../../src/worker/ingestion/team-manager') +const mockFetchTeamTokens = fetchTeamTokensWithRecordings as jest.MockedFunction + +describe('TeamService', () => { + let teamService: TeamService + let mockPostgres: jest.Mocked + + beforeEach(() => { + jest.useFakeTimers() + mockPostgres = {} as jest.Mocked + mockFetchTeamTokens.mockReset() + + // Default mock implementation + mockFetchTeamTokens.mockResolvedValue({ + 'valid-token': { teamId: 1, consoleLogIngestionEnabled: true }, + 'valid-token-2': { teamId: 2, consoleLogIngestionEnabled: false }, + }) + + teamService = new TeamService(mockPostgres) + }) + + afterEach(() => { + jest.useRealTimers() + }) + + describe('getTeamByToken', () => { + it('should return team for valid token', async () => { + const team = await teamService.getTeamByToken('valid-token') + expect(team).toEqual({ + teamId: 1, + consoleLogIngestionEnabled: true, + }) + }) + + it('should return null for invalid token', async () => { + const team = await teamService.getTeamByToken('invalid-token') + expect(team).toBeNull() + }) + + it('should return null if teamId is missing', async () => { + mockFetchTeamTokens.mockResolvedValue({ + token: { teamId: null as any, consoleLogIngestionEnabled: true }, + }) + const team = await teamService.getTeamByToken('token') + expect(team).toBeNull() + }) + + it('should cache results and not fetch again within refresh period', async () => { + await teamService.getTeamByToken('valid-token') + await teamService.getTeamByToken('valid-token-2') + + // Advance time but not enough to trigger refresh + jest.advanceTimersByTime(4 * 60 * 1000) // 4 minutes (refresh is 5 minutes) + + await teamService.getTeamByToken('valid-token') + await teamService.getTeamByToken('valid-token-2') + + expect(mockFetchTeamTokens).toHaveBeenCalledTimes(1) + }) + + it('should refresh after max age', async () => { + await teamService.getTeamByToken('valid-token') + expect(mockFetchTeamTokens).toHaveBeenCalledTimes(1) + + // Move time forward past the refresh interval + jest.advanceTimersByTime(5 * 60 * 1000 + 1) + + // This should trigger a refresh + await teamService.getTeamByToken('valid-token') + expect(mockFetchTeamTokens).toHaveBeenCalledTimes(2) + }) + + it('should handle refresh errors and return cached data', async () => { + // First call succeeds + await teamService.getTeamByToken('valid-token') + expect(mockFetchTeamTokens).toHaveBeenCalledTimes(1) + + // Make next refresh fail + mockFetchTeamTokens.mockRejectedValueOnce(new Error('Refresh failed')) + + // Advance time to trigger refresh + jest.advanceTimersByTime(5 * 60 * 1000 + 1) + + // Should still return cached data + const team = await teamService.getTeamByToken('valid-token') + expect(team).toEqual({ + teamId: 1, + consoleLogIngestionEnabled: true, + }) + }) + + it('should eventually update cache after successful refresh', async () => { + // Initial fetch + const team1 = await teamService.getTeamByToken('valid-token') + expect(team1?.consoleLogIngestionEnabled).toBe(true) + + // Update mock data and capture the promise + const mockFetchPromise = Promise.resolve({ + 'valid-token': { teamId: 1, consoleLogIngestionEnabled: false }, + }) + mockFetchTeamTokens.mockReturnValue(mockFetchPromise) + + // Advance time to trigger refresh + jest.advanceTimersByTime(5 * 60 * 1000 + 1) + + // Wait for the new value to appear using a spinlock, don't advance time though + while ((await teamService.getTeamByToken('valid-token'))?.consoleLogIngestionEnabled !== false) { + await Promise.resolve() // Allow other promises to resolve + } + + const team2 = await teamService.getTeamByToken('valid-token') + expect(team2?.consoleLogIngestionEnabled).toBe(false) + }) + + it('should eventually return null when team is removed after refresh', async () => { + // Initial fetch + const team1 = await teamService.getTeamByToken('valid-token') + expect(team1?.teamId).toBe(1) + + // Update mock data to remove the team + const mockFetchPromise = Promise.resolve({ + 'valid-token-2': { teamId: 2, consoleLogIngestionEnabled: false }, // Remove valid-token + }) + mockFetchTeamTokens.mockReturnValue(mockFetchPromise) + + // Advance time to trigger refresh + jest.advanceTimersByTime(5 * 60 * 1000 + 1) + + // Wait for the team to be removed using a spinlock, don't advance time though + while ((await teamService.getTeamByToken('valid-token')) !== null) { + await Promise.resolve() // Allow other promises to resolve + } + + const team2 = await teamService.getTeamByToken('valid-token') + expect(team2).toBeNull() + }) + }) +}) diff --git a/plugin-server/tests/main/ingestion-queues/session-recording-v2/versions/lib-version-monitor.test.ts b/plugin-server/tests/main/ingestion-queues/session-recording-v2/versions/lib-version-monitor.test.ts index 145cb7d0f633e..b77de2187d775 100644 --- a/plugin-server/tests/main/ingestion-queues/session-recording-v2/versions/lib-version-monitor.test.ts +++ b/plugin-server/tests/main/ingestion-queues/session-recording-v2/versions/lib-version-monitor.test.ts @@ -1,187 +1,79 @@ -import { Message, MessageHeader } from 'node-rdkafka' - import { MessageWithTeam } from '../../../../../src/main/ingestion-queues/session-recording-v2/teams/types' -import { - BatchMessageProcessor, - CaptureIngestionWarningFn, -} from '../../../../../src/main/ingestion-queues/session-recording-v2/types' import { LibVersionMonitor } from '../../../../../src/main/ingestion-queues/session-recording-v2/versions/lib-version-monitor' import { VersionMetrics } from '../../../../../src/main/ingestion-queues/session-recording-v2/versions/version-metrics' describe('LibVersionMonitor', () => { - let mockCaptureWarning: jest.MockedFunction - let mockMetrics: jest.Mocked - let mockSourceProcessor: jest.Mocked> - let monitor: LibVersionMonitor + let monitor: LibVersionMonitor + let mockCaptureWarning: jest.Mock + let mockVersionMetrics: jest.Mocked beforeEach(() => { - jest.clearAllMocks() mockCaptureWarning = jest.fn() - mockMetrics = { + mockVersionMetrics = { incrementLibVersionWarning: jest.fn(), - } as any - mockSourceProcessor = { - parseBatch: jest.fn(), - } - monitor = new LibVersionMonitor(mockSourceProcessor, mockCaptureWarning, mockMetrics) + } as unknown as jest.Mocked + monitor = new LibVersionMonitor(mockCaptureWarning, mockVersionMetrics) }) - describe('parseBatch', () => { - it('should process messages and return them unmodified', async () => { - const inputMessages: Message[] = [{ partition: 1 } as Message] - const processedMessages: MessageWithTeam[] = [ - { - team: { teamId: 1, consoleLogIngestionEnabled: true }, - message: { - distinct_id: 'test_id', - session_id: 'test_session', - eventsByWindowId: {}, - eventsRange: { start: 0, end: 0 }, - headers: [{ lib_version: '1.74.0' }] as MessageHeader[], - metadata: { - partition: 0, - topic: 'test', - rawSize: 0, - offset: 0, - timestamp: 0, - }, - }, - }, - ] - - mockSourceProcessor.parseBatch.mockResolvedValue(processedMessages) - - const result = await monitor.parseBatch(inputMessages) - expect(result).toBe(processedMessages) - expect(mockSourceProcessor.parseBatch).toHaveBeenCalledWith(inputMessages) - }) - - it('should trigger warning for old versions', async () => { - const inputMessages: Message[] = [{ partition: 1 } as Message] - const processedMessages: MessageWithTeam[] = [ - { - team: { teamId: 1, consoleLogIngestionEnabled: true }, - message: { - distinct_id: 'test_id', - session_id: 'test_session', - eventsByWindowId: {}, - eventsRange: { start: 0, end: 0 }, - headers: [{ lib_version: '1.74.0' }] as MessageHeader[], - metadata: { - partition: 0, - topic: 'test', - rawSize: 0, - offset: 0, - timestamp: 0, - }, - }, - }, - ] - - mockSourceProcessor.parseBatch.mockResolvedValue(processedMessages) - - await monitor.parseBatch(inputMessages) - - expect(mockMetrics.incrementLibVersionWarning).toHaveBeenCalled() - expect(mockCaptureWarning).toHaveBeenCalledWith( - 1, - 'replay_lib_version_too_old', - { - libVersion: '1.74.0', - parsedVersion: { major: 1, minor: 74 }, - }, - { key: '1.74.0' } - ) - }) - - it('should not trigger warning for newer versions', async () => { - const inputMessages: Message[] = [{ partition: 1 } as Message] - const processedMessages: MessageWithTeam[] = [ - { - team: { teamId: 1, consoleLogIngestionEnabled: true }, - message: { - distinct_id: 'test_id', - session_id: 'test_session', - eventsByWindowId: {}, - eventsRange: { start: 0, end: 0 }, - headers: [{ lib_version: '1.76.0' }] as MessageHeader[], - metadata: { - partition: 0, - topic: 'test', - rawSize: 0, - offset: 0, - timestamp: 0, - }, - }, - }, - ] - - mockSourceProcessor.parseBatch.mockResolvedValue(processedMessages) - - await monitor.parseBatch(inputMessages) - - expect(mockMetrics.incrementLibVersionWarning).not.toHaveBeenCalled() - expect(mockCaptureWarning).not.toHaveBeenCalled() - }) - - it('should handle invalid version formats', async () => { - const inputMessages: Message[] = [{ partition: 1 } as Message] - const processedMessages: MessageWithTeam[] = [ - { - team: { teamId: 1, consoleLogIngestionEnabled: true }, - message: { - distinct_id: 'test_id', - session_id: 'test_session', - eventsByWindowId: {}, - eventsRange: { start: 0, end: 0 }, - headers: [{ lib_version: 'invalid' }] as MessageHeader[], - metadata: { - partition: 0, - topic: 'test', - rawSize: 0, - offset: 0, - timestamp: 0, - }, - }, - }, - ] + const createMessage = (headers: any[] = []): MessageWithTeam => ({ + team: { teamId: 1, consoleLogIngestionEnabled: false }, + message: { + metadata: { + partition: 1, + topic: 'test-topic', + offset: 1, + timestamp: 1234567890, + rawSize: 100, + }, + headers, + distinct_id: 'distinct_id', + session_id: 'session1', + eventsByWindowId: { window1: [] }, + eventsRange: { start: 0, end: 0 }, + }, + }) - mockSourceProcessor.parseBatch.mockResolvedValue(processedMessages) + it('should warn on old lib version (< 1.75.0)', async () => { + const message = createMessage([{ lib_version: '1.74.0' }]) + const result = await monitor.processBatch([message]) + + expect(result).toEqual([message]) + expect(mockVersionMetrics.incrementLibVersionWarning).toHaveBeenCalled() + expect(mockCaptureWarning).toHaveBeenCalledWith( + 1, + 'replay_lib_version_too_old', + { + libVersion: '1.74.0', + parsedVersion: { major: 1, minor: 74 }, + }, + { key: '1.74.0' } + ) + }) - await monitor.parseBatch(inputMessages) + it('should not warn on new lib version (>= 1.75.0)', async () => { + const message = createMessage([{ lib_version: '1.75.0' }]) + const result = await monitor.processBatch([message]) - expect(mockMetrics.incrementLibVersionWarning).not.toHaveBeenCalled() - expect(mockCaptureWarning).not.toHaveBeenCalled() - }) + expect(result).toEqual([message]) + expect(mockVersionMetrics.incrementLibVersionWarning).not.toHaveBeenCalled() + expect(mockCaptureWarning).not.toHaveBeenCalled() + }) - it('should handle missing version header', async () => { - const inputMessages: Message[] = [{ partition: 1 } as Message] - const processedMessages: MessageWithTeam[] = [ - { - team: { teamId: 1, consoleLogIngestionEnabled: true }, - message: { - distinct_id: 'test_id', - session_id: 'test_session', - eventsByWindowId: {}, - eventsRange: { start: 0, end: 0 }, - headers: [] as MessageHeader[], - metadata: { - partition: 0, - topic: 'test', - rawSize: 0, - offset: 0, - timestamp: 0, - }, - }, - }, - ] + it('should handle invalid lib version', async () => { + const message = createMessage([{ lib_version: 'invalid' }]) + const result = await monitor.processBatch([message]) - mockSourceProcessor.parseBatch.mockResolvedValue(processedMessages) + expect(result).toEqual([message]) + expect(mockVersionMetrics.incrementLibVersionWarning).not.toHaveBeenCalled() + expect(mockCaptureWarning).not.toHaveBeenCalled() + }) - await monitor.parseBatch(inputMessages) + it('should handle missing lib version', async () => { + const message = createMessage() + const result = await monitor.processBatch([message]) - expect(mockMetrics.incrementLibVersionWarning).not.toHaveBeenCalled() - expect(mockCaptureWarning).not.toHaveBeenCalled() - }) + expect(result).toEqual([message]) + expect(mockVersionMetrics.incrementLibVersionWarning).not.toHaveBeenCalled() + expect(mockCaptureWarning).not.toHaveBeenCalled() }) }) diff --git a/plugin-server/tests/main/process-event.test.ts b/plugin-server/tests/main/process-event.test.ts index e33da08a760d7..57396d360ee09 100644 --- a/plugin-server/tests/main/process-event.test.ts +++ b/plugin-server/tests/main/process-event.test.ts @@ -103,7 +103,7 @@ async function processEvent( ...data, } as any as PluginEvent - const runner = new EventPipelineRunner(hub, pluginEvent, new EventsProcessor(hub)) + const runner = new EventPipelineRunner(hub, pluginEvent) await runner.runEventPipeline(pluginEvent) await delayUntilEventIngested(() => hub.db.fetchEvents(), ++processEventCounter) @@ -163,7 +163,7 @@ const capture = async (hub: Hub, eventName: string, properties: any = {}) => { team_id: team.id, uuid: new UUIDT().toString(), } - const runner = new EventPipelineRunner(hub, event, new EventsProcessor(hub)) + const runner = new EventPipelineRunner(hub, event) await runner.runEventPipeline(event) await delayUntilEventIngested(() => hub.db.fetchEvents(), ++mockClientEventCounter) } @@ -1651,7 +1651,7 @@ describe('validates eventUuid', () => { properties: { price: 299.99, name: 'AirPods Pro' }, } - const runner = new EventPipelineRunner(hub, pluginEvent, new EventsProcessor(hub)) + const runner = new EventPipelineRunner(hub, pluginEvent) const result = await runner.runEventPipeline(pluginEvent) expect(result.error).toBeDefined() @@ -1670,7 +1670,7 @@ describe('validates eventUuid', () => { properties: { price: 299.99, name: 'AirPods Pro' }, } - const runner = new EventPipelineRunner(hub, pluginEvent, new EventsProcessor(hub)) + const runner = new EventPipelineRunner(hub, pluginEvent) const result = await runner.runEventPipeline(pluginEvent) expect(result.error).toBeDefined() diff --git a/plugin-server/tests/main/teardown.test.ts b/plugin-server/tests/main/teardown.test.ts index 89d1026a1e7e8..5a0fa7a6d9b0b 100644 --- a/plugin-server/tests/main/teardown.test.ts +++ b/plugin-server/tests/main/teardown.test.ts @@ -5,7 +5,6 @@ import { waitForExpect } from '../../functional_tests/expectations' import { startPluginsServer } from '../../src/main/pluginsServer' import { Hub, LogLevel, PluginLogEntry, PluginLogEntrySource, PluginLogEntryType } from '../../src/types' import { EventPipelineRunner } from '../../src/worker/ingestion/event-pipeline/runner' -import { EventsProcessor } from '../../src/worker/ingestion/process-event' import { makePiscina } from '../../src/worker/piscina' import { pluginConfig39 } from '../helpers/plugins' import { resetTestDatabase } from '../helpers/sql' @@ -37,7 +36,7 @@ async function getLogEntriesForPluginConfig(hub: Hub, pluginConfigId: number) { describe('teardown', () => { const processEvent = async (hub: Hub, event: PluginEvent) => { - const result = await new EventPipelineRunner(hub, event, new EventsProcessor(hub)).runEventPipeline(event) + const result = await new EventPipelineRunner(hub, event).runEventPipeline(event) const resultEvent = result.args[0] return resultEvent } diff --git a/plugin-server/tests/server.test.ts b/plugin-server/tests/server.test.ts index a4a816c9c2108..a4733648b1ce5 100644 --- a/plugin-server/tests/server.test.ts +++ b/plugin-server/tests/server.test.ts @@ -94,7 +94,6 @@ describe('server', () => { processAsyncOnEventHandlers: true, processAsyncWebhooksHandlers: true, cdpProcessedEvents: true, - cdpFunctionCallbacks: true, cdpCyclotronWorker: true, syncInlinePlugins: true, } diff --git a/plugin-server/tests/worker/dead-letter-queue.test.ts b/plugin-server/tests/worker/dead-letter-queue.test.ts index b9c79471fb42a..82b7e546c66fa 100644 --- a/plugin-server/tests/worker/dead-letter-queue.test.ts +++ b/plugin-server/tests/worker/dead-letter-queue.test.ts @@ -4,7 +4,6 @@ import { Hub, LogLevel } from '../../src/types' import { closeHub, createHub } from '../../src/utils/db/hub' import { UUIDT } from '../../src/utils/utils' import { EventPipelineRunner } from '../../src/worker/ingestion/event-pipeline/runner' -import { EventsProcessor } from '../../src/worker/ingestion/process-event' import { generateEventDeadLetterQueueMessage } from '../../src/worker/ingestion/utils' import { delayUntilEventIngested, resetTestDatabaseClickhouse } from '../helpers/clickhouse' import { resetTestDatabase } from '../helpers/sql' @@ -60,9 +59,7 @@ describe('events dead letter queue', () => { test('events get sent to dead letter queue on error', async () => { const event = createEvent() - const ingestResponse1 = await new EventPipelineRunner(hub, event, new EventsProcessor(hub)).runEventPipeline( - event - ) + const ingestResponse1 = await new EventPipelineRunner(hub, event).runEventPipeline(event) expect(ingestResponse1).toEqual({ lastStep: 'prepareEventStep', error: 'database unavailable', diff --git a/plugin-server/tests/worker/ingestion/event-pipeline/event-pipeline-integration.test.ts b/plugin-server/tests/worker/ingestion/event-pipeline/event-pipeline-integration.test.ts index 5c1ebcbb49394..e42713e3dda76 100644 --- a/plugin-server/tests/worker/ingestion/event-pipeline/event-pipeline-integration.test.ts +++ b/plugin-server/tests/worker/ingestion/event-pipeline/event-pipeline-integration.test.ts @@ -15,7 +15,6 @@ import { } from '../../../../src/worker/ingestion/event-pipeline/runAsyncHandlersStep' import { EventPipelineRunner } from '../../../../src/worker/ingestion/event-pipeline/runner' import { HookCommander } from '../../../../src/worker/ingestion/hooks' -import { EventsProcessor } from '../../../../src/worker/ingestion/process-event' import { setupPlugins } from '../../../../src/worker/plugins/setup' import { delayUntilEventIngested, resetTestDatabaseClickhouse } from '../../../helpers/clickhouse' import { commonUserId } from '../../../helpers/plugins' @@ -30,7 +29,7 @@ describe('Event Pipeline integration test', () => { let hookCannon: HookCommander const ingestEvent = async (event: PluginEvent) => { - const runner = new EventPipelineRunner(hub, event, new EventsProcessor(hub)) + const runner = new EventPipelineRunner(hub, event) const result = await runner.runEventPipeline(event) const postIngestionEvent = convertToPostIngestionEvent(result.args[0]) return Promise.all([ @@ -255,13 +254,13 @@ describe('Event Pipeline integration test', () => { uuid: new UUIDT().toString(), } - await new EventPipelineRunner(hub, event, new EventsProcessor(hub)).runEventPipeline(event) + await new EventPipelineRunner(hub, event).runEventPipeline(event) expect(hub.db.fetchPerson).toHaveBeenCalledTimes(1) // we query before creating expect(hub.db.createPerson).toHaveBeenCalledTimes(1) // second time single fetch - await new EventPipelineRunner(hub, event, new EventsProcessor(hub)).runEventPipeline(event) + await new EventPipelineRunner(hub, event).runEventPipeline(event) expect(hub.db.fetchPerson).toHaveBeenCalledTimes(2) }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bba986a54dbe0..68b074f5dff66 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -312,8 +312,8 @@ dependencies: specifier: ^9.3.0 version: 9.3.0(postcss@8.4.31) posthog-js: - specifier: 1.207.8 - version: 1.207.8 + specifier: 1.210.2 + version: 1.210.2 posthog-js-lite: specifier: 3.0.0 version: 3.0.0 @@ -18184,8 +18184,8 @@ packages: resolution: {integrity: sha512-dyajjnfzZD1tht4N7p7iwf7nBnR1MjVaVu+MKr+7gBgA39bn28wizCIJZztZPtHy4PY0YwtSGgwfBCuG/hnHgA==} dev: false - /posthog-js@1.207.8: - resolution: {integrity: sha512-fFbOs0YwjlU9qn/9dQEXufbT/JeiOl7sEmKFy7AEB8Qsb7lbPLM6847svDCj1XzILj/xTs84yKYJmcYPjPdDzQ==} + /posthog-js@1.210.2: + resolution: {integrity: sha512-rIbn/h9ur7uA0PS4dClOr9w6txLfHS94yh9yafA5VM2eXToM951XtMYtIQ6bi6wFzpvpFvTQFeYLQ/9/xZ59AQ==} dependencies: core-js: 3.40.0 fflate: 0.4.8 @@ -18818,7 +18818,7 @@ packages: react: '>=15' dependencies: react: 18.2.0 - unlayer-types: 1.197.0 + unlayer-types: 1.199.0 dev: false /react-error-boundary@3.1.4(react@18.2.0): @@ -21429,8 +21429,8 @@ packages: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} - /unlayer-types@1.197.0: - resolution: {integrity: sha512-gz0RuaJikPVsGs/LWyCBWMo5uG7r66kn8hfRA9cKcI/f1vXKgiruI/IKCH+z5b8ONevYxSARz/s8mIlDxng4Hg==} + /unlayer-types@1.199.0: + resolution: {integrity: sha512-ZBHpty2RkDc4BygHqw18ZwNrTvX31Rf10NY4SuncdqYkEj6wqHoNZiVoIhObQJrhPskT1mYdL8CTgLsSq42SeQ==} dev: false /unpipe@1.0.0: diff --git a/posthog/api/capture.py b/posthog/api/capture.py index e7ad106c405e8..2aaa5c26cf68a 100644 --- a/posthog/api/capture.py +++ b/posthog/api/capture.py @@ -858,6 +858,8 @@ def capture_internal( if extra_headers is None: extra_headers = [] + headers = [("token", token), ("distinct_id", distinct_id), *extra_headers] + parsed_event = build_kafka_event_data( distinct_id=distinct_id, ip=ip, @@ -871,7 +873,6 @@ def capture_internal( if event["event"] in SESSION_RECORDING_EVENT_NAMES: session_id = event["properties"]["$session_id"] - headers = [("token", token), *extra_headers] overflowing = False if token in settings.REPLAY_OVERFLOW_FORCED_TOKENS: @@ -900,7 +901,9 @@ def capture_internal( else: kafka_partition_key = candidate_partition_key - return log_event(parsed_event, event["event"], partition_key=kafka_partition_key, historical=historical) + return log_event( + parsed_event, event["event"], partition_key=kafka_partition_key, historical=historical, headers=headers + ) def is_randomly_partitioned(candidate_partition_key: str) -> bool: diff --git a/posthog/api/cohort.py b/posthog/api/cohort.py index 54985f23722b2..2862012210bb4 100644 --- a/posthog/api/cohort.py +++ b/posthog/api/cohort.py @@ -1,5 +1,6 @@ import csv +from collections import defaultdict from django.db import DatabaseError from loginas.utils import is_impersonated_session from sentry_sdk import start_span @@ -309,8 +310,74 @@ def safely_get_queryset(self, queryset) -> QuerySet: if search_query: queryset = queryset.filter(name__icontains=search_query) + # TODO: remove this filter once we can support behavioral cohorts for feature flags, it's only + # used in the feature flag property filter UI + if self.request.query_params.get("hide_behavioral_cohorts", "false").lower() == "true": + all_cohorts = {cohort.id: cohort for cohort in queryset.all()} + behavioral_cohort_ids = self._find_behavioral_cohorts(all_cohorts) + queryset = queryset.exclude(id__in=behavioral_cohort_ids) + return queryset.prefetch_related("experiment_set", "created_by", "team").order_by("-created_at") + def _find_behavioral_cohorts(self, all_cohorts: dict[int, Cohort]) -> set[int]: + """ + Find all cohorts that have behavioral filters or reference cohorts with behavioral filters + using a graph-based approach. + """ + graph, behavioral_cohorts = self._build_cohort_dependency_graph(all_cohorts) + affected_cohorts = set(behavioral_cohorts) + + def find_affected_cohorts() -> None: + changed = True + while changed: + changed = False + for source_id in list(graph.keys()): + if source_id not in affected_cohorts: + # NB: If this cohort points to any affected cohort, it's also affected + if any(target_id in affected_cohorts for target_id in graph[source_id]): + affected_cohorts.add(source_id) + changed = True + + find_affected_cohorts() + return affected_cohorts + + def _build_cohort_dependency_graph(self, all_cohorts: dict[int, Cohort]) -> tuple[dict[int, set[int]], set[int]]: + """ + Builds a directed graph of cohort dependencies and identifies behavioral cohorts. + Returns (adjacency_list, behavioral_cohort_ids). + """ + graph = defaultdict(set) + behavioral_cohorts = set() + + def check_property_values(values: Any, source_id: int) -> None: + """Process property values to build graph edges and identify behavioral cohorts.""" + if not isinstance(values, list): + return + + for value in values: + if not isinstance(value, dict): + continue + + if value.get("type") == "behavioral": + behavioral_cohorts.add(source_id) + elif value.get("type") == "cohort": + try: + target_id = int(value.get("value", "0")) + if target_id in all_cohorts: + graph[source_id].add(target_id) + except ValueError: + continue + elif value.get("type") in ("AND", "OR") and value.get("values"): + check_property_values(value["values"], source_id) + + for cohort_id, cohort in all_cohorts.items(): + if cohort.filters: + properties = cohort.filters.get("properties", {}) + if isinstance(properties, dict): + check_property_values(properties.get("values", []), cohort_id) + + return graph, behavioral_cohorts + @action( methods=["GET"], detail=True, diff --git a/posthog/api/data_color_theme.py b/posthog/api/data_color_theme.py index eb4b91e77253d..b3b9e55bcac90 100644 --- a/posthog/api/data_color_theme.py +++ b/posthog/api/data_color_theme.py @@ -6,6 +6,7 @@ from posthog.api.routing import TeamAndOrgViewSetMixin from posthog.api.shared import UserBasicSerializer from posthog.auth import SharingAccessTokenAuthentication +from posthog.constants import AvailableFeature from posthog.models import DataColorTheme @@ -17,7 +18,20 @@ def has_object_permission(self, request, view, obj) -> bool: return True elif view.team == obj.team: return True - return request.user.is_staff + elif obj.is_global and request.user.is_staff: + return True + else: + return False + + +class PaidThemePermission(BasePermission): + message = "This feature is only available on paid plans." + + def has_object_permission(self, request, view, obj) -> bool: + if request.method in SAFE_METHODS or obj.is_global: + return True + + return view.organization.is_feature_available(AvailableFeature.DATA_COLOR_THEMES) class PublicDataColorThemeSerializer(serializers.ModelSerializer): @@ -55,7 +69,7 @@ class DataColorThemeViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet): scope_object = "INTERNAL" queryset = DataColorTheme.objects.all().order_by("-created_at") serializer_class = DataColorThemeSerializer - permission_classes = [GlobalThemePermission] + permission_classes = [GlobalThemePermission, PaidThemePermission] sharing_enabled_actions = ["retrieve", "list"] # override the team scope queryset to also include global themes diff --git a/posthog/api/decide.py b/posthog/api/decide.py index e3801869b02ab..2b6ebf418674b 100644 --- a/posthog/api/decide.py +++ b/posthog/api/decide.py @@ -253,7 +253,7 @@ def get_decide(request: HttpRequest): is_request_sampled_for_logging = random() < settings.DECIDE_REQUEST_LOGGING_SAMPLING_RATE if team: if is_request_sampled_for_logging: - logger.info( + logger.warn( "DECIDE_REQUEST_STARTED", team_id=team.id, distinct_id=data.get("distinct_id", None), @@ -337,7 +337,7 @@ def get_decide(request: HttpRequest): ).inc() if is_request_sampled_for_logging: - logger.info( + logger.warn( "DECIDE_REQUEST_SUCCEEDED", team_id=team.id, distinct_id=distinct_id, diff --git a/posthog/api/error_tracking.py b/posthog/api/error_tracking.py index f23edb5308560..6cdf331bb3044 100644 --- a/posthog/api/error_tracking.py +++ b/posthog/api/error_tracking.py @@ -36,7 +36,7 @@ class ObjectStorageUnavailable(Exception): class ErrorTrackingIssueSerializer(serializers.ModelSerializer): class Meta: model = ErrorTrackingIssue - fields = ["assignee", "status"] + fields = ["status"] class ErrorTrackingIssueViewSet(TeamAndOrgViewSetMixin, ForbidDestroyModel, viewsets.ModelViewSet): diff --git a/posthog/api/feature_flag.py b/posthog/api/feature_flag.py index dcbb9638930c5..f3bc76edcec5e 100644 --- a/posthog/api/feature_flag.py +++ b/posthog/api/feature_flag.py @@ -154,6 +154,7 @@ class Meta: "has_enriched_analytics", "user_access_level", "creation_context", + "is_remote_configuration", ] def get_can_edit(self, feature_flag: FeatureFlag) -> bool: diff --git a/posthog/api/team.py b/posthog/api/team.py index 93d3eabd329e3..d05e0cac6883e 100644 --- a/posthog/api/team.py +++ b/posthog/api/team.py @@ -26,6 +26,7 @@ ) from posthog.models.activity_logging.activity_page import activity_page_response from posthog.models.async_deletion import AsyncDeletion, DeletionType +from posthog.models.data_color_theme import DataColorTheme from posthog.models.group_type_mapping import GroupTypeMapping from posthog.models.organization import OrganizationMembership from posthog.models.product_intent.product_intent import calculate_product_activation @@ -236,6 +237,15 @@ class Meta: "user_access_level", ) + def to_representation(self, instance): + representation = super().to_representation(instance) + # fallback to the default posthog data theme id, if the color feature isn't available e.g. after a downgrade + if not instance.organization.is_feature_available(AvailableFeature.DATA_COLOR_THEMES): + representation["default_data_theme"] = ( + DataColorTheme.objects.filter(team_id__isnull=True).values_list("id", flat=True).first() + ) + return representation + def get_effective_membership_level(self, team: Team) -> Optional[OrganizationMembership.Level]: # TODO: Map from user_access_controls return self.user_permissions.team(team).effective_membership_level diff --git a/posthog/api/test/__snapshots__/test_cohort.ambr b/posthog/api/test/__snapshots__/test_cohort.ambr index a4408c817f962..4032bd1884f6a 100644 --- a/posthog/api/test/__snapshots__/test_cohort.ambr +++ b/posthog/api/test/__snapshots__/test_cohort.ambr @@ -174,7 +174,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), greaterOrEquals(timestamp, toDateTime64('2025-01-23 00:00:00.000000', 6, 'UTC')), lessOrEquals(timestamp, toDateTime64('2025-01-24 23:59:59.999999', 6, 'UTC')), equals(e.event, '$pageview'))) + WHERE and(equals(e.team_id, 99999), greaterOrEquals(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(timestamp, toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')), equals(e.event, '$pageview'))) GROUP BY actor_id) AS source ORDER BY source.id ASC LIMIT 100 SETTINGS optimize_aggregation_in_order=1, diff --git a/posthog/api/test/__snapshots__/test_decide.ambr b/posthog/api/test/__snapshots__/test_decide.ambr index 62aabb86e792b..78ed20c78730a 100644 --- a/posthog/api/test/__snapshots__/test_decide.ambr +++ b/posthog/api/test/__snapshots__/test_decide.ambr @@ -562,6 +562,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -577,6 +578,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -591,7 +593,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -824,12 +827,28 @@ # --- # name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.29 ''' - SELECT "posthog_productintent"."product_type", - "posthog_productintent"."created_at", - "posthog_productintent"."onboarding_completed_at", - "posthog_productintent"."updated_at" - FROM "posthog_productintent" - WHERE "posthog_productintent"."team_id" = 99999 + SELECT "posthog_organization"."id", + "posthog_organization"."name", + "posthog_organization"."slug", + "posthog_organization"."logo_media_id", + "posthog_organization"."created_at", + "posthog_organization"."updated_at", + "posthog_organization"."plugins_access_level", + "posthog_organization"."for_internal_metrics", + "posthog_organization"."is_member_join_email_enabled", + "posthog_organization"."enforce_2fa", + "posthog_organization"."is_hipaa", + "posthog_organization"."customer_id", + "posthog_organization"."available_product_features", + "posthog_organization"."usage", + "posthog_organization"."never_drop_data", + "posthog_organization"."customer_trust_scores", + "posthog_organization"."setup_section_2_completed", + "posthog_organization"."personalization", + "posthog_organization"."domain_whitelist" + FROM "posthog_organization" + WHERE "posthog_organization"."id" = '00000000-0000-0000-0000-000000000000'::uuid + LIMIT 21 ''' # --- # name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.3 @@ -908,6 +927,25 @@ ''' # --- # name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.30 + ''' + SELECT "posthog_datacolortheme"."id" + FROM "posthog_datacolortheme" + WHERE "posthog_datacolortheme"."team_id" IS NULL + ORDER BY "posthog_datacolortheme"."id" ASC + LIMIT 1 + ''' +# --- +# name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.31 + ''' + SELECT "posthog_productintent"."product_type", + "posthog_productintent"."created_at", + "posthog_productintent"."onboarding_completed_at", + "posthog_productintent"."updated_at" + FROM "posthog_productintent" + WHERE "posthog_productintent"."team_id" = 99999 + ''' +# --- +# name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.32 ''' SELECT "posthog_user"."id", "posthog_user"."password", @@ -940,7 +978,7 @@ LIMIT 21 ''' # --- -# name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.31 +# name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.33 ''' SELECT "posthog_featureflag"."id", "posthog_featureflag"."key", @@ -956,30 +994,15 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" AND "posthog_featureflag"."team_id" = 99999) ''' # --- -# name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.32 - ''' - SELECT "posthog_pluginconfig"."id", - "posthog_pluginconfig"."web_token", - "posthog_pluginsourcefile"."updated_at", - "posthog_plugin"."updated_at", - "posthog_pluginconfig"."updated_at" - FROM "posthog_pluginconfig" - INNER JOIN "posthog_plugin" ON ("posthog_pluginconfig"."plugin_id" = "posthog_plugin"."id") - INNER JOIN "posthog_pluginsourcefile" ON ("posthog_plugin"."id" = "posthog_pluginsourcefile"."plugin_id") - WHERE ("posthog_pluginconfig"."enabled" - AND "posthog_pluginsourcefile"."filename" = 'site.ts' - AND "posthog_pluginsourcefile"."status" = 'TRANSPILED' - AND "posthog_pluginconfig"."team_id" = 99999) - ''' -# --- -# name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.33 +# name: TestDecide.test_decide_doesnt_error_out_when_database_is_down.34 ''' SELECT "posthog_pluginconfig"."id", "posthog_pluginconfig"."web_token", @@ -1366,7 +1389,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -1583,6 +1607,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -1598,6 +1623,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -1612,7 +1638,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -2002,6 +2029,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -2017,6 +2045,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -2031,7 +2060,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -2445,7 +2475,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -2662,6 +2693,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -2677,6 +2709,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -2691,7 +2724,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -3069,6 +3103,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -3084,6 +3119,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -3098,7 +3134,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -3415,7 +3452,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -3748,6 +3786,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -3763,6 +3802,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -3777,7 +3817,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -4305,6 +4346,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -4320,6 +4362,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -4334,7 +4377,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -4922,6 +4966,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -4937,6 +4982,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -4951,7 +4997,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -5184,12 +5231,28 @@ # --- # name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.29 ''' - SELECT "posthog_productintent"."product_type", - "posthog_productintent"."created_at", - "posthog_productintent"."onboarding_completed_at", - "posthog_productintent"."updated_at" - FROM "posthog_productintent" - WHERE "posthog_productintent"."team_id" = 99999 + SELECT "posthog_organization"."id", + "posthog_organization"."name", + "posthog_organization"."slug", + "posthog_organization"."logo_media_id", + "posthog_organization"."created_at", + "posthog_organization"."updated_at", + "posthog_organization"."plugins_access_level", + "posthog_organization"."for_internal_metrics", + "posthog_organization"."is_member_join_email_enabled", + "posthog_organization"."enforce_2fa", + "posthog_organization"."is_hipaa", + "posthog_organization"."customer_id", + "posthog_organization"."available_product_features", + "posthog_organization"."usage", + "posthog_organization"."never_drop_data", + "posthog_organization"."customer_trust_scores", + "posthog_organization"."setup_section_2_completed", + "posthog_organization"."personalization", + "posthog_organization"."domain_whitelist" + FROM "posthog_organization" + WHERE "posthog_organization"."id" = '00000000-0000-0000-0000-000000000000'::uuid + LIMIT 21 ''' # --- # name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.3 @@ -5268,6 +5331,25 @@ ''' # --- # name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.30 + ''' + SELECT "posthog_datacolortheme"."id" + FROM "posthog_datacolortheme" + WHERE "posthog_datacolortheme"."team_id" IS NULL + ORDER BY "posthog_datacolortheme"."id" ASC + LIMIT 1 + ''' +# --- +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.31 + ''' + SELECT "posthog_productintent"."product_type", + "posthog_productintent"."created_at", + "posthog_productintent"."onboarding_completed_at", + "posthog_productintent"."updated_at" + FROM "posthog_productintent" + WHERE "posthog_productintent"."team_id" = 99999 + ''' +# --- +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.32 ''' SELECT "posthog_user"."id", "posthog_user"."password", @@ -5300,7 +5382,7 @@ LIMIT 21 ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.31 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.33 ''' SELECT "posthog_remoteconfig"."id", "posthog_remoteconfig"."team_id", @@ -5312,7 +5394,7 @@ LIMIT 21 ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.32 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.34 ''' SELECT "posthog_team"."id", "posthog_team"."uuid", @@ -5387,7 +5469,7 @@ LIMIT 21 ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.33 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.35 ''' SELECT COUNT(*) AS "__count" FROM "posthog_featureflag" @@ -5396,7 +5478,7 @@ AND "posthog_featureflag"."team_id" = 99999) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.34 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.36 ''' SELECT "posthog_survey"."id", "posthog_survey"."team_id", @@ -5442,6 +5524,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -5457,6 +5540,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -5471,7 +5555,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -5480,7 +5565,7 @@ AND NOT ("posthog_survey"."archived")) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.35 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.37 ''' SELECT "posthog_pluginconfig"."id", "posthog_pluginconfig"."web_token", @@ -5496,7 +5581,7 @@ AND "posthog_pluginconfig"."team_id" = 99999) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.36 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.38 ''' SELECT "posthog_pluginconfig"."id", "posthog_pluginsourcefile"."transpiled", @@ -5512,7 +5597,7 @@ AND "posthog_pluginconfig"."team_id" = 99999) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.37 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.39 ''' SELECT "posthog_hogfunction"."id", "posthog_hogfunction"."team_id", @@ -5612,41 +5697,6 @@ 'site_app')) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.38 - ''' - SELECT "posthog_featureflag"."id", - "posthog_featureflag"."key", - "posthog_featureflag"."name", - "posthog_featureflag"."filters", - "posthog_featureflag"."rollout_percentage", - "posthog_featureflag"."team_id", - "posthog_featureflag"."created_by_id", - "posthog_featureflag"."created_at", - "posthog_featureflag"."deleted", - "posthog_featureflag"."active", - "posthog_featureflag"."rollback_conditions", - "posthog_featureflag"."performed_rollback", - "posthog_featureflag"."ensure_experience_continuity", - "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" - FROM "posthog_featureflag" - WHERE ("posthog_featureflag"."active" - AND NOT "posthog_featureflag"."deleted" - AND "posthog_featureflag"."team_id" = 99999) - ''' -# --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.39 - ''' - SELECT "posthog_remoteconfig"."id", - "posthog_remoteconfig"."team_id", - "posthog_remoteconfig"."config", - "posthog_remoteconfig"."updated_at", - "posthog_remoteconfig"."synced_at" - FROM "posthog_remoteconfig" - WHERE "posthog_remoteconfig"."team_id" = 99999 - LIMIT 21 - ''' -# --- # name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.4 ''' SELECT "posthog_organizationmembership"."id", @@ -5682,6 +5732,42 @@ ''' # --- # name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.40 + ''' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."active" + AND NOT "posthog_featureflag"."deleted" + AND "posthog_featureflag"."team_id" = 99999) + ''' +# --- +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.41 + ''' + SELECT "posthog_remoteconfig"."id", + "posthog_remoteconfig"."team_id", + "posthog_remoteconfig"."config", + "posthog_remoteconfig"."updated_at", + "posthog_remoteconfig"."synced_at" + FROM "posthog_remoteconfig" + WHERE "posthog_remoteconfig"."team_id" = 99999 + LIMIT 21 + ''' +# --- +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.42 ''' SELECT "posthog_team"."id", "posthog_team"."uuid", @@ -5756,7 +5842,7 @@ LIMIT 21 ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.41 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.43 ''' SELECT COUNT(*) AS "__count" FROM "posthog_featureflag" @@ -5765,7 +5851,7 @@ AND "posthog_featureflag"."team_id" = 99999) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.42 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.44 ''' SELECT "posthog_survey"."id", "posthog_survey"."team_id", @@ -5811,6 +5897,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -5826,6 +5913,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -5840,7 +5928,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -5849,7 +5938,7 @@ AND NOT ("posthog_survey"."archived")) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.43 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.45 ''' SELECT "posthog_pluginconfig"."id", "posthog_pluginconfig"."web_token", @@ -5865,7 +5954,7 @@ AND "posthog_pluginconfig"."team_id" = 99999) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.44 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.46 ''' SELECT "posthog_pluginconfig"."id", "posthog_pluginsourcefile"."transpiled", @@ -5881,7 +5970,7 @@ AND "posthog_pluginconfig"."team_id" = 99999) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.45 +# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.47 ''' SELECT "posthog_hogfunction"."id", "posthog_hogfunction"."team_id", @@ -5981,53 +6070,6 @@ 'site_app')) ''' # --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.46 - ''' - SELECT "posthog_pluginconfig"."id", - "posthog_pluginsourcefile"."transpiled", - "posthog_pluginconfig"."web_token", - "posthog_plugin"."config_schema", - "posthog_pluginconfig"."config" - FROM "posthog_pluginconfig" - INNER JOIN "posthog_plugin" ON ("posthog_pluginconfig"."plugin_id" = "posthog_plugin"."id") - INNER JOIN "posthog_pluginsourcefile" ON ("posthog_plugin"."id" = "posthog_pluginsourcefile"."plugin_id") - WHERE ("posthog_pluginconfig"."enabled" - AND "posthog_pluginsourcefile"."filename" = 'site.ts' - AND "posthog_pluginsourcefile"."status" = 'TRANSPILED' - AND "posthog_pluginconfig"."team_id" = 99999) - ''' -# --- -# name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.47 - ''' - SELECT "posthog_hogfunction"."id", - "posthog_hogfunction"."team_id", - "posthog_hogfunction"."name", - "posthog_hogfunction"."description", - "posthog_hogfunction"."created_at", - "posthog_hogfunction"."created_by_id", - "posthog_hogfunction"."deleted", - "posthog_hogfunction"."updated_at", - "posthog_hogfunction"."enabled", - "posthog_hogfunction"."type", - "posthog_hogfunction"."icon_url", - "posthog_hogfunction"."hog", - "posthog_hogfunction"."bytecode", - "posthog_hogfunction"."transpiled", - "posthog_hogfunction"."inputs_schema", - "posthog_hogfunction"."inputs", - "posthog_hogfunction"."encrypted_inputs", - "posthog_hogfunction"."filters", - "posthog_hogfunction"."mappings", - "posthog_hogfunction"."masking", - "posthog_hogfunction"."template_id" - FROM "posthog_hogfunction" - WHERE ("posthog_hogfunction"."enabled" - AND "posthog_hogfunction"."team_id" = 99999 - AND "posthog_hogfunction"."type" IN ('site_destination', - 'site_app')) - LIMIT 21 - ''' -# --- # name: TestDecideRemoteConfig.test_decide_doesnt_error_out_when_database_is_down.48 ''' SELECT "posthog_hogfunction"."id", @@ -6395,7 +6437,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -6612,6 +6655,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -6627,6 +6671,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -6641,7 +6686,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -6920,6 +6966,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -6935,6 +6982,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -6949,7 +6997,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -7257,6 +7306,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -7272,6 +7322,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -7286,7 +7337,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -7684,6 +7736,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -7699,6 +7752,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -7713,7 +7767,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -8127,7 +8182,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -8344,6 +8400,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -8359,6 +8416,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -8373,7 +8431,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -8652,6 +8711,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -8667,6 +8727,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -8681,7 +8742,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -8985,6 +9047,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -9000,6 +9063,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -9014,7 +9078,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -9404,6 +9469,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -9419,6 +9485,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -9433,7 +9500,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") diff --git a/posthog/api/test/__snapshots__/test_feature_flag.ambr b/posthog/api/test/__snapshots__/test_feature_flag.ambr index 788ebf035efeb..1335c0a821764 100644 --- a/posthog/api/test/__snapshots__/test_feature_flag.ambr +++ b/posthog/api/test/__snapshots__/test_feature_flag.ambr @@ -360,7 +360,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE ("posthog_featureflag"."key" = 'some-feature2' @@ -554,7 +555,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE ("posthog_featureflag"."key" = 'some-feature-new' @@ -832,7 +834,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE ("posthog_featureflag"."key" = 'some-feature2' @@ -1043,7 +1046,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE ("posthog_featureflag"."key" = 'some-feature-new' @@ -1103,7 +1107,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE ("posthog_featureflag"."key" = 'some-feature2' @@ -1781,6 +1786,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", "posthog_user"."id", "posthog_user"."password", "posthog_user"."last_login", @@ -1841,7 +1847,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE ("posthog_featureflag"."key" = 'some-feature' diff --git a/posthog/api/test/__snapshots__/test_insight.ambr b/posthog/api/test/__snapshots__/test_insight.ambr index bca78c6a69bf3..88aae60526a1d 100644 --- a/posthog/api/test/__snapshots__/test_insight.ambr +++ b/posthog/api/test/__snapshots__/test_insight.ambr @@ -82,7 +82,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2012-01-08 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2012-01-15 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('user did things', 'user signed up')), and(ifNull(less(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(e.properties, 'int_value'), ''), 'null'), '^"|"$', ''), 'Int64'), 10), 0), 1)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('user did things', 'user signed up')), and(ifNull(less(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(e.properties, 'int_value'), ''), 'null'), '^"|"$', ''), 'Int64'), 10), 0), 1)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -247,7 +247,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2012-01-08 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2012-01-15 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('user did things', 'user signed up')), and(and(ifNull(less(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(e.properties, 'int_value'), ''), 'null'), '^"|"$', ''), 'Int64'), 10), 0), 1), ifNull(like(e__person.properties___fish, '%fish%'), 0))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('user did things', 'user signed up')), and(and(ifNull(less(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(e.properties, 'int_value'), ''), 'null'), '^"|"$', ''), 'Int64'), 10), 0), 1), ifNull(like(e__person.properties___fish, '%fish%'), 0))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -322,7 +322,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2012-01-08 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2012-01-15 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('user did things', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('user did things', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps diff --git a/posthog/api/test/__snapshots__/test_insight_funnels.ambr b/posthog/api/test/__snapshots__/test_insight_funnels.ambr index 0491d5cfcecb5..3412ec33beb1c 100644 --- a/posthog/api/test/__snapshots__/test_insight_funnels.ambr +++ b/posthog/api/test/__snapshots__/test_insight_funnels.ambr @@ -86,7 +86,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -122,7 +122,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -155,7 +155,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -188,7 +188,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -221,7 +221,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -300,7 +300,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -336,7 +336,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -369,7 +369,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -403,7 +403,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -488,7 +488,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -519,7 +519,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -547,7 +547,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -575,7 +575,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -603,7 +603,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -663,7 +663,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -694,7 +694,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -722,7 +722,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -751,7 +751,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -838,7 +838,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -881,7 +881,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -924,7 +924,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -955,7 +955,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -971,7 +971,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -987,7 +987,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1015,7 +1015,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1031,7 +1031,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1047,7 +1047,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1075,7 +1075,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1091,7 +1091,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1107,7 +1107,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1135,7 +1135,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1151,7 +1151,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1167,7 +1167,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1229,7 +1229,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1272,7 +1272,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1315,7 +1315,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -1346,7 +1346,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1362,7 +1362,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1378,7 +1378,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1406,7 +1406,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1422,7 +1422,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1438,7 +1438,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1467,7 +1467,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1483,7 +1483,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1499,7 +1499,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) diff --git a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr index e55922a925665..84f8add167e10 100644 --- a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr +++ b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr @@ -74,7 +74,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -291,6 +292,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -306,6 +308,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -320,7 +323,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -991,7 +995,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE ("posthog_featureflag"."key" = 'copied-flag-key' @@ -1413,7 +1418,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -1656,6 +1662,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -1671,6 +1678,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -1685,7 +1693,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -2328,7 +2337,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE (NOT "posthog_featureflag"."deleted" @@ -2683,7 +2693,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" INNER JOIN "posthog_team" ON ("posthog_featureflag"."team_id" = "posthog_team"."id") WHERE ("posthog_featureflag"."deleted" @@ -2850,7 +2861,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE (NOT "posthog_featureflag"."deleted" AND "posthog_featureflag"."key" = 'key-1' diff --git a/posthog/api/test/__snapshots__/test_query.ambr b/posthog/api/test/__snapshots__/test_query.ambr index f9640b33a49a7..37b984cdfd4a0 100644 --- a/posthog/api/test/__snapshots__/test_query.ambr +++ b/posthog/api/test/__snapshots__/test_query.ambr @@ -450,7 +450,7 @@ 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), ifNull(equals(events__person.properties___email, 'tom@posthog.com'), 0), less(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-01-10 12:14:05.000000', 6, 'UTC')), greater(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-01-09 12:14:00.000000', 6, 'UTC'))) + WHERE and(equals(events.team_id, 99999), ifNull(equals(events__person.properties___email, 'tom@posthog.com'), 0), less(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greater(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) ORDER BY events.event ASC LIMIT 101 OFFSET 0 SETTINGS readonly=2, @@ -488,7 +488,7 @@ 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), ifNull(equals(events__person.properties___email, 'tom@posthog.com'), 0), less(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-01-10 12:14:05.000000', 6, 'UTC')), greater(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-01-09 12:14:00.000000', 6, 'UTC'))) + WHERE and(equals(events.team_id, 99999), ifNull(equals(events__person.properties___email, 'tom@posthog.com'), 0), less(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greater(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) ORDER BY events.event ASC LIMIT 101 OFFSET 0 SETTINGS readonly=2, diff --git a/posthog/api/test/__snapshots__/test_survey.ambr b/posthog/api/test/__snapshots__/test_survey.ambr index 1cfc5163a0fbe..de84017d5425c 100644 --- a/posthog/api/test/__snapshots__/test_survey.ambr +++ b/posthog/api/test/__snapshots__/test_survey.ambr @@ -313,6 +313,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -328,6 +329,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -342,7 +344,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -508,7 +511,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -737,6 +741,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -752,6 +757,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -766,7 +772,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -932,7 +939,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -1224,6 +1232,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -1239,6 +1248,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -1253,7 +1263,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -1634,6 +1645,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -1649,6 +1661,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -1663,7 +1676,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -1960,6 +1974,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -1975,6 +1990,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -1989,7 +2005,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -2069,6 +2086,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -2084,6 +2102,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -2098,7 +2117,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -2264,7 +2284,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" diff --git a/posthog/api/test/batch_exports/test_backfill.py b/posthog/api/test/batch_exports/test_backfill.py index 797db7ea93fd1..fca6bf3c34760 100644 --- a/posthog/api/test/batch_exports/test_backfill.py +++ b/posthog/api/test/batch_exports/test_backfill.py @@ -1,4 +1,5 @@ import datetime as dt +from unittest.mock import ANY, patch import pytest from django.test.client import Client as HttpClient @@ -6,21 +7,24 @@ from rest_framework import status from posthog.api.test.batch_exports.conftest import start_test_worker +from posthog.api.test.batch_exports.fixtures import create_organization from posthog.api.test.batch_exports.operations import ( backfill_batch_export, create_batch_export_ok, ) -from posthog.api.test.batch_exports.fixtures import create_organization from posthog.api.test.test_team import create_team from posthog.api.test.test_user import create_user +from posthog.models.person.util import create_person from posthog.temporal.common.client import sync_connect +from posthog.test.base import _create_event pytestmark = [ pytest.mark.django_db, ] -def test_batch_export_backfill(client: HttpClient): +@pytest.mark.parametrize("model", ["events", "persons"]) +def test_batch_export_backfill(client: HttpClient, model): """Test a BatchExport can be backfilled. We should be able to create a Batch Export, then request that the Schedule @@ -42,6 +46,7 @@ def test_batch_export_backfill(client: HttpClient): "name": "my-production-s3-bucket-destination", "destination": destination_data, "interval": "hour", + "model": model, } organization = create_organization("Test Org") @@ -49,6 +54,23 @@ def test_batch_export_backfill(client: HttpClient): user = create_user("test@user.com", "Test User", organization) client.force_login(user) + # ensure there is data to backfill, otherwise validation will fail + if model == "events": + _create_event( + team=team, + event="$pageview", + distinct_id="person_1", + timestamp=dt.datetime(2021, 1, 1, 0, 0, 0, tzinfo=dt.UTC), + ) + else: + create_person( + team_id=team.pk, + properties={"distinct_id": "1"}, + uuid=None, + version=0, + timestamp=dt.datetime(2021, 1, 1, 0, 0, 0, tzinfo=dt.UTC), + ) + with start_test_worker(temporal): batch_export = create_batch_export_ok(client, team.pk, batch_export_data) batch_export_id = batch_export["id"] @@ -349,6 +371,11 @@ def test_batch_export_backfill_created_in_timezone(client: HttpClient): user = create_user("test@user.com", "Test User", organization) client.force_login(user) + # ensure there is data to backfill, otherwise validation will fail + _create_event( + team=team, event="$pageview", distinct_id="person_1", timestamp=dt.datetime(2021, 1, 1, 0, 0, 0, tzinfo=dt.UTC) + ) + with start_test_worker(temporal): batch_export = create_batch_export_ok(client, team.pk, batch_export_data) batch_export_id = batch_export["id"] @@ -365,3 +392,166 @@ def test_batch_export_backfill_created_in_timezone(client: HttpClient): assert response.status_code == status.HTTP_200_OK, data assert data["backfill_id"] == f"{batch_export_id}-Backfill-2021-01-01T05:00:00+00:00-2021-10-01T04:00:00+00:00" + + +@pytest.mark.parametrize("model", ["events", "persons"]) +def test_batch_export_backfill_when_start_at_is_before_earliest_backfill_start_at(client: HttpClient, model): + """Test that a BatchExport backfill will use the earliest possible backfill start date if start_at is before this. + + For example if the timestamp of the earliest event is 2021-01-02T00:10:00+00:00, and the BatchExport is created with + a start_at of 2021-01-01T00:00:00+00:00, then the backfill will use 2021-01-02T00:00:00+00:00 as the start_at date. + """ + temporal = sync_connect() + + destination_data = { + "type": "S3", + "config": { + "bucket_name": "my-production-s3-bucket", + "region": "us-east-1", + "prefix": "posthog-events/", + "aws_access_key_id": "abc123", + "aws_secret_access_key": "secret", + }, + } + batch_export_data = { + "name": "my-production-s3-bucket-destination", + "destination": destination_data, + "interval": "day", + "model": model, + } + + organization = create_organization("Test Org") + team = create_team(organization) + user = create_user("test@user.com", "Test User", organization) + client.force_login(user) + + if model == "events": + _create_event( + team=team, + event="$pageview", + distinct_id="person_1", + timestamp=dt.datetime(2021, 1, 2, 0, 10, 0, tzinfo=dt.UTC), + ) + else: + create_person( + team_id=team.pk, + properties={"distinct_id": "1"}, + uuid=None, + version=0, + timestamp=dt.datetime(2021, 1, 2, 0, 10, 0, tzinfo=dt.UTC), + ) + + with start_test_worker(temporal): + batch_export = create_batch_export_ok(client, team.pk, batch_export_data) + batch_export_id = batch_export["id"] + with patch("posthog.batch_exports.http.backfill_export", return_value=batch_export_id) as mock_backfill_export: + response = backfill_batch_export( + client, + team.pk, + batch_export_id, + "2021-01-01T00:00:00+00:00", + "2021-01-03T00:00:00+00:00", + ) + assert response.status_code == status.HTTP_200_OK, response.json() + + mock_backfill_export.assert_called_with( + ANY, # temporal instance will be a different object + batch_export_id, + team.pk, + dt.datetime.fromisoformat("2021-01-02T00:00:00+00:00").astimezone(team.timezone_info), + dt.datetime.fromisoformat("2021-01-03T00:00:00+00:00").astimezone(team.timezone_info), + ) + + +def test_batch_export_backfill_when_backfill_end_at_is_before_earliest_event(client: HttpClient): + """Test a BatchExport backfill fails if the end_at is before the earliest event. + + In this case, we know that the backfill range doesn't contain any data, so we can fail fast. + + For example if the timestamp of the earliest event is 2021-01-03T00:10:00+00:00, and the BatchExport is created with a + start_at of 2021-01-01T00:00:00+00:00 and an end_at of 2021-01-02T00:00:00+00:00, then the backfill will fail. + """ + temporal = sync_connect() + + destination_data = { + "type": "S3", + "config": { + "bucket_name": "my-production-s3-bucket", + "region": "us-east-1", + "prefix": "posthog-events/", + "aws_access_key_id": "abc123", + "aws_secret_access_key": "secret", + }, + } + batch_export_data = { + "name": "my-production-s3-bucket-destination", + "destination": destination_data, + "interval": "day", + } + + organization = create_organization("Test Org") + team = create_team(organization) + user = create_user("test@user.com", "Test User", organization) + client.force_login(user) + + _create_event( + team=team, event="$pageview", distinct_id="person_1", timestamp=dt.datetime(2021, 1, 3, 0, 10, 0, tzinfo=dt.UTC) + ) + with start_test_worker(temporal): + batch_export = create_batch_export_ok(client, team.pk, batch_export_data) + batch_export_id = batch_export["id"] + with patch("posthog.batch_exports.http.backfill_export", return_value=batch_export_id): + response = backfill_batch_export( + client, + team.pk, + batch_export_id, + "2021-01-01T00:00:00+00:00", + "2021-01-02T00:00:00+00:00", + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST, response.json() + assert ( + response.json()["detail"] + == "The provided backfill date range contains no data. The earliest possible backfill start date is 2021-01-03 00:00:00" + ) + + +@pytest.mark.parametrize("model", ["events", "persons"]) +def test_batch_export_backfill_when_no_data_exists(client: HttpClient, model): + """Test a BatchExport backfill fails if no data exists for the given model.""" + temporal = sync_connect() + + destination_data = { + "type": "S3", + "config": { + "bucket_name": "my-production-s3-bucket", + "region": "us-east-1", + "prefix": "posthog-events/", + "aws_access_key_id": "abc123", + "aws_secret_access_key": "secret", + }, + } + batch_export_data = { + "name": "my-production-s3-bucket-destination", + "destination": destination_data, + "interval": "day", + "model": model, + } + + organization = create_organization("Test Org") + team = create_team(organization) + user = create_user("test@user.com", "Test User", organization) + client.force_login(user) + + with start_test_worker(temporal): + batch_export = create_batch_export_ok(client, team.pk, batch_export_data) + batch_export_id = batch_export["id"] + with patch("posthog.batch_exports.http.backfill_export", return_value=batch_export_id): + response = backfill_batch_export( + client, + team.pk, + batch_export_id, + "2021-01-01T00:00:00+00:00", + "2021-01-02T00:00:00+00:00", + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST, response.json() + assert response.json()["detail"] == "There is no data to backfill for this model." diff --git a/posthog/api/test/batch_exports/test_delete.py b/posthog/api/test/batch_exports/test_delete.py index f5b303538707a..a657be2659974 100644 --- a/posthog/api/test/batch_exports/test_delete.py +++ b/posthog/api/test/batch_exports/test_delete.py @@ -1,4 +1,5 @@ import asyncio +import datetime as dt import pytest import temporalio.client @@ -20,6 +21,7 @@ from posthog.api.test.test_user import create_user from posthog.temporal.common.client import sync_connect from posthog.temporal.common.schedule import describe_schedule +from posthog.test.base import _create_event pytestmark = [ pytest.mark.django_db, @@ -141,6 +143,14 @@ def test_delete_batch_export_cancels_backfills(client: HttpClient): start_at = "2023-10-23T00:00:00+00:00" end_at = "2023-10-24T00:00:00+00:00" + + # ensure there is data to backfill, otherwise validation will fail + _create_event( + team=team, + event="$pageview", + distinct_id="person_1", + timestamp=dt.datetime(2023, 10, 23, 0, 0, 0, tzinfo=dt.UTC), + ) batch_export_backfill = backfill_batch_export_ok(client, team.pk, batch_export_id, start_at, end_at) # In order for the backfill to be cancelable, it needs to be running and requesting backfills. diff --git a/posthog/api/test/test_capture.py b/posthog/api/test/test_capture.py index 3e0a522c3404c..2329b05eaf5ec 100644 --- a/posthog/api/test/test_capture.py +++ b/posthog/api/test/test_capture.py @@ -371,7 +371,10 @@ def _do_test_capture_with_likely_anonymous_ids(self, kafka_produce, expect_rando topic=KAFKA_EVENTS_PLUGIN_INGESTION_TOPIC, data=ANY, key=None if expect_random_partitioning else ANY, - headers=None, + headers=[ + ("token", self.team.api_token), + ("distinct_id", distinct_id), + ], ) if not expect_random_partitioning: @@ -1916,10 +1919,11 @@ def test_recording_ingestion_can_write_headers_with_the_message(self, kafka_prod with self.settings( SESSION_RECORDING_KAFKA_MAX_REQUEST_SIZE_BYTES=20480, ): - self._send_august_2023_version_session_recording_event() + self._send_august_2023_version_session_recording_event(distinct_id="distinct_id123") assert kafka_produce.mock_calls[0].kwargs["headers"] == [ ("token", "token123"), + ("distinct_id", "distinct_id123"), ( # without setting a version in the URL the default is unknown "lib_version", @@ -1932,10 +1936,13 @@ def test_recording_ingestion_can_read_version_from_request(self, kafka_produce: with self.settings( SESSION_RECORDING_KAFKA_MAX_REQUEST_SIZE_BYTES=20480, ): - self._send_august_2023_version_session_recording_event(query_params="ver=1.123.4") + self._send_august_2023_version_session_recording_event( + query_params="ver=1.123.4", distinct_id="distinct_id123" + ) assert kafka_produce.mock_calls[0].kwargs["headers"] == [ ("token", "token123"), + ("distinct_id", "distinct_id123"), ( # without setting a version in the URL the default is unknown "lib_version", diff --git a/posthog/api/test/test_cohort.py b/posthog/api/test/test_cohort.py index 0bd10e02a46e0..598e67fb3eedc 100644 --- a/posthog/api/test/test_cohort.py +++ b/posthog/api/test/test_cohort.py @@ -298,8 +298,8 @@ def test_static_cohort_csv_upload(self, patch_calculate_cohort_from_list): content_type="application/csv", ) - #  A weird issue with pytest client, need to user Rest framework's one - #  see https://stackoverflow.com/questions/39906956/patch-and-put-dont-work-as-expected-when-pytest-is-interacting-with-rest-framew + # A weird issue with pytest client, need to user Rest framework's one + # see https://stackoverflow.com/questions/39906956/patch-and-put-dont-work-as-expected-when-pytest-is-interacting-with-rest-framew client = APIClient() client.force_login(self.user) response = client.patch( @@ -396,6 +396,116 @@ def test_cohort_list_with_search(self): response = self.client.get(f"/api/projects/{self.team.id}/cohorts?search=nomatch").json() self.assertEqual(len(response["results"]), 0) + @patch("posthog.api.cohort.report_user_action") + def test_list_cohorts_excludes_behavioral_cohorts(self, patch_capture): + # Create a regular cohort + regular_cohort = Cohort.objects.create( + team=self.team, + name="regular cohort", + filters={ + "properties": { + "type": "OR", + "values": [{"type": "person", "key": "email", "value": "test@posthog.com"}], + } + }, + ) + + # Create a behavioral cohort + Cohort.objects.create( + team=self.team, + name="behavioral cohort", + filters={ + "properties": { + "type": "OR", + "values": [ + { + "type": "OR", + "values": [ + { + "type": "behavioral", + "key": "$pageview", + "value": "performed_event", + "event_type": "events", + "time_value": 30, + "time_interval": "day", + } + ], + } + ], + } + }, + ) + + # Test without filter + response = self.client.get(f"/api/projects/{self.team.id}/cohorts") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.json()["results"]), 2) + + # Test with behavioral filter + response = self.client.get(f"/api/projects/{self.team.id}/cohorts?hide_behavioral_cohorts=true") + self.assertEqual(response.status_code, status.HTTP_200_OK) + results = response.json()["results"] + self.assertEqual(len(results), 1) + self.assertEqual(results[0]["id"], regular_cohort.id) + + @patch("posthog.api.cohort.report_user_action") + def test_list_cohorts_excludes_nested_behavioral_cohorts(self, patch_capture): + # Create a behavioral cohort + behavioral_cohort = Cohort.objects.create( + team=self.team, + name="behavioral cohort", + filters={ + "properties": { + "type": "OR", + "values": [ + { + "type": "behavioral", + "key": "$pageview", + "value": "performed_event", + "event_type": "events", + "time_value": 30, + "time_interval": "day", + } + ], + } + }, + ) + + # Create a cohort that references the behavioral cohort + Cohort.objects.create( + team=self.team, + name="cohort with nested behavioral", + filters={ + "properties": { + "type": "OR", + "values": [ + { + "type": "cohort", + "value": str(behavioral_cohort.pk), + } + ], + } + }, + ) + + # Create a regular cohort + regular_cohort = Cohort.objects.create( + team=self.team, + name="regular cohort not behavioral", + filters={ + "properties": { + "type": "OR", + "values": [{"type": "person", "key": "email", "value": "test@posthog.com"}], + } + }, + ) + + response = self.client.get(f"/api/projects/{self.team.id}/cohorts?hide_behavioral_cohorts=true") + self.assertEqual(response.status_code, status.HTTP_200_OK) + results = response.json()["results"] + self.assertEqual(len(results), 1) + self.assertEqual(results[0]["id"], regular_cohort.id) + def test_cohort_activity_log(self): self.team.app_urls = ["http://somewebsite.com"] self.team.save() diff --git a/posthog/api/test/test_data_color_theme.py b/posthog/api/test/test_data_color_theme.py index 94543450f584f..b90102245208c 100644 --- a/posthog/api/test/test_data_color_theme.py +++ b/posthog/api/test/test_data_color_theme.py @@ -1,6 +1,7 @@ from rest_framework import status from posthog.api.data_color_theme import DataColorTheme +from posthog.constants import AvailableFeature from posthog.models.organization import Organization from posthog.models.team.team import Team from posthog.test.base import APIBaseTest @@ -26,6 +27,11 @@ def test_can_fetch_own_themes(self) -> None: assert response.data[1]["name"] == "Custom theme 1" def test_can_edit_own_themes(self) -> None: + self.organization.available_product_features = [ + {"key": AvailableFeature.DATA_COLOR_THEMES, "name": AvailableFeature.DATA_COLOR_THEMES} + ] + self.organization.save() + theme = DataColorTheme.objects.create(name="Original name", colors=[], team=self.team) response = self.client.patch( @@ -35,6 +41,17 @@ def test_can_edit_own_themes(self) -> None: assert response.status_code == status.HTTP_200_OK assert DataColorTheme.objects.get(pk=theme.pk).name == "New name" + def test_can_not_edit_own_themes_when_feature_disabled(self) -> None: + theme = DataColorTheme.objects.create(name="Original name", colors=[], team=self.team) + + response = self.client.patch( + f"/api/environments/{self.team.pk}/data_color_themes/{theme.pk}", {"name": "New name"} + ) + + assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.json()["detail"] == "This feature is only available on paid plans." + assert DataColorTheme.objects.get(pk=theme.pk).name == "Original name" + def test_can_not_edit_public_themes(self) -> None: theme = DataColorTheme.objects.first() assert theme @@ -44,6 +61,7 @@ def test_can_not_edit_public_themes(self) -> None: ) assert response.status_code == status.HTTP_403_FORBIDDEN + assert response.json()["detail"] == "Only staff users can edit global themes." assert DataColorTheme.objects.get(pk=theme.pk).name == "Default Theme" def test_can_edit_public_themes_as_staff(self) -> None: diff --git a/posthog/api/test/test_error_tracking.py b/posthog/api/test/test_error_tracking.py index 26af809cb5d2e..38dbd9981e812 100644 --- a/posthog/api/test/test_error_tracking.py +++ b/posthog/api/test/test_error_tracking.py @@ -1,10 +1,8 @@ import os -import json from boto3 import resource from rest_framework import status -from django.utils.http import urlsafe_base64_encode from django.test import override_settings from django.core.files.uploadedfile import SimpleUploadedFile @@ -15,6 +13,7 @@ ErrorTrackingStackFrame, ErrorTrackingIssue, ErrorTrackingIssueAssignment, + ErrorTrackingIssueFingerprintV2, ) from botocore.config import Config from posthog.settings import ( @@ -33,6 +32,13 @@ def get_path_to(fixture_file: str) -> str: class TestErrorTracking(APIBaseTest): + def create_issue(self, fingerprints=None) -> ErrorTrackingIssue: + issue = ErrorTrackingIssue.objects.create(team=self.team) + fingerprints = fingerprints if fingerprints else [] + for fingerprint in fingerprints: + ErrorTrackingIssueFingerprintV2.objects.create(team=self.team, issue=issue, fingerprint=fingerprint) + return issue + def teardown_method(self, method) -> None: s3 = resource( "s3", @@ -45,59 +51,33 @@ def teardown_method(self, method) -> None: bucket = s3.Bucket(OBJECT_STORAGE_BUCKET) bucket.objects.filter(Prefix=TEST_BUCKET).delete() - def send_request(self, fingerprint, data, endpoint=""): - base64_fingerprint = urlsafe_base64_encode(json.dumps(fingerprint).encode("utf-8")) - request_method = self.client.patch if endpoint == "" else self.client.post - request_method( - f"/api/projects/{self.team.id}/error_tracking/{base64_fingerprint}/{endpoint}", - data=data, - ) - - # def test_reuses_existing_group_for_team(self): - # fingerprint = ["CustomFingerprint"] - # ErrorTrackingGroup.objects.create(fingerprint=fingerprint, team=self.team) - - # self.assertEqual(ErrorTrackingGroup.objects.count(), 1) - # self.send_request(fingerprint, {"assignee": self.user.id}) - # self.assertEqual(ErrorTrackingGroup.objects.count(), 1) - - # def test_creates_group_if_not_already_existing_for_team(self): - # fingerprint = ["CustomFingerprint"] - # other_team = Team.objects.create(organization=self.organization) - # ErrorTrackingGroup.objects.create(fingerprint=fingerprint, team=other_team) + def test_issue_update(self): + issue = self.create_issue() - # self.assertEqual(ErrorTrackingGroup.objects.count(), 1) - # self.send_request(fingerprint, {"assignee": self.user.id}) - # self.assertEqual(ErrorTrackingGroup.objects.count(), 2) - - # def test_can_only_update_allowed_fields(self): - # fingerprint = ["CustomFingerprint"] - # other_team = Team.objects.create(organization=self.organization) - # group = ErrorTrackingGroup.objects.create(fingerprint=fingerprint, team=other_team) - - # self.send_request(fingerprint, {"fingerprint": ["NewFingerprint"], "assignee": self.user.id}) - # group.refresh_from_db() - # self.assertEqual(group.fingerprint, ["CustomFingerprint"]) + response = self.client.patch( + f"/api/projects/{self.team.id}/error_tracking/issue/{issue.id}", data={"status": "resolved"} + ) + issue.refresh_from_db() - # def test_merging_of_an_existing_group(self): - # fingerprint = ["CustomFingerprint"] - # merging_fingerprints = [["NewFingerprint"]] - # group = ErrorTrackingGroup.objects.create(fingerprint=fingerprint, team=self.team) + assert response.status_code == 200 + assert response.json() == {"status": "resolved"} + assert issue.status == ErrorTrackingIssue.Status.RESOLVED - # self.send_request(fingerprint, {"merging_fingerprints": merging_fingerprints}, endpoint="merge") + def test_issue_merge(self): + issue_one = self.create_issue(fingerprints=["fingerprint_one"]) + issue_two = self.create_issue(fingerprints=["fingerprint_two"]) - # group.refresh_from_db() - # self.assertEqual(group.merged_fingerprints, merging_fingerprints) + assert ErrorTrackingIssue.objects.count() == 2 - # def test_merging_when_no_group_exists(self): - # fingerprint = ["CustomFingerprint"] - # merging_fingerprints = [["NewFingerprint"]] + repsonse = self.client.post( + f"/api/projects/{self.team.id}/error_tracking/issue/{issue_one.id}/merge", data={"ids": [issue_two.id]} + ) - # self.assertEqual(ErrorTrackingGroup.objects.count(), 0) - # self.send_request(fingerprint, {"merging_fingerprints": merging_fingerprints}, endpoint="merge") - # self.assertEqual(ErrorTrackingGroup.objects.count(), 1) - # groups = ErrorTrackingGroup.objects.only("merged_fingerprints") - # self.assertEqual(groups[0].merged_fingerprints, merging_fingerprints) + assert repsonse.status_code == 200 + assert ErrorTrackingIssueFingerprintV2.objects.filter(issue_id=issue_one.id).count() == 2 + assert ErrorTrackingIssueFingerprintV2.objects.filter(fingerprint="fingerprint_one", version=0).exists() + assert ErrorTrackingIssueFingerprintV2.objects.filter(fingerprint="fingerprint_two", version=1).exists() + assert ErrorTrackingIssue.objects.count() == 1 def test_can_upload_a_source_map(self) -> None: with self.settings(OBJECT_STORAGE_ENABLED=True, OBJECT_STORAGE_ERROR_TRACKING_SOURCE_MAPS_FOLDER=TEST_BUCKET): @@ -201,7 +181,7 @@ def test_fetching_stack_frames(self): self.assertEqual(response.json()["results"][0]["symbol_set_ref"], symbol_set.ref) def test_assigning_issues(self): - issue = ErrorTrackingIssue.objects.create(team=self.team) + issue = self.create_issue() self.assertEqual(ErrorTrackingIssueAssignment.objects.count(), 0) self.client.patch( diff --git a/posthog/api/test/test_organization_feature_flag.py b/posthog/api/test/test_organization_feature_flag.py index 29d6e6054a5a5..f3d2e623b0cc8 100644 --- a/posthog/api/test/test_organization_feature_flag.py +++ b/posthog/api/test/test_organization_feature_flag.py @@ -130,6 +130,7 @@ def test_copy_feature_flag_create_new(self): "has_enriched_analytics": False, "tags": [], "user_access_level": "editor", + "is_remote_configuration": False, } flag_response = response.json()["success"][0] @@ -207,6 +208,7 @@ def test_copy_feature_flag_update_existing(self): "features": ANY, "analytics_dashboards": ANY, "user_access_level": "editor", + "is_remote_configuration": False, } flag_response = response.json()["success"][0] @@ -328,6 +330,7 @@ def test_copy_feature_flag_update_override_deleted(self): "features": ANY, "analytics_dashboards": ANY, "user_access_level": "editor", + "is_remote_configuration": False, } flag_response = response.json()["success"][0] diff --git a/posthog/batch_exports/http.py b/posthog/batch_exports/http.py index c3929503635b2..c5675b7e780ce 100644 --- a/posthog/batch_exports/http.py +++ b/posthog/batch_exports/http.py @@ -1,11 +1,11 @@ import datetime as dt from typing import Any, TypedDict, cast -from loginas.utils import is_impersonated_session import posthoganalytics import structlog from django.db import transaction from django.utils.timezone import now +from loginas.utils import is_impersonated_session from rest_framework import filters, request, response, serializers, viewsets from rest_framework.exceptions import ( NotAuthenticated, @@ -28,6 +28,7 @@ backfill_export, cancel_running_batch_export_run, disable_and_delete_export, + fetch_earliest_backfill_start_at, pause_batch_export, sync_batch_export, unpause_batch_export, @@ -422,6 +423,34 @@ def backfill(self, request: request.Request, *args, **kwargs) -> response.Respon else: end_at = None + if start_at is not None or end_at is not None: + earliest_backfill_start_at = fetch_earliest_backfill_start_at( + team_id=self.team_id, + model=batch_export.model, + interval_time_delta=batch_export.interval_time_delta, + exclude_events=batch_export.destination.config.get("exclude_events", []), + include_events=batch_export.destination.config.get("include_events", []), + ) + if earliest_backfill_start_at is None: + raise ValidationError("There is no data to backfill for this model.") + + earliest_backfill_start_at = earliest_backfill_start_at.astimezone(self.team.timezone_info) + + if end_at is not None and end_at < earliest_backfill_start_at: + raise ValidationError( + "The provided backfill date range contains no data. The earliest possible backfill start date is " + f"{earliest_backfill_start_at.strftime('%Y-%m-%d %H:%M:%S')}", + ) + + if start_at is not None and start_at < earliest_backfill_start_at: + logger.info( + "Backfill start_at '%s' is before the earliest possible backfill start_at '%s', setting start_at " + "to earliest_backfill_start_at", + start_at, + earliest_backfill_start_at, + ) + start_at = earliest_backfill_start_at + if start_at is None or end_at is None: backfill_id = backfill_export(temporal, str(batch_export.pk), self.team_id, start_at, end_at) return response.Response({"backfill_id": backfill_id}) diff --git a/posthog/batch_exports/service.py b/posthog/batch_exports/service.py index f38e37fef7df5..40d16cc566b39 100644 --- a/posthog/batch_exports/service.py +++ b/posthog/batch_exports/service.py @@ -1,3 +1,4 @@ +import collections.abc import datetime as dt import typing from dataclasses import asdict, dataclass, fields @@ -24,6 +25,7 @@ BatchExportDestination, BatchExportRun, ) +from posthog.clickhouse.client import sync_execute from posthog.constants import BATCH_EXPORTS_TASK_QUEUE, SYNC_BATCH_EXPORTS_TASK_QUEUE from posthog.temporal.common.client import sync_connect from posthog.temporal.common.schedule import ( @@ -847,3 +849,71 @@ async def afetch_batch_export_runs_in_range( ).order_by("data_interval_start") return [run async for run in queryset] + + +def fetch_earliest_backfill_start_at( + *, + team_id: int, + model: str, + interval_time_delta: dt.timedelta, + exclude_events: collections.abc.Iterable[str] | None = None, + include_events: collections.abc.Iterable[str] | None = None, +) -> dt.datetime | None: + """Get the earliest start_at for a batch export backfill. + + If there is no data for the given model, return None. + """ + interval_seconds = int(interval_time_delta.total_seconds()) + if model == "events": + exclude_events = exclude_events or [] + include_events = include_events or [] + query = """ + SELECT toStartOfInterval(MIN(timestamp), INTERVAL %(interval_seconds)s SECONDS) + FROM events + WHERE team_id = %(team_id)s + AND (length(%(include_events)s::Array(String)) = 0 OR event IN %(include_events)s::Array(String)) + AND (length(%(exclude_events)s::Array(String)) = 0 OR event NOT IN %(exclude_events)s::Array(String)) + """ + query_args = { + "team_id": team_id, + "include_events": include_events, + "exclude_events": exclude_events, + "interval_seconds": interval_seconds, + } + result = sync_execute(query, query_args)[0][0] + # if no data, ClickHouse returns 1970-01-01 00:00:00 + # (we just compare the year rather than the whole object because in some cases the timestamp returned by + # ClickHouse has a timezone and sometimes it doesn't) + if result.year == 1970: + return None + return result + elif model == "persons": + # In the case of persons, we need to check 2 tables: person and person_distinct_id2 + # It's more efficient querying both tables separately and taking the minimum timestamp, rather than trying to + # join them together. + # In some cases we might have invalid timestamps, so we use an arbitrary date in the past to filter these out. + query = """ + SELECT toStartOfInterval(MIN(_timestamp), INTERVAL %(interval_seconds)s SECONDS) + FROM person + WHERE team_id = %(team_id)s + AND _timestamp > '2000-01-01' + UNION ALL + SELECT toStartOfInterval(MIN(_timestamp), INTERVAL %(interval_seconds)s SECONDS) + FROM person_distinct_id2 + WHERE team_id = %(team_id)s + AND _timestamp > '2000-01-01' + """ + query_args = { + "team_id": team_id, + "interval_seconds": interval_seconds, + } + results = sync_execute(query, query_args) + # if no data, ClickHouse returns 1970-01-01 00:00:00 + # (we just compare the year rather than the whole object because in some cases the timestamp returned by + # ClickHouse has a timezone and sometimes it doesn't) + results = [result[0] for result in results if result[0].year != 1970] + if not results: + return None + return min(results) + else: + raise ValueError(f"Invalid model: {model}") diff --git a/posthog/constants.py b/posthog/constants.py index 73a46e10f2476..514be977dec81 100644 --- a/posthog/constants.py +++ b/posthog/constants.py @@ -38,6 +38,7 @@ class AvailableFeature(StrEnum): MANAGED_REVERSE_PROXY = "managed_reverse_proxy" DATA_PIPELINES = "data_pipelines" ALERTS = "alerts" + DATA_COLOR_THEMES = "data_color_themes" TREND_FILTER_TYPE_ACTIONS = "actions" diff --git a/posthog/demo/matrix/models.py b/posthog/demo/matrix/models.py index 33590d57407dc..3b880c965b200 100644 --- a/posthog/demo/matrix/models.py +++ b/posthog/demo/matrix/models.py @@ -1,3 +1,4 @@ +from contextlib import contextmanager import datetime as dt from abc import ABC, abstractmethod from collections import defaultdict @@ -5,22 +6,14 @@ from dataclasses import dataclass from enum import Enum, auto from itertools import chain -from typing import ( - TYPE_CHECKING, - Any, - Generic, - Literal, - Optional, - TypeVar, -) +from typing import TYPE_CHECKING, Any, Generic, Literal, Optional, TypeVar +from collections.abc import Generator from collections.abc import Callable, Iterable from urllib.parse import urlparse, parse_qs -from uuid import UUID +from uuid import UUID, uuid4 import tiktoken -from posthog.models.utils import uuid7 - if TYPE_CHECKING: from posthog.demo.matrix.matrix import Cluster, Matrix @@ -28,10 +21,8 @@ # Refer to https://github.com/PostHog/posthog-ai-costs-app/tree/main/src/ai-cost-data for missing models LLM_COSTS_BY_MODEL = { - "gpt-4o": { - "prompt_token": 0.000005, - "completion_token": 0.000015, - }, + "gpt-4o": {"prompt_token": 2.5 / 1e6, "completion_token": 10 / 1e6}, + "gpt-4o-mini": {"prompt_token": 0.15 / 1e6, "completion_token": 0.6 / 1e6}, } SP = TypeVar("SP", bound="SimPerson") @@ -206,11 +197,43 @@ def capture_ai_generation( ] }, "$ai_latency": latency, - "$ai_trace_id": trace_id or str(uuid7()), + "$ai_trace_id": trace_id or str(uuid4()), }, distinct_id=distinct_id, ) + @contextmanager + def trace_ai( + self, + *, + distinct_id: str, + input_state: Any, + trace_id: Optional[str] = None, + ) -> Generator[tuple[str, Callable], None, None]: + """Capture an AI generation event.""" + trace_id = trace_id or str(uuid4()) + output_state = None + + def set_trace_output(output: Any): + nonlocal output_state + if output_state is not None: + raise ValueError("Output already set for this trace") + output_state = output + + try: + yield trace_id, set_trace_output + finally: + self.capture( + "$ai_trace", + { + "$ai_input_state": input_state, + "$ai_output_state": output_state, + "$ai_trace_name": "SpikeChain", + "$ai_trace_id": trace_id, + }, + distinct_id=distinct_id, + ) + class SimBrowserClient(SimClient): """A browser client for simulating client-side tracking.""" diff --git a/posthog/demo/products/spikegpt/models.py b/posthog/demo/products/spikegpt/models.py index f53402322b311..91bb6328a4f17 100644 --- a/posthog/demo/products/spikegpt/models.py +++ b/posthog/demo/products/spikegpt/models.py @@ -148,14 +148,44 @@ def start_chat(self): self.advance_timer(2 + len(message["content"]) / 10) self.active_client.capture("sent chat message", {"content": message["content"]}) else: - generation_time = len(message["content"]) / 25 - self.advance_timer(1 + generation_time) - self.cluster.matrix.server_client.capture_ai_generation( - distinct_id=self.active_client.active_distinct_id, - input=conversation_so_far, - output_content=message["content"], - latency=generation_time, - ) + with self.cluster.matrix.server_client.trace_ai( + distinct_id=self.active_client.active_distinct_id, input_state={"messages": conversation_so_far} + ) as (trace_id, set_trace_output): + # Chat generation + generation_time = len(message["content"]) / 25 + self.advance_timer(1 + generation_time) + self.cluster.matrix.server_client.capture_ai_generation( + distinct_id=self.active_client.active_distinct_id, + input=conversation_so_far, + output_content=message["content"], + latency=generation_time, + trace_id=trace_id, + ) + # Memorizer, which determines what memories to save using tool calling + generation_time = len(message["content"]) / 37 + self.advance_timer(1 + generation_time) + self.cluster.matrix.server_client.capture_ai_generation( + distinct_id=self.active_client.active_distinct_id, + input=[ + { + "role": "system", + "content": """ +Your task is to determine if there's something worth remembering about the user from the following conversation. +Use the "update_memory" tool for each piece of information worth adding to your memory. The user said:""".strip(), + }, + { + "role": "human", + "content": f''' +My message is:\n{message["content"]}\n\nWhat should you remember from this? +Output only the concise information to memorize, prefixed with "REMEMBER: "'''.strip(), + }, + ], + output_content="REMEMBER: Blah blah blah.", + latency=generation_time, + model="gpt-4o-mini", + trace_id=trace_id, + ) + set_trace_output({"messages": [*conversation_so_far, message], "memories": ["Blah blah blah."]}) conversation_so_far = [*conversation_so_far, message] # Copying here so that every event's list is distinct diff --git a/posthog/hogql/database/database.py b/posthog/hogql/database/database.py index 7c746ecf2e73a..c014099691818 100644 --- a/posthog/hogql/database/database.py +++ b/posthog/hogql/database/database.py @@ -586,7 +586,7 @@ def serialize_database( warehouse_schemas = ( list( ExternalDataSchema.objects.exclude(deleted=True) - .filter(table_id__in=[table.id for table in warehouse_tables]) + .filter(team_id=context.team_id, table_id__in=[table.id for table in warehouse_tables]) .all() ) if len(warehouse_tables) > 0 diff --git a/posthog/hogql/database/schema/test/__snapshots__/test_session_replay_events.ambr b/posthog/hogql/database/schema/test/__snapshots__/test_session_replay_events.ambr index a76a0571deeec..b23db7391b143 100644 --- a/posthog/hogql/database/schema/test/__snapshots__/test_session_replay_events.ambr +++ b/posthog/hogql/database/schema/test/__snapshots__/test_session_replay_events.ambr @@ -166,7 +166,7 @@ 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) PREWHERE greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), minus(toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC'), toIntervalDay(90))) + 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) PREWHERE greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), minus(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'), toIntervalDay(90))) WHERE equals(events.team_id, 99999)) AS raw_session_replay_events__events ON equals(session_replay_events.session_id, raw_session_replay_events__events.`$session_id`) LEFT JOIN (SELECT person.id AS id, diff --git a/posthog/hogql/grammar/HogQLParser.g4 b/posthog/hogql/grammar/HogQLParser.g4 index 49ad52a07c061..7734d3bc04102 100644 --- a/posthog/hogql/grammar/HogQLParser.g4 +++ b/posthog/hogql/grammar/HogQLParser.g4 @@ -222,9 +222,10 @@ columnLambdaExpr: ; +hogqlxChildElement: hogqlxTagElement | (LBRACE columnExpr RBRACE); hogqlxTagElement - : LT identifier hogqlxTagAttribute* SLASH GT # HogqlxTagElementClosed - | LT identifier hogqlxTagAttribute* GT (hogqlxTagElement | (LBRACE columnExpr RBRACE))? LT SLASH identifier GT # HogqlxTagElementNested + : LT identifier hogqlxTagAttribute* SLASH GT # HogqlxTagElementClosed + | LT identifier hogqlxTagAttribute* GT hogqlxChildElement* LT SLASH identifier GT # HogqlxTagElementNested ; hogqlxTagAttribute : identifier '=' string diff --git a/posthog/hogql/grammar/HogQLParser.interp b/posthog/hogql/grammar/HogQLParser.interp index 1d049b6b744a8..996d438b4aa59 100644 --- a/posthog/hogql/grammar/HogQLParser.interp +++ b/posthog/hogql/grammar/HogQLParser.interp @@ -390,6 +390,7 @@ columnTypeExpr columnExprList columnExpr columnLambdaExpr +hogqlxChildElement hogqlxTagElement hogqlxTagAttribute withExprList @@ -419,4 +420,4 @@ stringContentsFull atn: -[4, 1, 162, 1317, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 1, 0, 5, 0, 176, 8, 0, 10, 0, 12, 0, 179, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 185, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 194, 8, 3, 1, 4, 1, 4, 1, 4, 5, 4, 199, 8, 4, 10, 4, 12, 4, 202, 9, 4, 1, 4, 3, 4, 205, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 219, 8, 5, 1, 6, 1, 6, 3, 6, 223, 8, 6, 1, 6, 3, 6, 226, 8, 6, 1, 7, 1, 7, 3, 7, 230, 8, 7, 1, 7, 3, 7, 233, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 240, 8, 8, 1, 8, 1, 8, 3, 8, 244, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 5, 9, 251, 8, 9, 10, 9, 12, 9, 254, 9, 9, 1, 9, 1, 9, 3, 9, 258, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 267, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 275, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 282, 8, 12, 1, 12, 1, 12, 3, 12, 286, 8, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 292, 8, 12, 1, 12, 1, 12, 1, 12, 3, 12, 297, 8, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 305, 8, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 312, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 318, 8, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 3, 16, 330, 8, 16, 1, 17, 1, 17, 1, 18, 1, 18, 5, 18, 336, 8, 18, 10, 18, 12, 18, 339, 9, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 5, 20, 350, 8, 20, 10, 20, 12, 20, 353, 9, 20, 1, 20, 3, 20, 356, 8, 20, 1, 21, 1, 21, 1, 21, 3, 21, 361, 8, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 371, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 381, 8, 23, 1, 23, 1, 23, 1, 24, 1, 24, 5, 24, 387, 8, 24, 10, 24, 12, 24, 390, 9, 24, 1, 25, 3, 25, 393, 8, 25, 1, 25, 1, 25, 3, 25, 397, 8, 25, 1, 25, 3, 25, 400, 8, 25, 1, 25, 1, 25, 3, 25, 404, 8, 25, 1, 25, 3, 25, 407, 8, 25, 1, 25, 3, 25, 410, 8, 25, 1, 25, 3, 25, 413, 8, 25, 1, 25, 3, 25, 416, 8, 25, 1, 25, 1, 25, 3, 25, 420, 8, 25, 1, 25, 1, 25, 3, 25, 424, 8, 25, 1, 25, 3, 25, 427, 8, 25, 1, 25, 3, 25, 430, 8, 25, 1, 25, 3, 25, 433, 8, 25, 1, 25, 1, 25, 3, 25, 437, 8, 25, 1, 25, 3, 25, 440, 8, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 449, 8, 27, 1, 28, 1, 28, 1, 28, 1, 29, 3, 29, 455, 8, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 474, 8, 30, 10, 30, 12, 30, 477, 9, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 493, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 510, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 516, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 522, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 533, 8, 37, 3, 37, 535, 8, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 3, 40, 546, 8, 40, 1, 40, 3, 40, 549, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 555, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 563, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 5, 40, 569, 8, 40, 10, 40, 12, 40, 572, 9, 40, 1, 41, 3, 41, 575, 8, 41, 1, 41, 1, 41, 1, 41, 3, 41, 580, 8, 41, 1, 41, 3, 41, 583, 8, 41, 1, 41, 3, 41, 586, 8, 41, 1, 41, 1, 41, 3, 41, 590, 8, 41, 1, 41, 1, 41, 3, 41, 594, 8, 41, 1, 41, 3, 41, 597, 8, 41, 3, 41, 599, 8, 41, 1, 41, 3, 41, 602, 8, 41, 1, 41, 1, 41, 3, 41, 606, 8, 41, 1, 41, 1, 41, 3, 41, 610, 8, 41, 1, 41, 3, 41, 613, 8, 41, 3, 41, 615, 8, 41, 3, 41, 617, 8, 41, 1, 42, 1, 42, 1, 42, 3, 42, 622, 8, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 633, 8, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 639, 8, 44, 1, 45, 1, 45, 1, 45, 5, 45, 644, 8, 45, 10, 45, 12, 45, 647, 9, 45, 1, 46, 1, 46, 3, 46, 651, 8, 46, 1, 46, 1, 46, 3, 46, 655, 8, 46, 1, 46, 1, 46, 3, 46, 659, 8, 46, 1, 47, 1, 47, 1, 47, 1, 47, 3, 47, 665, 8, 47, 3, 47, 667, 8, 47, 1, 48, 1, 48, 1, 48, 5, 48, 672, 8, 48, 10, 48, 12, 48, 675, 9, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 3, 50, 682, 8, 50, 1, 50, 3, 50, 685, 8, 50, 1, 50, 3, 50, 688, 8, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 707, 8, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 721, 8, 55, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 735, 8, 57, 10, 57, 12, 57, 738, 9, 57, 1, 57, 3, 57, 741, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 750, 8, 57, 10, 57, 12, 57, 753, 9, 57, 1, 57, 3, 57, 756, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 765, 8, 57, 10, 57, 12, 57, 768, 9, 57, 1, 57, 3, 57, 771, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 3, 57, 778, 8, 57, 1, 57, 1, 57, 3, 57, 782, 8, 57, 1, 58, 1, 58, 1, 58, 5, 58, 787, 8, 58, 10, 58, 12, 58, 790, 9, 58, 1, 58, 3, 58, 793, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 798, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 4, 59, 805, 8, 59, 11, 59, 12, 59, 806, 1, 59, 1, 59, 3, 59, 811, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 837, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 854, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 860, 8, 59, 1, 59, 3, 59, 863, 8, 59, 1, 59, 3, 59, 866, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 876, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 882, 8, 59, 1, 59, 3, 59, 885, 8, 59, 1, 59, 3, 59, 888, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 896, 8, 59, 1, 59, 3, 59, 899, 8, 59, 1, 59, 1, 59, 3, 59, 903, 8, 59, 1, 59, 3, 59, 906, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 920, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 937, 8, 59, 1, 59, 1, 59, 1, 59, 3, 59, 942, 8, 59, 1, 59, 1, 59, 1, 59, 3, 59, 947, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 953, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 960, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 972, 8, 59, 1, 59, 1, 59, 3, 59, 976, 8, 59, 1, 59, 3, 59, 979, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 988, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1002, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1018, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1047, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1055, 8, 59, 5, 59, 1057, 8, 59, 10, 59, 12, 59, 1060, 9, 59, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 1066, 8, 60, 10, 60, 12, 60, 1069, 9, 60, 1, 60, 3, 60, 1072, 8, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 1079, 8, 60, 10, 60, 12, 60, 1082, 9, 60, 1, 60, 3, 60, 1085, 8, 60, 1, 60, 1, 60, 3, 60, 1089, 8, 60, 1, 60, 1, 60, 1, 60, 3, 60, 1094, 8, 60, 1, 61, 1, 61, 1, 61, 5, 61, 1099, 8, 61, 10, 61, 12, 61, 1102, 9, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 5, 61, 1110, 8, 61, 10, 61, 12, 61, 1113, 9, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 1121, 8, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 1128, 8, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 3, 62, 1141, 8, 62, 1, 63, 1, 63, 1, 63, 5, 63, 1146, 8, 63, 10, 63, 12, 63, 1149, 9, 63, 1, 63, 3, 63, 1152, 8, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 3, 64, 1164, 8, 64, 1, 65, 1, 65, 1, 65, 1, 65, 3, 65, 1170, 8, 65, 1, 65, 3, 65, 1173, 8, 65, 1, 66, 1, 66, 1, 66, 5, 66, 1178, 8, 66, 10, 66, 12, 66, 1181, 9, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 1192, 8, 67, 1, 67, 1, 67, 1, 67, 1, 67, 3, 67, 1198, 8, 67, 5, 67, 1200, 8, 67, 10, 67, 12, 67, 1203, 9, 67, 1, 68, 1, 68, 1, 68, 3, 68, 1208, 8, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 3, 69, 1215, 8, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 5, 70, 1222, 8, 70, 10, 70, 12, 70, 1225, 9, 70, 1, 70, 3, 70, 1228, 8, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 3, 72, 1238, 8, 72, 3, 72, 1240, 8, 72, 1, 73, 3, 73, 1243, 8, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 3, 73, 1251, 8, 73, 1, 74, 1, 74, 1, 74, 3, 74, 1256, 8, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 3, 78, 1266, 8, 78, 1, 79, 1, 79, 1, 79, 3, 79, 1271, 8, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 3, 82, 1283, 8, 82, 1, 83, 1, 83, 5, 83, 1287, 8, 83, 10, 83, 12, 83, 1290, 9, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 3, 84, 1299, 8, 84, 1, 85, 1, 85, 5, 85, 1303, 8, 85, 10, 85, 12, 85, 1306, 9, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 3, 86, 1315, 8, 86, 1, 86, 0, 3, 80, 118, 134, 87, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 0, 17, 2, 0, 32, 32, 37, 37, 2, 0, 18, 18, 77, 77, 2, 0, 46, 46, 54, 54, 3, 0, 1, 1, 4, 4, 8, 8, 4, 0, 1, 1, 3, 4, 8, 8, 83, 83, 2, 0, 54, 54, 76, 76, 2, 0, 1, 1, 4, 4, 2, 0, 7, 7, 22, 23, 2, 0, 31, 31, 52, 52, 2, 0, 74, 74, 79, 79, 3, 0, 10, 10, 53, 53, 93, 93, 2, 0, 43, 43, 56, 56, 1, 0, 110, 111, 2, 0, 121, 121, 142, 142, 7, 0, 21, 21, 40, 40, 58, 59, 73, 73, 81, 81, 100, 100, 106, 106, 19, 0, 1, 13, 15, 20, 22, 26, 28, 29, 31, 31, 33, 36, 38, 39, 41, 44, 46, 46, 48, 54, 56, 57, 61, 61, 63, 72, 74, 80, 82, 86, 88, 95, 97, 99, 101, 102, 104, 105, 4, 0, 20, 20, 31, 31, 41, 41, 51, 51, 1493, 0, 177, 1, 0, 0, 0, 2, 184, 1, 0, 0, 0, 4, 186, 1, 0, 0, 0, 6, 188, 1, 0, 0, 0, 8, 195, 1, 0, 0, 0, 10, 218, 1, 0, 0, 0, 12, 220, 1, 0, 0, 0, 14, 227, 1, 0, 0, 0, 16, 234, 1, 0, 0, 0, 18, 247, 1, 0, 0, 0, 20, 259, 1, 0, 0, 0, 22, 268, 1, 0, 0, 0, 24, 276, 1, 0, 0, 0, 26, 298, 1, 0, 0, 0, 28, 313, 1, 0, 0, 0, 30, 322, 1, 0, 0, 0, 32, 327, 1, 0, 0, 0, 34, 331, 1, 0, 0, 0, 36, 333, 1, 0, 0, 0, 38, 342, 1, 0, 0, 0, 40, 346, 1, 0, 0, 0, 42, 360, 1, 0, 0, 0, 44, 370, 1, 0, 0, 0, 46, 380, 1, 0, 0, 0, 48, 384, 1, 0, 0, 0, 50, 392, 1, 0, 0, 0, 52, 441, 1, 0, 0, 0, 54, 444, 1, 0, 0, 0, 56, 450, 1, 0, 0, 0, 58, 454, 1, 0, 0, 0, 60, 460, 1, 0, 0, 0, 62, 478, 1, 0, 0, 0, 64, 481, 1, 0, 0, 0, 66, 484, 1, 0, 0, 0, 68, 494, 1, 0, 0, 0, 70, 497, 1, 0, 0, 0, 72, 501, 1, 0, 0, 0, 74, 534, 1, 0, 0, 0, 76, 536, 1, 0, 0, 0, 78, 539, 1, 0, 0, 0, 80, 554, 1, 0, 0, 0, 82, 616, 1, 0, 0, 0, 84, 621, 1, 0, 0, 0, 86, 632, 1, 0, 0, 0, 88, 634, 1, 0, 0, 0, 90, 640, 1, 0, 0, 0, 92, 648, 1, 0, 0, 0, 94, 666, 1, 0, 0, 0, 96, 668, 1, 0, 0, 0, 98, 676, 1, 0, 0, 0, 100, 681, 1, 0, 0, 0, 102, 689, 1, 0, 0, 0, 104, 693, 1, 0, 0, 0, 106, 697, 1, 0, 0, 0, 108, 706, 1, 0, 0, 0, 110, 720, 1, 0, 0, 0, 112, 722, 1, 0, 0, 0, 114, 781, 1, 0, 0, 0, 116, 783, 1, 0, 0, 0, 118, 946, 1, 0, 0, 0, 120, 1088, 1, 0, 0, 0, 122, 1127, 1, 0, 0, 0, 124, 1140, 1, 0, 0, 0, 126, 1142, 1, 0, 0, 0, 128, 1163, 1, 0, 0, 0, 130, 1172, 1, 0, 0, 0, 132, 1174, 1, 0, 0, 0, 134, 1191, 1, 0, 0, 0, 136, 1204, 1, 0, 0, 0, 138, 1214, 1, 0, 0, 0, 140, 1218, 1, 0, 0, 0, 142, 1229, 1, 0, 0, 0, 144, 1239, 1, 0, 0, 0, 146, 1242, 1, 0, 0, 0, 148, 1255, 1, 0, 0, 0, 150, 1257, 1, 0, 0, 0, 152, 1259, 1, 0, 0, 0, 154, 1261, 1, 0, 0, 0, 156, 1265, 1, 0, 0, 0, 158, 1270, 1, 0, 0, 0, 160, 1272, 1, 0, 0, 0, 162, 1276, 1, 0, 0, 0, 164, 1282, 1, 0, 0, 0, 166, 1284, 1, 0, 0, 0, 168, 1298, 1, 0, 0, 0, 170, 1300, 1, 0, 0, 0, 172, 1314, 1, 0, 0, 0, 174, 176, 3, 2, 1, 0, 175, 174, 1, 0, 0, 0, 176, 179, 1, 0, 0, 0, 177, 175, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 180, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 180, 181, 5, 0, 0, 1, 181, 1, 1, 0, 0, 0, 182, 185, 3, 6, 3, 0, 183, 185, 3, 10, 5, 0, 184, 182, 1, 0, 0, 0, 184, 183, 1, 0, 0, 0, 185, 3, 1, 0, 0, 0, 186, 187, 3, 118, 59, 0, 187, 5, 1, 0, 0, 0, 188, 189, 5, 55, 0, 0, 189, 193, 3, 158, 79, 0, 190, 191, 5, 118, 0, 0, 191, 192, 5, 125, 0, 0, 192, 194, 3, 4, 2, 0, 193, 190, 1, 0, 0, 0, 193, 194, 1, 0, 0, 0, 194, 7, 1, 0, 0, 0, 195, 200, 3, 158, 79, 0, 196, 197, 5, 119, 0, 0, 197, 199, 3, 158, 79, 0, 198, 196, 1, 0, 0, 0, 199, 202, 1, 0, 0, 0, 200, 198, 1, 0, 0, 0, 200, 201, 1, 0, 0, 0, 201, 204, 1, 0, 0, 0, 202, 200, 1, 0, 0, 0, 203, 205, 5, 119, 0, 0, 204, 203, 1, 0, 0, 0, 204, 205, 1, 0, 0, 0, 205, 9, 1, 0, 0, 0, 206, 219, 3, 12, 6, 0, 207, 219, 3, 14, 7, 0, 208, 219, 3, 18, 9, 0, 209, 219, 3, 20, 10, 0, 210, 219, 3, 22, 11, 0, 211, 219, 3, 26, 13, 0, 212, 219, 3, 24, 12, 0, 213, 219, 3, 28, 14, 0, 214, 219, 3, 30, 15, 0, 215, 219, 3, 36, 18, 0, 216, 219, 3, 32, 16, 0, 217, 219, 3, 34, 17, 0, 218, 206, 1, 0, 0, 0, 218, 207, 1, 0, 0, 0, 218, 208, 1, 0, 0, 0, 218, 209, 1, 0, 0, 0, 218, 210, 1, 0, 0, 0, 218, 211, 1, 0, 0, 0, 218, 212, 1, 0, 0, 0, 218, 213, 1, 0, 0, 0, 218, 214, 1, 0, 0, 0, 218, 215, 1, 0, 0, 0, 218, 216, 1, 0, 0, 0, 218, 217, 1, 0, 0, 0, 219, 11, 1, 0, 0, 0, 220, 222, 5, 75, 0, 0, 221, 223, 3, 4, 2, 0, 222, 221, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 225, 1, 0, 0, 0, 224, 226, 5, 153, 0, 0, 225, 224, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 13, 1, 0, 0, 0, 227, 229, 5, 87, 0, 0, 228, 230, 3, 4, 2, 0, 229, 228, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 232, 1, 0, 0, 0, 231, 233, 5, 153, 0, 0, 232, 231, 1, 0, 0, 0, 232, 233, 1, 0, 0, 0, 233, 15, 1, 0, 0, 0, 234, 243, 5, 14, 0, 0, 235, 236, 5, 133, 0, 0, 236, 239, 3, 158, 79, 0, 237, 238, 5, 118, 0, 0, 238, 240, 3, 158, 79, 0, 239, 237, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 241, 1, 0, 0, 0, 241, 242, 5, 152, 0, 0, 242, 244, 1, 0, 0, 0, 243, 235, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 246, 3, 36, 18, 0, 246, 17, 1, 0, 0, 0, 247, 248, 5, 96, 0, 0, 248, 252, 3, 36, 18, 0, 249, 251, 3, 16, 8, 0, 250, 249, 1, 0, 0, 0, 251, 254, 1, 0, 0, 0, 252, 250, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0, 253, 257, 1, 0, 0, 0, 254, 252, 1, 0, 0, 0, 255, 256, 5, 30, 0, 0, 256, 258, 3, 36, 18, 0, 257, 255, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 19, 1, 0, 0, 0, 259, 260, 5, 42, 0, 0, 260, 261, 5, 133, 0, 0, 261, 262, 3, 4, 2, 0, 262, 263, 5, 152, 0, 0, 263, 266, 3, 10, 5, 0, 264, 265, 5, 25, 0, 0, 265, 267, 3, 10, 5, 0, 266, 264, 1, 0, 0, 0, 266, 267, 1, 0, 0, 0, 267, 21, 1, 0, 0, 0, 268, 269, 5, 103, 0, 0, 269, 270, 5, 133, 0, 0, 270, 271, 3, 4, 2, 0, 271, 272, 5, 152, 0, 0, 272, 274, 3, 10, 5, 0, 273, 275, 5, 153, 0, 0, 274, 273, 1, 0, 0, 0, 274, 275, 1, 0, 0, 0, 275, 23, 1, 0, 0, 0, 276, 277, 5, 34, 0, 0, 277, 281, 5, 133, 0, 0, 278, 282, 3, 6, 3, 0, 279, 282, 3, 30, 15, 0, 280, 282, 3, 4, 2, 0, 281, 278, 1, 0, 0, 0, 281, 279, 1, 0, 0, 0, 281, 280, 1, 0, 0, 0, 281, 282, 1, 0, 0, 0, 282, 283, 1, 0, 0, 0, 283, 285, 5, 153, 0, 0, 284, 286, 3, 4, 2, 0, 285, 284, 1, 0, 0, 0, 285, 286, 1, 0, 0, 0, 286, 287, 1, 0, 0, 0, 287, 291, 5, 153, 0, 0, 288, 292, 3, 6, 3, 0, 289, 292, 3, 30, 15, 0, 290, 292, 3, 4, 2, 0, 291, 288, 1, 0, 0, 0, 291, 289, 1, 0, 0, 0, 291, 290, 1, 0, 0, 0, 291, 292, 1, 0, 0, 0, 292, 293, 1, 0, 0, 0, 293, 294, 5, 152, 0, 0, 294, 296, 3, 10, 5, 0, 295, 297, 5, 153, 0, 0, 296, 295, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 25, 1, 0, 0, 0, 298, 299, 5, 34, 0, 0, 299, 300, 5, 133, 0, 0, 300, 301, 5, 55, 0, 0, 301, 304, 3, 158, 79, 0, 302, 303, 5, 119, 0, 0, 303, 305, 3, 158, 79, 0, 304, 302, 1, 0, 0, 0, 304, 305, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 307, 5, 44, 0, 0, 307, 308, 3, 4, 2, 0, 308, 309, 5, 152, 0, 0, 309, 311, 3, 10, 5, 0, 310, 312, 5, 153, 0, 0, 311, 310, 1, 0, 0, 0, 311, 312, 1, 0, 0, 0, 312, 27, 1, 0, 0, 0, 313, 314, 7, 0, 0, 0, 314, 315, 3, 158, 79, 0, 315, 317, 5, 133, 0, 0, 316, 318, 3, 8, 4, 0, 317, 316, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 319, 1, 0, 0, 0, 319, 320, 5, 152, 0, 0, 320, 321, 3, 36, 18, 0, 321, 29, 1, 0, 0, 0, 322, 323, 3, 4, 2, 0, 323, 324, 5, 118, 0, 0, 324, 325, 5, 125, 0, 0, 325, 326, 3, 4, 2, 0, 326, 31, 1, 0, 0, 0, 327, 329, 3, 4, 2, 0, 328, 330, 5, 153, 0, 0, 329, 328, 1, 0, 0, 0, 329, 330, 1, 0, 0, 0, 330, 33, 1, 0, 0, 0, 331, 332, 5, 153, 0, 0, 332, 35, 1, 0, 0, 0, 333, 337, 5, 131, 0, 0, 334, 336, 3, 2, 1, 0, 335, 334, 1, 0, 0, 0, 336, 339, 1, 0, 0, 0, 337, 335, 1, 0, 0, 0, 337, 338, 1, 0, 0, 0, 338, 340, 1, 0, 0, 0, 339, 337, 1, 0, 0, 0, 340, 341, 5, 150, 0, 0, 341, 37, 1, 0, 0, 0, 342, 343, 3, 4, 2, 0, 343, 344, 5, 118, 0, 0, 344, 345, 3, 4, 2, 0, 345, 39, 1, 0, 0, 0, 346, 351, 3, 38, 19, 0, 347, 348, 5, 119, 0, 0, 348, 350, 3, 38, 19, 0, 349, 347, 1, 0, 0, 0, 350, 353, 1, 0, 0, 0, 351, 349, 1, 0, 0, 0, 351, 352, 1, 0, 0, 0, 352, 355, 1, 0, 0, 0, 353, 351, 1, 0, 0, 0, 354, 356, 5, 119, 0, 0, 355, 354, 1, 0, 0, 0, 355, 356, 1, 0, 0, 0, 356, 41, 1, 0, 0, 0, 357, 361, 3, 48, 24, 0, 358, 361, 3, 50, 25, 0, 359, 361, 3, 122, 61, 0, 360, 357, 1, 0, 0, 0, 360, 358, 1, 0, 0, 0, 360, 359, 1, 0, 0, 0, 361, 362, 1, 0, 0, 0, 362, 363, 5, 0, 0, 1, 363, 43, 1, 0, 0, 0, 364, 371, 3, 50, 25, 0, 365, 366, 5, 133, 0, 0, 366, 367, 3, 48, 24, 0, 367, 368, 5, 152, 0, 0, 368, 371, 1, 0, 0, 0, 369, 371, 3, 162, 81, 0, 370, 364, 1, 0, 0, 0, 370, 365, 1, 0, 0, 0, 370, 369, 1, 0, 0, 0, 371, 45, 1, 0, 0, 0, 372, 381, 5, 27, 0, 0, 373, 374, 5, 98, 0, 0, 374, 381, 5, 1, 0, 0, 375, 376, 5, 98, 0, 0, 376, 381, 5, 24, 0, 0, 377, 381, 5, 47, 0, 0, 378, 379, 5, 47, 0, 0, 379, 381, 5, 24, 0, 0, 380, 372, 1, 0, 0, 0, 380, 373, 1, 0, 0, 0, 380, 375, 1, 0, 0, 0, 380, 377, 1, 0, 0, 0, 380, 378, 1, 0, 0, 0, 381, 382, 1, 0, 0, 0, 382, 383, 3, 44, 22, 0, 383, 47, 1, 0, 0, 0, 384, 388, 3, 44, 22, 0, 385, 387, 3, 46, 23, 0, 386, 385, 1, 0, 0, 0, 387, 390, 1, 0, 0, 0, 388, 386, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 49, 1, 0, 0, 0, 390, 388, 1, 0, 0, 0, 391, 393, 3, 52, 26, 0, 392, 391, 1, 0, 0, 0, 392, 393, 1, 0, 0, 0, 393, 394, 1, 0, 0, 0, 394, 396, 5, 82, 0, 0, 395, 397, 5, 24, 0, 0, 396, 395, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 399, 1, 0, 0, 0, 398, 400, 3, 54, 27, 0, 399, 398, 1, 0, 0, 0, 399, 400, 1, 0, 0, 0, 400, 401, 1, 0, 0, 0, 401, 403, 3, 116, 58, 0, 402, 404, 3, 56, 28, 0, 403, 402, 1, 0, 0, 0, 403, 404, 1, 0, 0, 0, 404, 406, 1, 0, 0, 0, 405, 407, 3, 58, 29, 0, 406, 405, 1, 0, 0, 0, 406, 407, 1, 0, 0, 0, 407, 409, 1, 0, 0, 0, 408, 410, 3, 62, 31, 0, 409, 408, 1, 0, 0, 0, 409, 410, 1, 0, 0, 0, 410, 412, 1, 0, 0, 0, 411, 413, 3, 64, 32, 0, 412, 411, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 415, 1, 0, 0, 0, 414, 416, 3, 66, 33, 0, 415, 414, 1, 0, 0, 0, 415, 416, 1, 0, 0, 0, 416, 419, 1, 0, 0, 0, 417, 418, 5, 105, 0, 0, 418, 420, 7, 1, 0, 0, 419, 417, 1, 0, 0, 0, 419, 420, 1, 0, 0, 0, 420, 423, 1, 0, 0, 0, 421, 422, 5, 105, 0, 0, 422, 424, 5, 92, 0, 0, 423, 421, 1, 0, 0, 0, 423, 424, 1, 0, 0, 0, 424, 426, 1, 0, 0, 0, 425, 427, 3, 68, 34, 0, 426, 425, 1, 0, 0, 0, 426, 427, 1, 0, 0, 0, 427, 429, 1, 0, 0, 0, 428, 430, 3, 60, 30, 0, 429, 428, 1, 0, 0, 0, 429, 430, 1, 0, 0, 0, 430, 432, 1, 0, 0, 0, 431, 433, 3, 70, 35, 0, 432, 431, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 436, 1, 0, 0, 0, 434, 437, 3, 74, 37, 0, 435, 437, 3, 76, 38, 0, 436, 434, 1, 0, 0, 0, 436, 435, 1, 0, 0, 0, 436, 437, 1, 0, 0, 0, 437, 439, 1, 0, 0, 0, 438, 440, 3, 78, 39, 0, 439, 438, 1, 0, 0, 0, 439, 440, 1, 0, 0, 0, 440, 51, 1, 0, 0, 0, 441, 442, 5, 105, 0, 0, 442, 443, 3, 126, 63, 0, 443, 53, 1, 0, 0, 0, 444, 445, 5, 91, 0, 0, 445, 448, 5, 111, 0, 0, 446, 447, 5, 105, 0, 0, 447, 449, 5, 88, 0, 0, 448, 446, 1, 0, 0, 0, 448, 449, 1, 0, 0, 0, 449, 55, 1, 0, 0, 0, 450, 451, 5, 35, 0, 0, 451, 452, 3, 80, 40, 0, 452, 57, 1, 0, 0, 0, 453, 455, 7, 2, 0, 0, 454, 453, 1, 0, 0, 0, 454, 455, 1, 0, 0, 0, 455, 456, 1, 0, 0, 0, 456, 457, 5, 5, 0, 0, 457, 458, 5, 50, 0, 0, 458, 459, 3, 116, 58, 0, 459, 59, 1, 0, 0, 0, 460, 461, 5, 104, 0, 0, 461, 462, 3, 158, 79, 0, 462, 463, 5, 6, 0, 0, 463, 464, 5, 133, 0, 0, 464, 465, 3, 100, 50, 0, 465, 475, 5, 152, 0, 0, 466, 467, 5, 119, 0, 0, 467, 468, 3, 158, 79, 0, 468, 469, 5, 6, 0, 0, 469, 470, 5, 133, 0, 0, 470, 471, 3, 100, 50, 0, 471, 472, 5, 152, 0, 0, 472, 474, 1, 0, 0, 0, 473, 466, 1, 0, 0, 0, 474, 477, 1, 0, 0, 0, 475, 473, 1, 0, 0, 0, 475, 476, 1, 0, 0, 0, 476, 61, 1, 0, 0, 0, 477, 475, 1, 0, 0, 0, 478, 479, 5, 72, 0, 0, 479, 480, 3, 118, 59, 0, 480, 63, 1, 0, 0, 0, 481, 482, 5, 102, 0, 0, 482, 483, 3, 118, 59, 0, 483, 65, 1, 0, 0, 0, 484, 485, 5, 38, 0, 0, 485, 492, 5, 11, 0, 0, 486, 487, 7, 1, 0, 0, 487, 488, 5, 133, 0, 0, 488, 489, 3, 116, 58, 0, 489, 490, 5, 152, 0, 0, 490, 493, 1, 0, 0, 0, 491, 493, 3, 116, 58, 0, 492, 486, 1, 0, 0, 0, 492, 491, 1, 0, 0, 0, 493, 67, 1, 0, 0, 0, 494, 495, 5, 39, 0, 0, 495, 496, 3, 118, 59, 0, 496, 69, 1, 0, 0, 0, 497, 498, 5, 67, 0, 0, 498, 499, 5, 11, 0, 0, 499, 500, 3, 90, 45, 0, 500, 71, 1, 0, 0, 0, 501, 502, 5, 67, 0, 0, 502, 503, 5, 11, 0, 0, 503, 504, 3, 116, 58, 0, 504, 73, 1, 0, 0, 0, 505, 506, 5, 57, 0, 0, 506, 509, 3, 118, 59, 0, 507, 508, 5, 119, 0, 0, 508, 510, 3, 118, 59, 0, 509, 507, 1, 0, 0, 0, 509, 510, 1, 0, 0, 0, 510, 515, 1, 0, 0, 0, 511, 512, 5, 105, 0, 0, 512, 516, 5, 88, 0, 0, 513, 514, 5, 11, 0, 0, 514, 516, 3, 116, 58, 0, 515, 511, 1, 0, 0, 0, 515, 513, 1, 0, 0, 0, 515, 516, 1, 0, 0, 0, 516, 535, 1, 0, 0, 0, 517, 518, 5, 57, 0, 0, 518, 521, 3, 118, 59, 0, 519, 520, 5, 105, 0, 0, 520, 522, 5, 88, 0, 0, 521, 519, 1, 0, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 524, 5, 64, 0, 0, 524, 525, 3, 118, 59, 0, 525, 535, 1, 0, 0, 0, 526, 527, 5, 57, 0, 0, 527, 528, 3, 118, 59, 0, 528, 529, 5, 64, 0, 0, 529, 532, 3, 118, 59, 0, 530, 531, 5, 11, 0, 0, 531, 533, 3, 116, 58, 0, 532, 530, 1, 0, 0, 0, 532, 533, 1, 0, 0, 0, 533, 535, 1, 0, 0, 0, 534, 505, 1, 0, 0, 0, 534, 517, 1, 0, 0, 0, 534, 526, 1, 0, 0, 0, 535, 75, 1, 0, 0, 0, 536, 537, 5, 64, 0, 0, 537, 538, 3, 118, 59, 0, 538, 77, 1, 0, 0, 0, 539, 540, 5, 84, 0, 0, 540, 541, 3, 96, 48, 0, 541, 79, 1, 0, 0, 0, 542, 543, 6, 40, -1, 0, 543, 545, 3, 134, 67, 0, 544, 546, 5, 29, 0, 0, 545, 544, 1, 0, 0, 0, 545, 546, 1, 0, 0, 0, 546, 548, 1, 0, 0, 0, 547, 549, 3, 88, 44, 0, 548, 547, 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 555, 1, 0, 0, 0, 550, 551, 5, 133, 0, 0, 551, 552, 3, 80, 40, 0, 552, 553, 5, 152, 0, 0, 553, 555, 1, 0, 0, 0, 554, 542, 1, 0, 0, 0, 554, 550, 1, 0, 0, 0, 555, 570, 1, 0, 0, 0, 556, 557, 10, 3, 0, 0, 557, 558, 3, 84, 42, 0, 558, 559, 3, 80, 40, 4, 559, 569, 1, 0, 0, 0, 560, 562, 10, 4, 0, 0, 561, 563, 3, 82, 41, 0, 562, 561, 1, 0, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 1, 0, 0, 0, 564, 565, 5, 50, 0, 0, 565, 566, 3, 80, 40, 0, 566, 567, 3, 86, 43, 0, 567, 569, 1, 0, 0, 0, 568, 556, 1, 0, 0, 0, 568, 560, 1, 0, 0, 0, 569, 572, 1, 0, 0, 0, 570, 568, 1, 0, 0, 0, 570, 571, 1, 0, 0, 0, 571, 81, 1, 0, 0, 0, 572, 570, 1, 0, 0, 0, 573, 575, 7, 3, 0, 0, 574, 573, 1, 0, 0, 0, 574, 575, 1, 0, 0, 0, 575, 576, 1, 0, 0, 0, 576, 583, 5, 46, 0, 0, 577, 579, 5, 46, 0, 0, 578, 580, 7, 3, 0, 0, 579, 578, 1, 0, 0, 0, 579, 580, 1, 0, 0, 0, 580, 583, 1, 0, 0, 0, 581, 583, 7, 3, 0, 0, 582, 574, 1, 0, 0, 0, 582, 577, 1, 0, 0, 0, 582, 581, 1, 0, 0, 0, 583, 617, 1, 0, 0, 0, 584, 586, 7, 4, 0, 0, 585, 584, 1, 0, 0, 0, 585, 586, 1, 0, 0, 0, 586, 587, 1, 0, 0, 0, 587, 589, 7, 5, 0, 0, 588, 590, 5, 68, 0, 0, 589, 588, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 599, 1, 0, 0, 0, 591, 593, 7, 5, 0, 0, 592, 594, 5, 68, 0, 0, 593, 592, 1, 0, 0, 0, 593, 594, 1, 0, 0, 0, 594, 596, 1, 0, 0, 0, 595, 597, 7, 4, 0, 0, 596, 595, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 599, 1, 0, 0, 0, 598, 585, 1, 0, 0, 0, 598, 591, 1, 0, 0, 0, 599, 617, 1, 0, 0, 0, 600, 602, 7, 6, 0, 0, 601, 600, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 605, 5, 36, 0, 0, 604, 606, 5, 68, 0, 0, 605, 604, 1, 0, 0, 0, 605, 606, 1, 0, 0, 0, 606, 615, 1, 0, 0, 0, 607, 609, 5, 36, 0, 0, 608, 610, 5, 68, 0, 0, 609, 608, 1, 0, 0, 0, 609, 610, 1, 0, 0, 0, 610, 612, 1, 0, 0, 0, 611, 613, 7, 6, 0, 0, 612, 611, 1, 0, 0, 0, 612, 613, 1, 0, 0, 0, 613, 615, 1, 0, 0, 0, 614, 601, 1, 0, 0, 0, 614, 607, 1, 0, 0, 0, 615, 617, 1, 0, 0, 0, 616, 582, 1, 0, 0, 0, 616, 598, 1, 0, 0, 0, 616, 614, 1, 0, 0, 0, 617, 83, 1, 0, 0, 0, 618, 619, 5, 17, 0, 0, 619, 622, 5, 50, 0, 0, 620, 622, 5, 119, 0, 0, 621, 618, 1, 0, 0, 0, 621, 620, 1, 0, 0, 0, 622, 85, 1, 0, 0, 0, 623, 624, 5, 65, 0, 0, 624, 633, 3, 116, 58, 0, 625, 626, 5, 99, 0, 0, 626, 627, 5, 133, 0, 0, 627, 628, 3, 116, 58, 0, 628, 629, 5, 152, 0, 0, 629, 633, 1, 0, 0, 0, 630, 631, 5, 99, 0, 0, 631, 633, 3, 116, 58, 0, 632, 623, 1, 0, 0, 0, 632, 625, 1, 0, 0, 0, 632, 630, 1, 0, 0, 0, 633, 87, 1, 0, 0, 0, 634, 635, 5, 80, 0, 0, 635, 638, 3, 94, 47, 0, 636, 637, 5, 64, 0, 0, 637, 639, 3, 94, 47, 0, 638, 636, 1, 0, 0, 0, 638, 639, 1, 0, 0, 0, 639, 89, 1, 0, 0, 0, 640, 645, 3, 92, 46, 0, 641, 642, 5, 119, 0, 0, 642, 644, 3, 92, 46, 0, 643, 641, 1, 0, 0, 0, 644, 647, 1, 0, 0, 0, 645, 643, 1, 0, 0, 0, 645, 646, 1, 0, 0, 0, 646, 91, 1, 0, 0, 0, 647, 645, 1, 0, 0, 0, 648, 650, 3, 118, 59, 0, 649, 651, 7, 7, 0, 0, 650, 649, 1, 0, 0, 0, 650, 651, 1, 0, 0, 0, 651, 654, 1, 0, 0, 0, 652, 653, 5, 63, 0, 0, 653, 655, 7, 8, 0, 0, 654, 652, 1, 0, 0, 0, 654, 655, 1, 0, 0, 0, 655, 658, 1, 0, 0, 0, 656, 657, 5, 16, 0, 0, 657, 659, 5, 113, 0, 0, 658, 656, 1, 0, 0, 0, 658, 659, 1, 0, 0, 0, 659, 93, 1, 0, 0, 0, 660, 667, 3, 162, 81, 0, 661, 664, 3, 146, 73, 0, 662, 663, 5, 154, 0, 0, 663, 665, 3, 146, 73, 0, 664, 662, 1, 0, 0, 0, 664, 665, 1, 0, 0, 0, 665, 667, 1, 0, 0, 0, 666, 660, 1, 0, 0, 0, 666, 661, 1, 0, 0, 0, 667, 95, 1, 0, 0, 0, 668, 673, 3, 98, 49, 0, 669, 670, 5, 119, 0, 0, 670, 672, 3, 98, 49, 0, 671, 669, 1, 0, 0, 0, 672, 675, 1, 0, 0, 0, 673, 671, 1, 0, 0, 0, 673, 674, 1, 0, 0, 0, 674, 97, 1, 0, 0, 0, 675, 673, 1, 0, 0, 0, 676, 677, 3, 158, 79, 0, 677, 678, 5, 125, 0, 0, 678, 679, 3, 148, 74, 0, 679, 99, 1, 0, 0, 0, 680, 682, 3, 102, 51, 0, 681, 680, 1, 0, 0, 0, 681, 682, 1, 0, 0, 0, 682, 684, 1, 0, 0, 0, 683, 685, 3, 104, 52, 0, 684, 683, 1, 0, 0, 0, 684, 685, 1, 0, 0, 0, 685, 687, 1, 0, 0, 0, 686, 688, 3, 106, 53, 0, 687, 686, 1, 0, 0, 0, 687, 688, 1, 0, 0, 0, 688, 101, 1, 0, 0, 0, 689, 690, 5, 70, 0, 0, 690, 691, 5, 11, 0, 0, 691, 692, 3, 116, 58, 0, 692, 103, 1, 0, 0, 0, 693, 694, 5, 67, 0, 0, 694, 695, 5, 11, 0, 0, 695, 696, 3, 90, 45, 0, 696, 105, 1, 0, 0, 0, 697, 698, 7, 9, 0, 0, 698, 699, 3, 108, 54, 0, 699, 107, 1, 0, 0, 0, 700, 707, 3, 110, 55, 0, 701, 702, 5, 9, 0, 0, 702, 703, 3, 110, 55, 0, 703, 704, 5, 2, 0, 0, 704, 705, 3, 110, 55, 0, 705, 707, 1, 0, 0, 0, 706, 700, 1, 0, 0, 0, 706, 701, 1, 0, 0, 0, 707, 109, 1, 0, 0, 0, 708, 709, 5, 19, 0, 0, 709, 721, 5, 78, 0, 0, 710, 711, 5, 97, 0, 0, 711, 721, 5, 71, 0, 0, 712, 713, 5, 97, 0, 0, 713, 721, 5, 33, 0, 0, 714, 715, 3, 146, 73, 0, 715, 716, 5, 71, 0, 0, 716, 721, 1, 0, 0, 0, 717, 718, 3, 146, 73, 0, 718, 719, 5, 33, 0, 0, 719, 721, 1, 0, 0, 0, 720, 708, 1, 0, 0, 0, 720, 710, 1, 0, 0, 0, 720, 712, 1, 0, 0, 0, 720, 714, 1, 0, 0, 0, 720, 717, 1, 0, 0, 0, 721, 111, 1, 0, 0, 0, 722, 723, 3, 118, 59, 0, 723, 724, 5, 0, 0, 1, 724, 113, 1, 0, 0, 0, 725, 782, 3, 158, 79, 0, 726, 727, 3, 158, 79, 0, 727, 728, 5, 133, 0, 0, 728, 729, 3, 158, 79, 0, 729, 736, 3, 114, 57, 0, 730, 731, 5, 119, 0, 0, 731, 732, 3, 158, 79, 0, 732, 733, 3, 114, 57, 0, 733, 735, 1, 0, 0, 0, 734, 730, 1, 0, 0, 0, 735, 738, 1, 0, 0, 0, 736, 734, 1, 0, 0, 0, 736, 737, 1, 0, 0, 0, 737, 740, 1, 0, 0, 0, 738, 736, 1, 0, 0, 0, 739, 741, 5, 119, 0, 0, 740, 739, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 742, 1, 0, 0, 0, 742, 743, 5, 152, 0, 0, 743, 782, 1, 0, 0, 0, 744, 745, 3, 158, 79, 0, 745, 746, 5, 133, 0, 0, 746, 751, 3, 160, 80, 0, 747, 748, 5, 119, 0, 0, 748, 750, 3, 160, 80, 0, 749, 747, 1, 0, 0, 0, 750, 753, 1, 0, 0, 0, 751, 749, 1, 0, 0, 0, 751, 752, 1, 0, 0, 0, 752, 755, 1, 0, 0, 0, 753, 751, 1, 0, 0, 0, 754, 756, 5, 119, 0, 0, 755, 754, 1, 0, 0, 0, 755, 756, 1, 0, 0, 0, 756, 757, 1, 0, 0, 0, 757, 758, 5, 152, 0, 0, 758, 782, 1, 0, 0, 0, 759, 760, 3, 158, 79, 0, 760, 761, 5, 133, 0, 0, 761, 766, 3, 114, 57, 0, 762, 763, 5, 119, 0, 0, 763, 765, 3, 114, 57, 0, 764, 762, 1, 0, 0, 0, 765, 768, 1, 0, 0, 0, 766, 764, 1, 0, 0, 0, 766, 767, 1, 0, 0, 0, 767, 770, 1, 0, 0, 0, 768, 766, 1, 0, 0, 0, 769, 771, 5, 119, 0, 0, 770, 769, 1, 0, 0, 0, 770, 771, 1, 0, 0, 0, 771, 772, 1, 0, 0, 0, 772, 773, 5, 152, 0, 0, 773, 782, 1, 0, 0, 0, 774, 775, 3, 158, 79, 0, 775, 777, 5, 133, 0, 0, 776, 778, 3, 116, 58, 0, 777, 776, 1, 0, 0, 0, 777, 778, 1, 0, 0, 0, 778, 779, 1, 0, 0, 0, 779, 780, 5, 152, 0, 0, 780, 782, 1, 0, 0, 0, 781, 725, 1, 0, 0, 0, 781, 726, 1, 0, 0, 0, 781, 744, 1, 0, 0, 0, 781, 759, 1, 0, 0, 0, 781, 774, 1, 0, 0, 0, 782, 115, 1, 0, 0, 0, 783, 788, 3, 118, 59, 0, 784, 785, 5, 119, 0, 0, 785, 787, 3, 118, 59, 0, 786, 784, 1, 0, 0, 0, 787, 790, 1, 0, 0, 0, 788, 786, 1, 0, 0, 0, 788, 789, 1, 0, 0, 0, 789, 792, 1, 0, 0, 0, 790, 788, 1, 0, 0, 0, 791, 793, 5, 119, 0, 0, 792, 791, 1, 0, 0, 0, 792, 793, 1, 0, 0, 0, 793, 117, 1, 0, 0, 0, 794, 795, 6, 59, -1, 0, 795, 797, 5, 12, 0, 0, 796, 798, 3, 118, 59, 0, 797, 796, 1, 0, 0, 0, 797, 798, 1, 0, 0, 0, 798, 804, 1, 0, 0, 0, 799, 800, 5, 101, 0, 0, 800, 801, 3, 118, 59, 0, 801, 802, 5, 86, 0, 0, 802, 803, 3, 118, 59, 0, 803, 805, 1, 0, 0, 0, 804, 799, 1, 0, 0, 0, 805, 806, 1, 0, 0, 0, 806, 804, 1, 0, 0, 0, 806, 807, 1, 0, 0, 0, 807, 810, 1, 0, 0, 0, 808, 809, 5, 25, 0, 0, 809, 811, 3, 118, 59, 0, 810, 808, 1, 0, 0, 0, 810, 811, 1, 0, 0, 0, 811, 812, 1, 0, 0, 0, 812, 813, 5, 26, 0, 0, 813, 947, 1, 0, 0, 0, 814, 815, 5, 13, 0, 0, 815, 816, 5, 133, 0, 0, 816, 817, 3, 118, 59, 0, 817, 818, 5, 6, 0, 0, 818, 819, 3, 114, 57, 0, 819, 820, 5, 152, 0, 0, 820, 947, 1, 0, 0, 0, 821, 822, 5, 20, 0, 0, 822, 947, 5, 113, 0, 0, 823, 824, 5, 48, 0, 0, 824, 947, 5, 113, 0, 0, 825, 826, 5, 48, 0, 0, 826, 827, 3, 118, 59, 0, 827, 828, 3, 150, 75, 0, 828, 947, 1, 0, 0, 0, 829, 830, 5, 85, 0, 0, 830, 831, 5, 133, 0, 0, 831, 832, 3, 118, 59, 0, 832, 833, 5, 35, 0, 0, 833, 836, 3, 118, 59, 0, 834, 835, 5, 34, 0, 0, 835, 837, 3, 118, 59, 0, 836, 834, 1, 0, 0, 0, 836, 837, 1, 0, 0, 0, 837, 838, 1, 0, 0, 0, 838, 839, 5, 152, 0, 0, 839, 947, 1, 0, 0, 0, 840, 841, 5, 89, 0, 0, 841, 947, 5, 113, 0, 0, 842, 843, 5, 94, 0, 0, 843, 844, 5, 133, 0, 0, 844, 845, 7, 10, 0, 0, 845, 846, 3, 164, 82, 0, 846, 847, 5, 35, 0, 0, 847, 848, 3, 118, 59, 0, 848, 849, 5, 152, 0, 0, 849, 947, 1, 0, 0, 0, 850, 851, 3, 158, 79, 0, 851, 853, 5, 133, 0, 0, 852, 854, 3, 116, 58, 0, 853, 852, 1, 0, 0, 0, 853, 854, 1, 0, 0, 0, 854, 855, 1, 0, 0, 0, 855, 856, 5, 152, 0, 0, 856, 865, 1, 0, 0, 0, 857, 859, 5, 133, 0, 0, 858, 860, 5, 24, 0, 0, 859, 858, 1, 0, 0, 0, 859, 860, 1, 0, 0, 0, 860, 862, 1, 0, 0, 0, 861, 863, 3, 116, 58, 0, 862, 861, 1, 0, 0, 0, 862, 863, 1, 0, 0, 0, 863, 864, 1, 0, 0, 0, 864, 866, 5, 152, 0, 0, 865, 857, 1, 0, 0, 0, 865, 866, 1, 0, 0, 0, 866, 867, 1, 0, 0, 0, 867, 868, 5, 69, 0, 0, 868, 869, 5, 133, 0, 0, 869, 870, 3, 100, 50, 0, 870, 871, 5, 152, 0, 0, 871, 947, 1, 0, 0, 0, 872, 873, 3, 158, 79, 0, 873, 875, 5, 133, 0, 0, 874, 876, 3, 116, 58, 0, 875, 874, 1, 0, 0, 0, 875, 876, 1, 0, 0, 0, 876, 877, 1, 0, 0, 0, 877, 878, 5, 152, 0, 0, 878, 887, 1, 0, 0, 0, 879, 881, 5, 133, 0, 0, 880, 882, 5, 24, 0, 0, 881, 880, 1, 0, 0, 0, 881, 882, 1, 0, 0, 0, 882, 884, 1, 0, 0, 0, 883, 885, 3, 116, 58, 0, 884, 883, 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 886, 1, 0, 0, 0, 886, 888, 5, 152, 0, 0, 887, 879, 1, 0, 0, 0, 887, 888, 1, 0, 0, 0, 888, 889, 1, 0, 0, 0, 889, 890, 5, 69, 0, 0, 890, 891, 3, 158, 79, 0, 891, 947, 1, 0, 0, 0, 892, 898, 3, 158, 79, 0, 893, 895, 5, 133, 0, 0, 894, 896, 3, 116, 58, 0, 895, 894, 1, 0, 0, 0, 895, 896, 1, 0, 0, 0, 896, 897, 1, 0, 0, 0, 897, 899, 5, 152, 0, 0, 898, 893, 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 900, 1, 0, 0, 0, 900, 902, 5, 133, 0, 0, 901, 903, 5, 24, 0, 0, 902, 901, 1, 0, 0, 0, 902, 903, 1, 0, 0, 0, 903, 905, 1, 0, 0, 0, 904, 906, 3, 116, 58, 0, 905, 904, 1, 0, 0, 0, 905, 906, 1, 0, 0, 0, 906, 907, 1, 0, 0, 0, 907, 908, 5, 152, 0, 0, 908, 947, 1, 0, 0, 0, 909, 947, 3, 122, 61, 0, 910, 947, 3, 166, 83, 0, 911, 947, 3, 148, 74, 0, 912, 913, 5, 121, 0, 0, 913, 947, 3, 118, 59, 20, 914, 915, 5, 61, 0, 0, 915, 947, 3, 118, 59, 14, 916, 917, 3, 138, 69, 0, 917, 918, 5, 123, 0, 0, 918, 920, 1, 0, 0, 0, 919, 916, 1, 0, 0, 0, 919, 920, 1, 0, 0, 0, 920, 921, 1, 0, 0, 0, 921, 947, 5, 115, 0, 0, 922, 923, 5, 133, 0, 0, 923, 924, 3, 48, 24, 0, 924, 925, 5, 152, 0, 0, 925, 947, 1, 0, 0, 0, 926, 927, 5, 133, 0, 0, 927, 928, 3, 118, 59, 0, 928, 929, 5, 152, 0, 0, 929, 947, 1, 0, 0, 0, 930, 931, 5, 133, 0, 0, 931, 932, 3, 116, 58, 0, 932, 933, 5, 152, 0, 0, 933, 947, 1, 0, 0, 0, 934, 936, 5, 132, 0, 0, 935, 937, 3, 116, 58, 0, 936, 935, 1, 0, 0, 0, 936, 937, 1, 0, 0, 0, 937, 938, 1, 0, 0, 0, 938, 947, 5, 151, 0, 0, 939, 941, 5, 131, 0, 0, 940, 942, 3, 40, 20, 0, 941, 940, 1, 0, 0, 0, 941, 942, 1, 0, 0, 0, 942, 943, 1, 0, 0, 0, 943, 947, 5, 150, 0, 0, 944, 947, 3, 120, 60, 0, 945, 947, 3, 130, 65, 0, 946, 794, 1, 0, 0, 0, 946, 814, 1, 0, 0, 0, 946, 821, 1, 0, 0, 0, 946, 823, 1, 0, 0, 0, 946, 825, 1, 0, 0, 0, 946, 829, 1, 0, 0, 0, 946, 840, 1, 0, 0, 0, 946, 842, 1, 0, 0, 0, 946, 850, 1, 0, 0, 0, 946, 872, 1, 0, 0, 0, 946, 892, 1, 0, 0, 0, 946, 909, 1, 0, 0, 0, 946, 910, 1, 0, 0, 0, 946, 911, 1, 0, 0, 0, 946, 912, 1, 0, 0, 0, 946, 914, 1, 0, 0, 0, 946, 919, 1, 0, 0, 0, 946, 922, 1, 0, 0, 0, 946, 926, 1, 0, 0, 0, 946, 930, 1, 0, 0, 0, 946, 934, 1, 0, 0, 0, 946, 939, 1, 0, 0, 0, 946, 944, 1, 0, 0, 0, 946, 945, 1, 0, 0, 0, 947, 1058, 1, 0, 0, 0, 948, 952, 10, 19, 0, 0, 949, 953, 5, 115, 0, 0, 950, 953, 5, 154, 0, 0, 951, 953, 5, 141, 0, 0, 952, 949, 1, 0, 0, 0, 952, 950, 1, 0, 0, 0, 952, 951, 1, 0, 0, 0, 953, 954, 1, 0, 0, 0, 954, 1057, 3, 118, 59, 20, 955, 959, 10, 18, 0, 0, 956, 960, 5, 142, 0, 0, 957, 960, 5, 121, 0, 0, 958, 960, 5, 120, 0, 0, 959, 956, 1, 0, 0, 0, 959, 957, 1, 0, 0, 0, 959, 958, 1, 0, 0, 0, 960, 961, 1, 0, 0, 0, 961, 1057, 3, 118, 59, 19, 962, 987, 10, 17, 0, 0, 963, 988, 5, 124, 0, 0, 964, 988, 5, 125, 0, 0, 965, 988, 5, 136, 0, 0, 966, 988, 5, 134, 0, 0, 967, 988, 5, 135, 0, 0, 968, 988, 5, 126, 0, 0, 969, 988, 5, 127, 0, 0, 970, 972, 5, 61, 0, 0, 971, 970, 1, 0, 0, 0, 971, 972, 1, 0, 0, 0, 972, 973, 1, 0, 0, 0, 973, 975, 5, 44, 0, 0, 974, 976, 5, 15, 0, 0, 975, 974, 1, 0, 0, 0, 975, 976, 1, 0, 0, 0, 976, 988, 1, 0, 0, 0, 977, 979, 5, 61, 0, 0, 978, 977, 1, 0, 0, 0, 978, 979, 1, 0, 0, 0, 979, 980, 1, 0, 0, 0, 980, 988, 7, 11, 0, 0, 981, 988, 5, 148, 0, 0, 982, 988, 5, 149, 0, 0, 983, 988, 5, 138, 0, 0, 984, 988, 5, 129, 0, 0, 985, 988, 5, 130, 0, 0, 986, 988, 5, 137, 0, 0, 987, 963, 1, 0, 0, 0, 987, 964, 1, 0, 0, 0, 987, 965, 1, 0, 0, 0, 987, 966, 1, 0, 0, 0, 987, 967, 1, 0, 0, 0, 987, 968, 1, 0, 0, 0, 987, 969, 1, 0, 0, 0, 987, 971, 1, 0, 0, 0, 987, 978, 1, 0, 0, 0, 987, 981, 1, 0, 0, 0, 987, 982, 1, 0, 0, 0, 987, 983, 1, 0, 0, 0, 987, 984, 1, 0, 0, 0, 987, 985, 1, 0, 0, 0, 987, 986, 1, 0, 0, 0, 988, 989, 1, 0, 0, 0, 989, 1057, 3, 118, 59, 18, 990, 991, 10, 15, 0, 0, 991, 992, 5, 140, 0, 0, 992, 1057, 3, 118, 59, 16, 993, 994, 10, 13, 0, 0, 994, 995, 5, 2, 0, 0, 995, 1057, 3, 118, 59, 14, 996, 997, 10, 12, 0, 0, 997, 998, 5, 66, 0, 0, 998, 1057, 3, 118, 59, 13, 999, 1001, 10, 11, 0, 0, 1000, 1002, 5, 61, 0, 0, 1001, 1000, 1, 0, 0, 0, 1001, 1002, 1, 0, 0, 0, 1002, 1003, 1, 0, 0, 0, 1003, 1004, 5, 9, 0, 0, 1004, 1005, 3, 118, 59, 0, 1005, 1006, 5, 2, 0, 0, 1006, 1007, 3, 118, 59, 12, 1007, 1057, 1, 0, 0, 0, 1008, 1009, 10, 10, 0, 0, 1009, 1010, 5, 143, 0, 0, 1010, 1011, 3, 118, 59, 0, 1011, 1012, 5, 118, 0, 0, 1012, 1013, 3, 118, 59, 10, 1013, 1057, 1, 0, 0, 0, 1014, 1015, 10, 30, 0, 0, 1015, 1017, 5, 133, 0, 0, 1016, 1018, 3, 116, 58, 0, 1017, 1016, 1, 0, 0, 0, 1017, 1018, 1, 0, 0, 0, 1018, 1019, 1, 0, 0, 0, 1019, 1057, 5, 152, 0, 0, 1020, 1021, 10, 26, 0, 0, 1021, 1022, 5, 132, 0, 0, 1022, 1023, 3, 118, 59, 0, 1023, 1024, 5, 151, 0, 0, 1024, 1057, 1, 0, 0, 0, 1025, 1026, 10, 25, 0, 0, 1026, 1027, 5, 123, 0, 0, 1027, 1057, 5, 111, 0, 0, 1028, 1029, 10, 24, 0, 0, 1029, 1030, 5, 123, 0, 0, 1030, 1057, 3, 158, 79, 0, 1031, 1032, 10, 23, 0, 0, 1032, 1033, 5, 139, 0, 0, 1033, 1034, 5, 132, 0, 0, 1034, 1035, 3, 118, 59, 0, 1035, 1036, 5, 151, 0, 0, 1036, 1057, 1, 0, 0, 0, 1037, 1038, 10, 22, 0, 0, 1038, 1039, 5, 139, 0, 0, 1039, 1057, 5, 111, 0, 0, 1040, 1041, 10, 21, 0, 0, 1041, 1042, 5, 139, 0, 0, 1042, 1057, 3, 158, 79, 0, 1043, 1044, 10, 16, 0, 0, 1044, 1046, 5, 49, 0, 0, 1045, 1047, 5, 61, 0, 0, 1046, 1045, 1, 0, 0, 0, 1046, 1047, 1, 0, 0, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1057, 5, 62, 0, 0, 1049, 1054, 10, 9, 0, 0, 1050, 1051, 5, 6, 0, 0, 1051, 1055, 3, 158, 79, 0, 1052, 1053, 5, 6, 0, 0, 1053, 1055, 5, 113, 0, 0, 1054, 1050, 1, 0, 0, 0, 1054, 1052, 1, 0, 0, 0, 1055, 1057, 1, 0, 0, 0, 1056, 948, 1, 0, 0, 0, 1056, 955, 1, 0, 0, 0, 1056, 962, 1, 0, 0, 0, 1056, 990, 1, 0, 0, 0, 1056, 993, 1, 0, 0, 0, 1056, 996, 1, 0, 0, 0, 1056, 999, 1, 0, 0, 0, 1056, 1008, 1, 0, 0, 0, 1056, 1014, 1, 0, 0, 0, 1056, 1020, 1, 0, 0, 0, 1056, 1025, 1, 0, 0, 0, 1056, 1028, 1, 0, 0, 0, 1056, 1031, 1, 0, 0, 0, 1056, 1037, 1, 0, 0, 0, 1056, 1040, 1, 0, 0, 0, 1056, 1043, 1, 0, 0, 0, 1056, 1049, 1, 0, 0, 0, 1057, 1060, 1, 0, 0, 0, 1058, 1056, 1, 0, 0, 0, 1058, 1059, 1, 0, 0, 0, 1059, 119, 1, 0, 0, 0, 1060, 1058, 1, 0, 0, 0, 1061, 1062, 5, 133, 0, 0, 1062, 1067, 3, 158, 79, 0, 1063, 1064, 5, 119, 0, 0, 1064, 1066, 3, 158, 79, 0, 1065, 1063, 1, 0, 0, 0, 1066, 1069, 1, 0, 0, 0, 1067, 1065, 1, 0, 0, 0, 1067, 1068, 1, 0, 0, 0, 1068, 1071, 1, 0, 0, 0, 1069, 1067, 1, 0, 0, 0, 1070, 1072, 5, 119, 0, 0, 1071, 1070, 1, 0, 0, 0, 1071, 1072, 1, 0, 0, 0, 1072, 1073, 1, 0, 0, 0, 1073, 1074, 5, 152, 0, 0, 1074, 1089, 1, 0, 0, 0, 1075, 1080, 3, 158, 79, 0, 1076, 1077, 5, 119, 0, 0, 1077, 1079, 3, 158, 79, 0, 1078, 1076, 1, 0, 0, 0, 1079, 1082, 1, 0, 0, 0, 1080, 1078, 1, 0, 0, 0, 1080, 1081, 1, 0, 0, 0, 1081, 1084, 1, 0, 0, 0, 1082, 1080, 1, 0, 0, 0, 1083, 1085, 5, 119, 0, 0, 1084, 1083, 1, 0, 0, 0, 1084, 1085, 1, 0, 0, 0, 1085, 1089, 1, 0, 0, 0, 1086, 1087, 5, 133, 0, 0, 1087, 1089, 5, 152, 0, 0, 1088, 1061, 1, 0, 0, 0, 1088, 1075, 1, 0, 0, 0, 1088, 1086, 1, 0, 0, 0, 1089, 1090, 1, 0, 0, 0, 1090, 1093, 5, 114, 0, 0, 1091, 1094, 3, 118, 59, 0, 1092, 1094, 3, 36, 18, 0, 1093, 1091, 1, 0, 0, 0, 1093, 1092, 1, 0, 0, 0, 1094, 121, 1, 0, 0, 0, 1095, 1096, 5, 135, 0, 0, 1096, 1100, 3, 158, 79, 0, 1097, 1099, 3, 124, 62, 0, 1098, 1097, 1, 0, 0, 0, 1099, 1102, 1, 0, 0, 0, 1100, 1098, 1, 0, 0, 0, 1100, 1101, 1, 0, 0, 0, 1101, 1103, 1, 0, 0, 0, 1102, 1100, 1, 0, 0, 0, 1103, 1104, 5, 154, 0, 0, 1104, 1105, 5, 127, 0, 0, 1105, 1128, 1, 0, 0, 0, 1106, 1107, 5, 135, 0, 0, 1107, 1111, 3, 158, 79, 0, 1108, 1110, 3, 124, 62, 0, 1109, 1108, 1, 0, 0, 0, 1110, 1113, 1, 0, 0, 0, 1111, 1109, 1, 0, 0, 0, 1111, 1112, 1, 0, 0, 0, 1112, 1114, 1, 0, 0, 0, 1113, 1111, 1, 0, 0, 0, 1114, 1120, 5, 127, 0, 0, 1115, 1121, 3, 122, 61, 0, 1116, 1117, 5, 131, 0, 0, 1117, 1118, 3, 118, 59, 0, 1118, 1119, 5, 150, 0, 0, 1119, 1121, 1, 0, 0, 0, 1120, 1115, 1, 0, 0, 0, 1120, 1116, 1, 0, 0, 0, 1120, 1121, 1, 0, 0, 0, 1121, 1122, 1, 0, 0, 0, 1122, 1123, 5, 135, 0, 0, 1123, 1124, 5, 154, 0, 0, 1124, 1125, 3, 158, 79, 0, 1125, 1126, 5, 127, 0, 0, 1126, 1128, 1, 0, 0, 0, 1127, 1095, 1, 0, 0, 0, 1127, 1106, 1, 0, 0, 0, 1128, 123, 1, 0, 0, 0, 1129, 1130, 3, 158, 79, 0, 1130, 1131, 5, 125, 0, 0, 1131, 1132, 3, 164, 82, 0, 1132, 1141, 1, 0, 0, 0, 1133, 1134, 3, 158, 79, 0, 1134, 1135, 5, 125, 0, 0, 1135, 1136, 5, 131, 0, 0, 1136, 1137, 3, 118, 59, 0, 1137, 1138, 5, 150, 0, 0, 1138, 1141, 1, 0, 0, 0, 1139, 1141, 3, 158, 79, 0, 1140, 1129, 1, 0, 0, 0, 1140, 1133, 1, 0, 0, 0, 1140, 1139, 1, 0, 0, 0, 1141, 125, 1, 0, 0, 0, 1142, 1147, 3, 128, 64, 0, 1143, 1144, 5, 119, 0, 0, 1144, 1146, 3, 128, 64, 0, 1145, 1143, 1, 0, 0, 0, 1146, 1149, 1, 0, 0, 0, 1147, 1145, 1, 0, 0, 0, 1147, 1148, 1, 0, 0, 0, 1148, 1151, 1, 0, 0, 0, 1149, 1147, 1, 0, 0, 0, 1150, 1152, 5, 119, 0, 0, 1151, 1150, 1, 0, 0, 0, 1151, 1152, 1, 0, 0, 0, 1152, 127, 1, 0, 0, 0, 1153, 1154, 3, 158, 79, 0, 1154, 1155, 5, 6, 0, 0, 1155, 1156, 5, 133, 0, 0, 1156, 1157, 3, 48, 24, 0, 1157, 1158, 5, 152, 0, 0, 1158, 1164, 1, 0, 0, 0, 1159, 1160, 3, 118, 59, 0, 1160, 1161, 5, 6, 0, 0, 1161, 1162, 3, 158, 79, 0, 1162, 1164, 1, 0, 0, 0, 1163, 1153, 1, 0, 0, 0, 1163, 1159, 1, 0, 0, 0, 1164, 129, 1, 0, 0, 0, 1165, 1173, 3, 162, 81, 0, 1166, 1167, 3, 138, 69, 0, 1167, 1168, 5, 123, 0, 0, 1168, 1170, 1, 0, 0, 0, 1169, 1166, 1, 0, 0, 0, 1169, 1170, 1, 0, 0, 0, 1170, 1171, 1, 0, 0, 0, 1171, 1173, 3, 132, 66, 0, 1172, 1165, 1, 0, 0, 0, 1172, 1169, 1, 0, 0, 0, 1173, 131, 1, 0, 0, 0, 1174, 1179, 3, 158, 79, 0, 1175, 1176, 5, 123, 0, 0, 1176, 1178, 3, 158, 79, 0, 1177, 1175, 1, 0, 0, 0, 1178, 1181, 1, 0, 0, 0, 1179, 1177, 1, 0, 0, 0, 1179, 1180, 1, 0, 0, 0, 1180, 133, 1, 0, 0, 0, 1181, 1179, 1, 0, 0, 0, 1182, 1183, 6, 67, -1, 0, 1183, 1192, 3, 138, 69, 0, 1184, 1192, 3, 136, 68, 0, 1185, 1186, 5, 133, 0, 0, 1186, 1187, 3, 48, 24, 0, 1187, 1188, 5, 152, 0, 0, 1188, 1192, 1, 0, 0, 0, 1189, 1192, 3, 122, 61, 0, 1190, 1192, 3, 162, 81, 0, 1191, 1182, 1, 0, 0, 0, 1191, 1184, 1, 0, 0, 0, 1191, 1185, 1, 0, 0, 0, 1191, 1189, 1, 0, 0, 0, 1191, 1190, 1, 0, 0, 0, 1192, 1201, 1, 0, 0, 0, 1193, 1197, 10, 3, 0, 0, 1194, 1198, 3, 156, 78, 0, 1195, 1196, 5, 6, 0, 0, 1196, 1198, 3, 158, 79, 0, 1197, 1194, 1, 0, 0, 0, 1197, 1195, 1, 0, 0, 0, 1198, 1200, 1, 0, 0, 0, 1199, 1193, 1, 0, 0, 0, 1200, 1203, 1, 0, 0, 0, 1201, 1199, 1, 0, 0, 0, 1201, 1202, 1, 0, 0, 0, 1202, 135, 1, 0, 0, 0, 1203, 1201, 1, 0, 0, 0, 1204, 1205, 3, 158, 79, 0, 1205, 1207, 5, 133, 0, 0, 1206, 1208, 3, 140, 70, 0, 1207, 1206, 1, 0, 0, 0, 1207, 1208, 1, 0, 0, 0, 1208, 1209, 1, 0, 0, 0, 1209, 1210, 5, 152, 0, 0, 1210, 137, 1, 0, 0, 0, 1211, 1212, 3, 142, 71, 0, 1212, 1213, 5, 123, 0, 0, 1213, 1215, 1, 0, 0, 0, 1214, 1211, 1, 0, 0, 0, 1214, 1215, 1, 0, 0, 0, 1215, 1216, 1, 0, 0, 0, 1216, 1217, 3, 158, 79, 0, 1217, 139, 1, 0, 0, 0, 1218, 1223, 3, 118, 59, 0, 1219, 1220, 5, 119, 0, 0, 1220, 1222, 3, 118, 59, 0, 1221, 1219, 1, 0, 0, 0, 1222, 1225, 1, 0, 0, 0, 1223, 1221, 1, 0, 0, 0, 1223, 1224, 1, 0, 0, 0, 1224, 1227, 1, 0, 0, 0, 1225, 1223, 1, 0, 0, 0, 1226, 1228, 5, 119, 0, 0, 1227, 1226, 1, 0, 0, 0, 1227, 1228, 1, 0, 0, 0, 1228, 141, 1, 0, 0, 0, 1229, 1230, 3, 158, 79, 0, 1230, 143, 1, 0, 0, 0, 1231, 1240, 5, 109, 0, 0, 1232, 1233, 5, 123, 0, 0, 1233, 1240, 7, 12, 0, 0, 1234, 1235, 5, 111, 0, 0, 1235, 1237, 5, 123, 0, 0, 1236, 1238, 7, 12, 0, 0, 1237, 1236, 1, 0, 0, 0, 1237, 1238, 1, 0, 0, 0, 1238, 1240, 1, 0, 0, 0, 1239, 1231, 1, 0, 0, 0, 1239, 1232, 1, 0, 0, 0, 1239, 1234, 1, 0, 0, 0, 1240, 145, 1, 0, 0, 0, 1241, 1243, 7, 13, 0, 0, 1242, 1241, 1, 0, 0, 0, 1242, 1243, 1, 0, 0, 0, 1243, 1250, 1, 0, 0, 0, 1244, 1251, 3, 144, 72, 0, 1245, 1251, 5, 110, 0, 0, 1246, 1251, 5, 111, 0, 0, 1247, 1251, 5, 112, 0, 0, 1248, 1251, 5, 45, 0, 0, 1249, 1251, 5, 60, 0, 0, 1250, 1244, 1, 0, 0, 0, 1250, 1245, 1, 0, 0, 0, 1250, 1246, 1, 0, 0, 0, 1250, 1247, 1, 0, 0, 0, 1250, 1248, 1, 0, 0, 0, 1250, 1249, 1, 0, 0, 0, 1251, 147, 1, 0, 0, 0, 1252, 1256, 3, 146, 73, 0, 1253, 1256, 5, 113, 0, 0, 1254, 1256, 5, 62, 0, 0, 1255, 1252, 1, 0, 0, 0, 1255, 1253, 1, 0, 0, 0, 1255, 1254, 1, 0, 0, 0, 1256, 149, 1, 0, 0, 0, 1257, 1258, 7, 14, 0, 0, 1258, 151, 1, 0, 0, 0, 1259, 1260, 7, 15, 0, 0, 1260, 153, 1, 0, 0, 0, 1261, 1262, 7, 16, 0, 0, 1262, 155, 1, 0, 0, 0, 1263, 1266, 5, 108, 0, 0, 1264, 1266, 3, 154, 77, 0, 1265, 1263, 1, 0, 0, 0, 1265, 1264, 1, 0, 0, 0, 1266, 157, 1, 0, 0, 0, 1267, 1271, 5, 108, 0, 0, 1268, 1271, 3, 150, 75, 0, 1269, 1271, 3, 152, 76, 0, 1270, 1267, 1, 0, 0, 0, 1270, 1268, 1, 0, 0, 0, 1270, 1269, 1, 0, 0, 0, 1271, 159, 1, 0, 0, 0, 1272, 1273, 3, 164, 82, 0, 1273, 1274, 5, 125, 0, 0, 1274, 1275, 3, 146, 73, 0, 1275, 161, 1, 0, 0, 0, 1276, 1277, 5, 131, 0, 0, 1277, 1278, 3, 118, 59, 0, 1278, 1279, 5, 150, 0, 0, 1279, 163, 1, 0, 0, 0, 1280, 1283, 5, 113, 0, 0, 1281, 1283, 3, 166, 83, 0, 1282, 1280, 1, 0, 0, 0, 1282, 1281, 1, 0, 0, 0, 1283, 165, 1, 0, 0, 0, 1284, 1288, 5, 145, 0, 0, 1285, 1287, 3, 168, 84, 0, 1286, 1285, 1, 0, 0, 0, 1287, 1290, 1, 0, 0, 0, 1288, 1286, 1, 0, 0, 0, 1288, 1289, 1, 0, 0, 0, 1289, 1291, 1, 0, 0, 0, 1290, 1288, 1, 0, 0, 0, 1291, 1292, 5, 147, 0, 0, 1292, 167, 1, 0, 0, 0, 1293, 1294, 5, 160, 0, 0, 1294, 1295, 3, 118, 59, 0, 1295, 1296, 5, 150, 0, 0, 1296, 1299, 1, 0, 0, 0, 1297, 1299, 5, 159, 0, 0, 1298, 1293, 1, 0, 0, 0, 1298, 1297, 1, 0, 0, 0, 1299, 169, 1, 0, 0, 0, 1300, 1304, 5, 146, 0, 0, 1301, 1303, 3, 172, 86, 0, 1302, 1301, 1, 0, 0, 0, 1303, 1306, 1, 0, 0, 0, 1304, 1302, 1, 0, 0, 0, 1304, 1305, 1, 0, 0, 0, 1305, 1307, 1, 0, 0, 0, 1306, 1304, 1, 0, 0, 0, 1307, 1308, 5, 0, 0, 1, 1308, 171, 1, 0, 0, 0, 1309, 1310, 5, 162, 0, 0, 1310, 1311, 3, 118, 59, 0, 1311, 1312, 5, 150, 0, 0, 1312, 1315, 1, 0, 0, 0, 1313, 1315, 5, 161, 0, 0, 1314, 1309, 1, 0, 0, 0, 1314, 1313, 1, 0, 0, 0, 1315, 173, 1, 0, 0, 0, 168, 177, 184, 193, 200, 204, 218, 222, 225, 229, 232, 239, 243, 252, 257, 266, 274, 281, 285, 291, 296, 304, 311, 317, 329, 337, 351, 355, 360, 370, 380, 388, 392, 396, 399, 403, 406, 409, 412, 415, 419, 423, 426, 429, 432, 436, 439, 448, 454, 475, 492, 509, 515, 521, 532, 534, 545, 548, 554, 562, 568, 570, 574, 579, 582, 585, 589, 593, 596, 598, 601, 605, 609, 612, 614, 616, 621, 632, 638, 645, 650, 654, 658, 664, 666, 673, 681, 684, 687, 706, 720, 736, 740, 751, 755, 766, 770, 777, 781, 788, 792, 797, 806, 810, 836, 853, 859, 862, 865, 875, 881, 884, 887, 895, 898, 902, 905, 919, 936, 941, 946, 952, 959, 971, 975, 978, 987, 1001, 1017, 1046, 1054, 1056, 1058, 1067, 1071, 1080, 1084, 1088, 1093, 1100, 1111, 1120, 1127, 1140, 1147, 1151, 1163, 1169, 1172, 1179, 1191, 1197, 1201, 1207, 1214, 1223, 1227, 1237, 1239, 1242, 1250, 1255, 1265, 1270, 1282, 1288, 1298, 1304, 1314] \ No newline at end of file +[4, 1, 162, 1325, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 1, 0, 5, 0, 178, 8, 0, 10, 0, 12, 0, 181, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 187, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 196, 8, 3, 1, 4, 1, 4, 1, 4, 5, 4, 201, 8, 4, 10, 4, 12, 4, 204, 9, 4, 1, 4, 3, 4, 207, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 221, 8, 5, 1, 6, 1, 6, 3, 6, 225, 8, 6, 1, 6, 3, 6, 228, 8, 6, 1, 7, 1, 7, 3, 7, 232, 8, 7, 1, 7, 3, 7, 235, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 242, 8, 8, 1, 8, 1, 8, 3, 8, 246, 8, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 5, 9, 253, 8, 9, 10, 9, 12, 9, 256, 9, 9, 1, 9, 1, 9, 3, 9, 260, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 269, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 277, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 284, 8, 12, 1, 12, 1, 12, 3, 12, 288, 8, 12, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 294, 8, 12, 1, 12, 1, 12, 1, 12, 3, 12, 299, 8, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 307, 8, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 3, 13, 314, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 320, 8, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 3, 16, 332, 8, 16, 1, 17, 1, 17, 1, 18, 1, 18, 5, 18, 338, 8, 18, 10, 18, 12, 18, 341, 9, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 5, 20, 352, 8, 20, 10, 20, 12, 20, 355, 9, 20, 1, 20, 3, 20, 358, 8, 20, 1, 21, 1, 21, 1, 21, 3, 21, 363, 8, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 373, 8, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 3, 23, 383, 8, 23, 1, 23, 1, 23, 1, 24, 1, 24, 5, 24, 389, 8, 24, 10, 24, 12, 24, 392, 9, 24, 1, 25, 3, 25, 395, 8, 25, 1, 25, 1, 25, 3, 25, 399, 8, 25, 1, 25, 3, 25, 402, 8, 25, 1, 25, 1, 25, 3, 25, 406, 8, 25, 1, 25, 3, 25, 409, 8, 25, 1, 25, 3, 25, 412, 8, 25, 1, 25, 3, 25, 415, 8, 25, 1, 25, 3, 25, 418, 8, 25, 1, 25, 1, 25, 3, 25, 422, 8, 25, 1, 25, 1, 25, 3, 25, 426, 8, 25, 1, 25, 3, 25, 429, 8, 25, 1, 25, 3, 25, 432, 8, 25, 1, 25, 3, 25, 435, 8, 25, 1, 25, 1, 25, 3, 25, 439, 8, 25, 1, 25, 3, 25, 442, 8, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 451, 8, 27, 1, 28, 1, 28, 1, 28, 1, 29, 3, 29, 457, 8, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 476, 8, 30, 10, 30, 12, 30, 479, 9, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 3, 33, 495, 8, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 512, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 518, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 524, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 535, 8, 37, 3, 37, 537, 8, 37, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 3, 40, 548, 8, 40, 1, 40, 3, 40, 551, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 557, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 3, 40, 565, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 5, 40, 571, 8, 40, 10, 40, 12, 40, 574, 9, 40, 1, 41, 3, 41, 577, 8, 41, 1, 41, 1, 41, 1, 41, 3, 41, 582, 8, 41, 1, 41, 3, 41, 585, 8, 41, 1, 41, 3, 41, 588, 8, 41, 1, 41, 1, 41, 3, 41, 592, 8, 41, 1, 41, 1, 41, 3, 41, 596, 8, 41, 1, 41, 3, 41, 599, 8, 41, 3, 41, 601, 8, 41, 1, 41, 3, 41, 604, 8, 41, 1, 41, 1, 41, 3, 41, 608, 8, 41, 1, 41, 1, 41, 3, 41, 612, 8, 41, 1, 41, 3, 41, 615, 8, 41, 3, 41, 617, 8, 41, 3, 41, 619, 8, 41, 1, 42, 1, 42, 1, 42, 3, 42, 624, 8, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 3, 43, 635, 8, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 641, 8, 44, 1, 45, 1, 45, 1, 45, 5, 45, 646, 8, 45, 10, 45, 12, 45, 649, 9, 45, 1, 46, 1, 46, 3, 46, 653, 8, 46, 1, 46, 1, 46, 3, 46, 657, 8, 46, 1, 46, 1, 46, 3, 46, 661, 8, 46, 1, 47, 1, 47, 1, 47, 1, 47, 3, 47, 667, 8, 47, 3, 47, 669, 8, 47, 1, 48, 1, 48, 1, 48, 5, 48, 674, 8, 48, 10, 48, 12, 48, 677, 9, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 3, 50, 684, 8, 50, 1, 50, 3, 50, 687, 8, 50, 1, 50, 3, 50, 690, 8, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 3, 54, 709, 8, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 3, 55, 723, 8, 55, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 737, 8, 57, 10, 57, 12, 57, 740, 9, 57, 1, 57, 3, 57, 743, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 752, 8, 57, 10, 57, 12, 57, 755, 9, 57, 1, 57, 3, 57, 758, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 5, 57, 767, 8, 57, 10, 57, 12, 57, 770, 9, 57, 1, 57, 3, 57, 773, 8, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 3, 57, 780, 8, 57, 1, 57, 1, 57, 3, 57, 784, 8, 57, 1, 58, 1, 58, 1, 58, 5, 58, 789, 8, 58, 10, 58, 12, 58, 792, 9, 58, 1, 58, 3, 58, 795, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 800, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 4, 59, 807, 8, 59, 11, 59, 12, 59, 808, 1, 59, 1, 59, 3, 59, 813, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 839, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 856, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 862, 8, 59, 1, 59, 3, 59, 865, 8, 59, 1, 59, 3, 59, 868, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 878, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 884, 8, 59, 1, 59, 3, 59, 887, 8, 59, 1, 59, 3, 59, 890, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 898, 8, 59, 1, 59, 3, 59, 901, 8, 59, 1, 59, 1, 59, 3, 59, 905, 8, 59, 1, 59, 3, 59, 908, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 922, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 939, 8, 59, 1, 59, 1, 59, 1, 59, 3, 59, 944, 8, 59, 1, 59, 1, 59, 1, 59, 3, 59, 949, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 955, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 962, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 974, 8, 59, 1, 59, 1, 59, 3, 59, 978, 8, 59, 1, 59, 3, 59, 981, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 990, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1004, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1020, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1049, 8, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 1057, 8, 59, 5, 59, 1059, 8, 59, 10, 59, 12, 59, 1062, 9, 59, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 1068, 8, 60, 10, 60, 12, 60, 1071, 9, 60, 1, 60, 3, 60, 1074, 8, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 5, 60, 1081, 8, 60, 10, 60, 12, 60, 1084, 9, 60, 1, 60, 3, 60, 1087, 8, 60, 1, 60, 1, 60, 3, 60, 1091, 8, 60, 1, 60, 1, 60, 1, 60, 3, 60, 1096, 8, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 1103, 8, 61, 1, 62, 1, 62, 1, 62, 5, 62, 1108, 8, 62, 10, 62, 12, 62, 1111, 9, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 5, 62, 1119, 8, 62, 10, 62, 12, 62, 1122, 9, 62, 1, 62, 1, 62, 5, 62, 1126, 8, 62, 10, 62, 12, 62, 1129, 9, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 3, 62, 1136, 8, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 3, 63, 1149, 8, 63, 1, 64, 1, 64, 1, 64, 5, 64, 1154, 8, 64, 10, 64, 12, 64, 1157, 9, 64, 1, 64, 3, 64, 1160, 8, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 3, 65, 1172, 8, 65, 1, 66, 1, 66, 1, 66, 1, 66, 3, 66, 1178, 8, 66, 1, 66, 3, 66, 1181, 8, 66, 1, 67, 1, 67, 1, 67, 5, 67, 1186, 8, 67, 10, 67, 12, 67, 1189, 9, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 3, 68, 1200, 8, 68, 1, 68, 1, 68, 1, 68, 1, 68, 3, 68, 1206, 8, 68, 5, 68, 1208, 8, 68, 10, 68, 12, 68, 1211, 9, 68, 1, 69, 1, 69, 1, 69, 3, 69, 1216, 8, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 3, 70, 1223, 8, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 5, 71, 1230, 8, 71, 10, 71, 12, 71, 1233, 9, 71, 1, 71, 3, 71, 1236, 8, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 3, 73, 1246, 8, 73, 3, 73, 1248, 8, 73, 1, 74, 3, 74, 1251, 8, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 3, 74, 1259, 8, 74, 1, 75, 1, 75, 1, 75, 3, 75, 1264, 8, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 79, 1, 79, 3, 79, 1274, 8, 79, 1, 80, 1, 80, 1, 80, 3, 80, 1279, 8, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 3, 83, 1291, 8, 83, 1, 84, 1, 84, 5, 84, 1295, 8, 84, 10, 84, 12, 84, 1298, 9, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 3, 85, 1307, 8, 85, 1, 86, 1, 86, 5, 86, 1311, 8, 86, 10, 86, 12, 86, 1314, 9, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 3, 87, 1323, 8, 87, 1, 87, 0, 3, 80, 118, 136, 88, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 0, 17, 2, 0, 32, 32, 37, 37, 2, 0, 18, 18, 77, 77, 2, 0, 46, 46, 54, 54, 3, 0, 1, 1, 4, 4, 8, 8, 4, 0, 1, 1, 3, 4, 8, 8, 83, 83, 2, 0, 54, 54, 76, 76, 2, 0, 1, 1, 4, 4, 2, 0, 7, 7, 22, 23, 2, 0, 31, 31, 52, 52, 2, 0, 74, 74, 79, 79, 3, 0, 10, 10, 53, 53, 93, 93, 2, 0, 43, 43, 56, 56, 1, 0, 110, 111, 2, 0, 121, 121, 142, 142, 7, 0, 21, 21, 40, 40, 58, 59, 73, 73, 81, 81, 100, 100, 106, 106, 19, 0, 1, 13, 15, 20, 22, 26, 28, 29, 31, 31, 33, 36, 38, 39, 41, 44, 46, 46, 48, 54, 56, 57, 61, 61, 63, 72, 74, 80, 82, 86, 88, 95, 97, 99, 101, 102, 104, 105, 4, 0, 20, 20, 31, 31, 41, 41, 51, 51, 1500, 0, 179, 1, 0, 0, 0, 2, 186, 1, 0, 0, 0, 4, 188, 1, 0, 0, 0, 6, 190, 1, 0, 0, 0, 8, 197, 1, 0, 0, 0, 10, 220, 1, 0, 0, 0, 12, 222, 1, 0, 0, 0, 14, 229, 1, 0, 0, 0, 16, 236, 1, 0, 0, 0, 18, 249, 1, 0, 0, 0, 20, 261, 1, 0, 0, 0, 22, 270, 1, 0, 0, 0, 24, 278, 1, 0, 0, 0, 26, 300, 1, 0, 0, 0, 28, 315, 1, 0, 0, 0, 30, 324, 1, 0, 0, 0, 32, 329, 1, 0, 0, 0, 34, 333, 1, 0, 0, 0, 36, 335, 1, 0, 0, 0, 38, 344, 1, 0, 0, 0, 40, 348, 1, 0, 0, 0, 42, 362, 1, 0, 0, 0, 44, 372, 1, 0, 0, 0, 46, 382, 1, 0, 0, 0, 48, 386, 1, 0, 0, 0, 50, 394, 1, 0, 0, 0, 52, 443, 1, 0, 0, 0, 54, 446, 1, 0, 0, 0, 56, 452, 1, 0, 0, 0, 58, 456, 1, 0, 0, 0, 60, 462, 1, 0, 0, 0, 62, 480, 1, 0, 0, 0, 64, 483, 1, 0, 0, 0, 66, 486, 1, 0, 0, 0, 68, 496, 1, 0, 0, 0, 70, 499, 1, 0, 0, 0, 72, 503, 1, 0, 0, 0, 74, 536, 1, 0, 0, 0, 76, 538, 1, 0, 0, 0, 78, 541, 1, 0, 0, 0, 80, 556, 1, 0, 0, 0, 82, 618, 1, 0, 0, 0, 84, 623, 1, 0, 0, 0, 86, 634, 1, 0, 0, 0, 88, 636, 1, 0, 0, 0, 90, 642, 1, 0, 0, 0, 92, 650, 1, 0, 0, 0, 94, 668, 1, 0, 0, 0, 96, 670, 1, 0, 0, 0, 98, 678, 1, 0, 0, 0, 100, 683, 1, 0, 0, 0, 102, 691, 1, 0, 0, 0, 104, 695, 1, 0, 0, 0, 106, 699, 1, 0, 0, 0, 108, 708, 1, 0, 0, 0, 110, 722, 1, 0, 0, 0, 112, 724, 1, 0, 0, 0, 114, 783, 1, 0, 0, 0, 116, 785, 1, 0, 0, 0, 118, 948, 1, 0, 0, 0, 120, 1090, 1, 0, 0, 0, 122, 1102, 1, 0, 0, 0, 124, 1135, 1, 0, 0, 0, 126, 1148, 1, 0, 0, 0, 128, 1150, 1, 0, 0, 0, 130, 1171, 1, 0, 0, 0, 132, 1180, 1, 0, 0, 0, 134, 1182, 1, 0, 0, 0, 136, 1199, 1, 0, 0, 0, 138, 1212, 1, 0, 0, 0, 140, 1222, 1, 0, 0, 0, 142, 1226, 1, 0, 0, 0, 144, 1237, 1, 0, 0, 0, 146, 1247, 1, 0, 0, 0, 148, 1250, 1, 0, 0, 0, 150, 1263, 1, 0, 0, 0, 152, 1265, 1, 0, 0, 0, 154, 1267, 1, 0, 0, 0, 156, 1269, 1, 0, 0, 0, 158, 1273, 1, 0, 0, 0, 160, 1278, 1, 0, 0, 0, 162, 1280, 1, 0, 0, 0, 164, 1284, 1, 0, 0, 0, 166, 1290, 1, 0, 0, 0, 168, 1292, 1, 0, 0, 0, 170, 1306, 1, 0, 0, 0, 172, 1308, 1, 0, 0, 0, 174, 1322, 1, 0, 0, 0, 176, 178, 3, 2, 1, 0, 177, 176, 1, 0, 0, 0, 178, 181, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 179, 180, 1, 0, 0, 0, 180, 182, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 182, 183, 5, 0, 0, 1, 183, 1, 1, 0, 0, 0, 184, 187, 3, 6, 3, 0, 185, 187, 3, 10, 5, 0, 186, 184, 1, 0, 0, 0, 186, 185, 1, 0, 0, 0, 187, 3, 1, 0, 0, 0, 188, 189, 3, 118, 59, 0, 189, 5, 1, 0, 0, 0, 190, 191, 5, 55, 0, 0, 191, 195, 3, 160, 80, 0, 192, 193, 5, 118, 0, 0, 193, 194, 5, 125, 0, 0, 194, 196, 3, 4, 2, 0, 195, 192, 1, 0, 0, 0, 195, 196, 1, 0, 0, 0, 196, 7, 1, 0, 0, 0, 197, 202, 3, 160, 80, 0, 198, 199, 5, 119, 0, 0, 199, 201, 3, 160, 80, 0, 200, 198, 1, 0, 0, 0, 201, 204, 1, 0, 0, 0, 202, 200, 1, 0, 0, 0, 202, 203, 1, 0, 0, 0, 203, 206, 1, 0, 0, 0, 204, 202, 1, 0, 0, 0, 205, 207, 5, 119, 0, 0, 206, 205, 1, 0, 0, 0, 206, 207, 1, 0, 0, 0, 207, 9, 1, 0, 0, 0, 208, 221, 3, 12, 6, 0, 209, 221, 3, 14, 7, 0, 210, 221, 3, 18, 9, 0, 211, 221, 3, 20, 10, 0, 212, 221, 3, 22, 11, 0, 213, 221, 3, 26, 13, 0, 214, 221, 3, 24, 12, 0, 215, 221, 3, 28, 14, 0, 216, 221, 3, 30, 15, 0, 217, 221, 3, 36, 18, 0, 218, 221, 3, 32, 16, 0, 219, 221, 3, 34, 17, 0, 220, 208, 1, 0, 0, 0, 220, 209, 1, 0, 0, 0, 220, 210, 1, 0, 0, 0, 220, 211, 1, 0, 0, 0, 220, 212, 1, 0, 0, 0, 220, 213, 1, 0, 0, 0, 220, 214, 1, 0, 0, 0, 220, 215, 1, 0, 0, 0, 220, 216, 1, 0, 0, 0, 220, 217, 1, 0, 0, 0, 220, 218, 1, 0, 0, 0, 220, 219, 1, 0, 0, 0, 221, 11, 1, 0, 0, 0, 222, 224, 5, 75, 0, 0, 223, 225, 3, 4, 2, 0, 224, 223, 1, 0, 0, 0, 224, 225, 1, 0, 0, 0, 225, 227, 1, 0, 0, 0, 226, 228, 5, 153, 0, 0, 227, 226, 1, 0, 0, 0, 227, 228, 1, 0, 0, 0, 228, 13, 1, 0, 0, 0, 229, 231, 5, 87, 0, 0, 230, 232, 3, 4, 2, 0, 231, 230, 1, 0, 0, 0, 231, 232, 1, 0, 0, 0, 232, 234, 1, 0, 0, 0, 233, 235, 5, 153, 0, 0, 234, 233, 1, 0, 0, 0, 234, 235, 1, 0, 0, 0, 235, 15, 1, 0, 0, 0, 236, 245, 5, 14, 0, 0, 237, 238, 5, 133, 0, 0, 238, 241, 3, 160, 80, 0, 239, 240, 5, 118, 0, 0, 240, 242, 3, 160, 80, 0, 241, 239, 1, 0, 0, 0, 241, 242, 1, 0, 0, 0, 242, 243, 1, 0, 0, 0, 243, 244, 5, 152, 0, 0, 244, 246, 1, 0, 0, 0, 245, 237, 1, 0, 0, 0, 245, 246, 1, 0, 0, 0, 246, 247, 1, 0, 0, 0, 247, 248, 3, 36, 18, 0, 248, 17, 1, 0, 0, 0, 249, 250, 5, 96, 0, 0, 250, 254, 3, 36, 18, 0, 251, 253, 3, 16, 8, 0, 252, 251, 1, 0, 0, 0, 253, 256, 1, 0, 0, 0, 254, 252, 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 259, 1, 0, 0, 0, 256, 254, 1, 0, 0, 0, 257, 258, 5, 30, 0, 0, 258, 260, 3, 36, 18, 0, 259, 257, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, 19, 1, 0, 0, 0, 261, 262, 5, 42, 0, 0, 262, 263, 5, 133, 0, 0, 263, 264, 3, 4, 2, 0, 264, 265, 5, 152, 0, 0, 265, 268, 3, 10, 5, 0, 266, 267, 5, 25, 0, 0, 267, 269, 3, 10, 5, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 21, 1, 0, 0, 0, 270, 271, 5, 103, 0, 0, 271, 272, 5, 133, 0, 0, 272, 273, 3, 4, 2, 0, 273, 274, 5, 152, 0, 0, 274, 276, 3, 10, 5, 0, 275, 277, 5, 153, 0, 0, 276, 275, 1, 0, 0, 0, 276, 277, 1, 0, 0, 0, 277, 23, 1, 0, 0, 0, 278, 279, 5, 34, 0, 0, 279, 283, 5, 133, 0, 0, 280, 284, 3, 6, 3, 0, 281, 284, 3, 30, 15, 0, 282, 284, 3, 4, 2, 0, 283, 280, 1, 0, 0, 0, 283, 281, 1, 0, 0, 0, 283, 282, 1, 0, 0, 0, 283, 284, 1, 0, 0, 0, 284, 285, 1, 0, 0, 0, 285, 287, 5, 153, 0, 0, 286, 288, 3, 4, 2, 0, 287, 286, 1, 0, 0, 0, 287, 288, 1, 0, 0, 0, 288, 289, 1, 0, 0, 0, 289, 293, 5, 153, 0, 0, 290, 294, 3, 6, 3, 0, 291, 294, 3, 30, 15, 0, 292, 294, 3, 4, 2, 0, 293, 290, 1, 0, 0, 0, 293, 291, 1, 0, 0, 0, 293, 292, 1, 0, 0, 0, 293, 294, 1, 0, 0, 0, 294, 295, 1, 0, 0, 0, 295, 296, 5, 152, 0, 0, 296, 298, 3, 10, 5, 0, 297, 299, 5, 153, 0, 0, 298, 297, 1, 0, 0, 0, 298, 299, 1, 0, 0, 0, 299, 25, 1, 0, 0, 0, 300, 301, 5, 34, 0, 0, 301, 302, 5, 133, 0, 0, 302, 303, 5, 55, 0, 0, 303, 306, 3, 160, 80, 0, 304, 305, 5, 119, 0, 0, 305, 307, 3, 160, 80, 0, 306, 304, 1, 0, 0, 0, 306, 307, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308, 309, 5, 44, 0, 0, 309, 310, 3, 4, 2, 0, 310, 311, 5, 152, 0, 0, 311, 313, 3, 10, 5, 0, 312, 314, 5, 153, 0, 0, 313, 312, 1, 0, 0, 0, 313, 314, 1, 0, 0, 0, 314, 27, 1, 0, 0, 0, 315, 316, 7, 0, 0, 0, 316, 317, 3, 160, 80, 0, 317, 319, 5, 133, 0, 0, 318, 320, 3, 8, 4, 0, 319, 318, 1, 0, 0, 0, 319, 320, 1, 0, 0, 0, 320, 321, 1, 0, 0, 0, 321, 322, 5, 152, 0, 0, 322, 323, 3, 36, 18, 0, 323, 29, 1, 0, 0, 0, 324, 325, 3, 4, 2, 0, 325, 326, 5, 118, 0, 0, 326, 327, 5, 125, 0, 0, 327, 328, 3, 4, 2, 0, 328, 31, 1, 0, 0, 0, 329, 331, 3, 4, 2, 0, 330, 332, 5, 153, 0, 0, 331, 330, 1, 0, 0, 0, 331, 332, 1, 0, 0, 0, 332, 33, 1, 0, 0, 0, 333, 334, 5, 153, 0, 0, 334, 35, 1, 0, 0, 0, 335, 339, 5, 131, 0, 0, 336, 338, 3, 2, 1, 0, 337, 336, 1, 0, 0, 0, 338, 341, 1, 0, 0, 0, 339, 337, 1, 0, 0, 0, 339, 340, 1, 0, 0, 0, 340, 342, 1, 0, 0, 0, 341, 339, 1, 0, 0, 0, 342, 343, 5, 150, 0, 0, 343, 37, 1, 0, 0, 0, 344, 345, 3, 4, 2, 0, 345, 346, 5, 118, 0, 0, 346, 347, 3, 4, 2, 0, 347, 39, 1, 0, 0, 0, 348, 353, 3, 38, 19, 0, 349, 350, 5, 119, 0, 0, 350, 352, 3, 38, 19, 0, 351, 349, 1, 0, 0, 0, 352, 355, 1, 0, 0, 0, 353, 351, 1, 0, 0, 0, 353, 354, 1, 0, 0, 0, 354, 357, 1, 0, 0, 0, 355, 353, 1, 0, 0, 0, 356, 358, 5, 119, 0, 0, 357, 356, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 41, 1, 0, 0, 0, 359, 363, 3, 48, 24, 0, 360, 363, 3, 50, 25, 0, 361, 363, 3, 124, 62, 0, 362, 359, 1, 0, 0, 0, 362, 360, 1, 0, 0, 0, 362, 361, 1, 0, 0, 0, 363, 364, 1, 0, 0, 0, 364, 365, 5, 0, 0, 1, 365, 43, 1, 0, 0, 0, 366, 373, 3, 50, 25, 0, 367, 368, 5, 133, 0, 0, 368, 369, 3, 48, 24, 0, 369, 370, 5, 152, 0, 0, 370, 373, 1, 0, 0, 0, 371, 373, 3, 164, 82, 0, 372, 366, 1, 0, 0, 0, 372, 367, 1, 0, 0, 0, 372, 371, 1, 0, 0, 0, 373, 45, 1, 0, 0, 0, 374, 383, 5, 27, 0, 0, 375, 376, 5, 98, 0, 0, 376, 383, 5, 1, 0, 0, 377, 378, 5, 98, 0, 0, 378, 383, 5, 24, 0, 0, 379, 383, 5, 47, 0, 0, 380, 381, 5, 47, 0, 0, 381, 383, 5, 24, 0, 0, 382, 374, 1, 0, 0, 0, 382, 375, 1, 0, 0, 0, 382, 377, 1, 0, 0, 0, 382, 379, 1, 0, 0, 0, 382, 380, 1, 0, 0, 0, 383, 384, 1, 0, 0, 0, 384, 385, 3, 44, 22, 0, 385, 47, 1, 0, 0, 0, 386, 390, 3, 44, 22, 0, 387, 389, 3, 46, 23, 0, 388, 387, 1, 0, 0, 0, 389, 392, 1, 0, 0, 0, 390, 388, 1, 0, 0, 0, 390, 391, 1, 0, 0, 0, 391, 49, 1, 0, 0, 0, 392, 390, 1, 0, 0, 0, 393, 395, 3, 52, 26, 0, 394, 393, 1, 0, 0, 0, 394, 395, 1, 0, 0, 0, 395, 396, 1, 0, 0, 0, 396, 398, 5, 82, 0, 0, 397, 399, 5, 24, 0, 0, 398, 397, 1, 0, 0, 0, 398, 399, 1, 0, 0, 0, 399, 401, 1, 0, 0, 0, 400, 402, 3, 54, 27, 0, 401, 400, 1, 0, 0, 0, 401, 402, 1, 0, 0, 0, 402, 403, 1, 0, 0, 0, 403, 405, 3, 116, 58, 0, 404, 406, 3, 56, 28, 0, 405, 404, 1, 0, 0, 0, 405, 406, 1, 0, 0, 0, 406, 408, 1, 0, 0, 0, 407, 409, 3, 58, 29, 0, 408, 407, 1, 0, 0, 0, 408, 409, 1, 0, 0, 0, 409, 411, 1, 0, 0, 0, 410, 412, 3, 62, 31, 0, 411, 410, 1, 0, 0, 0, 411, 412, 1, 0, 0, 0, 412, 414, 1, 0, 0, 0, 413, 415, 3, 64, 32, 0, 414, 413, 1, 0, 0, 0, 414, 415, 1, 0, 0, 0, 415, 417, 1, 0, 0, 0, 416, 418, 3, 66, 33, 0, 417, 416, 1, 0, 0, 0, 417, 418, 1, 0, 0, 0, 418, 421, 1, 0, 0, 0, 419, 420, 5, 105, 0, 0, 420, 422, 7, 1, 0, 0, 421, 419, 1, 0, 0, 0, 421, 422, 1, 0, 0, 0, 422, 425, 1, 0, 0, 0, 423, 424, 5, 105, 0, 0, 424, 426, 5, 92, 0, 0, 425, 423, 1, 0, 0, 0, 425, 426, 1, 0, 0, 0, 426, 428, 1, 0, 0, 0, 427, 429, 3, 68, 34, 0, 428, 427, 1, 0, 0, 0, 428, 429, 1, 0, 0, 0, 429, 431, 1, 0, 0, 0, 430, 432, 3, 60, 30, 0, 431, 430, 1, 0, 0, 0, 431, 432, 1, 0, 0, 0, 432, 434, 1, 0, 0, 0, 433, 435, 3, 70, 35, 0, 434, 433, 1, 0, 0, 0, 434, 435, 1, 0, 0, 0, 435, 438, 1, 0, 0, 0, 436, 439, 3, 74, 37, 0, 437, 439, 3, 76, 38, 0, 438, 436, 1, 0, 0, 0, 438, 437, 1, 0, 0, 0, 438, 439, 1, 0, 0, 0, 439, 441, 1, 0, 0, 0, 440, 442, 3, 78, 39, 0, 441, 440, 1, 0, 0, 0, 441, 442, 1, 0, 0, 0, 442, 51, 1, 0, 0, 0, 443, 444, 5, 105, 0, 0, 444, 445, 3, 128, 64, 0, 445, 53, 1, 0, 0, 0, 446, 447, 5, 91, 0, 0, 447, 450, 5, 111, 0, 0, 448, 449, 5, 105, 0, 0, 449, 451, 5, 88, 0, 0, 450, 448, 1, 0, 0, 0, 450, 451, 1, 0, 0, 0, 451, 55, 1, 0, 0, 0, 452, 453, 5, 35, 0, 0, 453, 454, 3, 80, 40, 0, 454, 57, 1, 0, 0, 0, 455, 457, 7, 2, 0, 0, 456, 455, 1, 0, 0, 0, 456, 457, 1, 0, 0, 0, 457, 458, 1, 0, 0, 0, 458, 459, 5, 5, 0, 0, 459, 460, 5, 50, 0, 0, 460, 461, 3, 116, 58, 0, 461, 59, 1, 0, 0, 0, 462, 463, 5, 104, 0, 0, 463, 464, 3, 160, 80, 0, 464, 465, 5, 6, 0, 0, 465, 466, 5, 133, 0, 0, 466, 467, 3, 100, 50, 0, 467, 477, 5, 152, 0, 0, 468, 469, 5, 119, 0, 0, 469, 470, 3, 160, 80, 0, 470, 471, 5, 6, 0, 0, 471, 472, 5, 133, 0, 0, 472, 473, 3, 100, 50, 0, 473, 474, 5, 152, 0, 0, 474, 476, 1, 0, 0, 0, 475, 468, 1, 0, 0, 0, 476, 479, 1, 0, 0, 0, 477, 475, 1, 0, 0, 0, 477, 478, 1, 0, 0, 0, 478, 61, 1, 0, 0, 0, 479, 477, 1, 0, 0, 0, 480, 481, 5, 72, 0, 0, 481, 482, 3, 118, 59, 0, 482, 63, 1, 0, 0, 0, 483, 484, 5, 102, 0, 0, 484, 485, 3, 118, 59, 0, 485, 65, 1, 0, 0, 0, 486, 487, 5, 38, 0, 0, 487, 494, 5, 11, 0, 0, 488, 489, 7, 1, 0, 0, 489, 490, 5, 133, 0, 0, 490, 491, 3, 116, 58, 0, 491, 492, 5, 152, 0, 0, 492, 495, 1, 0, 0, 0, 493, 495, 3, 116, 58, 0, 494, 488, 1, 0, 0, 0, 494, 493, 1, 0, 0, 0, 495, 67, 1, 0, 0, 0, 496, 497, 5, 39, 0, 0, 497, 498, 3, 118, 59, 0, 498, 69, 1, 0, 0, 0, 499, 500, 5, 67, 0, 0, 500, 501, 5, 11, 0, 0, 501, 502, 3, 90, 45, 0, 502, 71, 1, 0, 0, 0, 503, 504, 5, 67, 0, 0, 504, 505, 5, 11, 0, 0, 505, 506, 3, 116, 58, 0, 506, 73, 1, 0, 0, 0, 507, 508, 5, 57, 0, 0, 508, 511, 3, 118, 59, 0, 509, 510, 5, 119, 0, 0, 510, 512, 3, 118, 59, 0, 511, 509, 1, 0, 0, 0, 511, 512, 1, 0, 0, 0, 512, 517, 1, 0, 0, 0, 513, 514, 5, 105, 0, 0, 514, 518, 5, 88, 0, 0, 515, 516, 5, 11, 0, 0, 516, 518, 3, 116, 58, 0, 517, 513, 1, 0, 0, 0, 517, 515, 1, 0, 0, 0, 517, 518, 1, 0, 0, 0, 518, 537, 1, 0, 0, 0, 519, 520, 5, 57, 0, 0, 520, 523, 3, 118, 59, 0, 521, 522, 5, 105, 0, 0, 522, 524, 5, 88, 0, 0, 523, 521, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 525, 1, 0, 0, 0, 525, 526, 5, 64, 0, 0, 526, 527, 3, 118, 59, 0, 527, 537, 1, 0, 0, 0, 528, 529, 5, 57, 0, 0, 529, 530, 3, 118, 59, 0, 530, 531, 5, 64, 0, 0, 531, 534, 3, 118, 59, 0, 532, 533, 5, 11, 0, 0, 533, 535, 3, 116, 58, 0, 534, 532, 1, 0, 0, 0, 534, 535, 1, 0, 0, 0, 535, 537, 1, 0, 0, 0, 536, 507, 1, 0, 0, 0, 536, 519, 1, 0, 0, 0, 536, 528, 1, 0, 0, 0, 537, 75, 1, 0, 0, 0, 538, 539, 5, 64, 0, 0, 539, 540, 3, 118, 59, 0, 540, 77, 1, 0, 0, 0, 541, 542, 5, 84, 0, 0, 542, 543, 3, 96, 48, 0, 543, 79, 1, 0, 0, 0, 544, 545, 6, 40, -1, 0, 545, 547, 3, 136, 68, 0, 546, 548, 5, 29, 0, 0, 547, 546, 1, 0, 0, 0, 547, 548, 1, 0, 0, 0, 548, 550, 1, 0, 0, 0, 549, 551, 3, 88, 44, 0, 550, 549, 1, 0, 0, 0, 550, 551, 1, 0, 0, 0, 551, 557, 1, 0, 0, 0, 552, 553, 5, 133, 0, 0, 553, 554, 3, 80, 40, 0, 554, 555, 5, 152, 0, 0, 555, 557, 1, 0, 0, 0, 556, 544, 1, 0, 0, 0, 556, 552, 1, 0, 0, 0, 557, 572, 1, 0, 0, 0, 558, 559, 10, 3, 0, 0, 559, 560, 3, 84, 42, 0, 560, 561, 3, 80, 40, 4, 561, 571, 1, 0, 0, 0, 562, 564, 10, 4, 0, 0, 563, 565, 3, 82, 41, 0, 564, 563, 1, 0, 0, 0, 564, 565, 1, 0, 0, 0, 565, 566, 1, 0, 0, 0, 566, 567, 5, 50, 0, 0, 567, 568, 3, 80, 40, 0, 568, 569, 3, 86, 43, 0, 569, 571, 1, 0, 0, 0, 570, 558, 1, 0, 0, 0, 570, 562, 1, 0, 0, 0, 571, 574, 1, 0, 0, 0, 572, 570, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 81, 1, 0, 0, 0, 574, 572, 1, 0, 0, 0, 575, 577, 7, 3, 0, 0, 576, 575, 1, 0, 0, 0, 576, 577, 1, 0, 0, 0, 577, 578, 1, 0, 0, 0, 578, 585, 5, 46, 0, 0, 579, 581, 5, 46, 0, 0, 580, 582, 7, 3, 0, 0, 581, 580, 1, 0, 0, 0, 581, 582, 1, 0, 0, 0, 582, 585, 1, 0, 0, 0, 583, 585, 7, 3, 0, 0, 584, 576, 1, 0, 0, 0, 584, 579, 1, 0, 0, 0, 584, 583, 1, 0, 0, 0, 585, 619, 1, 0, 0, 0, 586, 588, 7, 4, 0, 0, 587, 586, 1, 0, 0, 0, 587, 588, 1, 0, 0, 0, 588, 589, 1, 0, 0, 0, 589, 591, 7, 5, 0, 0, 590, 592, 5, 68, 0, 0, 591, 590, 1, 0, 0, 0, 591, 592, 1, 0, 0, 0, 592, 601, 1, 0, 0, 0, 593, 595, 7, 5, 0, 0, 594, 596, 5, 68, 0, 0, 595, 594, 1, 0, 0, 0, 595, 596, 1, 0, 0, 0, 596, 598, 1, 0, 0, 0, 597, 599, 7, 4, 0, 0, 598, 597, 1, 0, 0, 0, 598, 599, 1, 0, 0, 0, 599, 601, 1, 0, 0, 0, 600, 587, 1, 0, 0, 0, 600, 593, 1, 0, 0, 0, 601, 619, 1, 0, 0, 0, 602, 604, 7, 6, 0, 0, 603, 602, 1, 0, 0, 0, 603, 604, 1, 0, 0, 0, 604, 605, 1, 0, 0, 0, 605, 607, 5, 36, 0, 0, 606, 608, 5, 68, 0, 0, 607, 606, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 617, 1, 0, 0, 0, 609, 611, 5, 36, 0, 0, 610, 612, 5, 68, 0, 0, 611, 610, 1, 0, 0, 0, 611, 612, 1, 0, 0, 0, 612, 614, 1, 0, 0, 0, 613, 615, 7, 6, 0, 0, 614, 613, 1, 0, 0, 0, 614, 615, 1, 0, 0, 0, 615, 617, 1, 0, 0, 0, 616, 603, 1, 0, 0, 0, 616, 609, 1, 0, 0, 0, 617, 619, 1, 0, 0, 0, 618, 584, 1, 0, 0, 0, 618, 600, 1, 0, 0, 0, 618, 616, 1, 0, 0, 0, 619, 83, 1, 0, 0, 0, 620, 621, 5, 17, 0, 0, 621, 624, 5, 50, 0, 0, 622, 624, 5, 119, 0, 0, 623, 620, 1, 0, 0, 0, 623, 622, 1, 0, 0, 0, 624, 85, 1, 0, 0, 0, 625, 626, 5, 65, 0, 0, 626, 635, 3, 116, 58, 0, 627, 628, 5, 99, 0, 0, 628, 629, 5, 133, 0, 0, 629, 630, 3, 116, 58, 0, 630, 631, 5, 152, 0, 0, 631, 635, 1, 0, 0, 0, 632, 633, 5, 99, 0, 0, 633, 635, 3, 116, 58, 0, 634, 625, 1, 0, 0, 0, 634, 627, 1, 0, 0, 0, 634, 632, 1, 0, 0, 0, 635, 87, 1, 0, 0, 0, 636, 637, 5, 80, 0, 0, 637, 640, 3, 94, 47, 0, 638, 639, 5, 64, 0, 0, 639, 641, 3, 94, 47, 0, 640, 638, 1, 0, 0, 0, 640, 641, 1, 0, 0, 0, 641, 89, 1, 0, 0, 0, 642, 647, 3, 92, 46, 0, 643, 644, 5, 119, 0, 0, 644, 646, 3, 92, 46, 0, 645, 643, 1, 0, 0, 0, 646, 649, 1, 0, 0, 0, 647, 645, 1, 0, 0, 0, 647, 648, 1, 0, 0, 0, 648, 91, 1, 0, 0, 0, 649, 647, 1, 0, 0, 0, 650, 652, 3, 118, 59, 0, 651, 653, 7, 7, 0, 0, 652, 651, 1, 0, 0, 0, 652, 653, 1, 0, 0, 0, 653, 656, 1, 0, 0, 0, 654, 655, 5, 63, 0, 0, 655, 657, 7, 8, 0, 0, 656, 654, 1, 0, 0, 0, 656, 657, 1, 0, 0, 0, 657, 660, 1, 0, 0, 0, 658, 659, 5, 16, 0, 0, 659, 661, 5, 113, 0, 0, 660, 658, 1, 0, 0, 0, 660, 661, 1, 0, 0, 0, 661, 93, 1, 0, 0, 0, 662, 669, 3, 164, 82, 0, 663, 666, 3, 148, 74, 0, 664, 665, 5, 154, 0, 0, 665, 667, 3, 148, 74, 0, 666, 664, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 669, 1, 0, 0, 0, 668, 662, 1, 0, 0, 0, 668, 663, 1, 0, 0, 0, 669, 95, 1, 0, 0, 0, 670, 675, 3, 98, 49, 0, 671, 672, 5, 119, 0, 0, 672, 674, 3, 98, 49, 0, 673, 671, 1, 0, 0, 0, 674, 677, 1, 0, 0, 0, 675, 673, 1, 0, 0, 0, 675, 676, 1, 0, 0, 0, 676, 97, 1, 0, 0, 0, 677, 675, 1, 0, 0, 0, 678, 679, 3, 160, 80, 0, 679, 680, 5, 125, 0, 0, 680, 681, 3, 150, 75, 0, 681, 99, 1, 0, 0, 0, 682, 684, 3, 102, 51, 0, 683, 682, 1, 0, 0, 0, 683, 684, 1, 0, 0, 0, 684, 686, 1, 0, 0, 0, 685, 687, 3, 104, 52, 0, 686, 685, 1, 0, 0, 0, 686, 687, 1, 0, 0, 0, 687, 689, 1, 0, 0, 0, 688, 690, 3, 106, 53, 0, 689, 688, 1, 0, 0, 0, 689, 690, 1, 0, 0, 0, 690, 101, 1, 0, 0, 0, 691, 692, 5, 70, 0, 0, 692, 693, 5, 11, 0, 0, 693, 694, 3, 116, 58, 0, 694, 103, 1, 0, 0, 0, 695, 696, 5, 67, 0, 0, 696, 697, 5, 11, 0, 0, 697, 698, 3, 90, 45, 0, 698, 105, 1, 0, 0, 0, 699, 700, 7, 9, 0, 0, 700, 701, 3, 108, 54, 0, 701, 107, 1, 0, 0, 0, 702, 709, 3, 110, 55, 0, 703, 704, 5, 9, 0, 0, 704, 705, 3, 110, 55, 0, 705, 706, 5, 2, 0, 0, 706, 707, 3, 110, 55, 0, 707, 709, 1, 0, 0, 0, 708, 702, 1, 0, 0, 0, 708, 703, 1, 0, 0, 0, 709, 109, 1, 0, 0, 0, 710, 711, 5, 19, 0, 0, 711, 723, 5, 78, 0, 0, 712, 713, 5, 97, 0, 0, 713, 723, 5, 71, 0, 0, 714, 715, 5, 97, 0, 0, 715, 723, 5, 33, 0, 0, 716, 717, 3, 148, 74, 0, 717, 718, 5, 71, 0, 0, 718, 723, 1, 0, 0, 0, 719, 720, 3, 148, 74, 0, 720, 721, 5, 33, 0, 0, 721, 723, 1, 0, 0, 0, 722, 710, 1, 0, 0, 0, 722, 712, 1, 0, 0, 0, 722, 714, 1, 0, 0, 0, 722, 716, 1, 0, 0, 0, 722, 719, 1, 0, 0, 0, 723, 111, 1, 0, 0, 0, 724, 725, 3, 118, 59, 0, 725, 726, 5, 0, 0, 1, 726, 113, 1, 0, 0, 0, 727, 784, 3, 160, 80, 0, 728, 729, 3, 160, 80, 0, 729, 730, 5, 133, 0, 0, 730, 731, 3, 160, 80, 0, 731, 738, 3, 114, 57, 0, 732, 733, 5, 119, 0, 0, 733, 734, 3, 160, 80, 0, 734, 735, 3, 114, 57, 0, 735, 737, 1, 0, 0, 0, 736, 732, 1, 0, 0, 0, 737, 740, 1, 0, 0, 0, 738, 736, 1, 0, 0, 0, 738, 739, 1, 0, 0, 0, 739, 742, 1, 0, 0, 0, 740, 738, 1, 0, 0, 0, 741, 743, 5, 119, 0, 0, 742, 741, 1, 0, 0, 0, 742, 743, 1, 0, 0, 0, 743, 744, 1, 0, 0, 0, 744, 745, 5, 152, 0, 0, 745, 784, 1, 0, 0, 0, 746, 747, 3, 160, 80, 0, 747, 748, 5, 133, 0, 0, 748, 753, 3, 162, 81, 0, 749, 750, 5, 119, 0, 0, 750, 752, 3, 162, 81, 0, 751, 749, 1, 0, 0, 0, 752, 755, 1, 0, 0, 0, 753, 751, 1, 0, 0, 0, 753, 754, 1, 0, 0, 0, 754, 757, 1, 0, 0, 0, 755, 753, 1, 0, 0, 0, 756, 758, 5, 119, 0, 0, 757, 756, 1, 0, 0, 0, 757, 758, 1, 0, 0, 0, 758, 759, 1, 0, 0, 0, 759, 760, 5, 152, 0, 0, 760, 784, 1, 0, 0, 0, 761, 762, 3, 160, 80, 0, 762, 763, 5, 133, 0, 0, 763, 768, 3, 114, 57, 0, 764, 765, 5, 119, 0, 0, 765, 767, 3, 114, 57, 0, 766, 764, 1, 0, 0, 0, 767, 770, 1, 0, 0, 0, 768, 766, 1, 0, 0, 0, 768, 769, 1, 0, 0, 0, 769, 772, 1, 0, 0, 0, 770, 768, 1, 0, 0, 0, 771, 773, 5, 119, 0, 0, 772, 771, 1, 0, 0, 0, 772, 773, 1, 0, 0, 0, 773, 774, 1, 0, 0, 0, 774, 775, 5, 152, 0, 0, 775, 784, 1, 0, 0, 0, 776, 777, 3, 160, 80, 0, 777, 779, 5, 133, 0, 0, 778, 780, 3, 116, 58, 0, 779, 778, 1, 0, 0, 0, 779, 780, 1, 0, 0, 0, 780, 781, 1, 0, 0, 0, 781, 782, 5, 152, 0, 0, 782, 784, 1, 0, 0, 0, 783, 727, 1, 0, 0, 0, 783, 728, 1, 0, 0, 0, 783, 746, 1, 0, 0, 0, 783, 761, 1, 0, 0, 0, 783, 776, 1, 0, 0, 0, 784, 115, 1, 0, 0, 0, 785, 790, 3, 118, 59, 0, 786, 787, 5, 119, 0, 0, 787, 789, 3, 118, 59, 0, 788, 786, 1, 0, 0, 0, 789, 792, 1, 0, 0, 0, 790, 788, 1, 0, 0, 0, 790, 791, 1, 0, 0, 0, 791, 794, 1, 0, 0, 0, 792, 790, 1, 0, 0, 0, 793, 795, 5, 119, 0, 0, 794, 793, 1, 0, 0, 0, 794, 795, 1, 0, 0, 0, 795, 117, 1, 0, 0, 0, 796, 797, 6, 59, -1, 0, 797, 799, 5, 12, 0, 0, 798, 800, 3, 118, 59, 0, 799, 798, 1, 0, 0, 0, 799, 800, 1, 0, 0, 0, 800, 806, 1, 0, 0, 0, 801, 802, 5, 101, 0, 0, 802, 803, 3, 118, 59, 0, 803, 804, 5, 86, 0, 0, 804, 805, 3, 118, 59, 0, 805, 807, 1, 0, 0, 0, 806, 801, 1, 0, 0, 0, 807, 808, 1, 0, 0, 0, 808, 806, 1, 0, 0, 0, 808, 809, 1, 0, 0, 0, 809, 812, 1, 0, 0, 0, 810, 811, 5, 25, 0, 0, 811, 813, 3, 118, 59, 0, 812, 810, 1, 0, 0, 0, 812, 813, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 815, 5, 26, 0, 0, 815, 949, 1, 0, 0, 0, 816, 817, 5, 13, 0, 0, 817, 818, 5, 133, 0, 0, 818, 819, 3, 118, 59, 0, 819, 820, 5, 6, 0, 0, 820, 821, 3, 114, 57, 0, 821, 822, 5, 152, 0, 0, 822, 949, 1, 0, 0, 0, 823, 824, 5, 20, 0, 0, 824, 949, 5, 113, 0, 0, 825, 826, 5, 48, 0, 0, 826, 949, 5, 113, 0, 0, 827, 828, 5, 48, 0, 0, 828, 829, 3, 118, 59, 0, 829, 830, 3, 152, 76, 0, 830, 949, 1, 0, 0, 0, 831, 832, 5, 85, 0, 0, 832, 833, 5, 133, 0, 0, 833, 834, 3, 118, 59, 0, 834, 835, 5, 35, 0, 0, 835, 838, 3, 118, 59, 0, 836, 837, 5, 34, 0, 0, 837, 839, 3, 118, 59, 0, 838, 836, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 840, 1, 0, 0, 0, 840, 841, 5, 152, 0, 0, 841, 949, 1, 0, 0, 0, 842, 843, 5, 89, 0, 0, 843, 949, 5, 113, 0, 0, 844, 845, 5, 94, 0, 0, 845, 846, 5, 133, 0, 0, 846, 847, 7, 10, 0, 0, 847, 848, 3, 166, 83, 0, 848, 849, 5, 35, 0, 0, 849, 850, 3, 118, 59, 0, 850, 851, 5, 152, 0, 0, 851, 949, 1, 0, 0, 0, 852, 853, 3, 160, 80, 0, 853, 855, 5, 133, 0, 0, 854, 856, 3, 116, 58, 0, 855, 854, 1, 0, 0, 0, 855, 856, 1, 0, 0, 0, 856, 857, 1, 0, 0, 0, 857, 858, 5, 152, 0, 0, 858, 867, 1, 0, 0, 0, 859, 861, 5, 133, 0, 0, 860, 862, 5, 24, 0, 0, 861, 860, 1, 0, 0, 0, 861, 862, 1, 0, 0, 0, 862, 864, 1, 0, 0, 0, 863, 865, 3, 116, 58, 0, 864, 863, 1, 0, 0, 0, 864, 865, 1, 0, 0, 0, 865, 866, 1, 0, 0, 0, 866, 868, 5, 152, 0, 0, 867, 859, 1, 0, 0, 0, 867, 868, 1, 0, 0, 0, 868, 869, 1, 0, 0, 0, 869, 870, 5, 69, 0, 0, 870, 871, 5, 133, 0, 0, 871, 872, 3, 100, 50, 0, 872, 873, 5, 152, 0, 0, 873, 949, 1, 0, 0, 0, 874, 875, 3, 160, 80, 0, 875, 877, 5, 133, 0, 0, 876, 878, 3, 116, 58, 0, 877, 876, 1, 0, 0, 0, 877, 878, 1, 0, 0, 0, 878, 879, 1, 0, 0, 0, 879, 880, 5, 152, 0, 0, 880, 889, 1, 0, 0, 0, 881, 883, 5, 133, 0, 0, 882, 884, 5, 24, 0, 0, 883, 882, 1, 0, 0, 0, 883, 884, 1, 0, 0, 0, 884, 886, 1, 0, 0, 0, 885, 887, 3, 116, 58, 0, 886, 885, 1, 0, 0, 0, 886, 887, 1, 0, 0, 0, 887, 888, 1, 0, 0, 0, 888, 890, 5, 152, 0, 0, 889, 881, 1, 0, 0, 0, 889, 890, 1, 0, 0, 0, 890, 891, 1, 0, 0, 0, 891, 892, 5, 69, 0, 0, 892, 893, 3, 160, 80, 0, 893, 949, 1, 0, 0, 0, 894, 900, 3, 160, 80, 0, 895, 897, 5, 133, 0, 0, 896, 898, 3, 116, 58, 0, 897, 896, 1, 0, 0, 0, 897, 898, 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 901, 5, 152, 0, 0, 900, 895, 1, 0, 0, 0, 900, 901, 1, 0, 0, 0, 901, 902, 1, 0, 0, 0, 902, 904, 5, 133, 0, 0, 903, 905, 5, 24, 0, 0, 904, 903, 1, 0, 0, 0, 904, 905, 1, 0, 0, 0, 905, 907, 1, 0, 0, 0, 906, 908, 3, 116, 58, 0, 907, 906, 1, 0, 0, 0, 907, 908, 1, 0, 0, 0, 908, 909, 1, 0, 0, 0, 909, 910, 5, 152, 0, 0, 910, 949, 1, 0, 0, 0, 911, 949, 3, 124, 62, 0, 912, 949, 3, 168, 84, 0, 913, 949, 3, 150, 75, 0, 914, 915, 5, 121, 0, 0, 915, 949, 3, 118, 59, 20, 916, 917, 5, 61, 0, 0, 917, 949, 3, 118, 59, 14, 918, 919, 3, 140, 70, 0, 919, 920, 5, 123, 0, 0, 920, 922, 1, 0, 0, 0, 921, 918, 1, 0, 0, 0, 921, 922, 1, 0, 0, 0, 922, 923, 1, 0, 0, 0, 923, 949, 5, 115, 0, 0, 924, 925, 5, 133, 0, 0, 925, 926, 3, 48, 24, 0, 926, 927, 5, 152, 0, 0, 927, 949, 1, 0, 0, 0, 928, 929, 5, 133, 0, 0, 929, 930, 3, 118, 59, 0, 930, 931, 5, 152, 0, 0, 931, 949, 1, 0, 0, 0, 932, 933, 5, 133, 0, 0, 933, 934, 3, 116, 58, 0, 934, 935, 5, 152, 0, 0, 935, 949, 1, 0, 0, 0, 936, 938, 5, 132, 0, 0, 937, 939, 3, 116, 58, 0, 938, 937, 1, 0, 0, 0, 938, 939, 1, 0, 0, 0, 939, 940, 1, 0, 0, 0, 940, 949, 5, 151, 0, 0, 941, 943, 5, 131, 0, 0, 942, 944, 3, 40, 20, 0, 943, 942, 1, 0, 0, 0, 943, 944, 1, 0, 0, 0, 944, 945, 1, 0, 0, 0, 945, 949, 5, 150, 0, 0, 946, 949, 3, 120, 60, 0, 947, 949, 3, 132, 66, 0, 948, 796, 1, 0, 0, 0, 948, 816, 1, 0, 0, 0, 948, 823, 1, 0, 0, 0, 948, 825, 1, 0, 0, 0, 948, 827, 1, 0, 0, 0, 948, 831, 1, 0, 0, 0, 948, 842, 1, 0, 0, 0, 948, 844, 1, 0, 0, 0, 948, 852, 1, 0, 0, 0, 948, 874, 1, 0, 0, 0, 948, 894, 1, 0, 0, 0, 948, 911, 1, 0, 0, 0, 948, 912, 1, 0, 0, 0, 948, 913, 1, 0, 0, 0, 948, 914, 1, 0, 0, 0, 948, 916, 1, 0, 0, 0, 948, 921, 1, 0, 0, 0, 948, 924, 1, 0, 0, 0, 948, 928, 1, 0, 0, 0, 948, 932, 1, 0, 0, 0, 948, 936, 1, 0, 0, 0, 948, 941, 1, 0, 0, 0, 948, 946, 1, 0, 0, 0, 948, 947, 1, 0, 0, 0, 949, 1060, 1, 0, 0, 0, 950, 954, 10, 19, 0, 0, 951, 955, 5, 115, 0, 0, 952, 955, 5, 154, 0, 0, 953, 955, 5, 141, 0, 0, 954, 951, 1, 0, 0, 0, 954, 952, 1, 0, 0, 0, 954, 953, 1, 0, 0, 0, 955, 956, 1, 0, 0, 0, 956, 1059, 3, 118, 59, 20, 957, 961, 10, 18, 0, 0, 958, 962, 5, 142, 0, 0, 959, 962, 5, 121, 0, 0, 960, 962, 5, 120, 0, 0, 961, 958, 1, 0, 0, 0, 961, 959, 1, 0, 0, 0, 961, 960, 1, 0, 0, 0, 962, 963, 1, 0, 0, 0, 963, 1059, 3, 118, 59, 19, 964, 989, 10, 17, 0, 0, 965, 990, 5, 124, 0, 0, 966, 990, 5, 125, 0, 0, 967, 990, 5, 136, 0, 0, 968, 990, 5, 134, 0, 0, 969, 990, 5, 135, 0, 0, 970, 990, 5, 126, 0, 0, 971, 990, 5, 127, 0, 0, 972, 974, 5, 61, 0, 0, 973, 972, 1, 0, 0, 0, 973, 974, 1, 0, 0, 0, 974, 975, 1, 0, 0, 0, 975, 977, 5, 44, 0, 0, 976, 978, 5, 15, 0, 0, 977, 976, 1, 0, 0, 0, 977, 978, 1, 0, 0, 0, 978, 990, 1, 0, 0, 0, 979, 981, 5, 61, 0, 0, 980, 979, 1, 0, 0, 0, 980, 981, 1, 0, 0, 0, 981, 982, 1, 0, 0, 0, 982, 990, 7, 11, 0, 0, 983, 990, 5, 148, 0, 0, 984, 990, 5, 149, 0, 0, 985, 990, 5, 138, 0, 0, 986, 990, 5, 129, 0, 0, 987, 990, 5, 130, 0, 0, 988, 990, 5, 137, 0, 0, 989, 965, 1, 0, 0, 0, 989, 966, 1, 0, 0, 0, 989, 967, 1, 0, 0, 0, 989, 968, 1, 0, 0, 0, 989, 969, 1, 0, 0, 0, 989, 970, 1, 0, 0, 0, 989, 971, 1, 0, 0, 0, 989, 973, 1, 0, 0, 0, 989, 980, 1, 0, 0, 0, 989, 983, 1, 0, 0, 0, 989, 984, 1, 0, 0, 0, 989, 985, 1, 0, 0, 0, 989, 986, 1, 0, 0, 0, 989, 987, 1, 0, 0, 0, 989, 988, 1, 0, 0, 0, 990, 991, 1, 0, 0, 0, 991, 1059, 3, 118, 59, 18, 992, 993, 10, 15, 0, 0, 993, 994, 5, 140, 0, 0, 994, 1059, 3, 118, 59, 16, 995, 996, 10, 13, 0, 0, 996, 997, 5, 2, 0, 0, 997, 1059, 3, 118, 59, 14, 998, 999, 10, 12, 0, 0, 999, 1000, 5, 66, 0, 0, 1000, 1059, 3, 118, 59, 13, 1001, 1003, 10, 11, 0, 0, 1002, 1004, 5, 61, 0, 0, 1003, 1002, 1, 0, 0, 0, 1003, 1004, 1, 0, 0, 0, 1004, 1005, 1, 0, 0, 0, 1005, 1006, 5, 9, 0, 0, 1006, 1007, 3, 118, 59, 0, 1007, 1008, 5, 2, 0, 0, 1008, 1009, 3, 118, 59, 12, 1009, 1059, 1, 0, 0, 0, 1010, 1011, 10, 10, 0, 0, 1011, 1012, 5, 143, 0, 0, 1012, 1013, 3, 118, 59, 0, 1013, 1014, 5, 118, 0, 0, 1014, 1015, 3, 118, 59, 10, 1015, 1059, 1, 0, 0, 0, 1016, 1017, 10, 30, 0, 0, 1017, 1019, 5, 133, 0, 0, 1018, 1020, 3, 116, 58, 0, 1019, 1018, 1, 0, 0, 0, 1019, 1020, 1, 0, 0, 0, 1020, 1021, 1, 0, 0, 0, 1021, 1059, 5, 152, 0, 0, 1022, 1023, 10, 26, 0, 0, 1023, 1024, 5, 132, 0, 0, 1024, 1025, 3, 118, 59, 0, 1025, 1026, 5, 151, 0, 0, 1026, 1059, 1, 0, 0, 0, 1027, 1028, 10, 25, 0, 0, 1028, 1029, 5, 123, 0, 0, 1029, 1059, 5, 111, 0, 0, 1030, 1031, 10, 24, 0, 0, 1031, 1032, 5, 123, 0, 0, 1032, 1059, 3, 160, 80, 0, 1033, 1034, 10, 23, 0, 0, 1034, 1035, 5, 139, 0, 0, 1035, 1036, 5, 132, 0, 0, 1036, 1037, 3, 118, 59, 0, 1037, 1038, 5, 151, 0, 0, 1038, 1059, 1, 0, 0, 0, 1039, 1040, 10, 22, 0, 0, 1040, 1041, 5, 139, 0, 0, 1041, 1059, 5, 111, 0, 0, 1042, 1043, 10, 21, 0, 0, 1043, 1044, 5, 139, 0, 0, 1044, 1059, 3, 160, 80, 0, 1045, 1046, 10, 16, 0, 0, 1046, 1048, 5, 49, 0, 0, 1047, 1049, 5, 61, 0, 0, 1048, 1047, 1, 0, 0, 0, 1048, 1049, 1, 0, 0, 0, 1049, 1050, 1, 0, 0, 0, 1050, 1059, 5, 62, 0, 0, 1051, 1056, 10, 9, 0, 0, 1052, 1053, 5, 6, 0, 0, 1053, 1057, 3, 160, 80, 0, 1054, 1055, 5, 6, 0, 0, 1055, 1057, 5, 113, 0, 0, 1056, 1052, 1, 0, 0, 0, 1056, 1054, 1, 0, 0, 0, 1057, 1059, 1, 0, 0, 0, 1058, 950, 1, 0, 0, 0, 1058, 957, 1, 0, 0, 0, 1058, 964, 1, 0, 0, 0, 1058, 992, 1, 0, 0, 0, 1058, 995, 1, 0, 0, 0, 1058, 998, 1, 0, 0, 0, 1058, 1001, 1, 0, 0, 0, 1058, 1010, 1, 0, 0, 0, 1058, 1016, 1, 0, 0, 0, 1058, 1022, 1, 0, 0, 0, 1058, 1027, 1, 0, 0, 0, 1058, 1030, 1, 0, 0, 0, 1058, 1033, 1, 0, 0, 0, 1058, 1039, 1, 0, 0, 0, 1058, 1042, 1, 0, 0, 0, 1058, 1045, 1, 0, 0, 0, 1058, 1051, 1, 0, 0, 0, 1059, 1062, 1, 0, 0, 0, 1060, 1058, 1, 0, 0, 0, 1060, 1061, 1, 0, 0, 0, 1061, 119, 1, 0, 0, 0, 1062, 1060, 1, 0, 0, 0, 1063, 1064, 5, 133, 0, 0, 1064, 1069, 3, 160, 80, 0, 1065, 1066, 5, 119, 0, 0, 1066, 1068, 3, 160, 80, 0, 1067, 1065, 1, 0, 0, 0, 1068, 1071, 1, 0, 0, 0, 1069, 1067, 1, 0, 0, 0, 1069, 1070, 1, 0, 0, 0, 1070, 1073, 1, 0, 0, 0, 1071, 1069, 1, 0, 0, 0, 1072, 1074, 5, 119, 0, 0, 1073, 1072, 1, 0, 0, 0, 1073, 1074, 1, 0, 0, 0, 1074, 1075, 1, 0, 0, 0, 1075, 1076, 5, 152, 0, 0, 1076, 1091, 1, 0, 0, 0, 1077, 1082, 3, 160, 80, 0, 1078, 1079, 5, 119, 0, 0, 1079, 1081, 3, 160, 80, 0, 1080, 1078, 1, 0, 0, 0, 1081, 1084, 1, 0, 0, 0, 1082, 1080, 1, 0, 0, 0, 1082, 1083, 1, 0, 0, 0, 1083, 1086, 1, 0, 0, 0, 1084, 1082, 1, 0, 0, 0, 1085, 1087, 5, 119, 0, 0, 1086, 1085, 1, 0, 0, 0, 1086, 1087, 1, 0, 0, 0, 1087, 1091, 1, 0, 0, 0, 1088, 1089, 5, 133, 0, 0, 1089, 1091, 5, 152, 0, 0, 1090, 1063, 1, 0, 0, 0, 1090, 1077, 1, 0, 0, 0, 1090, 1088, 1, 0, 0, 0, 1091, 1092, 1, 0, 0, 0, 1092, 1095, 5, 114, 0, 0, 1093, 1096, 3, 118, 59, 0, 1094, 1096, 3, 36, 18, 0, 1095, 1093, 1, 0, 0, 0, 1095, 1094, 1, 0, 0, 0, 1096, 121, 1, 0, 0, 0, 1097, 1103, 3, 124, 62, 0, 1098, 1099, 5, 131, 0, 0, 1099, 1100, 3, 118, 59, 0, 1100, 1101, 5, 150, 0, 0, 1101, 1103, 1, 0, 0, 0, 1102, 1097, 1, 0, 0, 0, 1102, 1098, 1, 0, 0, 0, 1103, 123, 1, 0, 0, 0, 1104, 1105, 5, 135, 0, 0, 1105, 1109, 3, 160, 80, 0, 1106, 1108, 3, 126, 63, 0, 1107, 1106, 1, 0, 0, 0, 1108, 1111, 1, 0, 0, 0, 1109, 1107, 1, 0, 0, 0, 1109, 1110, 1, 0, 0, 0, 1110, 1112, 1, 0, 0, 0, 1111, 1109, 1, 0, 0, 0, 1112, 1113, 5, 154, 0, 0, 1113, 1114, 5, 127, 0, 0, 1114, 1136, 1, 0, 0, 0, 1115, 1116, 5, 135, 0, 0, 1116, 1120, 3, 160, 80, 0, 1117, 1119, 3, 126, 63, 0, 1118, 1117, 1, 0, 0, 0, 1119, 1122, 1, 0, 0, 0, 1120, 1118, 1, 0, 0, 0, 1120, 1121, 1, 0, 0, 0, 1121, 1123, 1, 0, 0, 0, 1122, 1120, 1, 0, 0, 0, 1123, 1127, 5, 127, 0, 0, 1124, 1126, 3, 122, 61, 0, 1125, 1124, 1, 0, 0, 0, 1126, 1129, 1, 0, 0, 0, 1127, 1125, 1, 0, 0, 0, 1127, 1128, 1, 0, 0, 0, 1128, 1130, 1, 0, 0, 0, 1129, 1127, 1, 0, 0, 0, 1130, 1131, 5, 135, 0, 0, 1131, 1132, 5, 154, 0, 0, 1132, 1133, 3, 160, 80, 0, 1133, 1134, 5, 127, 0, 0, 1134, 1136, 1, 0, 0, 0, 1135, 1104, 1, 0, 0, 0, 1135, 1115, 1, 0, 0, 0, 1136, 125, 1, 0, 0, 0, 1137, 1138, 3, 160, 80, 0, 1138, 1139, 5, 125, 0, 0, 1139, 1140, 3, 166, 83, 0, 1140, 1149, 1, 0, 0, 0, 1141, 1142, 3, 160, 80, 0, 1142, 1143, 5, 125, 0, 0, 1143, 1144, 5, 131, 0, 0, 1144, 1145, 3, 118, 59, 0, 1145, 1146, 5, 150, 0, 0, 1146, 1149, 1, 0, 0, 0, 1147, 1149, 3, 160, 80, 0, 1148, 1137, 1, 0, 0, 0, 1148, 1141, 1, 0, 0, 0, 1148, 1147, 1, 0, 0, 0, 1149, 127, 1, 0, 0, 0, 1150, 1155, 3, 130, 65, 0, 1151, 1152, 5, 119, 0, 0, 1152, 1154, 3, 130, 65, 0, 1153, 1151, 1, 0, 0, 0, 1154, 1157, 1, 0, 0, 0, 1155, 1153, 1, 0, 0, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1159, 1, 0, 0, 0, 1157, 1155, 1, 0, 0, 0, 1158, 1160, 5, 119, 0, 0, 1159, 1158, 1, 0, 0, 0, 1159, 1160, 1, 0, 0, 0, 1160, 129, 1, 0, 0, 0, 1161, 1162, 3, 160, 80, 0, 1162, 1163, 5, 6, 0, 0, 1163, 1164, 5, 133, 0, 0, 1164, 1165, 3, 48, 24, 0, 1165, 1166, 5, 152, 0, 0, 1166, 1172, 1, 0, 0, 0, 1167, 1168, 3, 118, 59, 0, 1168, 1169, 5, 6, 0, 0, 1169, 1170, 3, 160, 80, 0, 1170, 1172, 1, 0, 0, 0, 1171, 1161, 1, 0, 0, 0, 1171, 1167, 1, 0, 0, 0, 1172, 131, 1, 0, 0, 0, 1173, 1181, 3, 164, 82, 0, 1174, 1175, 3, 140, 70, 0, 1175, 1176, 5, 123, 0, 0, 1176, 1178, 1, 0, 0, 0, 1177, 1174, 1, 0, 0, 0, 1177, 1178, 1, 0, 0, 0, 1178, 1179, 1, 0, 0, 0, 1179, 1181, 3, 134, 67, 0, 1180, 1173, 1, 0, 0, 0, 1180, 1177, 1, 0, 0, 0, 1181, 133, 1, 0, 0, 0, 1182, 1187, 3, 160, 80, 0, 1183, 1184, 5, 123, 0, 0, 1184, 1186, 3, 160, 80, 0, 1185, 1183, 1, 0, 0, 0, 1186, 1189, 1, 0, 0, 0, 1187, 1185, 1, 0, 0, 0, 1187, 1188, 1, 0, 0, 0, 1188, 135, 1, 0, 0, 0, 1189, 1187, 1, 0, 0, 0, 1190, 1191, 6, 68, -1, 0, 1191, 1200, 3, 140, 70, 0, 1192, 1200, 3, 138, 69, 0, 1193, 1194, 5, 133, 0, 0, 1194, 1195, 3, 48, 24, 0, 1195, 1196, 5, 152, 0, 0, 1196, 1200, 1, 0, 0, 0, 1197, 1200, 3, 124, 62, 0, 1198, 1200, 3, 164, 82, 0, 1199, 1190, 1, 0, 0, 0, 1199, 1192, 1, 0, 0, 0, 1199, 1193, 1, 0, 0, 0, 1199, 1197, 1, 0, 0, 0, 1199, 1198, 1, 0, 0, 0, 1200, 1209, 1, 0, 0, 0, 1201, 1205, 10, 3, 0, 0, 1202, 1206, 3, 158, 79, 0, 1203, 1204, 5, 6, 0, 0, 1204, 1206, 3, 160, 80, 0, 1205, 1202, 1, 0, 0, 0, 1205, 1203, 1, 0, 0, 0, 1206, 1208, 1, 0, 0, 0, 1207, 1201, 1, 0, 0, 0, 1208, 1211, 1, 0, 0, 0, 1209, 1207, 1, 0, 0, 0, 1209, 1210, 1, 0, 0, 0, 1210, 137, 1, 0, 0, 0, 1211, 1209, 1, 0, 0, 0, 1212, 1213, 3, 160, 80, 0, 1213, 1215, 5, 133, 0, 0, 1214, 1216, 3, 142, 71, 0, 1215, 1214, 1, 0, 0, 0, 1215, 1216, 1, 0, 0, 0, 1216, 1217, 1, 0, 0, 0, 1217, 1218, 5, 152, 0, 0, 1218, 139, 1, 0, 0, 0, 1219, 1220, 3, 144, 72, 0, 1220, 1221, 5, 123, 0, 0, 1221, 1223, 1, 0, 0, 0, 1222, 1219, 1, 0, 0, 0, 1222, 1223, 1, 0, 0, 0, 1223, 1224, 1, 0, 0, 0, 1224, 1225, 3, 160, 80, 0, 1225, 141, 1, 0, 0, 0, 1226, 1231, 3, 118, 59, 0, 1227, 1228, 5, 119, 0, 0, 1228, 1230, 3, 118, 59, 0, 1229, 1227, 1, 0, 0, 0, 1230, 1233, 1, 0, 0, 0, 1231, 1229, 1, 0, 0, 0, 1231, 1232, 1, 0, 0, 0, 1232, 1235, 1, 0, 0, 0, 1233, 1231, 1, 0, 0, 0, 1234, 1236, 5, 119, 0, 0, 1235, 1234, 1, 0, 0, 0, 1235, 1236, 1, 0, 0, 0, 1236, 143, 1, 0, 0, 0, 1237, 1238, 3, 160, 80, 0, 1238, 145, 1, 0, 0, 0, 1239, 1248, 5, 109, 0, 0, 1240, 1241, 5, 123, 0, 0, 1241, 1248, 7, 12, 0, 0, 1242, 1243, 5, 111, 0, 0, 1243, 1245, 5, 123, 0, 0, 1244, 1246, 7, 12, 0, 0, 1245, 1244, 1, 0, 0, 0, 1245, 1246, 1, 0, 0, 0, 1246, 1248, 1, 0, 0, 0, 1247, 1239, 1, 0, 0, 0, 1247, 1240, 1, 0, 0, 0, 1247, 1242, 1, 0, 0, 0, 1248, 147, 1, 0, 0, 0, 1249, 1251, 7, 13, 0, 0, 1250, 1249, 1, 0, 0, 0, 1250, 1251, 1, 0, 0, 0, 1251, 1258, 1, 0, 0, 0, 1252, 1259, 3, 146, 73, 0, 1253, 1259, 5, 110, 0, 0, 1254, 1259, 5, 111, 0, 0, 1255, 1259, 5, 112, 0, 0, 1256, 1259, 5, 45, 0, 0, 1257, 1259, 5, 60, 0, 0, 1258, 1252, 1, 0, 0, 0, 1258, 1253, 1, 0, 0, 0, 1258, 1254, 1, 0, 0, 0, 1258, 1255, 1, 0, 0, 0, 1258, 1256, 1, 0, 0, 0, 1258, 1257, 1, 0, 0, 0, 1259, 149, 1, 0, 0, 0, 1260, 1264, 3, 148, 74, 0, 1261, 1264, 5, 113, 0, 0, 1262, 1264, 5, 62, 0, 0, 1263, 1260, 1, 0, 0, 0, 1263, 1261, 1, 0, 0, 0, 1263, 1262, 1, 0, 0, 0, 1264, 151, 1, 0, 0, 0, 1265, 1266, 7, 14, 0, 0, 1266, 153, 1, 0, 0, 0, 1267, 1268, 7, 15, 0, 0, 1268, 155, 1, 0, 0, 0, 1269, 1270, 7, 16, 0, 0, 1270, 157, 1, 0, 0, 0, 1271, 1274, 5, 108, 0, 0, 1272, 1274, 3, 156, 78, 0, 1273, 1271, 1, 0, 0, 0, 1273, 1272, 1, 0, 0, 0, 1274, 159, 1, 0, 0, 0, 1275, 1279, 5, 108, 0, 0, 1276, 1279, 3, 152, 76, 0, 1277, 1279, 3, 154, 77, 0, 1278, 1275, 1, 0, 0, 0, 1278, 1276, 1, 0, 0, 0, 1278, 1277, 1, 0, 0, 0, 1279, 161, 1, 0, 0, 0, 1280, 1281, 3, 166, 83, 0, 1281, 1282, 5, 125, 0, 0, 1282, 1283, 3, 148, 74, 0, 1283, 163, 1, 0, 0, 0, 1284, 1285, 5, 131, 0, 0, 1285, 1286, 3, 118, 59, 0, 1286, 1287, 5, 150, 0, 0, 1287, 165, 1, 0, 0, 0, 1288, 1291, 5, 113, 0, 0, 1289, 1291, 3, 168, 84, 0, 1290, 1288, 1, 0, 0, 0, 1290, 1289, 1, 0, 0, 0, 1291, 167, 1, 0, 0, 0, 1292, 1296, 5, 145, 0, 0, 1293, 1295, 3, 170, 85, 0, 1294, 1293, 1, 0, 0, 0, 1295, 1298, 1, 0, 0, 0, 1296, 1294, 1, 0, 0, 0, 1296, 1297, 1, 0, 0, 0, 1297, 1299, 1, 0, 0, 0, 1298, 1296, 1, 0, 0, 0, 1299, 1300, 5, 147, 0, 0, 1300, 169, 1, 0, 0, 0, 1301, 1302, 5, 160, 0, 0, 1302, 1303, 3, 118, 59, 0, 1303, 1304, 5, 150, 0, 0, 1304, 1307, 1, 0, 0, 0, 1305, 1307, 5, 159, 0, 0, 1306, 1301, 1, 0, 0, 0, 1306, 1305, 1, 0, 0, 0, 1307, 171, 1, 0, 0, 0, 1308, 1312, 5, 146, 0, 0, 1309, 1311, 3, 174, 87, 0, 1310, 1309, 1, 0, 0, 0, 1311, 1314, 1, 0, 0, 0, 1312, 1310, 1, 0, 0, 0, 1312, 1313, 1, 0, 0, 0, 1313, 1315, 1, 0, 0, 0, 1314, 1312, 1, 0, 0, 0, 1315, 1316, 5, 0, 0, 1, 1316, 173, 1, 0, 0, 0, 1317, 1318, 5, 162, 0, 0, 1318, 1319, 3, 118, 59, 0, 1319, 1320, 5, 150, 0, 0, 1320, 1323, 1, 0, 0, 0, 1321, 1323, 5, 161, 0, 0, 1322, 1317, 1, 0, 0, 0, 1322, 1321, 1, 0, 0, 0, 1323, 175, 1, 0, 0, 0, 169, 179, 186, 195, 202, 206, 220, 224, 227, 231, 234, 241, 245, 254, 259, 268, 276, 283, 287, 293, 298, 306, 313, 319, 331, 339, 353, 357, 362, 372, 382, 390, 394, 398, 401, 405, 408, 411, 414, 417, 421, 425, 428, 431, 434, 438, 441, 450, 456, 477, 494, 511, 517, 523, 534, 536, 547, 550, 556, 564, 570, 572, 576, 581, 584, 587, 591, 595, 598, 600, 603, 607, 611, 614, 616, 618, 623, 634, 640, 647, 652, 656, 660, 666, 668, 675, 683, 686, 689, 708, 722, 738, 742, 753, 757, 768, 772, 779, 783, 790, 794, 799, 808, 812, 838, 855, 861, 864, 867, 877, 883, 886, 889, 897, 900, 904, 907, 921, 938, 943, 948, 954, 961, 973, 977, 980, 989, 1003, 1019, 1048, 1056, 1058, 1060, 1069, 1073, 1082, 1086, 1090, 1095, 1102, 1109, 1120, 1127, 1135, 1148, 1155, 1159, 1171, 1177, 1180, 1187, 1199, 1205, 1209, 1215, 1222, 1231, 1235, 1245, 1247, 1250, 1258, 1263, 1273, 1278, 1290, 1296, 1306, 1312, 1322] \ No newline at end of file diff --git a/posthog/hogql/grammar/HogQLParser.py b/posthog/hogql/grammar/HogQLParser.py index 5bc629c2140b7..21634859c5e7b 100644 --- a/posthog/hogql/grammar/HogQLParser.py +++ b/posthog/hogql/grammar/HogQLParser.py @@ -10,7 +10,7 @@ def serializedATN(): return [ - 4,1,162,1317,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6, + 4,1,162,1325,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6, 7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7, 13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2, 20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7, @@ -23,525 +23,529 @@ def serializedATN(): 65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70,7,70,2,71,7,71,2, 72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77,7,77,2,78,7, 78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84,7,84,2, - 85,7,85,2,86,7,86,1,0,5,0,176,8,0,10,0,12,0,179,9,0,1,0,1,0,1,1, - 1,1,3,1,185,8,1,1,2,1,2,1,3,1,3,1,3,1,3,1,3,3,3,194,8,3,1,4,1,4, - 1,4,5,4,199,8,4,10,4,12,4,202,9,4,1,4,3,4,205,8,4,1,5,1,5,1,5,1, - 5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,219,8,5,1,6,1,6,3,6,223,8, - 6,1,6,3,6,226,8,6,1,7,1,7,3,7,230,8,7,1,7,3,7,233,8,7,1,8,1,8,1, - 8,1,8,1,8,3,8,240,8,8,1,8,1,8,3,8,244,8,8,1,8,1,8,1,9,1,9,1,9,5, - 9,251,8,9,10,9,12,9,254,9,9,1,9,1,9,3,9,258,8,9,1,10,1,10,1,10,1, - 10,1,10,1,10,1,10,3,10,267,8,10,1,11,1,11,1,11,1,11,1,11,1,11,3, - 11,275,8,11,1,12,1,12,1,12,1,12,1,12,3,12,282,8,12,1,12,1,12,3,12, - 286,8,12,1,12,1,12,1,12,1,12,3,12,292,8,12,1,12,1,12,1,12,3,12,297, - 8,12,1,13,1,13,1,13,1,13,1,13,1,13,3,13,305,8,13,1,13,1,13,1,13, - 1,13,1,13,3,13,312,8,13,1,14,1,14,1,14,1,14,3,14,318,8,14,1,14,1, - 14,1,14,1,15,1,15,1,15,1,15,1,15,1,16,1,16,3,16,330,8,16,1,17,1, - 17,1,18,1,18,5,18,336,8,18,10,18,12,18,339,9,18,1,18,1,18,1,19,1, - 19,1,19,1,19,1,20,1,20,1,20,5,20,350,8,20,10,20,12,20,353,9,20,1, - 20,3,20,356,8,20,1,21,1,21,1,21,3,21,361,8,21,1,21,1,21,1,22,1,22, - 1,22,1,22,1,22,1,22,3,22,371,8,22,1,23,1,23,1,23,1,23,1,23,1,23, - 1,23,1,23,3,23,381,8,23,1,23,1,23,1,24,1,24,5,24,387,8,24,10,24, - 12,24,390,9,24,1,25,3,25,393,8,25,1,25,1,25,3,25,397,8,25,1,25,3, - 25,400,8,25,1,25,1,25,3,25,404,8,25,1,25,3,25,407,8,25,1,25,3,25, - 410,8,25,1,25,3,25,413,8,25,1,25,3,25,416,8,25,1,25,1,25,3,25,420, - 8,25,1,25,1,25,3,25,424,8,25,1,25,3,25,427,8,25,1,25,3,25,430,8, - 25,1,25,3,25,433,8,25,1,25,1,25,3,25,437,8,25,1,25,3,25,440,8,25, - 1,26,1,26,1,26,1,27,1,27,1,27,1,27,3,27,449,8,27,1,28,1,28,1,28, - 1,29,3,29,455,8,29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,30, - 1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,5,30,474,8,30,10,30,12,30, - 477,9,30,1,31,1,31,1,31,1,32,1,32,1,32,1,33,1,33,1,33,1,33,1,33, - 1,33,1,33,1,33,3,33,493,8,33,1,34,1,34,1,34,1,35,1,35,1,35,1,35, - 1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,3,37,510,8,37,1,37,1,37, - 1,37,1,37,3,37,516,8,37,1,37,1,37,1,37,1,37,3,37,522,8,37,1,37,1, - 37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,3,37,533,8,37,3,37,535,8,37, - 1,38,1,38,1,38,1,39,1,39,1,39,1,40,1,40,1,40,3,40,546,8,40,1,40, - 3,40,549,8,40,1,40,1,40,1,40,1,40,3,40,555,8,40,1,40,1,40,1,40,1, - 40,1,40,1,40,3,40,563,8,40,1,40,1,40,1,40,1,40,5,40,569,8,40,10, - 40,12,40,572,9,40,1,41,3,41,575,8,41,1,41,1,41,1,41,3,41,580,8,41, - 1,41,3,41,583,8,41,1,41,3,41,586,8,41,1,41,1,41,3,41,590,8,41,1, - 41,1,41,3,41,594,8,41,1,41,3,41,597,8,41,3,41,599,8,41,1,41,3,41, - 602,8,41,1,41,1,41,3,41,606,8,41,1,41,1,41,3,41,610,8,41,1,41,3, - 41,613,8,41,3,41,615,8,41,3,41,617,8,41,1,42,1,42,1,42,3,42,622, - 8,42,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43,3,43,633,8,43, - 1,44,1,44,1,44,1,44,3,44,639,8,44,1,45,1,45,1,45,5,45,644,8,45,10, - 45,12,45,647,9,45,1,46,1,46,3,46,651,8,46,1,46,1,46,3,46,655,8,46, - 1,46,1,46,3,46,659,8,46,1,47,1,47,1,47,1,47,3,47,665,8,47,3,47,667, - 8,47,1,48,1,48,1,48,5,48,672,8,48,10,48,12,48,675,9,48,1,49,1,49, - 1,49,1,49,1,50,3,50,682,8,50,1,50,3,50,685,8,50,1,50,3,50,688,8, - 50,1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,53,1,53,1,53,1,54,1, - 54,1,54,1,54,1,54,1,54,3,54,707,8,54,1,55,1,55,1,55,1,55,1,55,1, - 55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,721,8,55,1,56,1,56,1,56,1, - 57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,5,57,735,8,57,10,57,12, - 57,738,9,57,1,57,3,57,741,8,57,1,57,1,57,1,57,1,57,1,57,1,57,1,57, - 5,57,750,8,57,10,57,12,57,753,9,57,1,57,3,57,756,8,57,1,57,1,57, - 1,57,1,57,1,57,1,57,1,57,5,57,765,8,57,10,57,12,57,768,9,57,1,57, - 3,57,771,8,57,1,57,1,57,1,57,1,57,1,57,3,57,778,8,57,1,57,1,57,3, - 57,782,8,57,1,58,1,58,1,58,5,58,787,8,58,10,58,12,58,790,9,58,1, - 58,3,58,793,8,58,1,59,1,59,1,59,3,59,798,8,59,1,59,1,59,1,59,1,59, - 1,59,4,59,805,8,59,11,59,12,59,806,1,59,1,59,3,59,811,8,59,1,59, + 85,7,85,2,86,7,86,2,87,7,87,1,0,5,0,178,8,0,10,0,12,0,181,9,0,1, + 0,1,0,1,1,1,1,3,1,187,8,1,1,2,1,2,1,3,1,3,1,3,1,3,1,3,3,3,196,8, + 3,1,4,1,4,1,4,5,4,201,8,4,10,4,12,4,204,9,4,1,4,3,4,207,8,4,1,5, + 1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,221,8,5,1,6,1,6, + 3,6,225,8,6,1,6,3,6,228,8,6,1,7,1,7,3,7,232,8,7,1,7,3,7,235,8,7, + 1,8,1,8,1,8,1,8,1,8,3,8,242,8,8,1,8,1,8,3,8,246,8,8,1,8,1,8,1,9, + 1,9,1,9,5,9,253,8,9,10,9,12,9,256,9,9,1,9,1,9,3,9,260,8,9,1,10,1, + 10,1,10,1,10,1,10,1,10,1,10,3,10,269,8,10,1,11,1,11,1,11,1,11,1, + 11,1,11,3,11,277,8,11,1,12,1,12,1,12,1,12,1,12,3,12,284,8,12,1,12, + 1,12,3,12,288,8,12,1,12,1,12,1,12,1,12,3,12,294,8,12,1,12,1,12,1, + 12,3,12,299,8,12,1,13,1,13,1,13,1,13,1,13,1,13,3,13,307,8,13,1,13, + 1,13,1,13,1,13,1,13,3,13,314,8,13,1,14,1,14,1,14,1,14,3,14,320,8, + 14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,16,1,16,3,16,332,8, + 16,1,17,1,17,1,18,1,18,5,18,338,8,18,10,18,12,18,341,9,18,1,18,1, + 18,1,19,1,19,1,19,1,19,1,20,1,20,1,20,5,20,352,8,20,10,20,12,20, + 355,9,20,1,20,3,20,358,8,20,1,21,1,21,1,21,3,21,363,8,21,1,21,1, + 21,1,22,1,22,1,22,1,22,1,22,1,22,3,22,373,8,22,1,23,1,23,1,23,1, + 23,1,23,1,23,1,23,1,23,3,23,383,8,23,1,23,1,23,1,24,1,24,5,24,389, + 8,24,10,24,12,24,392,9,24,1,25,3,25,395,8,25,1,25,1,25,3,25,399, + 8,25,1,25,3,25,402,8,25,1,25,1,25,3,25,406,8,25,1,25,3,25,409,8, + 25,1,25,3,25,412,8,25,1,25,3,25,415,8,25,1,25,3,25,418,8,25,1,25, + 1,25,3,25,422,8,25,1,25,1,25,3,25,426,8,25,1,25,3,25,429,8,25,1, + 25,3,25,432,8,25,1,25,3,25,435,8,25,1,25,1,25,3,25,439,8,25,1,25, + 3,25,442,8,25,1,26,1,26,1,26,1,27,1,27,1,27,1,27,3,27,451,8,27,1, + 28,1,28,1,28,1,29,3,29,457,8,29,1,29,1,29,1,29,1,29,1,30,1,30,1, + 30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,5,30,476,8, + 30,10,30,12,30,479,9,30,1,31,1,31,1,31,1,32,1,32,1,32,1,33,1,33, + 1,33,1,33,1,33,1,33,1,33,1,33,3,33,495,8,33,1,34,1,34,1,34,1,35, + 1,35,1,35,1,35,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,3,37,512, + 8,37,1,37,1,37,1,37,1,37,3,37,518,8,37,1,37,1,37,1,37,1,37,3,37, + 524,8,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,1,37,3,37,535,8, + 37,3,37,537,8,37,1,38,1,38,1,38,1,39,1,39,1,39,1,40,1,40,1,40,3, + 40,548,8,40,1,40,3,40,551,8,40,1,40,1,40,1,40,1,40,3,40,557,8,40, + 1,40,1,40,1,40,1,40,1,40,1,40,3,40,565,8,40,1,40,1,40,1,40,1,40, + 5,40,571,8,40,10,40,12,40,574,9,40,1,41,3,41,577,8,41,1,41,1,41, + 1,41,3,41,582,8,41,1,41,3,41,585,8,41,1,41,3,41,588,8,41,1,41,1, + 41,3,41,592,8,41,1,41,1,41,3,41,596,8,41,1,41,3,41,599,8,41,3,41, + 601,8,41,1,41,3,41,604,8,41,1,41,1,41,3,41,608,8,41,1,41,1,41,3, + 41,612,8,41,1,41,3,41,615,8,41,3,41,617,8,41,3,41,619,8,41,1,42, + 1,42,1,42,3,42,624,8,42,1,43,1,43,1,43,1,43,1,43,1,43,1,43,1,43, + 1,43,3,43,635,8,43,1,44,1,44,1,44,1,44,3,44,641,8,44,1,45,1,45,1, + 45,5,45,646,8,45,10,45,12,45,649,9,45,1,46,1,46,3,46,653,8,46,1, + 46,1,46,3,46,657,8,46,1,46,1,46,3,46,661,8,46,1,47,1,47,1,47,1,47, + 3,47,667,8,47,3,47,669,8,47,1,48,1,48,1,48,5,48,674,8,48,10,48,12, + 48,677,9,48,1,49,1,49,1,49,1,49,1,50,3,50,684,8,50,1,50,3,50,687, + 8,50,1,50,3,50,690,8,50,1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52, + 1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,54,3,54,709,8,54,1,55, + 1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,3,55,723, + 8,55,1,56,1,56,1,56,1,57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,1,57, + 5,57,737,8,57,10,57,12,57,740,9,57,1,57,3,57,743,8,57,1,57,1,57, + 1,57,1,57,1,57,1,57,1,57,5,57,752,8,57,10,57,12,57,755,9,57,1,57, + 3,57,758,8,57,1,57,1,57,1,57,1,57,1,57,1,57,1,57,5,57,767,8,57,10, + 57,12,57,770,9,57,1,57,3,57,773,8,57,1,57,1,57,1,57,1,57,1,57,3, + 57,780,8,57,1,57,1,57,3,57,784,8,57,1,58,1,58,1,58,5,58,789,8,58, + 10,58,12,58,792,9,58,1,58,3,58,795,8,58,1,59,1,59,1,59,3,59,800, + 8,59,1,59,1,59,1,59,1,59,1,59,4,59,807,8,59,11,59,12,59,808,1,59, + 1,59,3,59,813,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,837,8,59, - 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,3,59,854,8,59,1,59,1,59,1,59,1,59,3,59,860,8,59,1,59,3, - 59,863,8,59,1,59,3,59,866,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,3,59,876,8,59,1,59,1,59,1,59,1,59,3,59,882,8,59,1,59,3,59,885, - 8,59,1,59,3,59,888,8,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,896,8, - 59,1,59,3,59,899,8,59,1,59,1,59,3,59,903,8,59,1,59,3,59,906,8,59, - 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59, - 920,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,1,59,1,59,3,59,937,8,59,1,59,1,59,1,59,3,59,942,8,59,1, - 59,1,59,1,59,3,59,947,8,59,1,59,1,59,1,59,1,59,3,59,953,8,59,1,59, - 1,59,1,59,1,59,1,59,3,59,960,8,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,1,59,1,59,3,59,972,8,59,1,59,1,59,3,59,976,8,59,1,59,3, - 59,979,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,988,8,59,1,59, - 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,1002, - 8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,3,59,1018,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, - 1,59,1,59,1,59,1,59,1,59,1,59,3,59,1047,8,59,1,59,1,59,1,59,1,59, - 1,59,1,59,3,59,1055,8,59,5,59,1057,8,59,10,59,12,59,1060,9,59,1, - 60,1,60,1,60,1,60,5,60,1066,8,60,10,60,12,60,1069,9,60,1,60,3,60, - 1072,8,60,1,60,1,60,1,60,1,60,1,60,5,60,1079,8,60,10,60,12,60,1082, - 9,60,1,60,3,60,1085,8,60,1,60,1,60,3,60,1089,8,60,1,60,1,60,1,60, - 3,60,1094,8,60,1,61,1,61,1,61,5,61,1099,8,61,10,61,12,61,1102,9, - 61,1,61,1,61,1,61,1,61,1,61,1,61,5,61,1110,8,61,10,61,12,61,1113, - 9,61,1,61,1,61,1,61,1,61,1,61,1,61,3,61,1121,8,61,1,61,1,61,1,61, - 1,61,1,61,3,61,1128,8,61,1,62,1,62,1,62,1,62,1,62,1,62,1,62,1,62, - 1,62,1,62,1,62,3,62,1141,8,62,1,63,1,63,1,63,5,63,1146,8,63,10,63, - 12,63,1149,9,63,1,63,3,63,1152,8,63,1,64,1,64,1,64,1,64,1,64,1,64, - 1,64,1,64,1,64,1,64,3,64,1164,8,64,1,65,1,65,1,65,1,65,3,65,1170, - 8,65,1,65,3,65,1173,8,65,1,66,1,66,1,66,5,66,1178,8,66,10,66,12, - 66,1181,9,66,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,3,67,1192, - 8,67,1,67,1,67,1,67,1,67,3,67,1198,8,67,5,67,1200,8,67,10,67,12, - 67,1203,9,67,1,68,1,68,1,68,3,68,1208,8,68,1,68,1,68,1,69,1,69,1, - 69,3,69,1215,8,69,1,69,1,69,1,70,1,70,1,70,5,70,1222,8,70,10,70, - 12,70,1225,9,70,1,70,3,70,1228,8,70,1,71,1,71,1,72,1,72,1,72,1,72, - 1,72,1,72,3,72,1238,8,72,3,72,1240,8,72,1,73,3,73,1243,8,73,1,73, - 1,73,1,73,1,73,1,73,1,73,3,73,1251,8,73,1,74,1,74,1,74,3,74,1256, - 8,74,1,75,1,75,1,76,1,76,1,77,1,77,1,78,1,78,3,78,1266,8,78,1,79, - 1,79,1,79,3,79,1271,8,79,1,80,1,80,1,80,1,80,1,81,1,81,1,81,1,81, - 1,82,1,82,3,82,1283,8,82,1,83,1,83,5,83,1287,8,83,10,83,12,83,1290, - 9,83,1,83,1,83,1,84,1,84,1,84,1,84,1,84,3,84,1299,8,84,1,85,1,85, - 5,85,1303,8,85,10,85,12,85,1306,9,85,1,85,1,85,1,86,1,86,1,86,1, - 86,1,86,3,86,1315,8,86,1,86,0,3,80,118,134,87,0,2,4,6,8,10,12,14, - 16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58, - 60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100, - 102,104,106,108,110,112,114,116,118,120,122,124,126,128,130,132, - 134,136,138,140,142,144,146,148,150,152,154,156,158,160,162,164, - 166,168,170,172,0,17,2,0,32,32,37,37,2,0,18,18,77,77,2,0,46,46,54, - 54,3,0,1,1,4,4,8,8,4,0,1,1,3,4,8,8,83,83,2,0,54,54,76,76,2,0,1,1, - 4,4,2,0,7,7,22,23,2,0,31,31,52,52,2,0,74,74,79,79,3,0,10,10,53,53, - 93,93,2,0,43,43,56,56,1,0,110,111,2,0,121,121,142,142,7,0,21,21, - 40,40,58,59,73,73,81,81,100,100,106,106,19,0,1,13,15,20,22,26,28, - 29,31,31,33,36,38,39,41,44,46,46,48,54,56,57,61,61,63,72,74,80,82, - 86,88,95,97,99,101,102,104,105,4,0,20,20,31,31,41,41,51,51,1493, - 0,177,1,0,0,0,2,184,1,0,0,0,4,186,1,0,0,0,6,188,1,0,0,0,8,195,1, - 0,0,0,10,218,1,0,0,0,12,220,1,0,0,0,14,227,1,0,0,0,16,234,1,0,0, - 0,18,247,1,0,0,0,20,259,1,0,0,0,22,268,1,0,0,0,24,276,1,0,0,0,26, - 298,1,0,0,0,28,313,1,0,0,0,30,322,1,0,0,0,32,327,1,0,0,0,34,331, - 1,0,0,0,36,333,1,0,0,0,38,342,1,0,0,0,40,346,1,0,0,0,42,360,1,0, - 0,0,44,370,1,0,0,0,46,380,1,0,0,0,48,384,1,0,0,0,50,392,1,0,0,0, - 52,441,1,0,0,0,54,444,1,0,0,0,56,450,1,0,0,0,58,454,1,0,0,0,60,460, - 1,0,0,0,62,478,1,0,0,0,64,481,1,0,0,0,66,484,1,0,0,0,68,494,1,0, - 0,0,70,497,1,0,0,0,72,501,1,0,0,0,74,534,1,0,0,0,76,536,1,0,0,0, - 78,539,1,0,0,0,80,554,1,0,0,0,82,616,1,0,0,0,84,621,1,0,0,0,86,632, - 1,0,0,0,88,634,1,0,0,0,90,640,1,0,0,0,92,648,1,0,0,0,94,666,1,0, - 0,0,96,668,1,0,0,0,98,676,1,0,0,0,100,681,1,0,0,0,102,689,1,0,0, - 0,104,693,1,0,0,0,106,697,1,0,0,0,108,706,1,0,0,0,110,720,1,0,0, - 0,112,722,1,0,0,0,114,781,1,0,0,0,116,783,1,0,0,0,118,946,1,0,0, - 0,120,1088,1,0,0,0,122,1127,1,0,0,0,124,1140,1,0,0,0,126,1142,1, - 0,0,0,128,1163,1,0,0,0,130,1172,1,0,0,0,132,1174,1,0,0,0,134,1191, - 1,0,0,0,136,1204,1,0,0,0,138,1214,1,0,0,0,140,1218,1,0,0,0,142,1229, - 1,0,0,0,144,1239,1,0,0,0,146,1242,1,0,0,0,148,1255,1,0,0,0,150,1257, - 1,0,0,0,152,1259,1,0,0,0,154,1261,1,0,0,0,156,1265,1,0,0,0,158,1270, - 1,0,0,0,160,1272,1,0,0,0,162,1276,1,0,0,0,164,1282,1,0,0,0,166,1284, - 1,0,0,0,168,1298,1,0,0,0,170,1300,1,0,0,0,172,1314,1,0,0,0,174,176, - 3,2,1,0,175,174,1,0,0,0,176,179,1,0,0,0,177,175,1,0,0,0,177,178, - 1,0,0,0,178,180,1,0,0,0,179,177,1,0,0,0,180,181,5,0,0,1,181,1,1, - 0,0,0,182,185,3,6,3,0,183,185,3,10,5,0,184,182,1,0,0,0,184,183,1, - 0,0,0,185,3,1,0,0,0,186,187,3,118,59,0,187,5,1,0,0,0,188,189,5,55, - 0,0,189,193,3,158,79,0,190,191,5,118,0,0,191,192,5,125,0,0,192,194, - 3,4,2,0,193,190,1,0,0,0,193,194,1,0,0,0,194,7,1,0,0,0,195,200,3, - 158,79,0,196,197,5,119,0,0,197,199,3,158,79,0,198,196,1,0,0,0,199, - 202,1,0,0,0,200,198,1,0,0,0,200,201,1,0,0,0,201,204,1,0,0,0,202, - 200,1,0,0,0,203,205,5,119,0,0,204,203,1,0,0,0,204,205,1,0,0,0,205, - 9,1,0,0,0,206,219,3,12,6,0,207,219,3,14,7,0,208,219,3,18,9,0,209, - 219,3,20,10,0,210,219,3,22,11,0,211,219,3,26,13,0,212,219,3,24,12, - 0,213,219,3,28,14,0,214,219,3,30,15,0,215,219,3,36,18,0,216,219, - 3,32,16,0,217,219,3,34,17,0,218,206,1,0,0,0,218,207,1,0,0,0,218, - 208,1,0,0,0,218,209,1,0,0,0,218,210,1,0,0,0,218,211,1,0,0,0,218, - 212,1,0,0,0,218,213,1,0,0,0,218,214,1,0,0,0,218,215,1,0,0,0,218, - 216,1,0,0,0,218,217,1,0,0,0,219,11,1,0,0,0,220,222,5,75,0,0,221, - 223,3,4,2,0,222,221,1,0,0,0,222,223,1,0,0,0,223,225,1,0,0,0,224, - 226,5,153,0,0,225,224,1,0,0,0,225,226,1,0,0,0,226,13,1,0,0,0,227, - 229,5,87,0,0,228,230,3,4,2,0,229,228,1,0,0,0,229,230,1,0,0,0,230, - 232,1,0,0,0,231,233,5,153,0,0,232,231,1,0,0,0,232,233,1,0,0,0,233, - 15,1,0,0,0,234,243,5,14,0,0,235,236,5,133,0,0,236,239,3,158,79,0, - 237,238,5,118,0,0,238,240,3,158,79,0,239,237,1,0,0,0,239,240,1,0, - 0,0,240,241,1,0,0,0,241,242,5,152,0,0,242,244,1,0,0,0,243,235,1, - 0,0,0,243,244,1,0,0,0,244,245,1,0,0,0,245,246,3,36,18,0,246,17,1, - 0,0,0,247,248,5,96,0,0,248,252,3,36,18,0,249,251,3,16,8,0,250,249, - 1,0,0,0,251,254,1,0,0,0,252,250,1,0,0,0,252,253,1,0,0,0,253,257, - 1,0,0,0,254,252,1,0,0,0,255,256,5,30,0,0,256,258,3,36,18,0,257,255, - 1,0,0,0,257,258,1,0,0,0,258,19,1,0,0,0,259,260,5,42,0,0,260,261, - 5,133,0,0,261,262,3,4,2,0,262,263,5,152,0,0,263,266,3,10,5,0,264, - 265,5,25,0,0,265,267,3,10,5,0,266,264,1,0,0,0,266,267,1,0,0,0,267, - 21,1,0,0,0,268,269,5,103,0,0,269,270,5,133,0,0,270,271,3,4,2,0,271, - 272,5,152,0,0,272,274,3,10,5,0,273,275,5,153,0,0,274,273,1,0,0,0, - 274,275,1,0,0,0,275,23,1,0,0,0,276,277,5,34,0,0,277,281,5,133,0, - 0,278,282,3,6,3,0,279,282,3,30,15,0,280,282,3,4,2,0,281,278,1,0, - 0,0,281,279,1,0,0,0,281,280,1,0,0,0,281,282,1,0,0,0,282,283,1,0, - 0,0,283,285,5,153,0,0,284,286,3,4,2,0,285,284,1,0,0,0,285,286,1, - 0,0,0,286,287,1,0,0,0,287,291,5,153,0,0,288,292,3,6,3,0,289,292, - 3,30,15,0,290,292,3,4,2,0,291,288,1,0,0,0,291,289,1,0,0,0,291,290, - 1,0,0,0,291,292,1,0,0,0,292,293,1,0,0,0,293,294,5,152,0,0,294,296, - 3,10,5,0,295,297,5,153,0,0,296,295,1,0,0,0,296,297,1,0,0,0,297,25, - 1,0,0,0,298,299,5,34,0,0,299,300,5,133,0,0,300,301,5,55,0,0,301, - 304,3,158,79,0,302,303,5,119,0,0,303,305,3,158,79,0,304,302,1,0, - 0,0,304,305,1,0,0,0,305,306,1,0,0,0,306,307,5,44,0,0,307,308,3,4, - 2,0,308,309,5,152,0,0,309,311,3,10,5,0,310,312,5,153,0,0,311,310, - 1,0,0,0,311,312,1,0,0,0,312,27,1,0,0,0,313,314,7,0,0,0,314,315,3, - 158,79,0,315,317,5,133,0,0,316,318,3,8,4,0,317,316,1,0,0,0,317,318, - 1,0,0,0,318,319,1,0,0,0,319,320,5,152,0,0,320,321,3,36,18,0,321, - 29,1,0,0,0,322,323,3,4,2,0,323,324,5,118,0,0,324,325,5,125,0,0,325, - 326,3,4,2,0,326,31,1,0,0,0,327,329,3,4,2,0,328,330,5,153,0,0,329, - 328,1,0,0,0,329,330,1,0,0,0,330,33,1,0,0,0,331,332,5,153,0,0,332, - 35,1,0,0,0,333,337,5,131,0,0,334,336,3,2,1,0,335,334,1,0,0,0,336, - 339,1,0,0,0,337,335,1,0,0,0,337,338,1,0,0,0,338,340,1,0,0,0,339, - 337,1,0,0,0,340,341,5,150,0,0,341,37,1,0,0,0,342,343,3,4,2,0,343, - 344,5,118,0,0,344,345,3,4,2,0,345,39,1,0,0,0,346,351,3,38,19,0,347, - 348,5,119,0,0,348,350,3,38,19,0,349,347,1,0,0,0,350,353,1,0,0,0, - 351,349,1,0,0,0,351,352,1,0,0,0,352,355,1,0,0,0,353,351,1,0,0,0, - 354,356,5,119,0,0,355,354,1,0,0,0,355,356,1,0,0,0,356,41,1,0,0,0, - 357,361,3,48,24,0,358,361,3,50,25,0,359,361,3,122,61,0,360,357,1, - 0,0,0,360,358,1,0,0,0,360,359,1,0,0,0,361,362,1,0,0,0,362,363,5, - 0,0,1,363,43,1,0,0,0,364,371,3,50,25,0,365,366,5,133,0,0,366,367, - 3,48,24,0,367,368,5,152,0,0,368,371,1,0,0,0,369,371,3,162,81,0,370, - 364,1,0,0,0,370,365,1,0,0,0,370,369,1,0,0,0,371,45,1,0,0,0,372,381, - 5,27,0,0,373,374,5,98,0,0,374,381,5,1,0,0,375,376,5,98,0,0,376,381, - 5,24,0,0,377,381,5,47,0,0,378,379,5,47,0,0,379,381,5,24,0,0,380, - 372,1,0,0,0,380,373,1,0,0,0,380,375,1,0,0,0,380,377,1,0,0,0,380, - 378,1,0,0,0,381,382,1,0,0,0,382,383,3,44,22,0,383,47,1,0,0,0,384, - 388,3,44,22,0,385,387,3,46,23,0,386,385,1,0,0,0,387,390,1,0,0,0, - 388,386,1,0,0,0,388,389,1,0,0,0,389,49,1,0,0,0,390,388,1,0,0,0,391, - 393,3,52,26,0,392,391,1,0,0,0,392,393,1,0,0,0,393,394,1,0,0,0,394, - 396,5,82,0,0,395,397,5,24,0,0,396,395,1,0,0,0,396,397,1,0,0,0,397, - 399,1,0,0,0,398,400,3,54,27,0,399,398,1,0,0,0,399,400,1,0,0,0,400, - 401,1,0,0,0,401,403,3,116,58,0,402,404,3,56,28,0,403,402,1,0,0,0, - 403,404,1,0,0,0,404,406,1,0,0,0,405,407,3,58,29,0,406,405,1,0,0, - 0,406,407,1,0,0,0,407,409,1,0,0,0,408,410,3,62,31,0,409,408,1,0, - 0,0,409,410,1,0,0,0,410,412,1,0,0,0,411,413,3,64,32,0,412,411,1, - 0,0,0,412,413,1,0,0,0,413,415,1,0,0,0,414,416,3,66,33,0,415,414, - 1,0,0,0,415,416,1,0,0,0,416,419,1,0,0,0,417,418,5,105,0,0,418,420, - 7,1,0,0,419,417,1,0,0,0,419,420,1,0,0,0,420,423,1,0,0,0,421,422, - 5,105,0,0,422,424,5,92,0,0,423,421,1,0,0,0,423,424,1,0,0,0,424,426, - 1,0,0,0,425,427,3,68,34,0,426,425,1,0,0,0,426,427,1,0,0,0,427,429, - 1,0,0,0,428,430,3,60,30,0,429,428,1,0,0,0,429,430,1,0,0,0,430,432, - 1,0,0,0,431,433,3,70,35,0,432,431,1,0,0,0,432,433,1,0,0,0,433,436, - 1,0,0,0,434,437,3,74,37,0,435,437,3,76,38,0,436,434,1,0,0,0,436, - 435,1,0,0,0,436,437,1,0,0,0,437,439,1,0,0,0,438,440,3,78,39,0,439, - 438,1,0,0,0,439,440,1,0,0,0,440,51,1,0,0,0,441,442,5,105,0,0,442, - 443,3,126,63,0,443,53,1,0,0,0,444,445,5,91,0,0,445,448,5,111,0,0, - 446,447,5,105,0,0,447,449,5,88,0,0,448,446,1,0,0,0,448,449,1,0,0, - 0,449,55,1,0,0,0,450,451,5,35,0,0,451,452,3,80,40,0,452,57,1,0,0, - 0,453,455,7,2,0,0,454,453,1,0,0,0,454,455,1,0,0,0,455,456,1,0,0, - 0,456,457,5,5,0,0,457,458,5,50,0,0,458,459,3,116,58,0,459,59,1,0, - 0,0,460,461,5,104,0,0,461,462,3,158,79,0,462,463,5,6,0,0,463,464, - 5,133,0,0,464,465,3,100,50,0,465,475,5,152,0,0,466,467,5,119,0,0, - 467,468,3,158,79,0,468,469,5,6,0,0,469,470,5,133,0,0,470,471,3,100, - 50,0,471,472,5,152,0,0,472,474,1,0,0,0,473,466,1,0,0,0,474,477,1, - 0,0,0,475,473,1,0,0,0,475,476,1,0,0,0,476,61,1,0,0,0,477,475,1,0, - 0,0,478,479,5,72,0,0,479,480,3,118,59,0,480,63,1,0,0,0,481,482,5, - 102,0,0,482,483,3,118,59,0,483,65,1,0,0,0,484,485,5,38,0,0,485,492, - 5,11,0,0,486,487,7,1,0,0,487,488,5,133,0,0,488,489,3,116,58,0,489, - 490,5,152,0,0,490,493,1,0,0,0,491,493,3,116,58,0,492,486,1,0,0,0, - 492,491,1,0,0,0,493,67,1,0,0,0,494,495,5,39,0,0,495,496,3,118,59, - 0,496,69,1,0,0,0,497,498,5,67,0,0,498,499,5,11,0,0,499,500,3,90, - 45,0,500,71,1,0,0,0,501,502,5,67,0,0,502,503,5,11,0,0,503,504,3, - 116,58,0,504,73,1,0,0,0,505,506,5,57,0,0,506,509,3,118,59,0,507, - 508,5,119,0,0,508,510,3,118,59,0,509,507,1,0,0,0,509,510,1,0,0,0, - 510,515,1,0,0,0,511,512,5,105,0,0,512,516,5,88,0,0,513,514,5,11, - 0,0,514,516,3,116,58,0,515,511,1,0,0,0,515,513,1,0,0,0,515,516,1, - 0,0,0,516,535,1,0,0,0,517,518,5,57,0,0,518,521,3,118,59,0,519,520, - 5,105,0,0,520,522,5,88,0,0,521,519,1,0,0,0,521,522,1,0,0,0,522,523, - 1,0,0,0,523,524,5,64,0,0,524,525,3,118,59,0,525,535,1,0,0,0,526, - 527,5,57,0,0,527,528,3,118,59,0,528,529,5,64,0,0,529,532,3,118,59, - 0,530,531,5,11,0,0,531,533,3,116,58,0,532,530,1,0,0,0,532,533,1, - 0,0,0,533,535,1,0,0,0,534,505,1,0,0,0,534,517,1,0,0,0,534,526,1, - 0,0,0,535,75,1,0,0,0,536,537,5,64,0,0,537,538,3,118,59,0,538,77, - 1,0,0,0,539,540,5,84,0,0,540,541,3,96,48,0,541,79,1,0,0,0,542,543, - 6,40,-1,0,543,545,3,134,67,0,544,546,5,29,0,0,545,544,1,0,0,0,545, - 546,1,0,0,0,546,548,1,0,0,0,547,549,3,88,44,0,548,547,1,0,0,0,548, - 549,1,0,0,0,549,555,1,0,0,0,550,551,5,133,0,0,551,552,3,80,40,0, - 552,553,5,152,0,0,553,555,1,0,0,0,554,542,1,0,0,0,554,550,1,0,0, - 0,555,570,1,0,0,0,556,557,10,3,0,0,557,558,3,84,42,0,558,559,3,80, - 40,4,559,569,1,0,0,0,560,562,10,4,0,0,561,563,3,82,41,0,562,561, - 1,0,0,0,562,563,1,0,0,0,563,564,1,0,0,0,564,565,5,50,0,0,565,566, - 3,80,40,0,566,567,3,86,43,0,567,569,1,0,0,0,568,556,1,0,0,0,568, - 560,1,0,0,0,569,572,1,0,0,0,570,568,1,0,0,0,570,571,1,0,0,0,571, - 81,1,0,0,0,572,570,1,0,0,0,573,575,7,3,0,0,574,573,1,0,0,0,574,575, - 1,0,0,0,575,576,1,0,0,0,576,583,5,46,0,0,577,579,5,46,0,0,578,580, - 7,3,0,0,579,578,1,0,0,0,579,580,1,0,0,0,580,583,1,0,0,0,581,583, - 7,3,0,0,582,574,1,0,0,0,582,577,1,0,0,0,582,581,1,0,0,0,583,617, - 1,0,0,0,584,586,7,4,0,0,585,584,1,0,0,0,585,586,1,0,0,0,586,587, - 1,0,0,0,587,589,7,5,0,0,588,590,5,68,0,0,589,588,1,0,0,0,589,590, - 1,0,0,0,590,599,1,0,0,0,591,593,7,5,0,0,592,594,5,68,0,0,593,592, - 1,0,0,0,593,594,1,0,0,0,594,596,1,0,0,0,595,597,7,4,0,0,596,595, - 1,0,0,0,596,597,1,0,0,0,597,599,1,0,0,0,598,585,1,0,0,0,598,591, - 1,0,0,0,599,617,1,0,0,0,600,602,7,6,0,0,601,600,1,0,0,0,601,602, - 1,0,0,0,602,603,1,0,0,0,603,605,5,36,0,0,604,606,5,68,0,0,605,604, - 1,0,0,0,605,606,1,0,0,0,606,615,1,0,0,0,607,609,5,36,0,0,608,610, - 5,68,0,0,609,608,1,0,0,0,609,610,1,0,0,0,610,612,1,0,0,0,611,613, - 7,6,0,0,612,611,1,0,0,0,612,613,1,0,0,0,613,615,1,0,0,0,614,601, - 1,0,0,0,614,607,1,0,0,0,615,617,1,0,0,0,616,582,1,0,0,0,616,598, - 1,0,0,0,616,614,1,0,0,0,617,83,1,0,0,0,618,619,5,17,0,0,619,622, - 5,50,0,0,620,622,5,119,0,0,621,618,1,0,0,0,621,620,1,0,0,0,622,85, - 1,0,0,0,623,624,5,65,0,0,624,633,3,116,58,0,625,626,5,99,0,0,626, - 627,5,133,0,0,627,628,3,116,58,0,628,629,5,152,0,0,629,633,1,0,0, - 0,630,631,5,99,0,0,631,633,3,116,58,0,632,623,1,0,0,0,632,625,1, - 0,0,0,632,630,1,0,0,0,633,87,1,0,0,0,634,635,5,80,0,0,635,638,3, - 94,47,0,636,637,5,64,0,0,637,639,3,94,47,0,638,636,1,0,0,0,638,639, - 1,0,0,0,639,89,1,0,0,0,640,645,3,92,46,0,641,642,5,119,0,0,642,644, - 3,92,46,0,643,641,1,0,0,0,644,647,1,0,0,0,645,643,1,0,0,0,645,646, - 1,0,0,0,646,91,1,0,0,0,647,645,1,0,0,0,648,650,3,118,59,0,649,651, - 7,7,0,0,650,649,1,0,0,0,650,651,1,0,0,0,651,654,1,0,0,0,652,653, - 5,63,0,0,653,655,7,8,0,0,654,652,1,0,0,0,654,655,1,0,0,0,655,658, - 1,0,0,0,656,657,5,16,0,0,657,659,5,113,0,0,658,656,1,0,0,0,658,659, - 1,0,0,0,659,93,1,0,0,0,660,667,3,162,81,0,661,664,3,146,73,0,662, - 663,5,154,0,0,663,665,3,146,73,0,664,662,1,0,0,0,664,665,1,0,0,0, - 665,667,1,0,0,0,666,660,1,0,0,0,666,661,1,0,0,0,667,95,1,0,0,0,668, - 673,3,98,49,0,669,670,5,119,0,0,670,672,3,98,49,0,671,669,1,0,0, - 0,672,675,1,0,0,0,673,671,1,0,0,0,673,674,1,0,0,0,674,97,1,0,0,0, - 675,673,1,0,0,0,676,677,3,158,79,0,677,678,5,125,0,0,678,679,3,148, - 74,0,679,99,1,0,0,0,680,682,3,102,51,0,681,680,1,0,0,0,681,682,1, - 0,0,0,682,684,1,0,0,0,683,685,3,104,52,0,684,683,1,0,0,0,684,685, - 1,0,0,0,685,687,1,0,0,0,686,688,3,106,53,0,687,686,1,0,0,0,687,688, - 1,0,0,0,688,101,1,0,0,0,689,690,5,70,0,0,690,691,5,11,0,0,691,692, - 3,116,58,0,692,103,1,0,0,0,693,694,5,67,0,0,694,695,5,11,0,0,695, - 696,3,90,45,0,696,105,1,0,0,0,697,698,7,9,0,0,698,699,3,108,54,0, - 699,107,1,0,0,0,700,707,3,110,55,0,701,702,5,9,0,0,702,703,3,110, - 55,0,703,704,5,2,0,0,704,705,3,110,55,0,705,707,1,0,0,0,706,700, - 1,0,0,0,706,701,1,0,0,0,707,109,1,0,0,0,708,709,5,19,0,0,709,721, - 5,78,0,0,710,711,5,97,0,0,711,721,5,71,0,0,712,713,5,97,0,0,713, - 721,5,33,0,0,714,715,3,146,73,0,715,716,5,71,0,0,716,721,1,0,0,0, - 717,718,3,146,73,0,718,719,5,33,0,0,719,721,1,0,0,0,720,708,1,0, - 0,0,720,710,1,0,0,0,720,712,1,0,0,0,720,714,1,0,0,0,720,717,1,0, - 0,0,721,111,1,0,0,0,722,723,3,118,59,0,723,724,5,0,0,1,724,113,1, - 0,0,0,725,782,3,158,79,0,726,727,3,158,79,0,727,728,5,133,0,0,728, - 729,3,158,79,0,729,736,3,114,57,0,730,731,5,119,0,0,731,732,3,158, - 79,0,732,733,3,114,57,0,733,735,1,0,0,0,734,730,1,0,0,0,735,738, - 1,0,0,0,736,734,1,0,0,0,736,737,1,0,0,0,737,740,1,0,0,0,738,736, - 1,0,0,0,739,741,5,119,0,0,740,739,1,0,0,0,740,741,1,0,0,0,741,742, - 1,0,0,0,742,743,5,152,0,0,743,782,1,0,0,0,744,745,3,158,79,0,745, - 746,5,133,0,0,746,751,3,160,80,0,747,748,5,119,0,0,748,750,3,160, - 80,0,749,747,1,0,0,0,750,753,1,0,0,0,751,749,1,0,0,0,751,752,1,0, - 0,0,752,755,1,0,0,0,753,751,1,0,0,0,754,756,5,119,0,0,755,754,1, - 0,0,0,755,756,1,0,0,0,756,757,1,0,0,0,757,758,5,152,0,0,758,782, - 1,0,0,0,759,760,3,158,79,0,760,761,5,133,0,0,761,766,3,114,57,0, - 762,763,5,119,0,0,763,765,3,114,57,0,764,762,1,0,0,0,765,768,1,0, - 0,0,766,764,1,0,0,0,766,767,1,0,0,0,767,770,1,0,0,0,768,766,1,0, - 0,0,769,771,5,119,0,0,770,769,1,0,0,0,770,771,1,0,0,0,771,772,1, - 0,0,0,772,773,5,152,0,0,773,782,1,0,0,0,774,775,3,158,79,0,775,777, - 5,133,0,0,776,778,3,116,58,0,777,776,1,0,0,0,777,778,1,0,0,0,778, - 779,1,0,0,0,779,780,5,152,0,0,780,782,1,0,0,0,781,725,1,0,0,0,781, - 726,1,0,0,0,781,744,1,0,0,0,781,759,1,0,0,0,781,774,1,0,0,0,782, - 115,1,0,0,0,783,788,3,118,59,0,784,785,5,119,0,0,785,787,3,118,59, - 0,786,784,1,0,0,0,787,790,1,0,0,0,788,786,1,0,0,0,788,789,1,0,0, - 0,789,792,1,0,0,0,790,788,1,0,0,0,791,793,5,119,0,0,792,791,1,0, - 0,0,792,793,1,0,0,0,793,117,1,0,0,0,794,795,6,59,-1,0,795,797,5, - 12,0,0,796,798,3,118,59,0,797,796,1,0,0,0,797,798,1,0,0,0,798,804, - 1,0,0,0,799,800,5,101,0,0,800,801,3,118,59,0,801,802,5,86,0,0,802, - 803,3,118,59,0,803,805,1,0,0,0,804,799,1,0,0,0,805,806,1,0,0,0,806, - 804,1,0,0,0,806,807,1,0,0,0,807,810,1,0,0,0,808,809,5,25,0,0,809, - 811,3,118,59,0,810,808,1,0,0,0,810,811,1,0,0,0,811,812,1,0,0,0,812, - 813,5,26,0,0,813,947,1,0,0,0,814,815,5,13,0,0,815,816,5,133,0,0, - 816,817,3,118,59,0,817,818,5,6,0,0,818,819,3,114,57,0,819,820,5, - 152,0,0,820,947,1,0,0,0,821,822,5,20,0,0,822,947,5,113,0,0,823,824, - 5,48,0,0,824,947,5,113,0,0,825,826,5,48,0,0,826,827,3,118,59,0,827, - 828,3,150,75,0,828,947,1,0,0,0,829,830,5,85,0,0,830,831,5,133,0, - 0,831,832,3,118,59,0,832,833,5,35,0,0,833,836,3,118,59,0,834,835, - 5,34,0,0,835,837,3,118,59,0,836,834,1,0,0,0,836,837,1,0,0,0,837, - 838,1,0,0,0,838,839,5,152,0,0,839,947,1,0,0,0,840,841,5,89,0,0,841, - 947,5,113,0,0,842,843,5,94,0,0,843,844,5,133,0,0,844,845,7,10,0, - 0,845,846,3,164,82,0,846,847,5,35,0,0,847,848,3,118,59,0,848,849, - 5,152,0,0,849,947,1,0,0,0,850,851,3,158,79,0,851,853,5,133,0,0,852, - 854,3,116,58,0,853,852,1,0,0,0,853,854,1,0,0,0,854,855,1,0,0,0,855, - 856,5,152,0,0,856,865,1,0,0,0,857,859,5,133,0,0,858,860,5,24,0,0, - 859,858,1,0,0,0,859,860,1,0,0,0,860,862,1,0,0,0,861,863,3,116,58, - 0,862,861,1,0,0,0,862,863,1,0,0,0,863,864,1,0,0,0,864,866,5,152, - 0,0,865,857,1,0,0,0,865,866,1,0,0,0,866,867,1,0,0,0,867,868,5,69, - 0,0,868,869,5,133,0,0,869,870,3,100,50,0,870,871,5,152,0,0,871,947, - 1,0,0,0,872,873,3,158,79,0,873,875,5,133,0,0,874,876,3,116,58,0, - 875,874,1,0,0,0,875,876,1,0,0,0,876,877,1,0,0,0,877,878,5,152,0, - 0,878,887,1,0,0,0,879,881,5,133,0,0,880,882,5,24,0,0,881,880,1,0, - 0,0,881,882,1,0,0,0,882,884,1,0,0,0,883,885,3,116,58,0,884,883,1, - 0,0,0,884,885,1,0,0,0,885,886,1,0,0,0,886,888,5,152,0,0,887,879, - 1,0,0,0,887,888,1,0,0,0,888,889,1,0,0,0,889,890,5,69,0,0,890,891, - 3,158,79,0,891,947,1,0,0,0,892,898,3,158,79,0,893,895,5,133,0,0, - 894,896,3,116,58,0,895,894,1,0,0,0,895,896,1,0,0,0,896,897,1,0,0, - 0,897,899,5,152,0,0,898,893,1,0,0,0,898,899,1,0,0,0,899,900,1,0, - 0,0,900,902,5,133,0,0,901,903,5,24,0,0,902,901,1,0,0,0,902,903,1, - 0,0,0,903,905,1,0,0,0,904,906,3,116,58,0,905,904,1,0,0,0,905,906, - 1,0,0,0,906,907,1,0,0,0,907,908,5,152,0,0,908,947,1,0,0,0,909,947, - 3,122,61,0,910,947,3,166,83,0,911,947,3,148,74,0,912,913,5,121,0, - 0,913,947,3,118,59,20,914,915,5,61,0,0,915,947,3,118,59,14,916,917, - 3,138,69,0,917,918,5,123,0,0,918,920,1,0,0,0,919,916,1,0,0,0,919, - 920,1,0,0,0,920,921,1,0,0,0,921,947,5,115,0,0,922,923,5,133,0,0, - 923,924,3,48,24,0,924,925,5,152,0,0,925,947,1,0,0,0,926,927,5,133, - 0,0,927,928,3,118,59,0,928,929,5,152,0,0,929,947,1,0,0,0,930,931, - 5,133,0,0,931,932,3,116,58,0,932,933,5,152,0,0,933,947,1,0,0,0,934, - 936,5,132,0,0,935,937,3,116,58,0,936,935,1,0,0,0,936,937,1,0,0,0, - 937,938,1,0,0,0,938,947,5,151,0,0,939,941,5,131,0,0,940,942,3,40, - 20,0,941,940,1,0,0,0,941,942,1,0,0,0,942,943,1,0,0,0,943,947,5,150, - 0,0,944,947,3,120,60,0,945,947,3,130,65,0,946,794,1,0,0,0,946,814, - 1,0,0,0,946,821,1,0,0,0,946,823,1,0,0,0,946,825,1,0,0,0,946,829, - 1,0,0,0,946,840,1,0,0,0,946,842,1,0,0,0,946,850,1,0,0,0,946,872, - 1,0,0,0,946,892,1,0,0,0,946,909,1,0,0,0,946,910,1,0,0,0,946,911, - 1,0,0,0,946,912,1,0,0,0,946,914,1,0,0,0,946,919,1,0,0,0,946,922, - 1,0,0,0,946,926,1,0,0,0,946,930,1,0,0,0,946,934,1,0,0,0,946,939, - 1,0,0,0,946,944,1,0,0,0,946,945,1,0,0,0,947,1058,1,0,0,0,948,952, - 10,19,0,0,949,953,5,115,0,0,950,953,5,154,0,0,951,953,5,141,0,0, - 952,949,1,0,0,0,952,950,1,0,0,0,952,951,1,0,0,0,953,954,1,0,0,0, - 954,1057,3,118,59,20,955,959,10,18,0,0,956,960,5,142,0,0,957,960, - 5,121,0,0,958,960,5,120,0,0,959,956,1,0,0,0,959,957,1,0,0,0,959, - 958,1,0,0,0,960,961,1,0,0,0,961,1057,3,118,59,19,962,987,10,17,0, - 0,963,988,5,124,0,0,964,988,5,125,0,0,965,988,5,136,0,0,966,988, - 5,134,0,0,967,988,5,135,0,0,968,988,5,126,0,0,969,988,5,127,0,0, - 970,972,5,61,0,0,971,970,1,0,0,0,971,972,1,0,0,0,972,973,1,0,0,0, - 973,975,5,44,0,0,974,976,5,15,0,0,975,974,1,0,0,0,975,976,1,0,0, - 0,976,988,1,0,0,0,977,979,5,61,0,0,978,977,1,0,0,0,978,979,1,0,0, - 0,979,980,1,0,0,0,980,988,7,11,0,0,981,988,5,148,0,0,982,988,5,149, - 0,0,983,988,5,138,0,0,984,988,5,129,0,0,985,988,5,130,0,0,986,988, - 5,137,0,0,987,963,1,0,0,0,987,964,1,0,0,0,987,965,1,0,0,0,987,966, - 1,0,0,0,987,967,1,0,0,0,987,968,1,0,0,0,987,969,1,0,0,0,987,971, - 1,0,0,0,987,978,1,0,0,0,987,981,1,0,0,0,987,982,1,0,0,0,987,983, - 1,0,0,0,987,984,1,0,0,0,987,985,1,0,0,0,987,986,1,0,0,0,988,989, - 1,0,0,0,989,1057,3,118,59,18,990,991,10,15,0,0,991,992,5,140,0,0, - 992,1057,3,118,59,16,993,994,10,13,0,0,994,995,5,2,0,0,995,1057, - 3,118,59,14,996,997,10,12,0,0,997,998,5,66,0,0,998,1057,3,118,59, - 13,999,1001,10,11,0,0,1000,1002,5,61,0,0,1001,1000,1,0,0,0,1001, - 1002,1,0,0,0,1002,1003,1,0,0,0,1003,1004,5,9,0,0,1004,1005,3,118, - 59,0,1005,1006,5,2,0,0,1006,1007,3,118,59,12,1007,1057,1,0,0,0,1008, - 1009,10,10,0,0,1009,1010,5,143,0,0,1010,1011,3,118,59,0,1011,1012, - 5,118,0,0,1012,1013,3,118,59,10,1013,1057,1,0,0,0,1014,1015,10,30, - 0,0,1015,1017,5,133,0,0,1016,1018,3,116,58,0,1017,1016,1,0,0,0,1017, - 1018,1,0,0,0,1018,1019,1,0,0,0,1019,1057,5,152,0,0,1020,1021,10, - 26,0,0,1021,1022,5,132,0,0,1022,1023,3,118,59,0,1023,1024,5,151, - 0,0,1024,1057,1,0,0,0,1025,1026,10,25,0,0,1026,1027,5,123,0,0,1027, - 1057,5,111,0,0,1028,1029,10,24,0,0,1029,1030,5,123,0,0,1030,1057, - 3,158,79,0,1031,1032,10,23,0,0,1032,1033,5,139,0,0,1033,1034,5,132, - 0,0,1034,1035,3,118,59,0,1035,1036,5,151,0,0,1036,1057,1,0,0,0,1037, - 1038,10,22,0,0,1038,1039,5,139,0,0,1039,1057,5,111,0,0,1040,1041, - 10,21,0,0,1041,1042,5,139,0,0,1042,1057,3,158,79,0,1043,1044,10, - 16,0,0,1044,1046,5,49,0,0,1045,1047,5,61,0,0,1046,1045,1,0,0,0,1046, - 1047,1,0,0,0,1047,1048,1,0,0,0,1048,1057,5,62,0,0,1049,1054,10,9, - 0,0,1050,1051,5,6,0,0,1051,1055,3,158,79,0,1052,1053,5,6,0,0,1053, - 1055,5,113,0,0,1054,1050,1,0,0,0,1054,1052,1,0,0,0,1055,1057,1,0, - 0,0,1056,948,1,0,0,0,1056,955,1,0,0,0,1056,962,1,0,0,0,1056,990, - 1,0,0,0,1056,993,1,0,0,0,1056,996,1,0,0,0,1056,999,1,0,0,0,1056, - 1008,1,0,0,0,1056,1014,1,0,0,0,1056,1020,1,0,0,0,1056,1025,1,0,0, - 0,1056,1028,1,0,0,0,1056,1031,1,0,0,0,1056,1037,1,0,0,0,1056,1040, - 1,0,0,0,1056,1043,1,0,0,0,1056,1049,1,0,0,0,1057,1060,1,0,0,0,1058, - 1056,1,0,0,0,1058,1059,1,0,0,0,1059,119,1,0,0,0,1060,1058,1,0,0, - 0,1061,1062,5,133,0,0,1062,1067,3,158,79,0,1063,1064,5,119,0,0,1064, - 1066,3,158,79,0,1065,1063,1,0,0,0,1066,1069,1,0,0,0,1067,1065,1, - 0,0,0,1067,1068,1,0,0,0,1068,1071,1,0,0,0,1069,1067,1,0,0,0,1070, - 1072,5,119,0,0,1071,1070,1,0,0,0,1071,1072,1,0,0,0,1072,1073,1,0, - 0,0,1073,1074,5,152,0,0,1074,1089,1,0,0,0,1075,1080,3,158,79,0,1076, - 1077,5,119,0,0,1077,1079,3,158,79,0,1078,1076,1,0,0,0,1079,1082, - 1,0,0,0,1080,1078,1,0,0,0,1080,1081,1,0,0,0,1081,1084,1,0,0,0,1082, - 1080,1,0,0,0,1083,1085,5,119,0,0,1084,1083,1,0,0,0,1084,1085,1,0, - 0,0,1085,1089,1,0,0,0,1086,1087,5,133,0,0,1087,1089,5,152,0,0,1088, - 1061,1,0,0,0,1088,1075,1,0,0,0,1088,1086,1,0,0,0,1089,1090,1,0,0, - 0,1090,1093,5,114,0,0,1091,1094,3,118,59,0,1092,1094,3,36,18,0,1093, - 1091,1,0,0,0,1093,1092,1,0,0,0,1094,121,1,0,0,0,1095,1096,5,135, - 0,0,1096,1100,3,158,79,0,1097,1099,3,124,62,0,1098,1097,1,0,0,0, - 1099,1102,1,0,0,0,1100,1098,1,0,0,0,1100,1101,1,0,0,0,1101,1103, - 1,0,0,0,1102,1100,1,0,0,0,1103,1104,5,154,0,0,1104,1105,5,127,0, - 0,1105,1128,1,0,0,0,1106,1107,5,135,0,0,1107,1111,3,158,79,0,1108, - 1110,3,124,62,0,1109,1108,1,0,0,0,1110,1113,1,0,0,0,1111,1109,1, - 0,0,0,1111,1112,1,0,0,0,1112,1114,1,0,0,0,1113,1111,1,0,0,0,1114, - 1120,5,127,0,0,1115,1121,3,122,61,0,1116,1117,5,131,0,0,1117,1118, - 3,118,59,0,1118,1119,5,150,0,0,1119,1121,1,0,0,0,1120,1115,1,0,0, - 0,1120,1116,1,0,0,0,1120,1121,1,0,0,0,1121,1122,1,0,0,0,1122,1123, - 5,135,0,0,1123,1124,5,154,0,0,1124,1125,3,158,79,0,1125,1126,5,127, - 0,0,1126,1128,1,0,0,0,1127,1095,1,0,0,0,1127,1106,1,0,0,0,1128,123, - 1,0,0,0,1129,1130,3,158,79,0,1130,1131,5,125,0,0,1131,1132,3,164, - 82,0,1132,1141,1,0,0,0,1133,1134,3,158,79,0,1134,1135,5,125,0,0, - 1135,1136,5,131,0,0,1136,1137,3,118,59,0,1137,1138,5,150,0,0,1138, - 1141,1,0,0,0,1139,1141,3,158,79,0,1140,1129,1,0,0,0,1140,1133,1, - 0,0,0,1140,1139,1,0,0,0,1141,125,1,0,0,0,1142,1147,3,128,64,0,1143, - 1144,5,119,0,0,1144,1146,3,128,64,0,1145,1143,1,0,0,0,1146,1149, - 1,0,0,0,1147,1145,1,0,0,0,1147,1148,1,0,0,0,1148,1151,1,0,0,0,1149, - 1147,1,0,0,0,1150,1152,5,119,0,0,1151,1150,1,0,0,0,1151,1152,1,0, - 0,0,1152,127,1,0,0,0,1153,1154,3,158,79,0,1154,1155,5,6,0,0,1155, - 1156,5,133,0,0,1156,1157,3,48,24,0,1157,1158,5,152,0,0,1158,1164, - 1,0,0,0,1159,1160,3,118,59,0,1160,1161,5,6,0,0,1161,1162,3,158,79, - 0,1162,1164,1,0,0,0,1163,1153,1,0,0,0,1163,1159,1,0,0,0,1164,129, - 1,0,0,0,1165,1173,3,162,81,0,1166,1167,3,138,69,0,1167,1168,5,123, - 0,0,1168,1170,1,0,0,0,1169,1166,1,0,0,0,1169,1170,1,0,0,0,1170,1171, - 1,0,0,0,1171,1173,3,132,66,0,1172,1165,1,0,0,0,1172,1169,1,0,0,0, - 1173,131,1,0,0,0,1174,1179,3,158,79,0,1175,1176,5,123,0,0,1176,1178, - 3,158,79,0,1177,1175,1,0,0,0,1178,1181,1,0,0,0,1179,1177,1,0,0,0, - 1179,1180,1,0,0,0,1180,133,1,0,0,0,1181,1179,1,0,0,0,1182,1183,6, - 67,-1,0,1183,1192,3,138,69,0,1184,1192,3,136,68,0,1185,1186,5,133, - 0,0,1186,1187,3,48,24,0,1187,1188,5,152,0,0,1188,1192,1,0,0,0,1189, - 1192,3,122,61,0,1190,1192,3,162,81,0,1191,1182,1,0,0,0,1191,1184, - 1,0,0,0,1191,1185,1,0,0,0,1191,1189,1,0,0,0,1191,1190,1,0,0,0,1192, - 1201,1,0,0,0,1193,1197,10,3,0,0,1194,1198,3,156,78,0,1195,1196,5, - 6,0,0,1196,1198,3,158,79,0,1197,1194,1,0,0,0,1197,1195,1,0,0,0,1198, - 1200,1,0,0,0,1199,1193,1,0,0,0,1200,1203,1,0,0,0,1201,1199,1,0,0, - 0,1201,1202,1,0,0,0,1202,135,1,0,0,0,1203,1201,1,0,0,0,1204,1205, - 3,158,79,0,1205,1207,5,133,0,0,1206,1208,3,140,70,0,1207,1206,1, - 0,0,0,1207,1208,1,0,0,0,1208,1209,1,0,0,0,1209,1210,5,152,0,0,1210, - 137,1,0,0,0,1211,1212,3,142,71,0,1212,1213,5,123,0,0,1213,1215,1, - 0,0,0,1214,1211,1,0,0,0,1214,1215,1,0,0,0,1215,1216,1,0,0,0,1216, - 1217,3,158,79,0,1217,139,1,0,0,0,1218,1223,3,118,59,0,1219,1220, - 5,119,0,0,1220,1222,3,118,59,0,1221,1219,1,0,0,0,1222,1225,1,0,0, - 0,1223,1221,1,0,0,0,1223,1224,1,0,0,0,1224,1227,1,0,0,0,1225,1223, - 1,0,0,0,1226,1228,5,119,0,0,1227,1226,1,0,0,0,1227,1228,1,0,0,0, - 1228,141,1,0,0,0,1229,1230,3,158,79,0,1230,143,1,0,0,0,1231,1240, - 5,109,0,0,1232,1233,5,123,0,0,1233,1240,7,12,0,0,1234,1235,5,111, - 0,0,1235,1237,5,123,0,0,1236,1238,7,12,0,0,1237,1236,1,0,0,0,1237, - 1238,1,0,0,0,1238,1240,1,0,0,0,1239,1231,1,0,0,0,1239,1232,1,0,0, - 0,1239,1234,1,0,0,0,1240,145,1,0,0,0,1241,1243,7,13,0,0,1242,1241, - 1,0,0,0,1242,1243,1,0,0,0,1243,1250,1,0,0,0,1244,1251,3,144,72,0, - 1245,1251,5,110,0,0,1246,1251,5,111,0,0,1247,1251,5,112,0,0,1248, - 1251,5,45,0,0,1249,1251,5,60,0,0,1250,1244,1,0,0,0,1250,1245,1,0, - 0,0,1250,1246,1,0,0,0,1250,1247,1,0,0,0,1250,1248,1,0,0,0,1250,1249, - 1,0,0,0,1251,147,1,0,0,0,1252,1256,3,146,73,0,1253,1256,5,113,0, - 0,1254,1256,5,62,0,0,1255,1252,1,0,0,0,1255,1253,1,0,0,0,1255,1254, - 1,0,0,0,1256,149,1,0,0,0,1257,1258,7,14,0,0,1258,151,1,0,0,0,1259, - 1260,7,15,0,0,1260,153,1,0,0,0,1261,1262,7,16,0,0,1262,155,1,0,0, - 0,1263,1266,5,108,0,0,1264,1266,3,154,77,0,1265,1263,1,0,0,0,1265, - 1264,1,0,0,0,1266,157,1,0,0,0,1267,1271,5,108,0,0,1268,1271,3,150, - 75,0,1269,1271,3,152,76,0,1270,1267,1,0,0,0,1270,1268,1,0,0,0,1270, - 1269,1,0,0,0,1271,159,1,0,0,0,1272,1273,3,164,82,0,1273,1274,5,125, - 0,0,1274,1275,3,146,73,0,1275,161,1,0,0,0,1276,1277,5,131,0,0,1277, - 1278,3,118,59,0,1278,1279,5,150,0,0,1279,163,1,0,0,0,1280,1283,5, - 113,0,0,1281,1283,3,166,83,0,1282,1280,1,0,0,0,1282,1281,1,0,0,0, - 1283,165,1,0,0,0,1284,1288,5,145,0,0,1285,1287,3,168,84,0,1286,1285, - 1,0,0,0,1287,1290,1,0,0,0,1288,1286,1,0,0,0,1288,1289,1,0,0,0,1289, - 1291,1,0,0,0,1290,1288,1,0,0,0,1291,1292,5,147,0,0,1292,167,1,0, - 0,0,1293,1294,5,160,0,0,1294,1295,3,118,59,0,1295,1296,5,150,0,0, - 1296,1299,1,0,0,0,1297,1299,5,159,0,0,1298,1293,1,0,0,0,1298,1297, - 1,0,0,0,1299,169,1,0,0,0,1300,1304,5,146,0,0,1301,1303,3,172,86, - 0,1302,1301,1,0,0,0,1303,1306,1,0,0,0,1304,1302,1,0,0,0,1304,1305, - 1,0,0,0,1305,1307,1,0,0,0,1306,1304,1,0,0,0,1307,1308,5,0,0,1,1308, - 171,1,0,0,0,1309,1310,5,162,0,0,1310,1311,3,118,59,0,1311,1312,5, - 150,0,0,1312,1315,1,0,0,0,1313,1315,5,161,0,0,1314,1309,1,0,0,0, - 1314,1313,1,0,0,0,1315,173,1,0,0,0,168,177,184,193,200,204,218,222, - 225,229,232,239,243,252,257,266,274,281,285,291,296,304,311,317, - 329,337,351,355,360,370,380,388,392,396,399,403,406,409,412,415, - 419,423,426,429,432,436,439,448,454,475,492,509,515,521,532,534, - 545,548,554,562,568,570,574,579,582,585,589,593,596,598,601,605, - 609,612,614,616,621,632,638,645,650,654,658,664,666,673,681,684, - 687,706,720,736,740,751,755,766,770,777,781,788,792,797,806,810, - 836,853,859,862,865,875,881,884,887,895,898,902,905,919,936,941, - 946,952,959,971,975,978,987,1001,1017,1046,1054,1056,1058,1067,1071, - 1080,1084,1088,1093,1100,1111,1120,1127,1140,1147,1151,1163,1169, - 1172,1179,1191,1197,1201,1207,1214,1223,1227,1237,1239,1242,1250, - 1255,1265,1270,1282,1288,1298,1304,1314 + 1,59,1,59,3,59,839,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, + 1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,856,8,59,1,59,1,59,1,59, + 1,59,3,59,862,8,59,1,59,3,59,865,8,59,1,59,3,59,868,8,59,1,59,1, + 59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,878,8,59,1,59,1,59,1,59,1, + 59,3,59,884,8,59,1,59,3,59,887,8,59,1,59,3,59,890,8,59,1,59,1,59, + 1,59,1,59,1,59,1,59,3,59,898,8,59,1,59,3,59,901,8,59,1,59,1,59,3, + 59,905,8,59,1,59,3,59,908,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59, + 1,59,1,59,1,59,1,59,1,59,3,59,922,8,59,1,59,1,59,1,59,1,59,1,59, + 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,939,8,59, + 1,59,1,59,1,59,3,59,944,8,59,1,59,1,59,1,59,3,59,949,8,59,1,59,1, + 59,1,59,1,59,3,59,955,8,59,1,59,1,59,1,59,1,59,1,59,3,59,962,8,59, + 1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,974,8,59, + 1,59,1,59,3,59,978,8,59,1,59,3,59,981,8,59,1,59,1,59,1,59,1,59,1, + 59,1,59,1,59,3,59,990,8,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1, + 59,1,59,1,59,1,59,1,59,3,59,1004,8,59,1,59,1,59,1,59,1,59,1,59,1, + 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,1020,8,59,1,59,1, + 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1, + 59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,3, + 59,1049,8,59,1,59,1,59,1,59,1,59,1,59,1,59,3,59,1057,8,59,5,59,1059, + 8,59,10,59,12,59,1062,9,59,1,60,1,60,1,60,1,60,5,60,1068,8,60,10, + 60,12,60,1071,9,60,1,60,3,60,1074,8,60,1,60,1,60,1,60,1,60,1,60, + 5,60,1081,8,60,10,60,12,60,1084,9,60,1,60,3,60,1087,8,60,1,60,1, + 60,3,60,1091,8,60,1,60,1,60,1,60,3,60,1096,8,60,1,61,1,61,1,61,1, + 61,1,61,3,61,1103,8,61,1,62,1,62,1,62,5,62,1108,8,62,10,62,12,62, + 1111,9,62,1,62,1,62,1,62,1,62,1,62,1,62,5,62,1119,8,62,10,62,12, + 62,1122,9,62,1,62,1,62,5,62,1126,8,62,10,62,12,62,1129,9,62,1,62, + 1,62,1,62,1,62,1,62,3,62,1136,8,62,1,63,1,63,1,63,1,63,1,63,1,63, + 1,63,1,63,1,63,1,63,1,63,3,63,1149,8,63,1,64,1,64,1,64,5,64,1154, + 8,64,10,64,12,64,1157,9,64,1,64,3,64,1160,8,64,1,65,1,65,1,65,1, + 65,1,65,1,65,1,65,1,65,1,65,1,65,3,65,1172,8,65,1,66,1,66,1,66,1, + 66,3,66,1178,8,66,1,66,3,66,1181,8,66,1,67,1,67,1,67,5,67,1186,8, + 67,10,67,12,67,1189,9,67,1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,68, + 1,68,3,68,1200,8,68,1,68,1,68,1,68,1,68,3,68,1206,8,68,5,68,1208, + 8,68,10,68,12,68,1211,9,68,1,69,1,69,1,69,3,69,1216,8,69,1,69,1, + 69,1,70,1,70,1,70,3,70,1223,8,70,1,70,1,70,1,71,1,71,1,71,5,71,1230, + 8,71,10,71,12,71,1233,9,71,1,71,3,71,1236,8,71,1,72,1,72,1,73,1, + 73,1,73,1,73,1,73,1,73,3,73,1246,8,73,3,73,1248,8,73,1,74,3,74,1251, + 8,74,1,74,1,74,1,74,1,74,1,74,1,74,3,74,1259,8,74,1,75,1,75,1,75, + 3,75,1264,8,75,1,76,1,76,1,77,1,77,1,78,1,78,1,79,1,79,3,79,1274, + 8,79,1,80,1,80,1,80,3,80,1279,8,80,1,81,1,81,1,81,1,81,1,82,1,82, + 1,82,1,82,1,83,1,83,3,83,1291,8,83,1,84,1,84,5,84,1295,8,84,10,84, + 12,84,1298,9,84,1,84,1,84,1,85,1,85,1,85,1,85,1,85,3,85,1307,8,85, + 1,86,1,86,5,86,1311,8,86,10,86,12,86,1314,9,86,1,86,1,86,1,87,1, + 87,1,87,1,87,1,87,3,87,1323,8,87,1,87,0,3,80,118,136,88,0,2,4,6, + 8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50, + 52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94, + 96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,128, + 130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160, + 162,164,166,168,170,172,174,0,17,2,0,32,32,37,37,2,0,18,18,77,77, + 2,0,46,46,54,54,3,0,1,1,4,4,8,8,4,0,1,1,3,4,8,8,83,83,2,0,54,54, + 76,76,2,0,1,1,4,4,2,0,7,7,22,23,2,0,31,31,52,52,2,0,74,74,79,79, + 3,0,10,10,53,53,93,93,2,0,43,43,56,56,1,0,110,111,2,0,121,121,142, + 142,7,0,21,21,40,40,58,59,73,73,81,81,100,100,106,106,19,0,1,13, + 15,20,22,26,28,29,31,31,33,36,38,39,41,44,46,46,48,54,56,57,61,61, + 63,72,74,80,82,86,88,95,97,99,101,102,104,105,4,0,20,20,31,31,41, + 41,51,51,1500,0,179,1,0,0,0,2,186,1,0,0,0,4,188,1,0,0,0,6,190,1, + 0,0,0,8,197,1,0,0,0,10,220,1,0,0,0,12,222,1,0,0,0,14,229,1,0,0,0, + 16,236,1,0,0,0,18,249,1,0,0,0,20,261,1,0,0,0,22,270,1,0,0,0,24,278, + 1,0,0,0,26,300,1,0,0,0,28,315,1,0,0,0,30,324,1,0,0,0,32,329,1,0, + 0,0,34,333,1,0,0,0,36,335,1,0,0,0,38,344,1,0,0,0,40,348,1,0,0,0, + 42,362,1,0,0,0,44,372,1,0,0,0,46,382,1,0,0,0,48,386,1,0,0,0,50,394, + 1,0,0,0,52,443,1,0,0,0,54,446,1,0,0,0,56,452,1,0,0,0,58,456,1,0, + 0,0,60,462,1,0,0,0,62,480,1,0,0,0,64,483,1,0,0,0,66,486,1,0,0,0, + 68,496,1,0,0,0,70,499,1,0,0,0,72,503,1,0,0,0,74,536,1,0,0,0,76,538, + 1,0,0,0,78,541,1,0,0,0,80,556,1,0,0,0,82,618,1,0,0,0,84,623,1,0, + 0,0,86,634,1,0,0,0,88,636,1,0,0,0,90,642,1,0,0,0,92,650,1,0,0,0, + 94,668,1,0,0,0,96,670,1,0,0,0,98,678,1,0,0,0,100,683,1,0,0,0,102, + 691,1,0,0,0,104,695,1,0,0,0,106,699,1,0,0,0,108,708,1,0,0,0,110, + 722,1,0,0,0,112,724,1,0,0,0,114,783,1,0,0,0,116,785,1,0,0,0,118, + 948,1,0,0,0,120,1090,1,0,0,0,122,1102,1,0,0,0,124,1135,1,0,0,0,126, + 1148,1,0,0,0,128,1150,1,0,0,0,130,1171,1,0,0,0,132,1180,1,0,0,0, + 134,1182,1,0,0,0,136,1199,1,0,0,0,138,1212,1,0,0,0,140,1222,1,0, + 0,0,142,1226,1,0,0,0,144,1237,1,0,0,0,146,1247,1,0,0,0,148,1250, + 1,0,0,0,150,1263,1,0,0,0,152,1265,1,0,0,0,154,1267,1,0,0,0,156,1269, + 1,0,0,0,158,1273,1,0,0,0,160,1278,1,0,0,0,162,1280,1,0,0,0,164,1284, + 1,0,0,0,166,1290,1,0,0,0,168,1292,1,0,0,0,170,1306,1,0,0,0,172,1308, + 1,0,0,0,174,1322,1,0,0,0,176,178,3,2,1,0,177,176,1,0,0,0,178,181, + 1,0,0,0,179,177,1,0,0,0,179,180,1,0,0,0,180,182,1,0,0,0,181,179, + 1,0,0,0,182,183,5,0,0,1,183,1,1,0,0,0,184,187,3,6,3,0,185,187,3, + 10,5,0,186,184,1,0,0,0,186,185,1,0,0,0,187,3,1,0,0,0,188,189,3,118, + 59,0,189,5,1,0,0,0,190,191,5,55,0,0,191,195,3,160,80,0,192,193,5, + 118,0,0,193,194,5,125,0,0,194,196,3,4,2,0,195,192,1,0,0,0,195,196, + 1,0,0,0,196,7,1,0,0,0,197,202,3,160,80,0,198,199,5,119,0,0,199,201, + 3,160,80,0,200,198,1,0,0,0,201,204,1,0,0,0,202,200,1,0,0,0,202,203, + 1,0,0,0,203,206,1,0,0,0,204,202,1,0,0,0,205,207,5,119,0,0,206,205, + 1,0,0,0,206,207,1,0,0,0,207,9,1,0,0,0,208,221,3,12,6,0,209,221,3, + 14,7,0,210,221,3,18,9,0,211,221,3,20,10,0,212,221,3,22,11,0,213, + 221,3,26,13,0,214,221,3,24,12,0,215,221,3,28,14,0,216,221,3,30,15, + 0,217,221,3,36,18,0,218,221,3,32,16,0,219,221,3,34,17,0,220,208, + 1,0,0,0,220,209,1,0,0,0,220,210,1,0,0,0,220,211,1,0,0,0,220,212, + 1,0,0,0,220,213,1,0,0,0,220,214,1,0,0,0,220,215,1,0,0,0,220,216, + 1,0,0,0,220,217,1,0,0,0,220,218,1,0,0,0,220,219,1,0,0,0,221,11,1, + 0,0,0,222,224,5,75,0,0,223,225,3,4,2,0,224,223,1,0,0,0,224,225,1, + 0,0,0,225,227,1,0,0,0,226,228,5,153,0,0,227,226,1,0,0,0,227,228, + 1,0,0,0,228,13,1,0,0,0,229,231,5,87,0,0,230,232,3,4,2,0,231,230, + 1,0,0,0,231,232,1,0,0,0,232,234,1,0,0,0,233,235,5,153,0,0,234,233, + 1,0,0,0,234,235,1,0,0,0,235,15,1,0,0,0,236,245,5,14,0,0,237,238, + 5,133,0,0,238,241,3,160,80,0,239,240,5,118,0,0,240,242,3,160,80, + 0,241,239,1,0,0,0,241,242,1,0,0,0,242,243,1,0,0,0,243,244,5,152, + 0,0,244,246,1,0,0,0,245,237,1,0,0,0,245,246,1,0,0,0,246,247,1,0, + 0,0,247,248,3,36,18,0,248,17,1,0,0,0,249,250,5,96,0,0,250,254,3, + 36,18,0,251,253,3,16,8,0,252,251,1,0,0,0,253,256,1,0,0,0,254,252, + 1,0,0,0,254,255,1,0,0,0,255,259,1,0,0,0,256,254,1,0,0,0,257,258, + 5,30,0,0,258,260,3,36,18,0,259,257,1,0,0,0,259,260,1,0,0,0,260,19, + 1,0,0,0,261,262,5,42,0,0,262,263,5,133,0,0,263,264,3,4,2,0,264,265, + 5,152,0,0,265,268,3,10,5,0,266,267,5,25,0,0,267,269,3,10,5,0,268, + 266,1,0,0,0,268,269,1,0,0,0,269,21,1,0,0,0,270,271,5,103,0,0,271, + 272,5,133,0,0,272,273,3,4,2,0,273,274,5,152,0,0,274,276,3,10,5,0, + 275,277,5,153,0,0,276,275,1,0,0,0,276,277,1,0,0,0,277,23,1,0,0,0, + 278,279,5,34,0,0,279,283,5,133,0,0,280,284,3,6,3,0,281,284,3,30, + 15,0,282,284,3,4,2,0,283,280,1,0,0,0,283,281,1,0,0,0,283,282,1,0, + 0,0,283,284,1,0,0,0,284,285,1,0,0,0,285,287,5,153,0,0,286,288,3, + 4,2,0,287,286,1,0,0,0,287,288,1,0,0,0,288,289,1,0,0,0,289,293,5, + 153,0,0,290,294,3,6,3,0,291,294,3,30,15,0,292,294,3,4,2,0,293,290, + 1,0,0,0,293,291,1,0,0,0,293,292,1,0,0,0,293,294,1,0,0,0,294,295, + 1,0,0,0,295,296,5,152,0,0,296,298,3,10,5,0,297,299,5,153,0,0,298, + 297,1,0,0,0,298,299,1,0,0,0,299,25,1,0,0,0,300,301,5,34,0,0,301, + 302,5,133,0,0,302,303,5,55,0,0,303,306,3,160,80,0,304,305,5,119, + 0,0,305,307,3,160,80,0,306,304,1,0,0,0,306,307,1,0,0,0,307,308,1, + 0,0,0,308,309,5,44,0,0,309,310,3,4,2,0,310,311,5,152,0,0,311,313, + 3,10,5,0,312,314,5,153,0,0,313,312,1,0,0,0,313,314,1,0,0,0,314,27, + 1,0,0,0,315,316,7,0,0,0,316,317,3,160,80,0,317,319,5,133,0,0,318, + 320,3,8,4,0,319,318,1,0,0,0,319,320,1,0,0,0,320,321,1,0,0,0,321, + 322,5,152,0,0,322,323,3,36,18,0,323,29,1,0,0,0,324,325,3,4,2,0,325, + 326,5,118,0,0,326,327,5,125,0,0,327,328,3,4,2,0,328,31,1,0,0,0,329, + 331,3,4,2,0,330,332,5,153,0,0,331,330,1,0,0,0,331,332,1,0,0,0,332, + 33,1,0,0,0,333,334,5,153,0,0,334,35,1,0,0,0,335,339,5,131,0,0,336, + 338,3,2,1,0,337,336,1,0,0,0,338,341,1,0,0,0,339,337,1,0,0,0,339, + 340,1,0,0,0,340,342,1,0,0,0,341,339,1,0,0,0,342,343,5,150,0,0,343, + 37,1,0,0,0,344,345,3,4,2,0,345,346,5,118,0,0,346,347,3,4,2,0,347, + 39,1,0,0,0,348,353,3,38,19,0,349,350,5,119,0,0,350,352,3,38,19,0, + 351,349,1,0,0,0,352,355,1,0,0,0,353,351,1,0,0,0,353,354,1,0,0,0, + 354,357,1,0,0,0,355,353,1,0,0,0,356,358,5,119,0,0,357,356,1,0,0, + 0,357,358,1,0,0,0,358,41,1,0,0,0,359,363,3,48,24,0,360,363,3,50, + 25,0,361,363,3,124,62,0,362,359,1,0,0,0,362,360,1,0,0,0,362,361, + 1,0,0,0,363,364,1,0,0,0,364,365,5,0,0,1,365,43,1,0,0,0,366,373,3, + 50,25,0,367,368,5,133,0,0,368,369,3,48,24,0,369,370,5,152,0,0,370, + 373,1,0,0,0,371,373,3,164,82,0,372,366,1,0,0,0,372,367,1,0,0,0,372, + 371,1,0,0,0,373,45,1,0,0,0,374,383,5,27,0,0,375,376,5,98,0,0,376, + 383,5,1,0,0,377,378,5,98,0,0,378,383,5,24,0,0,379,383,5,47,0,0,380, + 381,5,47,0,0,381,383,5,24,0,0,382,374,1,0,0,0,382,375,1,0,0,0,382, + 377,1,0,0,0,382,379,1,0,0,0,382,380,1,0,0,0,383,384,1,0,0,0,384, + 385,3,44,22,0,385,47,1,0,0,0,386,390,3,44,22,0,387,389,3,46,23,0, + 388,387,1,0,0,0,389,392,1,0,0,0,390,388,1,0,0,0,390,391,1,0,0,0, + 391,49,1,0,0,0,392,390,1,0,0,0,393,395,3,52,26,0,394,393,1,0,0,0, + 394,395,1,0,0,0,395,396,1,0,0,0,396,398,5,82,0,0,397,399,5,24,0, + 0,398,397,1,0,0,0,398,399,1,0,0,0,399,401,1,0,0,0,400,402,3,54,27, + 0,401,400,1,0,0,0,401,402,1,0,0,0,402,403,1,0,0,0,403,405,3,116, + 58,0,404,406,3,56,28,0,405,404,1,0,0,0,405,406,1,0,0,0,406,408,1, + 0,0,0,407,409,3,58,29,0,408,407,1,0,0,0,408,409,1,0,0,0,409,411, + 1,0,0,0,410,412,3,62,31,0,411,410,1,0,0,0,411,412,1,0,0,0,412,414, + 1,0,0,0,413,415,3,64,32,0,414,413,1,0,0,0,414,415,1,0,0,0,415,417, + 1,0,0,0,416,418,3,66,33,0,417,416,1,0,0,0,417,418,1,0,0,0,418,421, + 1,0,0,0,419,420,5,105,0,0,420,422,7,1,0,0,421,419,1,0,0,0,421,422, + 1,0,0,0,422,425,1,0,0,0,423,424,5,105,0,0,424,426,5,92,0,0,425,423, + 1,0,0,0,425,426,1,0,0,0,426,428,1,0,0,0,427,429,3,68,34,0,428,427, + 1,0,0,0,428,429,1,0,0,0,429,431,1,0,0,0,430,432,3,60,30,0,431,430, + 1,0,0,0,431,432,1,0,0,0,432,434,1,0,0,0,433,435,3,70,35,0,434,433, + 1,0,0,0,434,435,1,0,0,0,435,438,1,0,0,0,436,439,3,74,37,0,437,439, + 3,76,38,0,438,436,1,0,0,0,438,437,1,0,0,0,438,439,1,0,0,0,439,441, + 1,0,0,0,440,442,3,78,39,0,441,440,1,0,0,0,441,442,1,0,0,0,442,51, + 1,0,0,0,443,444,5,105,0,0,444,445,3,128,64,0,445,53,1,0,0,0,446, + 447,5,91,0,0,447,450,5,111,0,0,448,449,5,105,0,0,449,451,5,88,0, + 0,450,448,1,0,0,0,450,451,1,0,0,0,451,55,1,0,0,0,452,453,5,35,0, + 0,453,454,3,80,40,0,454,57,1,0,0,0,455,457,7,2,0,0,456,455,1,0,0, + 0,456,457,1,0,0,0,457,458,1,0,0,0,458,459,5,5,0,0,459,460,5,50,0, + 0,460,461,3,116,58,0,461,59,1,0,0,0,462,463,5,104,0,0,463,464,3, + 160,80,0,464,465,5,6,0,0,465,466,5,133,0,0,466,467,3,100,50,0,467, + 477,5,152,0,0,468,469,5,119,0,0,469,470,3,160,80,0,470,471,5,6,0, + 0,471,472,5,133,0,0,472,473,3,100,50,0,473,474,5,152,0,0,474,476, + 1,0,0,0,475,468,1,0,0,0,476,479,1,0,0,0,477,475,1,0,0,0,477,478, + 1,0,0,0,478,61,1,0,0,0,479,477,1,0,0,0,480,481,5,72,0,0,481,482, + 3,118,59,0,482,63,1,0,0,0,483,484,5,102,0,0,484,485,3,118,59,0,485, + 65,1,0,0,0,486,487,5,38,0,0,487,494,5,11,0,0,488,489,7,1,0,0,489, + 490,5,133,0,0,490,491,3,116,58,0,491,492,5,152,0,0,492,495,1,0,0, + 0,493,495,3,116,58,0,494,488,1,0,0,0,494,493,1,0,0,0,495,67,1,0, + 0,0,496,497,5,39,0,0,497,498,3,118,59,0,498,69,1,0,0,0,499,500,5, + 67,0,0,500,501,5,11,0,0,501,502,3,90,45,0,502,71,1,0,0,0,503,504, + 5,67,0,0,504,505,5,11,0,0,505,506,3,116,58,0,506,73,1,0,0,0,507, + 508,5,57,0,0,508,511,3,118,59,0,509,510,5,119,0,0,510,512,3,118, + 59,0,511,509,1,0,0,0,511,512,1,0,0,0,512,517,1,0,0,0,513,514,5,105, + 0,0,514,518,5,88,0,0,515,516,5,11,0,0,516,518,3,116,58,0,517,513, + 1,0,0,0,517,515,1,0,0,0,517,518,1,0,0,0,518,537,1,0,0,0,519,520, + 5,57,0,0,520,523,3,118,59,0,521,522,5,105,0,0,522,524,5,88,0,0,523, + 521,1,0,0,0,523,524,1,0,0,0,524,525,1,0,0,0,525,526,5,64,0,0,526, + 527,3,118,59,0,527,537,1,0,0,0,528,529,5,57,0,0,529,530,3,118,59, + 0,530,531,5,64,0,0,531,534,3,118,59,0,532,533,5,11,0,0,533,535,3, + 116,58,0,534,532,1,0,0,0,534,535,1,0,0,0,535,537,1,0,0,0,536,507, + 1,0,0,0,536,519,1,0,0,0,536,528,1,0,0,0,537,75,1,0,0,0,538,539,5, + 64,0,0,539,540,3,118,59,0,540,77,1,0,0,0,541,542,5,84,0,0,542,543, + 3,96,48,0,543,79,1,0,0,0,544,545,6,40,-1,0,545,547,3,136,68,0,546, + 548,5,29,0,0,547,546,1,0,0,0,547,548,1,0,0,0,548,550,1,0,0,0,549, + 551,3,88,44,0,550,549,1,0,0,0,550,551,1,0,0,0,551,557,1,0,0,0,552, + 553,5,133,0,0,553,554,3,80,40,0,554,555,5,152,0,0,555,557,1,0,0, + 0,556,544,1,0,0,0,556,552,1,0,0,0,557,572,1,0,0,0,558,559,10,3,0, + 0,559,560,3,84,42,0,560,561,3,80,40,4,561,571,1,0,0,0,562,564,10, + 4,0,0,563,565,3,82,41,0,564,563,1,0,0,0,564,565,1,0,0,0,565,566, + 1,0,0,0,566,567,5,50,0,0,567,568,3,80,40,0,568,569,3,86,43,0,569, + 571,1,0,0,0,570,558,1,0,0,0,570,562,1,0,0,0,571,574,1,0,0,0,572, + 570,1,0,0,0,572,573,1,0,0,0,573,81,1,0,0,0,574,572,1,0,0,0,575,577, + 7,3,0,0,576,575,1,0,0,0,576,577,1,0,0,0,577,578,1,0,0,0,578,585, + 5,46,0,0,579,581,5,46,0,0,580,582,7,3,0,0,581,580,1,0,0,0,581,582, + 1,0,0,0,582,585,1,0,0,0,583,585,7,3,0,0,584,576,1,0,0,0,584,579, + 1,0,0,0,584,583,1,0,0,0,585,619,1,0,0,0,586,588,7,4,0,0,587,586, + 1,0,0,0,587,588,1,0,0,0,588,589,1,0,0,0,589,591,7,5,0,0,590,592, + 5,68,0,0,591,590,1,0,0,0,591,592,1,0,0,0,592,601,1,0,0,0,593,595, + 7,5,0,0,594,596,5,68,0,0,595,594,1,0,0,0,595,596,1,0,0,0,596,598, + 1,0,0,0,597,599,7,4,0,0,598,597,1,0,0,0,598,599,1,0,0,0,599,601, + 1,0,0,0,600,587,1,0,0,0,600,593,1,0,0,0,601,619,1,0,0,0,602,604, + 7,6,0,0,603,602,1,0,0,0,603,604,1,0,0,0,604,605,1,0,0,0,605,607, + 5,36,0,0,606,608,5,68,0,0,607,606,1,0,0,0,607,608,1,0,0,0,608,617, + 1,0,0,0,609,611,5,36,0,0,610,612,5,68,0,0,611,610,1,0,0,0,611,612, + 1,0,0,0,612,614,1,0,0,0,613,615,7,6,0,0,614,613,1,0,0,0,614,615, + 1,0,0,0,615,617,1,0,0,0,616,603,1,0,0,0,616,609,1,0,0,0,617,619, + 1,0,0,0,618,584,1,0,0,0,618,600,1,0,0,0,618,616,1,0,0,0,619,83,1, + 0,0,0,620,621,5,17,0,0,621,624,5,50,0,0,622,624,5,119,0,0,623,620, + 1,0,0,0,623,622,1,0,0,0,624,85,1,0,0,0,625,626,5,65,0,0,626,635, + 3,116,58,0,627,628,5,99,0,0,628,629,5,133,0,0,629,630,3,116,58,0, + 630,631,5,152,0,0,631,635,1,0,0,0,632,633,5,99,0,0,633,635,3,116, + 58,0,634,625,1,0,0,0,634,627,1,0,0,0,634,632,1,0,0,0,635,87,1,0, + 0,0,636,637,5,80,0,0,637,640,3,94,47,0,638,639,5,64,0,0,639,641, + 3,94,47,0,640,638,1,0,0,0,640,641,1,0,0,0,641,89,1,0,0,0,642,647, + 3,92,46,0,643,644,5,119,0,0,644,646,3,92,46,0,645,643,1,0,0,0,646, + 649,1,0,0,0,647,645,1,0,0,0,647,648,1,0,0,0,648,91,1,0,0,0,649,647, + 1,0,0,0,650,652,3,118,59,0,651,653,7,7,0,0,652,651,1,0,0,0,652,653, + 1,0,0,0,653,656,1,0,0,0,654,655,5,63,0,0,655,657,7,8,0,0,656,654, + 1,0,0,0,656,657,1,0,0,0,657,660,1,0,0,0,658,659,5,16,0,0,659,661, + 5,113,0,0,660,658,1,0,0,0,660,661,1,0,0,0,661,93,1,0,0,0,662,669, + 3,164,82,0,663,666,3,148,74,0,664,665,5,154,0,0,665,667,3,148,74, + 0,666,664,1,0,0,0,666,667,1,0,0,0,667,669,1,0,0,0,668,662,1,0,0, + 0,668,663,1,0,0,0,669,95,1,0,0,0,670,675,3,98,49,0,671,672,5,119, + 0,0,672,674,3,98,49,0,673,671,1,0,0,0,674,677,1,0,0,0,675,673,1, + 0,0,0,675,676,1,0,0,0,676,97,1,0,0,0,677,675,1,0,0,0,678,679,3,160, + 80,0,679,680,5,125,0,0,680,681,3,150,75,0,681,99,1,0,0,0,682,684, + 3,102,51,0,683,682,1,0,0,0,683,684,1,0,0,0,684,686,1,0,0,0,685,687, + 3,104,52,0,686,685,1,0,0,0,686,687,1,0,0,0,687,689,1,0,0,0,688,690, + 3,106,53,0,689,688,1,0,0,0,689,690,1,0,0,0,690,101,1,0,0,0,691,692, + 5,70,0,0,692,693,5,11,0,0,693,694,3,116,58,0,694,103,1,0,0,0,695, + 696,5,67,0,0,696,697,5,11,0,0,697,698,3,90,45,0,698,105,1,0,0,0, + 699,700,7,9,0,0,700,701,3,108,54,0,701,107,1,0,0,0,702,709,3,110, + 55,0,703,704,5,9,0,0,704,705,3,110,55,0,705,706,5,2,0,0,706,707, + 3,110,55,0,707,709,1,0,0,0,708,702,1,0,0,0,708,703,1,0,0,0,709,109, + 1,0,0,0,710,711,5,19,0,0,711,723,5,78,0,0,712,713,5,97,0,0,713,723, + 5,71,0,0,714,715,5,97,0,0,715,723,5,33,0,0,716,717,3,148,74,0,717, + 718,5,71,0,0,718,723,1,0,0,0,719,720,3,148,74,0,720,721,5,33,0,0, + 721,723,1,0,0,0,722,710,1,0,0,0,722,712,1,0,0,0,722,714,1,0,0,0, + 722,716,1,0,0,0,722,719,1,0,0,0,723,111,1,0,0,0,724,725,3,118,59, + 0,725,726,5,0,0,1,726,113,1,0,0,0,727,784,3,160,80,0,728,729,3,160, + 80,0,729,730,5,133,0,0,730,731,3,160,80,0,731,738,3,114,57,0,732, + 733,5,119,0,0,733,734,3,160,80,0,734,735,3,114,57,0,735,737,1,0, + 0,0,736,732,1,0,0,0,737,740,1,0,0,0,738,736,1,0,0,0,738,739,1,0, + 0,0,739,742,1,0,0,0,740,738,1,0,0,0,741,743,5,119,0,0,742,741,1, + 0,0,0,742,743,1,0,0,0,743,744,1,0,0,0,744,745,5,152,0,0,745,784, + 1,0,0,0,746,747,3,160,80,0,747,748,5,133,0,0,748,753,3,162,81,0, + 749,750,5,119,0,0,750,752,3,162,81,0,751,749,1,0,0,0,752,755,1,0, + 0,0,753,751,1,0,0,0,753,754,1,0,0,0,754,757,1,0,0,0,755,753,1,0, + 0,0,756,758,5,119,0,0,757,756,1,0,0,0,757,758,1,0,0,0,758,759,1, + 0,0,0,759,760,5,152,0,0,760,784,1,0,0,0,761,762,3,160,80,0,762,763, + 5,133,0,0,763,768,3,114,57,0,764,765,5,119,0,0,765,767,3,114,57, + 0,766,764,1,0,0,0,767,770,1,0,0,0,768,766,1,0,0,0,768,769,1,0,0, + 0,769,772,1,0,0,0,770,768,1,0,0,0,771,773,5,119,0,0,772,771,1,0, + 0,0,772,773,1,0,0,0,773,774,1,0,0,0,774,775,5,152,0,0,775,784,1, + 0,0,0,776,777,3,160,80,0,777,779,5,133,0,0,778,780,3,116,58,0,779, + 778,1,0,0,0,779,780,1,0,0,0,780,781,1,0,0,0,781,782,5,152,0,0,782, + 784,1,0,0,0,783,727,1,0,0,0,783,728,1,0,0,0,783,746,1,0,0,0,783, + 761,1,0,0,0,783,776,1,0,0,0,784,115,1,0,0,0,785,790,3,118,59,0,786, + 787,5,119,0,0,787,789,3,118,59,0,788,786,1,0,0,0,789,792,1,0,0,0, + 790,788,1,0,0,0,790,791,1,0,0,0,791,794,1,0,0,0,792,790,1,0,0,0, + 793,795,5,119,0,0,794,793,1,0,0,0,794,795,1,0,0,0,795,117,1,0,0, + 0,796,797,6,59,-1,0,797,799,5,12,0,0,798,800,3,118,59,0,799,798, + 1,0,0,0,799,800,1,0,0,0,800,806,1,0,0,0,801,802,5,101,0,0,802,803, + 3,118,59,0,803,804,5,86,0,0,804,805,3,118,59,0,805,807,1,0,0,0,806, + 801,1,0,0,0,807,808,1,0,0,0,808,806,1,0,0,0,808,809,1,0,0,0,809, + 812,1,0,0,0,810,811,5,25,0,0,811,813,3,118,59,0,812,810,1,0,0,0, + 812,813,1,0,0,0,813,814,1,0,0,0,814,815,5,26,0,0,815,949,1,0,0,0, + 816,817,5,13,0,0,817,818,5,133,0,0,818,819,3,118,59,0,819,820,5, + 6,0,0,820,821,3,114,57,0,821,822,5,152,0,0,822,949,1,0,0,0,823,824, + 5,20,0,0,824,949,5,113,0,0,825,826,5,48,0,0,826,949,5,113,0,0,827, + 828,5,48,0,0,828,829,3,118,59,0,829,830,3,152,76,0,830,949,1,0,0, + 0,831,832,5,85,0,0,832,833,5,133,0,0,833,834,3,118,59,0,834,835, + 5,35,0,0,835,838,3,118,59,0,836,837,5,34,0,0,837,839,3,118,59,0, + 838,836,1,0,0,0,838,839,1,0,0,0,839,840,1,0,0,0,840,841,5,152,0, + 0,841,949,1,0,0,0,842,843,5,89,0,0,843,949,5,113,0,0,844,845,5,94, + 0,0,845,846,5,133,0,0,846,847,7,10,0,0,847,848,3,166,83,0,848,849, + 5,35,0,0,849,850,3,118,59,0,850,851,5,152,0,0,851,949,1,0,0,0,852, + 853,3,160,80,0,853,855,5,133,0,0,854,856,3,116,58,0,855,854,1,0, + 0,0,855,856,1,0,0,0,856,857,1,0,0,0,857,858,5,152,0,0,858,867,1, + 0,0,0,859,861,5,133,0,0,860,862,5,24,0,0,861,860,1,0,0,0,861,862, + 1,0,0,0,862,864,1,0,0,0,863,865,3,116,58,0,864,863,1,0,0,0,864,865, + 1,0,0,0,865,866,1,0,0,0,866,868,5,152,0,0,867,859,1,0,0,0,867,868, + 1,0,0,0,868,869,1,0,0,0,869,870,5,69,0,0,870,871,5,133,0,0,871,872, + 3,100,50,0,872,873,5,152,0,0,873,949,1,0,0,0,874,875,3,160,80,0, + 875,877,5,133,0,0,876,878,3,116,58,0,877,876,1,0,0,0,877,878,1,0, + 0,0,878,879,1,0,0,0,879,880,5,152,0,0,880,889,1,0,0,0,881,883,5, + 133,0,0,882,884,5,24,0,0,883,882,1,0,0,0,883,884,1,0,0,0,884,886, + 1,0,0,0,885,887,3,116,58,0,886,885,1,0,0,0,886,887,1,0,0,0,887,888, + 1,0,0,0,888,890,5,152,0,0,889,881,1,0,0,0,889,890,1,0,0,0,890,891, + 1,0,0,0,891,892,5,69,0,0,892,893,3,160,80,0,893,949,1,0,0,0,894, + 900,3,160,80,0,895,897,5,133,0,0,896,898,3,116,58,0,897,896,1,0, + 0,0,897,898,1,0,0,0,898,899,1,0,0,0,899,901,5,152,0,0,900,895,1, + 0,0,0,900,901,1,0,0,0,901,902,1,0,0,0,902,904,5,133,0,0,903,905, + 5,24,0,0,904,903,1,0,0,0,904,905,1,0,0,0,905,907,1,0,0,0,906,908, + 3,116,58,0,907,906,1,0,0,0,907,908,1,0,0,0,908,909,1,0,0,0,909,910, + 5,152,0,0,910,949,1,0,0,0,911,949,3,124,62,0,912,949,3,168,84,0, + 913,949,3,150,75,0,914,915,5,121,0,0,915,949,3,118,59,20,916,917, + 5,61,0,0,917,949,3,118,59,14,918,919,3,140,70,0,919,920,5,123,0, + 0,920,922,1,0,0,0,921,918,1,0,0,0,921,922,1,0,0,0,922,923,1,0,0, + 0,923,949,5,115,0,0,924,925,5,133,0,0,925,926,3,48,24,0,926,927, + 5,152,0,0,927,949,1,0,0,0,928,929,5,133,0,0,929,930,3,118,59,0,930, + 931,5,152,0,0,931,949,1,0,0,0,932,933,5,133,0,0,933,934,3,116,58, + 0,934,935,5,152,0,0,935,949,1,0,0,0,936,938,5,132,0,0,937,939,3, + 116,58,0,938,937,1,0,0,0,938,939,1,0,0,0,939,940,1,0,0,0,940,949, + 5,151,0,0,941,943,5,131,0,0,942,944,3,40,20,0,943,942,1,0,0,0,943, + 944,1,0,0,0,944,945,1,0,0,0,945,949,5,150,0,0,946,949,3,120,60,0, + 947,949,3,132,66,0,948,796,1,0,0,0,948,816,1,0,0,0,948,823,1,0,0, + 0,948,825,1,0,0,0,948,827,1,0,0,0,948,831,1,0,0,0,948,842,1,0,0, + 0,948,844,1,0,0,0,948,852,1,0,0,0,948,874,1,0,0,0,948,894,1,0,0, + 0,948,911,1,0,0,0,948,912,1,0,0,0,948,913,1,0,0,0,948,914,1,0,0, + 0,948,916,1,0,0,0,948,921,1,0,0,0,948,924,1,0,0,0,948,928,1,0,0, + 0,948,932,1,0,0,0,948,936,1,0,0,0,948,941,1,0,0,0,948,946,1,0,0, + 0,948,947,1,0,0,0,949,1060,1,0,0,0,950,954,10,19,0,0,951,955,5,115, + 0,0,952,955,5,154,0,0,953,955,5,141,0,0,954,951,1,0,0,0,954,952, + 1,0,0,0,954,953,1,0,0,0,955,956,1,0,0,0,956,1059,3,118,59,20,957, + 961,10,18,0,0,958,962,5,142,0,0,959,962,5,121,0,0,960,962,5,120, + 0,0,961,958,1,0,0,0,961,959,1,0,0,0,961,960,1,0,0,0,962,963,1,0, + 0,0,963,1059,3,118,59,19,964,989,10,17,0,0,965,990,5,124,0,0,966, + 990,5,125,0,0,967,990,5,136,0,0,968,990,5,134,0,0,969,990,5,135, + 0,0,970,990,5,126,0,0,971,990,5,127,0,0,972,974,5,61,0,0,973,972, + 1,0,0,0,973,974,1,0,0,0,974,975,1,0,0,0,975,977,5,44,0,0,976,978, + 5,15,0,0,977,976,1,0,0,0,977,978,1,0,0,0,978,990,1,0,0,0,979,981, + 5,61,0,0,980,979,1,0,0,0,980,981,1,0,0,0,981,982,1,0,0,0,982,990, + 7,11,0,0,983,990,5,148,0,0,984,990,5,149,0,0,985,990,5,138,0,0,986, + 990,5,129,0,0,987,990,5,130,0,0,988,990,5,137,0,0,989,965,1,0,0, + 0,989,966,1,0,0,0,989,967,1,0,0,0,989,968,1,0,0,0,989,969,1,0,0, + 0,989,970,1,0,0,0,989,971,1,0,0,0,989,973,1,0,0,0,989,980,1,0,0, + 0,989,983,1,0,0,0,989,984,1,0,0,0,989,985,1,0,0,0,989,986,1,0,0, + 0,989,987,1,0,0,0,989,988,1,0,0,0,990,991,1,0,0,0,991,1059,3,118, + 59,18,992,993,10,15,0,0,993,994,5,140,0,0,994,1059,3,118,59,16,995, + 996,10,13,0,0,996,997,5,2,0,0,997,1059,3,118,59,14,998,999,10,12, + 0,0,999,1000,5,66,0,0,1000,1059,3,118,59,13,1001,1003,10,11,0,0, + 1002,1004,5,61,0,0,1003,1002,1,0,0,0,1003,1004,1,0,0,0,1004,1005, + 1,0,0,0,1005,1006,5,9,0,0,1006,1007,3,118,59,0,1007,1008,5,2,0,0, + 1008,1009,3,118,59,12,1009,1059,1,0,0,0,1010,1011,10,10,0,0,1011, + 1012,5,143,0,0,1012,1013,3,118,59,0,1013,1014,5,118,0,0,1014,1015, + 3,118,59,10,1015,1059,1,0,0,0,1016,1017,10,30,0,0,1017,1019,5,133, + 0,0,1018,1020,3,116,58,0,1019,1018,1,0,0,0,1019,1020,1,0,0,0,1020, + 1021,1,0,0,0,1021,1059,5,152,0,0,1022,1023,10,26,0,0,1023,1024,5, + 132,0,0,1024,1025,3,118,59,0,1025,1026,5,151,0,0,1026,1059,1,0,0, + 0,1027,1028,10,25,0,0,1028,1029,5,123,0,0,1029,1059,5,111,0,0,1030, + 1031,10,24,0,0,1031,1032,5,123,0,0,1032,1059,3,160,80,0,1033,1034, + 10,23,0,0,1034,1035,5,139,0,0,1035,1036,5,132,0,0,1036,1037,3,118, + 59,0,1037,1038,5,151,0,0,1038,1059,1,0,0,0,1039,1040,10,22,0,0,1040, + 1041,5,139,0,0,1041,1059,5,111,0,0,1042,1043,10,21,0,0,1043,1044, + 5,139,0,0,1044,1059,3,160,80,0,1045,1046,10,16,0,0,1046,1048,5,49, + 0,0,1047,1049,5,61,0,0,1048,1047,1,0,0,0,1048,1049,1,0,0,0,1049, + 1050,1,0,0,0,1050,1059,5,62,0,0,1051,1056,10,9,0,0,1052,1053,5,6, + 0,0,1053,1057,3,160,80,0,1054,1055,5,6,0,0,1055,1057,5,113,0,0,1056, + 1052,1,0,0,0,1056,1054,1,0,0,0,1057,1059,1,0,0,0,1058,950,1,0,0, + 0,1058,957,1,0,0,0,1058,964,1,0,0,0,1058,992,1,0,0,0,1058,995,1, + 0,0,0,1058,998,1,0,0,0,1058,1001,1,0,0,0,1058,1010,1,0,0,0,1058, + 1016,1,0,0,0,1058,1022,1,0,0,0,1058,1027,1,0,0,0,1058,1030,1,0,0, + 0,1058,1033,1,0,0,0,1058,1039,1,0,0,0,1058,1042,1,0,0,0,1058,1045, + 1,0,0,0,1058,1051,1,0,0,0,1059,1062,1,0,0,0,1060,1058,1,0,0,0,1060, + 1061,1,0,0,0,1061,119,1,0,0,0,1062,1060,1,0,0,0,1063,1064,5,133, + 0,0,1064,1069,3,160,80,0,1065,1066,5,119,0,0,1066,1068,3,160,80, + 0,1067,1065,1,0,0,0,1068,1071,1,0,0,0,1069,1067,1,0,0,0,1069,1070, + 1,0,0,0,1070,1073,1,0,0,0,1071,1069,1,0,0,0,1072,1074,5,119,0,0, + 1073,1072,1,0,0,0,1073,1074,1,0,0,0,1074,1075,1,0,0,0,1075,1076, + 5,152,0,0,1076,1091,1,0,0,0,1077,1082,3,160,80,0,1078,1079,5,119, + 0,0,1079,1081,3,160,80,0,1080,1078,1,0,0,0,1081,1084,1,0,0,0,1082, + 1080,1,0,0,0,1082,1083,1,0,0,0,1083,1086,1,0,0,0,1084,1082,1,0,0, + 0,1085,1087,5,119,0,0,1086,1085,1,0,0,0,1086,1087,1,0,0,0,1087,1091, + 1,0,0,0,1088,1089,5,133,0,0,1089,1091,5,152,0,0,1090,1063,1,0,0, + 0,1090,1077,1,0,0,0,1090,1088,1,0,0,0,1091,1092,1,0,0,0,1092,1095, + 5,114,0,0,1093,1096,3,118,59,0,1094,1096,3,36,18,0,1095,1093,1,0, + 0,0,1095,1094,1,0,0,0,1096,121,1,0,0,0,1097,1103,3,124,62,0,1098, + 1099,5,131,0,0,1099,1100,3,118,59,0,1100,1101,5,150,0,0,1101,1103, + 1,0,0,0,1102,1097,1,0,0,0,1102,1098,1,0,0,0,1103,123,1,0,0,0,1104, + 1105,5,135,0,0,1105,1109,3,160,80,0,1106,1108,3,126,63,0,1107,1106, + 1,0,0,0,1108,1111,1,0,0,0,1109,1107,1,0,0,0,1109,1110,1,0,0,0,1110, + 1112,1,0,0,0,1111,1109,1,0,0,0,1112,1113,5,154,0,0,1113,1114,5,127, + 0,0,1114,1136,1,0,0,0,1115,1116,5,135,0,0,1116,1120,3,160,80,0,1117, + 1119,3,126,63,0,1118,1117,1,0,0,0,1119,1122,1,0,0,0,1120,1118,1, + 0,0,0,1120,1121,1,0,0,0,1121,1123,1,0,0,0,1122,1120,1,0,0,0,1123, + 1127,5,127,0,0,1124,1126,3,122,61,0,1125,1124,1,0,0,0,1126,1129, + 1,0,0,0,1127,1125,1,0,0,0,1127,1128,1,0,0,0,1128,1130,1,0,0,0,1129, + 1127,1,0,0,0,1130,1131,5,135,0,0,1131,1132,5,154,0,0,1132,1133,3, + 160,80,0,1133,1134,5,127,0,0,1134,1136,1,0,0,0,1135,1104,1,0,0,0, + 1135,1115,1,0,0,0,1136,125,1,0,0,0,1137,1138,3,160,80,0,1138,1139, + 5,125,0,0,1139,1140,3,166,83,0,1140,1149,1,0,0,0,1141,1142,3,160, + 80,0,1142,1143,5,125,0,0,1143,1144,5,131,0,0,1144,1145,3,118,59, + 0,1145,1146,5,150,0,0,1146,1149,1,0,0,0,1147,1149,3,160,80,0,1148, + 1137,1,0,0,0,1148,1141,1,0,0,0,1148,1147,1,0,0,0,1149,127,1,0,0, + 0,1150,1155,3,130,65,0,1151,1152,5,119,0,0,1152,1154,3,130,65,0, + 1153,1151,1,0,0,0,1154,1157,1,0,0,0,1155,1153,1,0,0,0,1155,1156, + 1,0,0,0,1156,1159,1,0,0,0,1157,1155,1,0,0,0,1158,1160,5,119,0,0, + 1159,1158,1,0,0,0,1159,1160,1,0,0,0,1160,129,1,0,0,0,1161,1162,3, + 160,80,0,1162,1163,5,6,0,0,1163,1164,5,133,0,0,1164,1165,3,48,24, + 0,1165,1166,5,152,0,0,1166,1172,1,0,0,0,1167,1168,3,118,59,0,1168, + 1169,5,6,0,0,1169,1170,3,160,80,0,1170,1172,1,0,0,0,1171,1161,1, + 0,0,0,1171,1167,1,0,0,0,1172,131,1,0,0,0,1173,1181,3,164,82,0,1174, + 1175,3,140,70,0,1175,1176,5,123,0,0,1176,1178,1,0,0,0,1177,1174, + 1,0,0,0,1177,1178,1,0,0,0,1178,1179,1,0,0,0,1179,1181,3,134,67,0, + 1180,1173,1,0,0,0,1180,1177,1,0,0,0,1181,133,1,0,0,0,1182,1187,3, + 160,80,0,1183,1184,5,123,0,0,1184,1186,3,160,80,0,1185,1183,1,0, + 0,0,1186,1189,1,0,0,0,1187,1185,1,0,0,0,1187,1188,1,0,0,0,1188,135, + 1,0,0,0,1189,1187,1,0,0,0,1190,1191,6,68,-1,0,1191,1200,3,140,70, + 0,1192,1200,3,138,69,0,1193,1194,5,133,0,0,1194,1195,3,48,24,0,1195, + 1196,5,152,0,0,1196,1200,1,0,0,0,1197,1200,3,124,62,0,1198,1200, + 3,164,82,0,1199,1190,1,0,0,0,1199,1192,1,0,0,0,1199,1193,1,0,0,0, + 1199,1197,1,0,0,0,1199,1198,1,0,0,0,1200,1209,1,0,0,0,1201,1205, + 10,3,0,0,1202,1206,3,158,79,0,1203,1204,5,6,0,0,1204,1206,3,160, + 80,0,1205,1202,1,0,0,0,1205,1203,1,0,0,0,1206,1208,1,0,0,0,1207, + 1201,1,0,0,0,1208,1211,1,0,0,0,1209,1207,1,0,0,0,1209,1210,1,0,0, + 0,1210,137,1,0,0,0,1211,1209,1,0,0,0,1212,1213,3,160,80,0,1213,1215, + 5,133,0,0,1214,1216,3,142,71,0,1215,1214,1,0,0,0,1215,1216,1,0,0, + 0,1216,1217,1,0,0,0,1217,1218,5,152,0,0,1218,139,1,0,0,0,1219,1220, + 3,144,72,0,1220,1221,5,123,0,0,1221,1223,1,0,0,0,1222,1219,1,0,0, + 0,1222,1223,1,0,0,0,1223,1224,1,0,0,0,1224,1225,3,160,80,0,1225, + 141,1,0,0,0,1226,1231,3,118,59,0,1227,1228,5,119,0,0,1228,1230,3, + 118,59,0,1229,1227,1,0,0,0,1230,1233,1,0,0,0,1231,1229,1,0,0,0,1231, + 1232,1,0,0,0,1232,1235,1,0,0,0,1233,1231,1,0,0,0,1234,1236,5,119, + 0,0,1235,1234,1,0,0,0,1235,1236,1,0,0,0,1236,143,1,0,0,0,1237,1238, + 3,160,80,0,1238,145,1,0,0,0,1239,1248,5,109,0,0,1240,1241,5,123, + 0,0,1241,1248,7,12,0,0,1242,1243,5,111,0,0,1243,1245,5,123,0,0,1244, + 1246,7,12,0,0,1245,1244,1,0,0,0,1245,1246,1,0,0,0,1246,1248,1,0, + 0,0,1247,1239,1,0,0,0,1247,1240,1,0,0,0,1247,1242,1,0,0,0,1248,147, + 1,0,0,0,1249,1251,7,13,0,0,1250,1249,1,0,0,0,1250,1251,1,0,0,0,1251, + 1258,1,0,0,0,1252,1259,3,146,73,0,1253,1259,5,110,0,0,1254,1259, + 5,111,0,0,1255,1259,5,112,0,0,1256,1259,5,45,0,0,1257,1259,5,60, + 0,0,1258,1252,1,0,0,0,1258,1253,1,0,0,0,1258,1254,1,0,0,0,1258,1255, + 1,0,0,0,1258,1256,1,0,0,0,1258,1257,1,0,0,0,1259,149,1,0,0,0,1260, + 1264,3,148,74,0,1261,1264,5,113,0,0,1262,1264,5,62,0,0,1263,1260, + 1,0,0,0,1263,1261,1,0,0,0,1263,1262,1,0,0,0,1264,151,1,0,0,0,1265, + 1266,7,14,0,0,1266,153,1,0,0,0,1267,1268,7,15,0,0,1268,155,1,0,0, + 0,1269,1270,7,16,0,0,1270,157,1,0,0,0,1271,1274,5,108,0,0,1272,1274, + 3,156,78,0,1273,1271,1,0,0,0,1273,1272,1,0,0,0,1274,159,1,0,0,0, + 1275,1279,5,108,0,0,1276,1279,3,152,76,0,1277,1279,3,154,77,0,1278, + 1275,1,0,0,0,1278,1276,1,0,0,0,1278,1277,1,0,0,0,1279,161,1,0,0, + 0,1280,1281,3,166,83,0,1281,1282,5,125,0,0,1282,1283,3,148,74,0, + 1283,163,1,0,0,0,1284,1285,5,131,0,0,1285,1286,3,118,59,0,1286,1287, + 5,150,0,0,1287,165,1,0,0,0,1288,1291,5,113,0,0,1289,1291,3,168,84, + 0,1290,1288,1,0,0,0,1290,1289,1,0,0,0,1291,167,1,0,0,0,1292,1296, + 5,145,0,0,1293,1295,3,170,85,0,1294,1293,1,0,0,0,1295,1298,1,0,0, + 0,1296,1294,1,0,0,0,1296,1297,1,0,0,0,1297,1299,1,0,0,0,1298,1296, + 1,0,0,0,1299,1300,5,147,0,0,1300,169,1,0,0,0,1301,1302,5,160,0,0, + 1302,1303,3,118,59,0,1303,1304,5,150,0,0,1304,1307,1,0,0,0,1305, + 1307,5,159,0,0,1306,1301,1,0,0,0,1306,1305,1,0,0,0,1307,171,1,0, + 0,0,1308,1312,5,146,0,0,1309,1311,3,174,87,0,1310,1309,1,0,0,0,1311, + 1314,1,0,0,0,1312,1310,1,0,0,0,1312,1313,1,0,0,0,1313,1315,1,0,0, + 0,1314,1312,1,0,0,0,1315,1316,5,0,0,1,1316,173,1,0,0,0,1317,1318, + 5,162,0,0,1318,1319,3,118,59,0,1319,1320,5,150,0,0,1320,1323,1,0, + 0,0,1321,1323,5,161,0,0,1322,1317,1,0,0,0,1322,1321,1,0,0,0,1323, + 175,1,0,0,0,169,179,186,195,202,206,220,224,227,231,234,241,245, + 254,259,268,276,283,287,293,298,306,313,319,331,339,353,357,362, + 372,382,390,394,398,401,405,408,411,414,417,421,425,428,431,434, + 438,441,450,456,477,494,511,517,523,534,536,547,550,556,564,570, + 572,576,581,584,587,591,595,598,600,603,607,611,614,616,618,623, + 634,640,647,652,656,660,666,668,675,683,686,689,708,722,738,742, + 753,757,768,772,779,783,790,794,799,808,812,838,855,861,864,867, + 877,883,886,889,897,900,904,907,921,938,943,948,954,961,973,977, + 980,989,1003,1019,1048,1056,1058,1060,1069,1073,1082,1086,1090,1095, + 1102,1109,1120,1127,1135,1148,1155,1159,1171,1177,1180,1187,1199, + 1205,1209,1215,1222,1231,1235,1245,1247,1250,1258,1263,1273,1278, + 1290,1296,1306,1312,1322 ] class HogQLParser ( Parser ): @@ -684,32 +688,33 @@ class HogQLParser ( Parser ): RULE_columnExprList = 58 RULE_columnExpr = 59 RULE_columnLambdaExpr = 60 - RULE_hogqlxTagElement = 61 - RULE_hogqlxTagAttribute = 62 - RULE_withExprList = 63 - RULE_withExpr = 64 - RULE_columnIdentifier = 65 - RULE_nestedIdentifier = 66 - RULE_tableExpr = 67 - RULE_tableFunctionExpr = 68 - RULE_tableIdentifier = 69 - RULE_tableArgList = 70 - RULE_databaseIdentifier = 71 - RULE_floatingLiteral = 72 - RULE_numberLiteral = 73 - RULE_literal = 74 - RULE_interval = 75 - RULE_keyword = 76 - RULE_keywordForAlias = 77 - RULE_alias = 78 - RULE_identifier = 79 - RULE_enumValue = 80 - RULE_placeholder = 81 - RULE_string = 82 - RULE_templateString = 83 - RULE_stringContents = 84 - RULE_fullTemplateString = 85 - RULE_stringContentsFull = 86 + RULE_hogqlxChildElement = 61 + RULE_hogqlxTagElement = 62 + RULE_hogqlxTagAttribute = 63 + RULE_withExprList = 64 + RULE_withExpr = 65 + RULE_columnIdentifier = 66 + RULE_nestedIdentifier = 67 + RULE_tableExpr = 68 + RULE_tableFunctionExpr = 69 + RULE_tableIdentifier = 70 + RULE_tableArgList = 71 + RULE_databaseIdentifier = 72 + RULE_floatingLiteral = 73 + RULE_numberLiteral = 74 + RULE_literal = 75 + RULE_interval = 76 + RULE_keyword = 77 + RULE_keywordForAlias = 78 + RULE_alias = 79 + RULE_identifier = 80 + RULE_enumValue = 81 + RULE_placeholder = 82 + RULE_string = 83 + RULE_templateString = 84 + RULE_stringContents = 85 + RULE_fullTemplateString = 86 + RULE_stringContentsFull = 87 ruleNames = [ "program", "declaration", "expression", "varDecl", "identifierList", "statement", "returnStmt", "throwStmt", "catchBlock", @@ -726,14 +731,15 @@ class HogQLParser ( Parser ): "settingExprList", "settingExpr", "windowExpr", "winPartitionByClause", "winOrderByClause", "winFrameClause", "winFrameExtend", "winFrameBound", "expr", "columnTypeExpr", "columnExprList", - "columnExpr", "columnLambdaExpr", "hogqlxTagElement", - "hogqlxTagAttribute", "withExprList", "withExpr", "columnIdentifier", - "nestedIdentifier", "tableExpr", "tableFunctionExpr", - "tableIdentifier", "tableArgList", "databaseIdentifier", - "floatingLiteral", "numberLiteral", "literal", "interval", - "keyword", "keywordForAlias", "alias", "identifier", - "enumValue", "placeholder", "string", "templateString", - "stringContents", "fullTemplateString", "stringContentsFull" ] + "columnExpr", "columnLambdaExpr", "hogqlxChildElement", + "hogqlxTagElement", "hogqlxTagAttribute", "withExprList", + "withExpr", "columnIdentifier", "nestedIdentifier", "tableExpr", + "tableFunctionExpr", "tableIdentifier", "tableArgList", + "databaseIdentifier", "floatingLiteral", "numberLiteral", + "literal", "interval", "keyword", "keywordForAlias", + "alias", "identifier", "enumValue", "placeholder", "string", + "templateString", "stringContents", "fullTemplateString", + "stringContentsFull" ] EOF = Token.EOF ALL=1 @@ -944,17 +950,17 @@ def program(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 177 + self.state = 179 self._errHandler.sync(self) _la = self._input.LA(1) while (((_la) & ~0x3f) == 0 and ((1 << _la) & -140738696331266) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944844006785023) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 4212759) != 0): - self.state = 174 + self.state = 176 self.declaration() - self.state = 179 + self.state = 181 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 180 + self.state = 182 self.match(HogQLParser.EOF) except RecognitionException as re: localctx.exception = re @@ -997,17 +1003,17 @@ def declaration(self): localctx = HogQLParser.DeclarationContext(self, self._ctx, self.state) self.enterRule(localctx, 2, self.RULE_declaration) try: - self.state = 184 + self.state = 186 self._errHandler.sync(self) token = self._input.LA(1) if token in [55]: self.enterOuterAlt(localctx, 1) - self.state = 182 + self.state = 184 self.varDecl() pass elif token in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 48, 49, 50, 51, 52, 53, 54, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 108, 109, 110, 111, 112, 113, 115, 121, 123, 131, 132, 133, 135, 142, 145, 153]: self.enterOuterAlt(localctx, 2) - self.state = 183 + self.state = 185 self.statement() pass else: @@ -1051,7 +1057,7 @@ def expression(self): self.enterRule(localctx, 4, self.RULE_expression) try: self.enterOuterAlt(localctx, 1) - self.state = 186 + self.state = 188 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -1105,19 +1111,19 @@ def varDecl(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 188 + self.state = 190 self.match(HogQLParser.LET) - self.state = 189 + self.state = 191 self.identifier() - self.state = 193 + self.state = 195 self._errHandler.sync(self) _la = self._input.LA(1) if _la==118: - self.state = 190 + self.state = 192 self.match(HogQLParser.COLON) - self.state = 191 + self.state = 193 self.match(HogQLParser.EQ_SINGLE) - self.state = 192 + self.state = 194 self.expression() @@ -1169,26 +1175,26 @@ def identifierList(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 195 + self.state = 197 self.identifier() - self.state = 200 + self.state = 202 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,3,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 196 + self.state = 198 self.match(HogQLParser.COMMA) - self.state = 197 + self.state = 199 self.identifier() - self.state = 202 + self.state = 204 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,3,self._ctx) - self.state = 204 + self.state = 206 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 203 + self.state = 205 self.match(HogQLParser.COMMA) @@ -1273,78 +1279,78 @@ def statement(self): localctx = HogQLParser.StatementContext(self, self._ctx, self.state) self.enterRule(localctx, 10, self.RULE_statement) try: - self.state = 218 + self.state = 220 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,5,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 206 + self.state = 208 self.returnStmt() pass elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 207 + self.state = 209 self.throwStmt() pass elif la_ == 3: self.enterOuterAlt(localctx, 3) - self.state = 208 + self.state = 210 self.tryCatchStmt() pass elif la_ == 4: self.enterOuterAlt(localctx, 4) - self.state = 209 + self.state = 211 self.ifStmt() pass elif la_ == 5: self.enterOuterAlt(localctx, 5) - self.state = 210 + self.state = 212 self.whileStmt() pass elif la_ == 6: self.enterOuterAlt(localctx, 6) - self.state = 211 + self.state = 213 self.forInStmt() pass elif la_ == 7: self.enterOuterAlt(localctx, 7) - self.state = 212 + self.state = 214 self.forStmt() pass elif la_ == 8: self.enterOuterAlt(localctx, 8) - self.state = 213 + self.state = 215 self.funcStmt() pass elif la_ == 9: self.enterOuterAlt(localctx, 9) - self.state = 214 + self.state = 216 self.varAssignment() pass elif la_ == 10: self.enterOuterAlt(localctx, 10) - self.state = 215 + self.state = 217 self.block() pass elif la_ == 11: self.enterOuterAlt(localctx, 11) - self.state = 216 + self.state = 218 self.exprStmt() pass elif la_ == 12: self.enterOuterAlt(localctx, 12) - self.state = 217 + self.state = 219 self.emptyStmt() pass @@ -1393,21 +1399,21 @@ def returnStmt(self): self.enterRule(localctx, 12, self.RULE_returnStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 220 - self.match(HogQLParser.RETURN) self.state = 222 + self.match(HogQLParser.RETURN) + self.state = 224 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,6,self._ctx) if la_ == 1: - self.state = 221 + self.state = 223 self.expression() - self.state = 225 + self.state = 227 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,7,self._ctx) if la_ == 1: - self.state = 224 + self.state = 226 self.match(HogQLParser.SEMICOLON) @@ -1455,21 +1461,21 @@ def throwStmt(self): self.enterRule(localctx, 14, self.RULE_throwStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 227 - self.match(HogQLParser.THROW) self.state = 229 + self.match(HogQLParser.THROW) + self.state = 231 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,8,self._ctx) if la_ == 1: - self.state = 228 + self.state = 230 self.expression() - self.state = 232 + self.state = 234 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,9,self._ctx) if la_ == 1: - self.state = 231 + self.state = 233 self.match(HogQLParser.SEMICOLON) @@ -1534,31 +1540,31 @@ def catchBlock(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 234 + self.state = 236 self.match(HogQLParser.CATCH) - self.state = 243 + self.state = 245 self._errHandler.sync(self) _la = self._input.LA(1) if _la==133: - self.state = 235 + self.state = 237 self.match(HogQLParser.LPAREN) - self.state = 236 + self.state = 238 localctx.catchVar = self.identifier() - self.state = 239 + self.state = 241 self._errHandler.sync(self) _la = self._input.LA(1) if _la==118: - self.state = 237 + self.state = 239 self.match(HogQLParser.COLON) - self.state = 238 + self.state = 240 localctx.catchType = self.identifier() - self.state = 241 + self.state = 243 self.match(HogQLParser.RPAREN) - self.state = 245 + self.state = 247 localctx.catchStmt = self.block() except RecognitionException as re: localctx.exception = re @@ -1617,27 +1623,27 @@ def tryCatchStmt(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 247 + self.state = 249 self.match(HogQLParser.TRY) - self.state = 248 + self.state = 250 localctx.tryStmt = self.block() - self.state = 252 + self.state = 254 self._errHandler.sync(self) _la = self._input.LA(1) while _la==14: - self.state = 249 + self.state = 251 self.catchBlock() - self.state = 254 + self.state = 256 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 257 + self.state = 259 self._errHandler.sync(self) _la = self._input.LA(1) if _la==30: - self.state = 255 + self.state = 257 self.match(HogQLParser.FINALLY) - self.state = 256 + self.state = 258 localctx.finallyStmt = self.block() @@ -1698,23 +1704,23 @@ def ifStmt(self): self.enterRule(localctx, 20, self.RULE_ifStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 259 + self.state = 261 self.match(HogQLParser.IF) - self.state = 260 + self.state = 262 self.match(HogQLParser.LPAREN) - self.state = 261 + self.state = 263 self.expression() - self.state = 262 + self.state = 264 self.match(HogQLParser.RPAREN) - self.state = 263 + self.state = 265 self.statement() - self.state = 266 + self.state = 268 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,14,self._ctx) if la_ == 1: - self.state = 264 + self.state = 266 self.match(HogQLParser.ELSE) - self.state = 265 + self.state = 267 self.statement() @@ -1772,21 +1778,21 @@ def whileStmt(self): self.enterRule(localctx, 22, self.RULE_whileStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 268 + self.state = 270 self.match(HogQLParser.WHILE) - self.state = 269 + self.state = 271 self.match(HogQLParser.LPAREN) - self.state = 270 + self.state = 272 self.expression() - self.state = 271 + self.state = 273 self.match(HogQLParser.RPAREN) - self.state = 272 - self.statement() self.state = 274 + self.statement() + self.state = 276 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,15,self._ctx) if la_ == 1: - self.state = 273 + self.state = 275 self.match(HogQLParser.SEMICOLON) @@ -1872,63 +1878,63 @@ def forStmt(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 276 + self.state = 278 self.match(HogQLParser.FOR) - self.state = 277 + self.state = 279 self.match(HogQLParser.LPAREN) - self.state = 281 + self.state = 283 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,16,self._ctx) if la_ == 1: - self.state = 278 + self.state = 280 localctx.initializerVarDeclr = self.varDecl() elif la_ == 2: - self.state = 279 + self.state = 281 localctx.initializerVarAssignment = self.varAssignment() elif la_ == 3: - self.state = 280 + self.state = 282 localctx.initializerExpression = self.expression() - self.state = 283 - self.match(HogQLParser.SEMICOLON) self.state = 285 + self.match(HogQLParser.SEMICOLON) + self.state = 287 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 284 + self.state = 286 localctx.condition = self.expression() - self.state = 287 + self.state = 289 self.match(HogQLParser.SEMICOLON) - self.state = 291 + self.state = 293 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,18,self._ctx) if la_ == 1: - self.state = 288 + self.state = 290 localctx.incrementVarDeclr = self.varDecl() elif la_ == 2: - self.state = 289 + self.state = 291 localctx.incrementVarAssignment = self.varAssignment() elif la_ == 3: - self.state = 290 + self.state = 292 localctx.incrementExpression = self.expression() - self.state = 293 + self.state = 295 self.match(HogQLParser.RPAREN) - self.state = 294 - self.statement() self.state = 296 + self.statement() + self.state = 298 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,19,self._ctx) if la_ == 1: - self.state = 295 + self.state = 297 self.match(HogQLParser.SEMICOLON) @@ -2003,37 +2009,37 @@ def forInStmt(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 298 + self.state = 300 self.match(HogQLParser.FOR) - self.state = 299 + self.state = 301 self.match(HogQLParser.LPAREN) - self.state = 300 + self.state = 302 self.match(HogQLParser.LET) - self.state = 301 + self.state = 303 self.identifier() - self.state = 304 + self.state = 306 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 302 + self.state = 304 self.match(HogQLParser.COMMA) - self.state = 303 + self.state = 305 self.identifier() - self.state = 306 + self.state = 308 self.match(HogQLParser.IN) - self.state = 307 + self.state = 309 self.expression() - self.state = 308 + self.state = 310 self.match(HogQLParser.RPAREN) - self.state = 309 - self.statement() self.state = 311 + self.statement() + self.state = 313 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,21,self._ctx) if la_ == 1: - self.state = 310 + self.state = 312 self.match(HogQLParser.SEMICOLON) @@ -2096,28 +2102,28 @@ def funcStmt(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 313 + self.state = 315 _la = self._input.LA(1) if not(_la==32 or _la==37): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 314 + self.state = 316 self.identifier() - self.state = 315 - self.match(HogQLParser.LPAREN) self.state = 317 + self.match(HogQLParser.LPAREN) + self.state = 319 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -5800812384855539714) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 25834219896831) != 0): - self.state = 316 + self.state = 318 self.identifierList() - self.state = 319 + self.state = 321 self.match(HogQLParser.RPAREN) - self.state = 320 + self.state = 322 self.block() except RecognitionException as re: localctx.exception = re @@ -2166,13 +2172,13 @@ def varAssignment(self): self.enterRule(localctx, 30, self.RULE_varAssignment) try: self.enterOuterAlt(localctx, 1) - self.state = 322 + self.state = 324 self.expression() - self.state = 323 + self.state = 325 self.match(HogQLParser.COLON) - self.state = 324 + self.state = 326 self.match(HogQLParser.EQ_SINGLE) - self.state = 325 + self.state = 327 self.expression() except RecognitionException as re: localctx.exception = re @@ -2215,13 +2221,13 @@ def exprStmt(self): self.enterRule(localctx, 32, self.RULE_exprStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 327 - self.expression() self.state = 329 + self.expression() + self.state = 331 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,23,self._ctx) if la_ == 1: - self.state = 328 + self.state = 330 self.match(HogQLParser.SEMICOLON) @@ -2262,7 +2268,7 @@ def emptyStmt(self): self.enterRule(localctx, 34, self.RULE_emptyStmt) try: self.enterOuterAlt(localctx, 1) - self.state = 331 + self.state = 333 self.match(HogQLParser.SEMICOLON) except RecognitionException as re: localctx.exception = re @@ -2312,19 +2318,19 @@ def block(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 333 + self.state = 335 self.match(HogQLParser.LBRACE) - self.state = 337 + self.state = 339 self._errHandler.sync(self) _la = self._input.LA(1) while (((_la) & ~0x3f) == 0 and ((1 << _la) & -140738696331266) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944844006785023) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 4212759) != 0): - self.state = 334 + self.state = 336 self.declaration() - self.state = 339 + self.state = 341 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 340 + self.state = 342 self.match(HogQLParser.RBRACE) except RecognitionException as re: localctx.exception = re @@ -2370,11 +2376,11 @@ def kvPair(self): self.enterRule(localctx, 38, self.RULE_kvPair) try: self.enterOuterAlt(localctx, 1) - self.state = 342 + self.state = 344 self.expression() - self.state = 343 + self.state = 345 self.match(HogQLParser.COLON) - self.state = 344 + self.state = 346 self.expression() except RecognitionException as re: localctx.exception = re @@ -2424,26 +2430,26 @@ def kvPairList(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 346 + self.state = 348 self.kvPair() - self.state = 351 + self.state = 353 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,25,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 347 + self.state = 349 self.match(HogQLParser.COMMA) - self.state = 348 + self.state = 350 self.kvPair() - self.state = 353 + self.state = 355 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,25,self._ctx) - self.state = 355 + self.state = 357 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 354 + self.state = 356 self.match(HogQLParser.COMMA) @@ -2496,26 +2502,26 @@ def select(self): self.enterRule(localctx, 42, self.RULE_select) try: self.enterOuterAlt(localctx, 1) - self.state = 360 + self.state = 362 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,27,self._ctx) if la_ == 1: - self.state = 357 + self.state = 359 self.selectSetStmt() pass elif la_ == 2: - self.state = 358 + self.state = 360 self.selectStmt() pass elif la_ == 3: - self.state = 359 + self.state = 361 self.hogqlxTagElement() pass - self.state = 362 + self.state = 364 self.match(HogQLParser.EOF) except RecognitionException as re: localctx.exception = re @@ -2568,26 +2574,26 @@ def selectStmtWithParens(self): localctx = HogQLParser.SelectStmtWithParensContext(self, self._ctx, self.state) self.enterRule(localctx, 44, self.RULE_selectStmtWithParens) try: - self.state = 370 + self.state = 372 self._errHandler.sync(self) token = self._input.LA(1) if token in [82, 105]: self.enterOuterAlt(localctx, 1) - self.state = 364 + self.state = 366 self.selectStmt() pass elif token in [133]: self.enterOuterAlt(localctx, 2) - self.state = 365 + self.state = 367 self.match(HogQLParser.LPAREN) - self.state = 366 + self.state = 368 self.selectSetStmt() - self.state = 367 + self.state = 369 self.match(HogQLParser.RPAREN) pass elif token in [131]: self.enterOuterAlt(localctx, 3) - self.state = 369 + self.state = 371 self.placeholder() pass else: @@ -2646,42 +2652,42 @@ def subsequentSelectSetClause(self): self.enterRule(localctx, 46, self.RULE_subsequentSelectSetClause) try: self.enterOuterAlt(localctx, 1) - self.state = 380 + self.state = 382 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,29,self._ctx) if la_ == 1: - self.state = 372 + self.state = 374 self.match(HogQLParser.EXCEPT) pass elif la_ == 2: - self.state = 373 + self.state = 375 self.match(HogQLParser.UNION) - self.state = 374 + self.state = 376 self.match(HogQLParser.ALL) pass elif la_ == 3: - self.state = 375 + self.state = 377 self.match(HogQLParser.UNION) - self.state = 376 + self.state = 378 self.match(HogQLParser.DISTINCT) pass elif la_ == 4: - self.state = 377 + self.state = 379 self.match(HogQLParser.INTERSECT) pass elif la_ == 5: - self.state = 378 + self.state = 380 self.match(HogQLParser.INTERSECT) - self.state = 379 + self.state = 381 self.match(HogQLParser.DISTINCT) pass - self.state = 382 + self.state = 384 self.selectStmtWithParens() except RecognitionException as re: localctx.exception = re @@ -2729,15 +2735,15 @@ def selectSetStmt(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 384 + self.state = 386 self.selectStmtWithParens() - self.state = 388 + self.state = 390 self._errHandler.sync(self) _la = self._input.LA(1) while _la==27 or _la==47 or _la==98: - self.state = 385 + self.state = 387 self.subsequentSelectSetClause() - self.state = 390 + self.state = 392 self._errHandler.sync(self) _la = self._input.LA(1) @@ -2857,81 +2863,81 @@ def selectStmt(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 392 + self.state = 394 self._errHandler.sync(self) _la = self._input.LA(1) if _la==105: - self.state = 391 + self.state = 393 localctx.with_ = self.withClause() - self.state = 394 - self.match(HogQLParser.SELECT) self.state = 396 + self.match(HogQLParser.SELECT) + self.state = 398 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,32,self._ctx) if la_ == 1: - self.state = 395 + self.state = 397 self.match(HogQLParser.DISTINCT) - self.state = 399 + self.state = 401 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,33,self._ctx) if la_ == 1: - self.state = 398 + self.state = 400 self.topClause() - self.state = 401 - localctx.columns = self.columnExprList() self.state = 403 + localctx.columns = self.columnExprList() + self.state = 405 self._errHandler.sync(self) _la = self._input.LA(1) if _la==35: - self.state = 402 + self.state = 404 localctx.from_ = self.fromClause() - self.state = 406 + self.state = 408 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 18084767253659680) != 0): - self.state = 405 + self.state = 407 self.arrayJoinClause() - self.state = 409 + self.state = 411 self._errHandler.sync(self) _la = self._input.LA(1) if _la==72: - self.state = 408 + self.state = 410 self.prewhereClause() - self.state = 412 + self.state = 414 self._errHandler.sync(self) _la = self._input.LA(1) if _la==102: - self.state = 411 + self.state = 413 localctx.where = self.whereClause() - self.state = 415 + self.state = 417 self._errHandler.sync(self) _la = self._input.LA(1) if _la==38: - self.state = 414 + self.state = 416 self.groupByClause() - self.state = 419 + self.state = 421 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,39,self._ctx) if la_ == 1: - self.state = 417 + self.state = 419 self.match(HogQLParser.WITH) - self.state = 418 + self.state = 420 _la = self._input.LA(1) if not(_la==18 or _la==77): self._errHandler.recoverInline(self) @@ -2940,60 +2946,60 @@ def selectStmt(self): self.consume() - self.state = 423 + self.state = 425 self._errHandler.sync(self) _la = self._input.LA(1) if _la==105: - self.state = 421 + self.state = 423 self.match(HogQLParser.WITH) - self.state = 422 + self.state = 424 self.match(HogQLParser.TOTALS) - self.state = 426 + self.state = 428 self._errHandler.sync(self) _la = self._input.LA(1) if _la==39: - self.state = 425 + self.state = 427 self.havingClause() - self.state = 429 + self.state = 431 self._errHandler.sync(self) _la = self._input.LA(1) if _la==104: - self.state = 428 + self.state = 430 self.windowClause() - self.state = 432 + self.state = 434 self._errHandler.sync(self) _la = self._input.LA(1) if _la==67: - self.state = 431 + self.state = 433 self.orderByClause() - self.state = 436 + self.state = 438 self._errHandler.sync(self) token = self._input.LA(1) if token in [57]: - self.state = 434 + self.state = 436 self.limitAndOffsetClause() pass elif token in [64]: - self.state = 435 + self.state = 437 self.offsetOnlyClause() pass elif token in [-1, 27, 47, 84, 98, 152]: pass else: pass - self.state = 439 + self.state = 441 self._errHandler.sync(self) _la = self._input.LA(1) if _la==84: - self.state = 438 + self.state = 440 self.settingsClause() @@ -3038,9 +3044,9 @@ def withClause(self): self.enterRule(localctx, 52, self.RULE_withClause) try: self.enterOuterAlt(localctx, 1) - self.state = 441 + self.state = 443 self.match(HogQLParser.WITH) - self.state = 442 + self.state = 444 self.withExprList() except RecognitionException as re: localctx.exception = re @@ -3088,17 +3094,17 @@ def topClause(self): self.enterRule(localctx, 54, self.RULE_topClause) try: self.enterOuterAlt(localctx, 1) - self.state = 444 + self.state = 446 self.match(HogQLParser.TOP) - self.state = 445 + self.state = 447 self.match(HogQLParser.DECIMAL_LITERAL) - self.state = 448 + self.state = 450 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,46,self._ctx) if la_ == 1: - self.state = 446 + self.state = 448 self.match(HogQLParser.WITH) - self.state = 447 + self.state = 449 self.match(HogQLParser.TIES) @@ -3143,9 +3149,9 @@ def fromClause(self): self.enterRule(localctx, 56, self.RULE_fromClause) try: self.enterOuterAlt(localctx, 1) - self.state = 450 + self.state = 452 self.match(HogQLParser.FROM) - self.state = 451 + self.state = 453 self.joinExpr(0) except RecognitionException as re: localctx.exception = re @@ -3198,11 +3204,11 @@ def arrayJoinClause(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 454 + self.state = 456 self._errHandler.sync(self) _la = self._input.LA(1) if _la==46 or _la==54: - self.state = 453 + self.state = 455 _la = self._input.LA(1) if not(_la==46 or _la==54): self._errHandler.recoverInline(self) @@ -3211,11 +3217,11 @@ def arrayJoinClause(self): self.consume() - self.state = 456 + self.state = 458 self.match(HogQLParser.ARRAY) - self.state = 457 + self.state = 459 self.match(HogQLParser.JOIN) - self.state = 458 + self.state = 460 self.columnExprList() except RecognitionException as re: localctx.exception = re @@ -3293,35 +3299,35 @@ def windowClause(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 460 + self.state = 462 self.match(HogQLParser.WINDOW) - self.state = 461 + self.state = 463 self.identifier() - self.state = 462 + self.state = 464 self.match(HogQLParser.AS) - self.state = 463 + self.state = 465 self.match(HogQLParser.LPAREN) - self.state = 464 + self.state = 466 self.windowExpr() - self.state = 465 + self.state = 467 self.match(HogQLParser.RPAREN) - self.state = 475 + self.state = 477 self._errHandler.sync(self) _la = self._input.LA(1) while _la==119: - self.state = 466 + self.state = 468 self.match(HogQLParser.COMMA) - self.state = 467 + self.state = 469 self.identifier() - self.state = 468 + self.state = 470 self.match(HogQLParser.AS) - self.state = 469 + self.state = 471 self.match(HogQLParser.LPAREN) - self.state = 470 + self.state = 472 self.windowExpr() - self.state = 471 + self.state = 473 self.match(HogQLParser.RPAREN) - self.state = 477 + self.state = 479 self._errHandler.sync(self) _la = self._input.LA(1) @@ -3366,9 +3372,9 @@ def prewhereClause(self): self.enterRule(localctx, 62, self.RULE_prewhereClause) try: self.enterOuterAlt(localctx, 1) - self.state = 478 + self.state = 480 self.match(HogQLParser.PREWHERE) - self.state = 479 + self.state = 481 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -3411,9 +3417,9 @@ def whereClause(self): self.enterRule(localctx, 64, self.RULE_whereClause) try: self.enterOuterAlt(localctx, 1) - self.state = 481 + self.state = 483 self.match(HogQLParser.WHERE) - self.state = 482 + self.state = 484 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -3472,31 +3478,31 @@ def groupByClause(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 484 + self.state = 486 self.match(HogQLParser.GROUP) - self.state = 485 + self.state = 487 self.match(HogQLParser.BY) - self.state = 492 + self.state = 494 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,49,self._ctx) if la_ == 1: - self.state = 486 + self.state = 488 _la = self._input.LA(1) if not(_la==18 or _la==77): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 487 + self.state = 489 self.match(HogQLParser.LPAREN) - self.state = 488 + self.state = 490 self.columnExprList() - self.state = 489 + self.state = 491 self.match(HogQLParser.RPAREN) pass elif la_ == 2: - self.state = 491 + self.state = 493 self.columnExprList() pass @@ -3542,9 +3548,9 @@ def havingClause(self): self.enterRule(localctx, 68, self.RULE_havingClause) try: self.enterOuterAlt(localctx, 1) - self.state = 494 + self.state = 496 self.match(HogQLParser.HAVING) - self.state = 495 + self.state = 497 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -3590,11 +3596,11 @@ def orderByClause(self): self.enterRule(localctx, 70, self.RULE_orderByClause) try: self.enterOuterAlt(localctx, 1) - self.state = 497 + self.state = 499 self.match(HogQLParser.ORDER) - self.state = 498 + self.state = 500 self.match(HogQLParser.BY) - self.state = 499 + self.state = 501 self.orderExprList() except RecognitionException as re: localctx.exception = re @@ -3640,11 +3646,11 @@ def projectionOrderByClause(self): self.enterRule(localctx, 72, self.RULE_projectionOrderByClause) try: self.enterOuterAlt(localctx, 1) - self.state = 501 + self.state = 503 self.match(HogQLParser.ORDER) - self.state = 502 + self.state = 504 self.match(HogQLParser.BY) - self.state = 503 + self.state = 505 self.columnExprList() except RecognitionException as re: localctx.exception = re @@ -3709,38 +3715,38 @@ def limitAndOffsetClause(self): self.enterRule(localctx, 74, self.RULE_limitAndOffsetClause) self._la = 0 # Token type try: - self.state = 534 + self.state = 536 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,54,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 505 + self.state = 507 self.match(HogQLParser.LIMIT) - self.state = 506 + self.state = 508 self.columnExpr(0) - self.state = 509 + self.state = 511 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 507 + self.state = 509 self.match(HogQLParser.COMMA) - self.state = 508 + self.state = 510 self.columnExpr(0) - self.state = 515 + self.state = 517 self._errHandler.sync(self) token = self._input.LA(1) if token in [105]: - self.state = 511 + self.state = 513 self.match(HogQLParser.WITH) - self.state = 512 + self.state = 514 self.match(HogQLParser.TIES) pass elif token in [11]: - self.state = 513 + self.state = 515 self.match(HogQLParser.BY) - self.state = 514 + self.state = 516 self.columnExprList() pass elif token in [-1, 27, 47, 84, 98, 152]: @@ -3751,43 +3757,43 @@ def limitAndOffsetClause(self): elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 517 + self.state = 519 self.match(HogQLParser.LIMIT) - self.state = 518 + self.state = 520 self.columnExpr(0) - self.state = 521 + self.state = 523 self._errHandler.sync(self) _la = self._input.LA(1) if _la==105: - self.state = 519 + self.state = 521 self.match(HogQLParser.WITH) - self.state = 520 + self.state = 522 self.match(HogQLParser.TIES) - self.state = 523 + self.state = 525 self.match(HogQLParser.OFFSET) - self.state = 524 + self.state = 526 self.columnExpr(0) pass elif la_ == 3: self.enterOuterAlt(localctx, 3) - self.state = 526 + self.state = 528 self.match(HogQLParser.LIMIT) - self.state = 527 + self.state = 529 self.columnExpr(0) - self.state = 528 + self.state = 530 self.match(HogQLParser.OFFSET) - self.state = 529 + self.state = 531 self.columnExpr(0) - self.state = 532 + self.state = 534 self._errHandler.sync(self) _la = self._input.LA(1) if _la==11: - self.state = 530 + self.state = 532 self.match(HogQLParser.BY) - self.state = 531 + self.state = 533 self.columnExprList() @@ -3835,9 +3841,9 @@ def offsetOnlyClause(self): self.enterRule(localctx, 76, self.RULE_offsetOnlyClause) try: self.enterOuterAlt(localctx, 1) - self.state = 536 + self.state = 538 self.match(HogQLParser.OFFSET) - self.state = 537 + self.state = 539 self.columnExpr(0) except RecognitionException as re: localctx.exception = re @@ -3880,9 +3886,9 @@ def settingsClause(self): self.enterRule(localctx, 78, self.RULE_settingsClause) try: self.enterOuterAlt(localctx, 1) - self.state = 539 + self.state = 541 self.match(HogQLParser.SETTINGS) - self.state = 540 + self.state = 542 self.settingExprList() except RecognitionException as re: localctx.exception = re @@ -4014,7 +4020,7 @@ def joinExpr(self, _p:int=0): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 554 + self.state = 556 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,57,self._ctx) if la_ == 1: @@ -4022,21 +4028,21 @@ def joinExpr(self, _p:int=0): self._ctx = localctx _prevctx = localctx - self.state = 543 - self.tableExpr(0) self.state = 545 + self.tableExpr(0) + self.state = 547 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,55,self._ctx) if la_ == 1: - self.state = 544 + self.state = 546 self.match(HogQLParser.FINAL) - self.state = 548 + self.state = 550 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,56,self._ctx) if la_ == 1: - self.state = 547 + self.state = 549 self.sampleClause() @@ -4046,17 +4052,17 @@ def joinExpr(self, _p:int=0): localctx = HogQLParser.JoinExprParensContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 550 + self.state = 552 self.match(HogQLParser.LPAREN) - self.state = 551 + self.state = 553 self.joinExpr(0) - self.state = 552 + self.state = 554 self.match(HogQLParser.RPAREN) pass self._ctx.stop = self._input.LT(-1) - self.state = 570 + self.state = 572 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,60,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: @@ -4064,47 +4070,47 @@ def joinExpr(self, _p:int=0): if self._parseListeners is not None: self.triggerExitRuleEvent() _prevctx = localctx - self.state = 568 + self.state = 570 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,59,self._ctx) if la_ == 1: localctx = HogQLParser.JoinExprCrossOpContext(self, HogQLParser.JoinExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_joinExpr) - self.state = 556 + self.state = 558 if not self.precpred(self._ctx, 3): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 3)") - self.state = 557 + self.state = 559 self.joinOpCross() - self.state = 558 + self.state = 560 self.joinExpr(4) pass elif la_ == 2: localctx = HogQLParser.JoinExprOpContext(self, HogQLParser.JoinExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_joinExpr) - self.state = 560 + self.state = 562 if not self.precpred(self._ctx, 4): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 4)") - self.state = 562 + self.state = 564 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 18084835973136666) != 0) or _la==76 or _la==83: - self.state = 561 + self.state = 563 self.joinOp() - self.state = 564 + self.state = 566 self.match(HogQLParser.JOIN) - self.state = 565 + self.state = 567 self.joinExpr(0) - self.state = 566 + self.state = 568 self.joinConstraintClause() pass - self.state = 572 + self.state = 574 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,60,self._ctx) @@ -4215,21 +4221,21 @@ def joinOp(self): self.enterRule(localctx, 82, self.RULE_joinOp) self._la = 0 # Token type try: - self.state = 616 + self.state = 618 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,74,self._ctx) if la_ == 1: localctx = HogQLParser.JoinOpInnerContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 582 + self.state = 584 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,63,self._ctx) if la_ == 1: - self.state = 574 + self.state = 576 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0): - self.state = 573 + self.state = 575 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0)): self._errHandler.recoverInline(self) @@ -4238,18 +4244,18 @@ def joinOp(self): self.consume() - self.state = 576 + self.state = 578 self.match(HogQLParser.INNER) pass elif la_ == 2: - self.state = 577 - self.match(HogQLParser.INNER) self.state = 579 + self.match(HogQLParser.INNER) + self.state = 581 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0): - self.state = 578 + self.state = 580 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0)): self._errHandler.recoverInline(self) @@ -4261,7 +4267,7 @@ def joinOp(self): pass elif la_ == 3: - self.state = 581 + self.state = 583 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 274) != 0)): self._errHandler.recoverInline(self) @@ -4276,15 +4282,15 @@ def joinOp(self): elif la_ == 2: localctx = HogQLParser.JoinOpLeftRightContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 598 + self.state = 600 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,68,self._ctx) if la_ == 1: - self.state = 585 + self.state = 587 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 282) != 0) or _la==83: - self.state = 584 + self.state = 586 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 282) != 0) or _la==83): self._errHandler.recoverInline(self) @@ -4293,44 +4299,44 @@ def joinOp(self): self.consume() - self.state = 587 + self.state = 589 _la = self._input.LA(1) if not(_la==54 or _la==76): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 589 + self.state = 591 self._errHandler.sync(self) _la = self._input.LA(1) if _la==68: - self.state = 588 + self.state = 590 self.match(HogQLParser.OUTER) pass elif la_ == 2: - self.state = 591 + self.state = 593 _la = self._input.LA(1) if not(_la==54 or _la==76): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 593 + self.state = 595 self._errHandler.sync(self) _la = self._input.LA(1) if _la==68: - self.state = 592 + self.state = 594 self.match(HogQLParser.OUTER) - self.state = 596 + self.state = 598 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 282) != 0) or _la==83: - self.state = 595 + self.state = 597 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 282) != 0) or _la==83): self._errHandler.recoverInline(self) @@ -4347,15 +4353,15 @@ def joinOp(self): elif la_ == 3: localctx = HogQLParser.JoinOpFullContext(self, localctx) self.enterOuterAlt(localctx, 3) - self.state = 614 + self.state = 616 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,73,self._ctx) if la_ == 1: - self.state = 601 + self.state = 603 self._errHandler.sync(self) _la = self._input.LA(1) if _la==1 or _la==4: - self.state = 600 + self.state = 602 _la = self._input.LA(1) if not(_la==1 or _la==4): self._errHandler.recoverInline(self) @@ -4364,34 +4370,34 @@ def joinOp(self): self.consume() - self.state = 603 - self.match(HogQLParser.FULL) self.state = 605 + self.match(HogQLParser.FULL) + self.state = 607 self._errHandler.sync(self) _la = self._input.LA(1) if _la==68: - self.state = 604 + self.state = 606 self.match(HogQLParser.OUTER) pass elif la_ == 2: - self.state = 607 - self.match(HogQLParser.FULL) self.state = 609 + self.match(HogQLParser.FULL) + self.state = 611 self._errHandler.sync(self) _la = self._input.LA(1) if _la==68: - self.state = 608 + self.state = 610 self.match(HogQLParser.OUTER) - self.state = 612 + self.state = 614 self._errHandler.sync(self) _la = self._input.LA(1) if _la==1 or _la==4: - self.state = 611 + self.state = 613 _la = self._input.LA(1) if not(_la==1 or _la==4): self._errHandler.recoverInline(self) @@ -4448,19 +4454,19 @@ def joinOpCross(self): localctx = HogQLParser.JoinOpCrossContext(self, self._ctx, self.state) self.enterRule(localctx, 84, self.RULE_joinOpCross) try: - self.state = 621 + self.state = 623 self._errHandler.sync(self) token = self._input.LA(1) if token in [17]: self.enterOuterAlt(localctx, 1) - self.state = 618 + self.state = 620 self.match(HogQLParser.CROSS) - self.state = 619 + self.state = 621 self.match(HogQLParser.JOIN) pass elif token in [119]: self.enterOuterAlt(localctx, 2) - self.state = 620 + self.state = 622 self.match(HogQLParser.COMMA) pass else: @@ -4515,34 +4521,34 @@ def joinConstraintClause(self): localctx = HogQLParser.JoinConstraintClauseContext(self, self._ctx, self.state) self.enterRule(localctx, 86, self.RULE_joinConstraintClause) try: - self.state = 632 + self.state = 634 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,76,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 623 + self.state = 625 self.match(HogQLParser.ON) - self.state = 624 + self.state = 626 self.columnExprList() pass elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 625 + self.state = 627 self.match(HogQLParser.USING) - self.state = 626 + self.state = 628 self.match(HogQLParser.LPAREN) - self.state = 627 + self.state = 629 self.columnExprList() - self.state = 628 + self.state = 630 self.match(HogQLParser.RPAREN) pass elif la_ == 3: self.enterOuterAlt(localctx, 3) - self.state = 630 + self.state = 632 self.match(HogQLParser.USING) - self.state = 631 + self.state = 633 self.columnExprList() pass @@ -4594,17 +4600,17 @@ def sampleClause(self): self.enterRule(localctx, 88, self.RULE_sampleClause) try: self.enterOuterAlt(localctx, 1) - self.state = 634 + self.state = 636 self.match(HogQLParser.SAMPLE) - self.state = 635 + self.state = 637 self.ratioExpr() - self.state = 638 + self.state = 640 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,77,self._ctx) if la_ == 1: - self.state = 636 + self.state = 638 self.match(HogQLParser.OFFSET) - self.state = 637 + self.state = 639 self.ratioExpr() @@ -4656,17 +4662,17 @@ def orderExprList(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 640 + self.state = 642 self.orderExpr() - self.state = 645 + self.state = 647 self._errHandler.sync(self) _la = self._input.LA(1) while _la==119: - self.state = 641 + self.state = 643 self.match(HogQLParser.COMMA) - self.state = 642 + self.state = 644 self.orderExpr() - self.state = 647 + self.state = 649 self._errHandler.sync(self) _la = self._input.LA(1) @@ -4733,13 +4739,13 @@ def orderExpr(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 648 - self.columnExpr(0) self.state = 650 + self.columnExpr(0) + self.state = 652 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & 12583040) != 0): - self.state = 649 + self.state = 651 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 12583040) != 0)): self._errHandler.recoverInline(self) @@ -4748,13 +4754,13 @@ def orderExpr(self): self.consume() - self.state = 654 + self.state = 656 self._errHandler.sync(self) _la = self._input.LA(1) if _la==63: - self.state = 652 + self.state = 654 self.match(HogQLParser.NULLS) - self.state = 653 + self.state = 655 _la = self._input.LA(1) if not(_la==31 or _la==52): self._errHandler.recoverInline(self) @@ -4763,13 +4769,13 @@ def orderExpr(self): self.consume() - self.state = 658 + self.state = 660 self._errHandler.sync(self) _la = self._input.LA(1) if _la==16: - self.state = 656 + self.state = 658 self.match(HogQLParser.COLLATE) - self.state = 657 + self.state = 659 self.match(HogQLParser.STRING_LITERAL) @@ -4820,25 +4826,25 @@ def ratioExpr(self): localctx = HogQLParser.RatioExprContext(self, self._ctx, self.state) self.enterRule(localctx, 94, self.RULE_ratioExpr) try: - self.state = 666 + self.state = 668 self._errHandler.sync(self) token = self._input.LA(1) if token in [131]: self.enterOuterAlt(localctx, 1) - self.state = 660 + self.state = 662 self.placeholder() pass elif token in [45, 60, 109, 110, 111, 112, 121, 123, 142]: self.enterOuterAlt(localctx, 2) - self.state = 661 + self.state = 663 self.numberLiteral() - self.state = 664 + self.state = 666 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,82,self._ctx) if la_ == 1: - self.state = 662 + self.state = 664 self.match(HogQLParser.SLASH) - self.state = 663 + self.state = 665 self.numberLiteral() @@ -4894,17 +4900,17 @@ def settingExprList(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 668 + self.state = 670 self.settingExpr() - self.state = 673 + self.state = 675 self._errHandler.sync(self) _la = self._input.LA(1) while _la==119: - self.state = 669 + self.state = 671 self.match(HogQLParser.COMMA) - self.state = 670 + self.state = 672 self.settingExpr() - self.state = 675 + self.state = 677 self._errHandler.sync(self) _la = self._input.LA(1) @@ -4953,11 +4959,11 @@ def settingExpr(self): self.enterRule(localctx, 98, self.RULE_settingExpr) try: self.enterOuterAlt(localctx, 1) - self.state = 676 + self.state = 678 self.identifier() - self.state = 677 + self.state = 679 self.match(HogQLParser.EQ_SINGLE) - self.state = 678 + self.state = 680 self.literal() except RecognitionException as re: localctx.exception = re @@ -5006,27 +5012,27 @@ def windowExpr(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 681 + self.state = 683 self._errHandler.sync(self) _la = self._input.LA(1) if _la==70: - self.state = 680 + self.state = 682 self.winPartitionByClause() - self.state = 684 + self.state = 686 self._errHandler.sync(self) _la = self._input.LA(1) if _la==67: - self.state = 683 + self.state = 685 self.winOrderByClause() - self.state = 687 + self.state = 689 self._errHandler.sync(self) _la = self._input.LA(1) if _la==74 or _la==79: - self.state = 686 + self.state = 688 self.winFrameClause() @@ -5074,11 +5080,11 @@ def winPartitionByClause(self): self.enterRule(localctx, 102, self.RULE_winPartitionByClause) try: self.enterOuterAlt(localctx, 1) - self.state = 689 + self.state = 691 self.match(HogQLParser.PARTITION) - self.state = 690 + self.state = 692 self.match(HogQLParser.BY) - self.state = 691 + self.state = 693 self.columnExprList() except RecognitionException as re: localctx.exception = re @@ -5124,11 +5130,11 @@ def winOrderByClause(self): self.enterRule(localctx, 104, self.RULE_winOrderByClause) try: self.enterOuterAlt(localctx, 1) - self.state = 693 + self.state = 695 self.match(HogQLParser.ORDER) - self.state = 694 + self.state = 696 self.match(HogQLParser.BY) - self.state = 695 + self.state = 697 self.orderExprList() except RecognitionException as re: localctx.exception = re @@ -5175,14 +5181,14 @@ def winFrameClause(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 697 + self.state = 699 _la = self._input.LA(1) if not(_la==74 or _la==79): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 698 + self.state = 700 self.winFrameExtend() except RecognitionException as re: localctx.exception = re @@ -5257,25 +5263,25 @@ def winFrameExtend(self): localctx = HogQLParser.WinFrameExtendContext(self, self._ctx, self.state) self.enterRule(localctx, 108, self.RULE_winFrameExtend) try: - self.state = 706 + self.state = 708 self._errHandler.sync(self) token = self._input.LA(1) if token in [19, 45, 60, 97, 109, 110, 111, 112, 121, 123, 142]: localctx = HogQLParser.FrameStartContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 700 + self.state = 702 self.winFrameBound() pass elif token in [9]: localctx = HogQLParser.FrameBetweenContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 701 + self.state = 703 self.match(HogQLParser.BETWEEN) - self.state = 702 + self.state = 704 self.winFrameBound() - self.state = 703 + self.state = 705 self.match(HogQLParser.AND) - self.state = 704 + self.state = 706 self.winFrameBound() pass else: @@ -5334,41 +5340,41 @@ def winFrameBound(self): self.enterRule(localctx, 110, self.RULE_winFrameBound) try: self.enterOuterAlt(localctx, 1) - self.state = 720 + self.state = 722 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,89,self._ctx) if la_ == 1: - self.state = 708 + self.state = 710 self.match(HogQLParser.CURRENT) - self.state = 709 + self.state = 711 self.match(HogQLParser.ROW) pass elif la_ == 2: - self.state = 710 + self.state = 712 self.match(HogQLParser.UNBOUNDED) - self.state = 711 + self.state = 713 self.match(HogQLParser.PRECEDING) pass elif la_ == 3: - self.state = 712 + self.state = 714 self.match(HogQLParser.UNBOUNDED) - self.state = 713 + self.state = 715 self.match(HogQLParser.FOLLOWING) pass elif la_ == 4: - self.state = 714 + self.state = 716 self.numberLiteral() - self.state = 715 + self.state = 717 self.match(HogQLParser.PRECEDING) pass elif la_ == 5: - self.state = 717 + self.state = 719 self.numberLiteral() - self.state = 718 + self.state = 720 self.match(HogQLParser.FOLLOWING) pass @@ -5414,9 +5420,9 @@ def expr(self): self.enterRule(localctx, 112, self.RULE_expr) try: self.enterOuterAlt(localctx, 1) - self.state = 722 + self.state = 724 self.columnExpr(0) - self.state = 723 + self.state = 725 self.match(HogQLParser.EOF) except RecognitionException as re: localctx.exception = re @@ -5591,138 +5597,138 @@ def columnTypeExpr(self): self.enterRule(localctx, 114, self.RULE_columnTypeExpr) self._la = 0 # Token type try: - self.state = 781 + self.state = 783 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,97,self._ctx) if la_ == 1: localctx = HogQLParser.ColumnTypeExprSimpleContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 725 + self.state = 727 self.identifier() pass elif la_ == 2: localctx = HogQLParser.ColumnTypeExprNestedContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 726 - self.identifier() - self.state = 727 - self.match(HogQLParser.LPAREN) self.state = 728 self.identifier() self.state = 729 + self.match(HogQLParser.LPAREN) + self.state = 730 + self.identifier() + self.state = 731 self.columnTypeExpr() - self.state = 736 + self.state = 738 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,90,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 730 + self.state = 732 self.match(HogQLParser.COMMA) - self.state = 731 + self.state = 733 self.identifier() - self.state = 732 + self.state = 734 self.columnTypeExpr() - self.state = 738 + self.state = 740 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,90,self._ctx) - self.state = 740 + self.state = 742 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 739 + self.state = 741 self.match(HogQLParser.COMMA) - self.state = 742 + self.state = 744 self.match(HogQLParser.RPAREN) pass elif la_ == 3: localctx = HogQLParser.ColumnTypeExprEnumContext(self, localctx) self.enterOuterAlt(localctx, 3) - self.state = 744 + self.state = 746 self.identifier() - self.state = 745 + self.state = 747 self.match(HogQLParser.LPAREN) - self.state = 746 + self.state = 748 self.enumValue() - self.state = 751 + self.state = 753 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,92,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 747 + self.state = 749 self.match(HogQLParser.COMMA) - self.state = 748 + self.state = 750 self.enumValue() - self.state = 753 + self.state = 755 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,92,self._ctx) - self.state = 755 + self.state = 757 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 754 + self.state = 756 self.match(HogQLParser.COMMA) - self.state = 757 + self.state = 759 self.match(HogQLParser.RPAREN) pass elif la_ == 4: localctx = HogQLParser.ColumnTypeExprComplexContext(self, localctx) self.enterOuterAlt(localctx, 4) - self.state = 759 + self.state = 761 self.identifier() - self.state = 760 + self.state = 762 self.match(HogQLParser.LPAREN) - self.state = 761 + self.state = 763 self.columnTypeExpr() - self.state = 766 + self.state = 768 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,94,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 762 + self.state = 764 self.match(HogQLParser.COMMA) - self.state = 763 + self.state = 765 self.columnTypeExpr() - self.state = 768 + self.state = 770 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,94,self._ctx) - self.state = 770 + self.state = 772 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 769 + self.state = 771 self.match(HogQLParser.COMMA) - self.state = 772 + self.state = 774 self.match(HogQLParser.RPAREN) pass elif la_ == 5: localctx = HogQLParser.ColumnTypeExprParamContext(self, localctx) self.enterOuterAlt(localctx, 5) - self.state = 774 + self.state = 776 self.identifier() - self.state = 775 - self.match(HogQLParser.LPAREN) self.state = 777 + self.match(HogQLParser.LPAREN) + self.state = 779 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 776 + self.state = 778 self.columnExprList() - self.state = 779 + self.state = 781 self.match(HogQLParser.RPAREN) pass @@ -5774,26 +5780,26 @@ def columnExprList(self): self.enterRule(localctx, 116, self.RULE_columnExprList) try: self.enterOuterAlt(localctx, 1) - self.state = 783 + self.state = 785 self.columnExpr(0) - self.state = 788 + self.state = 790 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,98,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 784 + self.state = 786 self.match(HogQLParser.COMMA) - self.state = 785 + self.state = 787 self.columnExpr(0) - self.state = 790 + self.state = 792 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,98,self._ctx) - self.state = 792 + self.state = 794 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,99,self._ctx) if la_ == 1: - self.state = 791 + self.state = 793 self.match(HogQLParser.COMMA) @@ -6851,7 +6857,7 @@ def columnExpr(self, _p:int=0): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 946 + self.state = 948 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,119,self._ctx) if la_ == 1: @@ -6859,45 +6865,45 @@ def columnExpr(self, _p:int=0): self._ctx = localctx _prevctx = localctx - self.state = 795 - self.match(HogQLParser.CASE) self.state = 797 + self.match(HogQLParser.CASE) + self.state = 799 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,100,self._ctx) if la_ == 1: - self.state = 796 + self.state = 798 localctx.caseExpr = self.columnExpr(0) - self.state = 804 + self.state = 806 self._errHandler.sync(self) _la = self._input.LA(1) while True: - self.state = 799 + self.state = 801 self.match(HogQLParser.WHEN) - self.state = 800 + self.state = 802 localctx.whenExpr = self.columnExpr(0) - self.state = 801 + self.state = 803 self.match(HogQLParser.THEN) - self.state = 802 + self.state = 804 localctx.thenExpr = self.columnExpr(0) - self.state = 806 + self.state = 808 self._errHandler.sync(self) _la = self._input.LA(1) if not (_la==101): break - self.state = 810 + self.state = 812 self._errHandler.sync(self) _la = self._input.LA(1) if _la==25: - self.state = 808 + self.state = 810 self.match(HogQLParser.ELSE) - self.state = 809 + self.state = 811 localctx.elseExpr = self.columnExpr(0) - self.state = 812 + self.state = 814 self.match(HogQLParser.END) pass @@ -6905,17 +6911,17 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprCastContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 814 + self.state = 816 self.match(HogQLParser.CAST) - self.state = 815 + self.state = 817 self.match(HogQLParser.LPAREN) - self.state = 816 + self.state = 818 self.columnExpr(0) - self.state = 817 + self.state = 819 self.match(HogQLParser.AS) - self.state = 818 + self.state = 820 self.columnTypeExpr() - self.state = 819 + self.state = 821 self.match(HogQLParser.RPAREN) pass @@ -6923,9 +6929,9 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprDateContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 821 + self.state = 823 self.match(HogQLParser.DATE) - self.state = 822 + self.state = 824 self.match(HogQLParser.STRING_LITERAL) pass @@ -6933,9 +6939,9 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprIntervalStringContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 823 + self.state = 825 self.match(HogQLParser.INTERVAL) - self.state = 824 + self.state = 826 self.match(HogQLParser.STRING_LITERAL) pass @@ -6943,11 +6949,11 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprIntervalContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 825 + self.state = 827 self.match(HogQLParser.INTERVAL) - self.state = 826 + self.state = 828 self.columnExpr(0) - self.state = 827 + self.state = 829 self.interval() pass @@ -6955,27 +6961,27 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprSubstringContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 829 + self.state = 831 self.match(HogQLParser.SUBSTRING) - self.state = 830 + self.state = 832 self.match(HogQLParser.LPAREN) - self.state = 831 + self.state = 833 self.columnExpr(0) - self.state = 832 + self.state = 834 self.match(HogQLParser.FROM) - self.state = 833 + self.state = 835 self.columnExpr(0) - self.state = 836 + self.state = 838 self._errHandler.sync(self) _la = self._input.LA(1) if _la==34: - self.state = 834 + self.state = 836 self.match(HogQLParser.FOR) - self.state = 835 + self.state = 837 self.columnExpr(0) - self.state = 838 + self.state = 840 self.match(HogQLParser.RPAREN) pass @@ -6983,9 +6989,9 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTimestampContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 840 + self.state = 842 self.match(HogQLParser.TIMESTAMP) - self.state = 841 + self.state = 843 self.match(HogQLParser.STRING_LITERAL) pass @@ -6993,24 +6999,24 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTrimContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 842 + self.state = 844 self.match(HogQLParser.TRIM) - self.state = 843 + self.state = 845 self.match(HogQLParser.LPAREN) - self.state = 844 + self.state = 846 _la = self._input.LA(1) if not(_la==10 or _la==53 or _la==93): self._errHandler.recoverInline(self) else: self._errHandler.reportMatch(self) self.consume() - self.state = 845 + self.state = 847 self.string() - self.state = 846 + self.state = 848 self.match(HogQLParser.FROM) - self.state = 847 + self.state = 849 self.columnExpr(0) - self.state = 848 + self.state = 850 self.match(HogQLParser.RPAREN) pass @@ -7018,54 +7024,54 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprWinFunctionContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 850 + self.state = 852 self.identifier() - self.state = 851 - self.match(HogQLParser.LPAREN) self.state = 853 + self.match(HogQLParser.LPAREN) + self.state = 855 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 852 + self.state = 854 localctx.columnExprs = self.columnExprList() - self.state = 855 + self.state = 857 self.match(HogQLParser.RPAREN) - self.state = 865 + self.state = 867 self._errHandler.sync(self) _la = self._input.LA(1) if _la==133: - self.state = 857 - self.match(HogQLParser.LPAREN) self.state = 859 + self.match(HogQLParser.LPAREN) + self.state = 861 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,105,self._ctx) if la_ == 1: - self.state = 858 + self.state = 860 self.match(HogQLParser.DISTINCT) - self.state = 862 + self.state = 864 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 861 + self.state = 863 localctx.columnArgList = self.columnExprList() - self.state = 864 + self.state = 866 self.match(HogQLParser.RPAREN) - self.state = 867 + self.state = 869 self.match(HogQLParser.OVER) - self.state = 868 + self.state = 870 self.match(HogQLParser.LPAREN) - self.state = 869 + self.state = 871 self.windowExpr() - self.state = 870 + self.state = 872 self.match(HogQLParser.RPAREN) pass @@ -7073,50 +7079,50 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprWinFunctionTargetContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 872 + self.state = 874 self.identifier() - self.state = 873 - self.match(HogQLParser.LPAREN) self.state = 875 + self.match(HogQLParser.LPAREN) + self.state = 877 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 874 + self.state = 876 localctx.columnExprs = self.columnExprList() - self.state = 877 + self.state = 879 self.match(HogQLParser.RPAREN) - self.state = 887 + self.state = 889 self._errHandler.sync(self) _la = self._input.LA(1) if _la==133: - self.state = 879 - self.match(HogQLParser.LPAREN) self.state = 881 + self.match(HogQLParser.LPAREN) + self.state = 883 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,109,self._ctx) if la_ == 1: - self.state = 880 + self.state = 882 self.match(HogQLParser.DISTINCT) - self.state = 884 + self.state = 886 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 883 + self.state = 885 localctx.columnArgList = self.columnExprList() - self.state = 886 + self.state = 888 self.match(HogQLParser.RPAREN) - self.state = 889 + self.state = 891 self.match(HogQLParser.OVER) - self.state = 890 + self.state = 892 self.identifier() pass @@ -7124,45 +7130,45 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprFunctionContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 892 + self.state = 894 self.identifier() - self.state = 898 + self.state = 900 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,113,self._ctx) if la_ == 1: - self.state = 893 - self.match(HogQLParser.LPAREN) self.state = 895 + self.match(HogQLParser.LPAREN) + self.state = 897 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 894 + self.state = 896 localctx.columnExprs = self.columnExprList() - self.state = 897 + self.state = 899 self.match(HogQLParser.RPAREN) - self.state = 900 - self.match(HogQLParser.LPAREN) self.state = 902 + self.match(HogQLParser.LPAREN) + self.state = 904 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,114,self._ctx) if la_ == 1: - self.state = 901 + self.state = 903 self.match(HogQLParser.DISTINCT) - self.state = 905 + self.state = 907 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 904 + self.state = 906 localctx.columnArgList = self.columnExprList() - self.state = 907 + self.state = 909 self.match(HogQLParser.RPAREN) pass @@ -7170,7 +7176,7 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTagElementContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 909 + self.state = 911 self.hogqlxTagElement() pass @@ -7178,7 +7184,7 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTemplateStringContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 910 + self.state = 912 self.templateString() pass @@ -7186,7 +7192,7 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprLiteralContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 911 + self.state = 913 self.literal() pass @@ -7194,9 +7200,9 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprNegateContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 912 + self.state = 914 self.match(HogQLParser.DASH) - self.state = 913 + self.state = 915 self.columnExpr(20) pass @@ -7204,9 +7210,9 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprNotContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 914 + self.state = 916 self.match(HogQLParser.NOT) - self.state = 915 + self.state = 917 self.columnExpr(14) pass @@ -7214,17 +7220,17 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprAsteriskContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 919 + self.state = 921 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -5800812384855539714) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 25834219896831) != 0): - self.state = 916 + self.state = 918 self.tableIdentifier() - self.state = 917 + self.state = 919 self.match(HogQLParser.DOT) - self.state = 921 + self.state = 923 self.match(HogQLParser.ASTERISK) pass @@ -7232,11 +7238,11 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprSubqueryContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 922 + self.state = 924 self.match(HogQLParser.LPAREN) - self.state = 923 + self.state = 925 self.selectSetStmt() - self.state = 924 + self.state = 926 self.match(HogQLParser.RPAREN) pass @@ -7244,11 +7250,11 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprParensContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 926 + self.state = 928 self.match(HogQLParser.LPAREN) - self.state = 927 + self.state = 929 self.columnExpr(0) - self.state = 928 + self.state = 930 self.match(HogQLParser.RPAREN) pass @@ -7256,11 +7262,11 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprTupleContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 930 + self.state = 932 self.match(HogQLParser.LPAREN) - self.state = 931 + self.state = 933 self.columnExprList() - self.state = 932 + self.state = 934 self.match(HogQLParser.RPAREN) pass @@ -7268,17 +7274,17 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprArrayContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 934 - self.match(HogQLParser.LBRACKET) self.state = 936 + self.match(HogQLParser.LBRACKET) + self.state = 938 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 935 + self.state = 937 self.columnExprList() - self.state = 938 + self.state = 940 self.match(HogQLParser.RBRACKET) pass @@ -7286,17 +7292,17 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprDictContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 939 - self.match(HogQLParser.LBRACE) self.state = 941 + self.match(HogQLParser.LBRACE) + self.state = 943 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 940 + self.state = 942 self.kvPairList() - self.state = 943 + self.state = 945 self.match(HogQLParser.RBRACE) pass @@ -7304,7 +7310,7 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprLambdaContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 944 + self.state = 946 self.columnLambdaExpr() pass @@ -7312,13 +7318,13 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprIdentifierContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 945 + self.state = 947 self.columnIdentifier() pass self._ctx.stop = self._input.LT(-1) - self.state = 1058 + self.state = 1060 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,131,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: @@ -7326,36 +7332,36 @@ def columnExpr(self, _p:int=0): if self._parseListeners is not None: self.triggerExitRuleEvent() _prevctx = localctx - self.state = 1056 + self.state = 1058 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,130,self._ctx) if la_ == 1: localctx = HogQLParser.ColumnExprPrecedence1Context(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) localctx.left = _prevctx self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 948 + self.state = 950 if not self.precpred(self._ctx, 19): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 19)") - self.state = 952 + self.state = 954 self._errHandler.sync(self) token = self._input.LA(1) if token in [115]: - self.state = 949 + self.state = 951 localctx.operator = self.match(HogQLParser.ASTERISK) pass elif token in [154]: - self.state = 950 + self.state = 952 localctx.operator = self.match(HogQLParser.SLASH) pass elif token in [141]: - self.state = 951 + self.state = 953 localctx.operator = self.match(HogQLParser.PERCENT) pass else: raise NoViableAltException(self) - self.state = 954 + self.state = 956 localctx.right = self.columnExpr(20) pass @@ -7363,29 +7369,29 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprPrecedence2Context(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) localctx.left = _prevctx self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 955 + self.state = 957 if not self.precpred(self._ctx, 18): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 18)") - self.state = 959 + self.state = 961 self._errHandler.sync(self) token = self._input.LA(1) if token in [142]: - self.state = 956 + self.state = 958 localctx.operator = self.match(HogQLParser.PLUS) pass elif token in [121]: - self.state = 957 + self.state = 959 localctx.operator = self.match(HogQLParser.DASH) pass elif token in [120]: - self.state = 958 + self.state = 960 localctx.operator = self.match(HogQLParser.CONCAT) pass else: raise NoViableAltException(self) - self.state = 961 + self.state = 963 localctx.right = self.columnExpr(19) pass @@ -7393,79 +7399,79 @@ def columnExpr(self, _p:int=0): localctx = HogQLParser.ColumnExprPrecedence3Context(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) localctx.left = _prevctx self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 962 + self.state = 964 if not self.precpred(self._ctx, 17): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 17)") - self.state = 987 + self.state = 989 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,125,self._ctx) if la_ == 1: - self.state = 963 + self.state = 965 localctx.operator = self.match(HogQLParser.EQ_DOUBLE) pass elif la_ == 2: - self.state = 964 + self.state = 966 localctx.operator = self.match(HogQLParser.EQ_SINGLE) pass elif la_ == 3: - self.state = 965 + self.state = 967 localctx.operator = self.match(HogQLParser.NOT_EQ) pass elif la_ == 4: - self.state = 966 + self.state = 968 localctx.operator = self.match(HogQLParser.LT_EQ) pass elif la_ == 5: - self.state = 967 + self.state = 969 localctx.operator = self.match(HogQLParser.LT) pass elif la_ == 6: - self.state = 968 + self.state = 970 localctx.operator = self.match(HogQLParser.GT_EQ) pass elif la_ == 7: - self.state = 969 + self.state = 971 localctx.operator = self.match(HogQLParser.GT) pass elif la_ == 8: - self.state = 971 + self.state = 973 self._errHandler.sync(self) _la = self._input.LA(1) if _la==61: - self.state = 970 + self.state = 972 localctx.operator = self.match(HogQLParser.NOT) - self.state = 973 - self.match(HogQLParser.IN) self.state = 975 + self.match(HogQLParser.IN) + self.state = 977 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,123,self._ctx) if la_ == 1: - self.state = 974 + self.state = 976 self.match(HogQLParser.COHORT) pass elif la_ == 9: - self.state = 978 + self.state = 980 self._errHandler.sync(self) _la = self._input.LA(1) if _la==61: - self.state = 977 + self.state = 979 localctx.operator = self.match(HogQLParser.NOT) - self.state = 980 + self.state = 982 _la = self._input.LA(1) if not(_la==43 or _la==56): self._errHandler.recoverInline(self) @@ -7475,268 +7481,268 @@ def columnExpr(self, _p:int=0): pass elif la_ == 10: - self.state = 981 + self.state = 983 localctx.operator = self.match(HogQLParser.REGEX_SINGLE) pass elif la_ == 11: - self.state = 982 + self.state = 984 localctx.operator = self.match(HogQLParser.REGEX_DOUBLE) pass elif la_ == 12: - self.state = 983 + self.state = 985 localctx.operator = self.match(HogQLParser.NOT_REGEX) pass elif la_ == 13: - self.state = 984 + self.state = 986 localctx.operator = self.match(HogQLParser.IREGEX_SINGLE) pass elif la_ == 14: - self.state = 985 + self.state = 987 localctx.operator = self.match(HogQLParser.IREGEX_DOUBLE) pass elif la_ == 15: - self.state = 986 + self.state = 988 localctx.operator = self.match(HogQLParser.NOT_IREGEX) pass - self.state = 989 + self.state = 991 localctx.right = self.columnExpr(18) pass elif la_ == 4: localctx = HogQLParser.ColumnExprNullishContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 990 + self.state = 992 if not self.precpred(self._ctx, 15): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 15)") - self.state = 991 + self.state = 993 self.match(HogQLParser.NULLISH) - self.state = 992 + self.state = 994 self.columnExpr(16) pass elif la_ == 5: localctx = HogQLParser.ColumnExprAndContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 993 + self.state = 995 if not self.precpred(self._ctx, 13): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 13)") - self.state = 994 + self.state = 996 self.match(HogQLParser.AND) - self.state = 995 + self.state = 997 self.columnExpr(14) pass elif la_ == 6: localctx = HogQLParser.ColumnExprOrContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 996 + self.state = 998 if not self.precpred(self._ctx, 12): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 12)") - self.state = 997 + self.state = 999 self.match(HogQLParser.OR) - self.state = 998 + self.state = 1000 self.columnExpr(13) pass elif la_ == 7: localctx = HogQLParser.ColumnExprBetweenContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 999 + self.state = 1001 if not self.precpred(self._ctx, 11): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 11)") - self.state = 1001 + self.state = 1003 self._errHandler.sync(self) _la = self._input.LA(1) if _la==61: - self.state = 1000 + self.state = 1002 self.match(HogQLParser.NOT) - self.state = 1003 + self.state = 1005 self.match(HogQLParser.BETWEEN) - self.state = 1004 + self.state = 1006 self.columnExpr(0) - self.state = 1005 + self.state = 1007 self.match(HogQLParser.AND) - self.state = 1006 + self.state = 1008 self.columnExpr(12) pass elif la_ == 8: localctx = HogQLParser.ColumnExprTernaryOpContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1008 + self.state = 1010 if not self.precpred(self._ctx, 10): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 10)") - self.state = 1009 + self.state = 1011 self.match(HogQLParser.QUERY) - self.state = 1010 + self.state = 1012 self.columnExpr(0) - self.state = 1011 + self.state = 1013 self.match(HogQLParser.COLON) - self.state = 1012 + self.state = 1014 self.columnExpr(10) pass elif la_ == 9: localctx = HogQLParser.ColumnExprCallContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1014 + self.state = 1016 if not self.precpred(self._ctx, 30): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 30)") - self.state = 1015 - self.match(HogQLParser.LPAREN) self.state = 1017 + self.match(HogQLParser.LPAREN) + self.state = 1019 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 1016 + self.state = 1018 self.columnExprList() - self.state = 1019 + self.state = 1021 self.match(HogQLParser.RPAREN) pass elif la_ == 10: localctx = HogQLParser.ColumnExprArrayAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1020 + self.state = 1022 if not self.precpred(self._ctx, 26): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 26)") - self.state = 1021 + self.state = 1023 self.match(HogQLParser.LBRACKET) - self.state = 1022 + self.state = 1024 self.columnExpr(0) - self.state = 1023 + self.state = 1025 self.match(HogQLParser.RBRACKET) pass elif la_ == 11: localctx = HogQLParser.ColumnExprTupleAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1025 + self.state = 1027 if not self.precpred(self._ctx, 25): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 25)") - self.state = 1026 + self.state = 1028 self.match(HogQLParser.DOT) - self.state = 1027 + self.state = 1029 self.match(HogQLParser.DECIMAL_LITERAL) pass elif la_ == 12: localctx = HogQLParser.ColumnExprPropertyAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1028 + self.state = 1030 if not self.precpred(self._ctx, 24): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 24)") - self.state = 1029 + self.state = 1031 self.match(HogQLParser.DOT) - self.state = 1030 + self.state = 1032 self.identifier() pass elif la_ == 13: localctx = HogQLParser.ColumnExprNullArrayAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1031 + self.state = 1033 if not self.precpred(self._ctx, 23): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 23)") - self.state = 1032 + self.state = 1034 self.match(HogQLParser.NULL_PROPERTY) - self.state = 1033 + self.state = 1035 self.match(HogQLParser.LBRACKET) - self.state = 1034 + self.state = 1036 self.columnExpr(0) - self.state = 1035 + self.state = 1037 self.match(HogQLParser.RBRACKET) pass elif la_ == 14: localctx = HogQLParser.ColumnExprNullTupleAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1037 + self.state = 1039 if not self.precpred(self._ctx, 22): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 22)") - self.state = 1038 + self.state = 1040 self.match(HogQLParser.NULL_PROPERTY) - self.state = 1039 + self.state = 1041 self.match(HogQLParser.DECIMAL_LITERAL) pass elif la_ == 15: localctx = HogQLParser.ColumnExprNullPropertyAccessContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1040 + self.state = 1042 if not self.precpred(self._ctx, 21): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 21)") - self.state = 1041 + self.state = 1043 self.match(HogQLParser.NULL_PROPERTY) - self.state = 1042 + self.state = 1044 self.identifier() pass elif la_ == 16: localctx = HogQLParser.ColumnExprIsNullContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1043 + self.state = 1045 if not self.precpred(self._ctx, 16): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 16)") - self.state = 1044 - self.match(HogQLParser.IS) self.state = 1046 + self.match(HogQLParser.IS) + self.state = 1048 self._errHandler.sync(self) _la = self._input.LA(1) if _la==61: - self.state = 1045 + self.state = 1047 self.match(HogQLParser.NOT) - self.state = 1048 + self.state = 1050 self.match(HogQLParser.NULL_SQL) pass elif la_ == 17: localctx = HogQLParser.ColumnExprAliasContext(self, HogQLParser.ColumnExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_columnExpr) - self.state = 1049 + self.state = 1051 if not self.precpred(self._ctx, 9): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 9)") - self.state = 1054 + self.state = 1056 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,129,self._ctx) if la_ == 1: - self.state = 1050 + self.state = 1052 self.match(HogQLParser.AS) - self.state = 1051 + self.state = 1053 self.identifier() pass elif la_ == 2: - self.state = 1052 + self.state = 1054 self.match(HogQLParser.AS) - self.state = 1053 + self.state = 1055 self.match(HogQLParser.STRING_LITERAL) pass @@ -7744,7 +7750,7 @@ def columnExpr(self, _p:int=0): pass - self.state = 1060 + self.state = 1062 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,131,self._ctx) @@ -7813,85 +7819,85 @@ def columnLambdaExpr(self): self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1088 + self.state = 1090 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,136,self._ctx) if la_ == 1: - self.state = 1061 + self.state = 1063 self.match(HogQLParser.LPAREN) - self.state = 1062 + self.state = 1064 self.identifier() - self.state = 1067 + self.state = 1069 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,132,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1063 + self.state = 1065 self.match(HogQLParser.COMMA) - self.state = 1064 + self.state = 1066 self.identifier() - self.state = 1069 + self.state = 1071 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,132,self._ctx) - self.state = 1071 + self.state = 1073 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 1070 + self.state = 1072 self.match(HogQLParser.COMMA) - self.state = 1073 + self.state = 1075 self.match(HogQLParser.RPAREN) pass elif la_ == 2: - self.state = 1075 + self.state = 1077 self.identifier() - self.state = 1080 + self.state = 1082 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,134,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1076 + self.state = 1078 self.match(HogQLParser.COMMA) - self.state = 1077 + self.state = 1079 self.identifier() - self.state = 1082 + self.state = 1084 self._errHandler.sync(self) _alt = self._interp.adaptivePredict(self._input,134,self._ctx) - self.state = 1084 + self.state = 1086 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 1083 + self.state = 1085 self.match(HogQLParser.COMMA) pass elif la_ == 3: - self.state = 1086 + self.state = 1088 self.match(HogQLParser.LPAREN) - self.state = 1087 + self.state = 1089 self.match(HogQLParser.RPAREN) pass - self.state = 1090 + self.state = 1092 self.match(HogQLParser.ARROW) - self.state = 1093 + self.state = 1095 self._errHandler.sync(self) la_ = self._interp.adaptivePredict(self._input,137,self._ctx) if la_ == 1: - self.state = 1091 + self.state = 1093 self.columnExpr(0) pass elif la_ == 2: - self.state = 1092 + self.state = 1094 self.block() pass @@ -7905,6 +7911,73 @@ def columnLambdaExpr(self): return localctx + class HogqlxChildElementContext(ParserRuleContext): + __slots__ = 'parser' + + def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): + super().__init__(parent, invokingState) + self.parser = parser + + def hogqlxTagElement(self): + return self.getTypedRuleContext(HogQLParser.HogqlxTagElementContext,0) + + + def LBRACE(self): + return self.getToken(HogQLParser.LBRACE, 0) + + def columnExpr(self): + return self.getTypedRuleContext(HogQLParser.ColumnExprContext,0) + + + def RBRACE(self): + return self.getToken(HogQLParser.RBRACE, 0) + + def getRuleIndex(self): + return HogQLParser.RULE_hogqlxChildElement + + def accept(self, visitor:ParseTreeVisitor): + if hasattr( visitor, "visitHogqlxChildElement" ): + return visitor.visitHogqlxChildElement(self) + else: + return visitor.visitChildren(self) + + + + + def hogqlxChildElement(self): + + localctx = HogQLParser.HogqlxChildElementContext(self, self._ctx, self.state) + self.enterRule(localctx, 122, self.RULE_hogqlxChildElement) + try: + self.state = 1102 + self._errHandler.sync(self) + token = self._input.LA(1) + if token in [135]: + self.enterOuterAlt(localctx, 1) + self.state = 1097 + self.hogqlxTagElement() + pass + elif token in [131]: + self.enterOuterAlt(localctx, 2) + self.state = 1098 + self.match(HogQLParser.LBRACE) + self.state = 1099 + self.columnExpr(0) + self.state = 1100 + self.match(HogQLParser.RBRACE) + pass + else: + raise NoViableAltException(self) + + except RecognitionException as re: + localctx.exception = re + self._errHandler.reportError(self, re) + self._errHandler.recover(self, re) + finally: + self.exitRule() + return localctx + + class HogqlxTagElementContext(ParserRuleContext): __slots__ = 'parser' @@ -7981,16 +8054,12 @@ def hogqlxTagAttribute(self, i:int=None): else: return self.getTypedRuleContext(HogQLParser.HogqlxTagAttributeContext,i) - def hogqlxTagElement(self): - return self.getTypedRuleContext(HogQLParser.HogqlxTagElementContext,0) - - def LBRACE(self): - return self.getToken(HogQLParser.LBRACE, 0) - def columnExpr(self): - return self.getTypedRuleContext(HogQLParser.ColumnExprContext,0) + def hogqlxChildElement(self, i:int=None): + if i is None: + return self.getTypedRuleContexts(HogQLParser.HogqlxChildElementContext) + else: + return self.getTypedRuleContext(HogQLParser.HogqlxChildElementContext,i) - def RBRACE(self): - return self.getToken(HogQLParser.RBRACE, 0) def accept(self, visitor:ParseTreeVisitor): if hasattr( visitor, "visitHogqlxTagElementNested" ): @@ -8003,77 +8072,72 @@ def accept(self, visitor:ParseTreeVisitor): def hogqlxTagElement(self): localctx = HogQLParser.HogqlxTagElementContext(self, self._ctx, self.state) - self.enterRule(localctx, 122, self.RULE_hogqlxTagElement) + self.enterRule(localctx, 124, self.RULE_hogqlxTagElement) self._la = 0 # Token type try: - self.state = 1127 + self.state = 1135 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,141,self._ctx) + la_ = self._interp.adaptivePredict(self._input,142,self._ctx) if la_ == 1: localctx = HogQLParser.HogqlxTagElementClosedContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 1095 + self.state = 1104 self.match(HogQLParser.LT) - self.state = 1096 + self.state = 1105 self.identifier() - self.state = 1100 + self.state = 1109 self._errHandler.sync(self) _la = self._input.LA(1) while (((_la) & ~0x3f) == 0 and ((1 << _la) & -5800812384855539714) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 25834219896831) != 0): - self.state = 1097 + self.state = 1106 self.hogqlxTagAttribute() - self.state = 1102 + self.state = 1111 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 1103 + self.state = 1112 self.match(HogQLParser.SLASH) - self.state = 1104 + self.state = 1113 self.match(HogQLParser.GT) pass elif la_ == 2: localctx = HogQLParser.HogqlxTagElementNestedContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 1106 + self.state = 1115 self.match(HogQLParser.LT) - self.state = 1107 + self.state = 1116 self.identifier() - self.state = 1111 + self.state = 1120 self._errHandler.sync(self) _la = self._input.LA(1) while (((_la) & ~0x3f) == 0 and ((1 << _la) & -5800812384855539714) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 25834219896831) != 0): - self.state = 1108 + self.state = 1117 self.hogqlxTagAttribute() - self.state = 1113 + self.state = 1122 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 1114 + self.state = 1123 self.match(HogQLParser.GT) - self.state = 1120 + self.state = 1127 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,140,self._ctx) - if la_ == 1: - self.state = 1115 - self.hogqlxTagElement() - - elif la_ == 2: - self.state = 1116 - self.match(HogQLParser.LBRACE) - self.state = 1117 - self.columnExpr(0) - self.state = 1118 - self.match(HogQLParser.RBRACE) - + _alt = self._interp.adaptivePredict(self._input,141,self._ctx) + while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: + if _alt==1: + self.state = 1124 + self.hogqlxChildElement() + self.state = 1129 + self._errHandler.sync(self) + _alt = self._interp.adaptivePredict(self._input,141,self._ctx) - self.state = 1122 + self.state = 1130 self.match(HogQLParser.LT) - self.state = 1123 + self.state = 1131 self.match(HogQLParser.SLASH) - self.state = 1124 + self.state = 1132 self.identifier() - self.state = 1125 + self.state = 1133 self.match(HogQLParser.GT) pass @@ -8130,38 +8194,38 @@ def accept(self, visitor:ParseTreeVisitor): def hogqlxTagAttribute(self): localctx = HogQLParser.HogqlxTagAttributeContext(self, self._ctx, self.state) - self.enterRule(localctx, 124, self.RULE_hogqlxTagAttribute) + self.enterRule(localctx, 126, self.RULE_hogqlxTagAttribute) try: - self.state = 1140 + self.state = 1148 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,142,self._ctx) + la_ = self._interp.adaptivePredict(self._input,143,self._ctx) if la_ == 1: self.enterOuterAlt(localctx, 1) - self.state = 1129 + self.state = 1137 self.identifier() - self.state = 1130 + self.state = 1138 self.match(HogQLParser.EQ_SINGLE) - self.state = 1131 + self.state = 1139 self.string() pass elif la_ == 2: self.enterOuterAlt(localctx, 2) - self.state = 1133 + self.state = 1141 self.identifier() - self.state = 1134 + self.state = 1142 self.match(HogQLParser.EQ_SINGLE) - self.state = 1135 + self.state = 1143 self.match(HogQLParser.LBRACE) - self.state = 1136 + self.state = 1144 self.columnExpr(0) - self.state = 1137 + self.state = 1145 self.match(HogQLParser.RBRACE) pass elif la_ == 3: self.enterOuterAlt(localctx, 3) - self.state = 1139 + self.state = 1147 self.identifier() pass @@ -8210,30 +8274,30 @@ def accept(self, visitor:ParseTreeVisitor): def withExprList(self): localctx = HogQLParser.WithExprListContext(self, self._ctx, self.state) - self.enterRule(localctx, 126, self.RULE_withExprList) + self.enterRule(localctx, 128, self.RULE_withExprList) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1142 + self.state = 1150 self.withExpr() - self.state = 1147 + self.state = 1155 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,143,self._ctx) + _alt = self._interp.adaptivePredict(self._input,144,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1143 + self.state = 1151 self.match(HogQLParser.COMMA) - self.state = 1144 + self.state = 1152 self.withExpr() - self.state = 1149 + self.state = 1157 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,143,self._ctx) + _alt = self._interp.adaptivePredict(self._input,144,self._ctx) - self.state = 1151 + self.state = 1159 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 1150 + self.state = 1158 self.match(HogQLParser.COMMA) @@ -8315,34 +8379,34 @@ def accept(self, visitor:ParseTreeVisitor): def withExpr(self): localctx = HogQLParser.WithExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 128, self.RULE_withExpr) + self.enterRule(localctx, 130, self.RULE_withExpr) try: - self.state = 1163 + self.state = 1171 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,145,self._ctx) + la_ = self._interp.adaptivePredict(self._input,146,self._ctx) if la_ == 1: localctx = HogQLParser.WithExprSubqueryContext(self, localctx) self.enterOuterAlt(localctx, 1) - self.state = 1153 + self.state = 1161 self.identifier() - self.state = 1154 + self.state = 1162 self.match(HogQLParser.AS) - self.state = 1155 + self.state = 1163 self.match(HogQLParser.LPAREN) - self.state = 1156 + self.state = 1164 self.selectSetStmt() - self.state = 1157 + self.state = 1165 self.match(HogQLParser.RPAREN) pass elif la_ == 2: localctx = HogQLParser.WithExprColumnContext(self, localctx) self.enterOuterAlt(localctx, 2) - self.state = 1159 + self.state = 1167 self.columnExpr(0) - self.state = 1160 + self.state = 1168 self.match(HogQLParser.AS) - self.state = 1161 + self.state = 1169 self.identifier() pass @@ -8393,29 +8457,29 @@ def accept(self, visitor:ParseTreeVisitor): def columnIdentifier(self): localctx = HogQLParser.ColumnIdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 130, self.RULE_columnIdentifier) + self.enterRule(localctx, 132, self.RULE_columnIdentifier) try: - self.state = 1172 + self.state = 1180 self._errHandler.sync(self) token = self._input.LA(1) if token in [131]: self.enterOuterAlt(localctx, 1) - self.state = 1165 + self.state = 1173 self.placeholder() pass elif token in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 31, 33, 34, 35, 36, 38, 39, 40, 41, 42, 43, 44, 46, 48, 49, 50, 51, 52, 53, 54, 56, 57, 58, 59, 61, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 88, 89, 90, 91, 92, 93, 94, 95, 97, 98, 99, 100, 101, 102, 104, 105, 106, 108]: self.enterOuterAlt(localctx, 2) - self.state = 1169 + self.state = 1177 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,146,self._ctx) + la_ = self._interp.adaptivePredict(self._input,147,self._ctx) if la_ == 1: - self.state = 1166 + self.state = 1174 self.tableIdentifier() - self.state = 1167 + self.state = 1175 self.match(HogQLParser.DOT) - self.state = 1171 + self.state = 1179 self.nestedIdentifier() pass else: @@ -8465,23 +8529,23 @@ def accept(self, visitor:ParseTreeVisitor): def nestedIdentifier(self): localctx = HogQLParser.NestedIdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 132, self.RULE_nestedIdentifier) + self.enterRule(localctx, 134, self.RULE_nestedIdentifier) try: self.enterOuterAlt(localctx, 1) - self.state = 1174 + self.state = 1182 self.identifier() - self.state = 1179 + self.state = 1187 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,148,self._ctx) + _alt = self._interp.adaptivePredict(self._input,149,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1175 + self.state = 1183 self.match(HogQLParser.DOT) - self.state = 1176 + self.state = 1184 self.identifier() - self.state = 1181 + self.state = 1189 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,148,self._ctx) + _alt = self._interp.adaptivePredict(self._input,149,self._ctx) except RecognitionException as re: localctx.exception = re @@ -8628,19 +8692,19 @@ def tableExpr(self, _p:int=0): _parentState = self.state localctx = HogQLParser.TableExprContext(self, self._ctx, _parentState) _prevctx = localctx - _startState = 134 - self.enterRecursionRule(localctx, 134, self.RULE_tableExpr, _p) + _startState = 136 + self.enterRecursionRule(localctx, 136, self.RULE_tableExpr, _p) try: self.enterOuterAlt(localctx, 1) - self.state = 1191 + self.state = 1199 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,149,self._ctx) + la_ = self._interp.adaptivePredict(self._input,150,self._ctx) if la_ == 1: localctx = HogQLParser.TableExprIdentifierContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1183 + self.state = 1191 self.tableIdentifier() pass @@ -8648,7 +8712,7 @@ def tableExpr(self, _p:int=0): localctx = HogQLParser.TableExprFunctionContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1184 + self.state = 1192 self.tableFunctionExpr() pass @@ -8656,11 +8720,11 @@ def tableExpr(self, _p:int=0): localctx = HogQLParser.TableExprSubqueryContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1185 + self.state = 1193 self.match(HogQLParser.LPAREN) - self.state = 1186 + self.state = 1194 self.selectSetStmt() - self.state = 1187 + self.state = 1195 self.match(HogQLParser.RPAREN) pass @@ -8668,7 +8732,7 @@ def tableExpr(self, _p:int=0): localctx = HogQLParser.TableExprTagContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1189 + self.state = 1197 self.hogqlxTagElement() pass @@ -8676,15 +8740,15 @@ def tableExpr(self, _p:int=0): localctx = HogQLParser.TableExprPlaceholderContext(self, localctx) self._ctx = localctx _prevctx = localctx - self.state = 1190 + self.state = 1198 self.placeholder() pass self._ctx.stop = self._input.LT(-1) - self.state = 1201 + self.state = 1209 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,151,self._ctx) + _alt = self._interp.adaptivePredict(self._input,152,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: if self._parseListeners is not None: @@ -8692,29 +8756,29 @@ def tableExpr(self, _p:int=0): _prevctx = localctx localctx = HogQLParser.TableExprAliasContext(self, HogQLParser.TableExprContext(self, _parentctx, _parentState)) self.pushNewRecursionContext(localctx, _startState, self.RULE_tableExpr) - self.state = 1193 + self.state = 1201 if not self.precpred(self._ctx, 3): from antlr4.error.Errors import FailedPredicateException raise FailedPredicateException(self, "self.precpred(self._ctx, 3)") - self.state = 1197 + self.state = 1205 self._errHandler.sync(self) token = self._input.LA(1) if token in [20, 31, 41, 51, 108]: - self.state = 1194 + self.state = 1202 self.alias() pass elif token in [6]: - self.state = 1195 + self.state = 1203 self.match(HogQLParser.AS) - self.state = 1196 + self.state = 1204 self.identifier() pass else: raise NoViableAltException(self) - self.state = 1203 + self.state = 1211 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,151,self._ctx) + _alt = self._interp.adaptivePredict(self._input,152,self._ctx) except RecognitionException as re: localctx.exception = re @@ -8761,23 +8825,23 @@ def accept(self, visitor:ParseTreeVisitor): def tableFunctionExpr(self): localctx = HogQLParser.TableFunctionExprContext(self, self._ctx, self.state) - self.enterRule(localctx, 136, self.RULE_tableFunctionExpr) + self.enterRule(localctx, 138, self.RULE_tableFunctionExpr) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1204 + self.state = 1212 self.identifier() - self.state = 1205 + self.state = 1213 self.match(HogQLParser.LPAREN) - self.state = 1207 + self.state = 1215 self._errHandler.sync(self) _la = self._input.LA(1) if (((_la) & ~0x3f) == 0 and ((1 << _la) & -36169677449216002) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 723944289947615231) != 0) or ((((_la - 131)) & ~0x3f) == 0 and ((1 << (_la - 131)) & 18455) != 0): - self.state = 1206 + self.state = 1214 self.tableArgList() - self.state = 1209 + self.state = 1217 self.match(HogQLParser.RPAREN) except RecognitionException as re: localctx.exception = re @@ -8821,20 +8885,20 @@ def accept(self, visitor:ParseTreeVisitor): def tableIdentifier(self): localctx = HogQLParser.TableIdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 138, self.RULE_tableIdentifier) + self.enterRule(localctx, 140, self.RULE_tableIdentifier) try: self.enterOuterAlt(localctx, 1) - self.state = 1214 + self.state = 1222 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,153,self._ctx) + la_ = self._interp.adaptivePredict(self._input,154,self._ctx) if la_ == 1: - self.state = 1211 + self.state = 1219 self.databaseIdentifier() - self.state = 1212 + self.state = 1220 self.match(HogQLParser.DOT) - self.state = 1216 + self.state = 1224 self.identifier() except RecognitionException as re: localctx.exception = re @@ -8880,30 +8944,30 @@ def accept(self, visitor:ParseTreeVisitor): def tableArgList(self): localctx = HogQLParser.TableArgListContext(self, self._ctx, self.state) - self.enterRule(localctx, 140, self.RULE_tableArgList) + self.enterRule(localctx, 142, self.RULE_tableArgList) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1218 + self.state = 1226 self.columnExpr(0) - self.state = 1223 + self.state = 1231 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,154,self._ctx) + _alt = self._interp.adaptivePredict(self._input,155,self._ctx) while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: if _alt==1: - self.state = 1219 + self.state = 1227 self.match(HogQLParser.COMMA) - self.state = 1220 + self.state = 1228 self.columnExpr(0) - self.state = 1225 + self.state = 1233 self._errHandler.sync(self) - _alt = self._interp.adaptivePredict(self._input,154,self._ctx) + _alt = self._interp.adaptivePredict(self._input,155,self._ctx) - self.state = 1227 + self.state = 1235 self._errHandler.sync(self) _la = self._input.LA(1) if _la==119: - self.state = 1226 + self.state = 1234 self.match(HogQLParser.COMMA) @@ -8942,10 +9006,10 @@ def accept(self, visitor:ParseTreeVisitor): def databaseIdentifier(self): localctx = HogQLParser.DatabaseIdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 142, self.RULE_databaseIdentifier) + self.enterRule(localctx, 144, self.RULE_databaseIdentifier) try: self.enterOuterAlt(localctx, 1) - self.state = 1229 + self.state = 1237 self.identifier() except RecognitionException as re: localctx.exception = re @@ -8993,22 +9057,22 @@ def accept(self, visitor:ParseTreeVisitor): def floatingLiteral(self): localctx = HogQLParser.FloatingLiteralContext(self, self._ctx, self.state) - self.enterRule(localctx, 144, self.RULE_floatingLiteral) + self.enterRule(localctx, 146, self.RULE_floatingLiteral) self._la = 0 # Token type try: - self.state = 1239 + self.state = 1247 self._errHandler.sync(self) token = self._input.LA(1) if token in [109]: self.enterOuterAlt(localctx, 1) - self.state = 1231 + self.state = 1239 self.match(HogQLParser.FLOATING_LITERAL) pass elif token in [123]: self.enterOuterAlt(localctx, 2) - self.state = 1232 + self.state = 1240 self.match(HogQLParser.DOT) - self.state = 1233 + self.state = 1241 _la = self._input.LA(1) if not(_la==110 or _la==111): self._errHandler.recoverInline(self) @@ -9018,15 +9082,15 @@ def floatingLiteral(self): pass elif token in [111]: self.enterOuterAlt(localctx, 3) - self.state = 1234 + self.state = 1242 self.match(HogQLParser.DECIMAL_LITERAL) - self.state = 1235 + self.state = 1243 self.match(HogQLParser.DOT) - self.state = 1237 + self.state = 1245 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,156,self._ctx) + la_ = self._interp.adaptivePredict(self._input,157,self._ctx) if la_ == 1: - self.state = 1236 + self.state = 1244 _la = self._input.LA(1) if not(_la==110 or _la==111): self._errHandler.recoverInline(self) @@ -9095,15 +9159,15 @@ def accept(self, visitor:ParseTreeVisitor): def numberLiteral(self): localctx = HogQLParser.NumberLiteralContext(self, self._ctx, self.state) - self.enterRule(localctx, 146, self.RULE_numberLiteral) + self.enterRule(localctx, 148, self.RULE_numberLiteral) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1242 + self.state = 1250 self._errHandler.sync(self) _la = self._input.LA(1) if _la==121 or _la==142: - self.state = 1241 + self.state = 1249 _la = self._input.LA(1) if not(_la==121 or _la==142): self._errHandler.recoverInline(self) @@ -9112,36 +9176,36 @@ def numberLiteral(self): self.consume() - self.state = 1250 + self.state = 1258 self._errHandler.sync(self) - la_ = self._interp.adaptivePredict(self._input,159,self._ctx) + la_ = self._interp.adaptivePredict(self._input,160,self._ctx) if la_ == 1: - self.state = 1244 + self.state = 1252 self.floatingLiteral() pass elif la_ == 2: - self.state = 1245 + self.state = 1253 self.match(HogQLParser.OCTAL_LITERAL) pass elif la_ == 3: - self.state = 1246 + self.state = 1254 self.match(HogQLParser.DECIMAL_LITERAL) pass elif la_ == 4: - self.state = 1247 + self.state = 1255 self.match(HogQLParser.HEXADECIMAL_LITERAL) pass elif la_ == 5: - self.state = 1248 + self.state = 1256 self.match(HogQLParser.INF) pass elif la_ == 6: - self.state = 1249 + self.state = 1257 self.match(HogQLParser.NAN_SQL) pass @@ -9187,24 +9251,24 @@ def accept(self, visitor:ParseTreeVisitor): def literal(self): localctx = HogQLParser.LiteralContext(self, self._ctx, self.state) - self.enterRule(localctx, 148, self.RULE_literal) + self.enterRule(localctx, 150, self.RULE_literal) try: - self.state = 1255 + self.state = 1263 self._errHandler.sync(self) token = self._input.LA(1) if token in [45, 60, 109, 110, 111, 112, 121, 123, 142]: self.enterOuterAlt(localctx, 1) - self.state = 1252 + self.state = 1260 self.numberLiteral() pass elif token in [113]: self.enterOuterAlt(localctx, 2) - self.state = 1253 + self.state = 1261 self.match(HogQLParser.STRING_LITERAL) pass elif token in [62]: self.enterOuterAlt(localctx, 3) - self.state = 1254 + self.state = 1262 self.match(HogQLParser.NULL_SQL) pass else: @@ -9265,11 +9329,11 @@ def accept(self, visitor:ParseTreeVisitor): def interval(self): localctx = HogQLParser.IntervalContext(self, self._ctx, self.state) - self.enterRule(localctx, 150, self.RULE_interval) + self.enterRule(localctx, 152, self.RULE_interval) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1257 + self.state = 1265 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 864692227968860160) != 0) or ((((_la - 73)) & ~0x3f) == 0 and ((1 << (_la - 73)) & 8724152577) != 0)): self._errHandler.recoverInline(self) @@ -9562,11 +9626,11 @@ def accept(self, visitor:ParseTreeVisitor): def keyword(self): localctx = HogQLParser.KeywordContext(self, self._ctx, self.state) - self.enterRule(localctx, 152, self.RULE_keyword) + self.enterRule(localctx, 154, self.RULE_keyword) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1259 + self.state = 1267 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & -6665504612824399874) != 0) or ((((_la - 64)) & ~0x3f) == 0 and ((1 << (_la - 64)) & 3775267732991) != 0)): self._errHandler.recoverInline(self) @@ -9616,11 +9680,11 @@ def accept(self, visitor:ParseTreeVisitor): def keywordForAlias(self): localctx = HogQLParser.KeywordForAliasContext(self, self._ctx, self.state) - self.enterRule(localctx, 154, self.RULE_keywordForAlias) + self.enterRule(localctx, 156, self.RULE_keywordForAlias) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1261 + self.state = 1269 _la = self._input.LA(1) if not((((_la) & ~0x3f) == 0 and ((1 << _la) & 2254000985473024) != 0)): self._errHandler.recoverInline(self) @@ -9665,19 +9729,19 @@ def accept(self, visitor:ParseTreeVisitor): def alias(self): localctx = HogQLParser.AliasContext(self, self._ctx, self.state) - self.enterRule(localctx, 156, self.RULE_alias) + self.enterRule(localctx, 158, self.RULE_alias) try: - self.state = 1265 + self.state = 1273 self._errHandler.sync(self) token = self._input.LA(1) if token in [108]: self.enterOuterAlt(localctx, 1) - self.state = 1263 + self.state = 1271 self.match(HogQLParser.IDENTIFIER) pass elif token in [20, 31, 41, 51]: self.enterOuterAlt(localctx, 2) - self.state = 1264 + self.state = 1272 self.keywordForAlias() pass else: @@ -9725,24 +9789,24 @@ def accept(self, visitor:ParseTreeVisitor): def identifier(self): localctx = HogQLParser.IdentifierContext(self, self._ctx, self.state) - self.enterRule(localctx, 158, self.RULE_identifier) + self.enterRule(localctx, 160, self.RULE_identifier) try: - self.state = 1270 + self.state = 1278 self._errHandler.sync(self) token = self._input.LA(1) if token in [108]: self.enterOuterAlt(localctx, 1) - self.state = 1267 + self.state = 1275 self.match(HogQLParser.IDENTIFIER) pass elif token in [21, 40, 58, 59, 73, 81, 100, 106]: self.enterOuterAlt(localctx, 2) - self.state = 1268 + self.state = 1276 self.interval() pass elif token in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 24, 25, 26, 28, 29, 31, 33, 34, 35, 36, 38, 39, 41, 42, 43, 44, 46, 48, 49, 50, 51, 52, 53, 54, 56, 57, 61, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 80, 82, 83, 84, 85, 86, 88, 89, 90, 91, 92, 93, 94, 95, 97, 98, 99, 101, 102, 104, 105]: self.enterOuterAlt(localctx, 3) - self.state = 1269 + self.state = 1277 self.keyword() pass else: @@ -9790,14 +9854,14 @@ def accept(self, visitor:ParseTreeVisitor): def enumValue(self): localctx = HogQLParser.EnumValueContext(self, self._ctx, self.state) - self.enterRule(localctx, 160, self.RULE_enumValue) + self.enterRule(localctx, 162, self.RULE_enumValue) try: self.enterOuterAlt(localctx, 1) - self.state = 1272 + self.state = 1280 self.string() - self.state = 1273 + self.state = 1281 self.match(HogQLParser.EQ_SINGLE) - self.state = 1274 + self.state = 1282 self.numberLiteral() except RecognitionException as re: localctx.exception = re @@ -9840,14 +9904,14 @@ def accept(self, visitor:ParseTreeVisitor): def placeholder(self): localctx = HogQLParser.PlaceholderContext(self, self._ctx, self.state) - self.enterRule(localctx, 162, self.RULE_placeholder) + self.enterRule(localctx, 164, self.RULE_placeholder) try: self.enterOuterAlt(localctx, 1) - self.state = 1276 + self.state = 1284 self.match(HogQLParser.LBRACE) - self.state = 1277 + self.state = 1285 self.columnExpr(0) - self.state = 1278 + self.state = 1286 self.match(HogQLParser.RBRACE) except RecognitionException as re: localctx.exception = re @@ -9887,19 +9951,19 @@ def accept(self, visitor:ParseTreeVisitor): def string(self): localctx = HogQLParser.StringContext(self, self._ctx, self.state) - self.enterRule(localctx, 164, self.RULE_string) + self.enterRule(localctx, 166, self.RULE_string) try: - self.state = 1282 + self.state = 1290 self._errHandler.sync(self) token = self._input.LA(1) if token in [113]: self.enterOuterAlt(localctx, 1) - self.state = 1280 + self.state = 1288 self.match(HogQLParser.STRING_LITERAL) pass elif token in [145]: self.enterOuterAlt(localctx, 2) - self.state = 1281 + self.state = 1289 self.templateString() pass else: @@ -9949,23 +10013,23 @@ def accept(self, visitor:ParseTreeVisitor): def templateString(self): localctx = HogQLParser.TemplateStringContext(self, self._ctx, self.state) - self.enterRule(localctx, 166, self.RULE_templateString) + self.enterRule(localctx, 168, self.RULE_templateString) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1284 + self.state = 1292 self.match(HogQLParser.QUOTE_SINGLE_TEMPLATE) - self.state = 1288 + self.state = 1296 self._errHandler.sync(self) _la = self._input.LA(1) while _la==159 or _la==160: - self.state = 1285 + self.state = 1293 self.stringContents() - self.state = 1290 + self.state = 1298 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 1291 + self.state = 1299 self.match(HogQLParser.QUOTE_SINGLE) except RecognitionException as re: localctx.exception = re @@ -10011,23 +10075,23 @@ def accept(self, visitor:ParseTreeVisitor): def stringContents(self): localctx = HogQLParser.StringContentsContext(self, self._ctx, self.state) - self.enterRule(localctx, 168, self.RULE_stringContents) + self.enterRule(localctx, 170, self.RULE_stringContents) try: - self.state = 1298 + self.state = 1306 self._errHandler.sync(self) token = self._input.LA(1) if token in [160]: self.enterOuterAlt(localctx, 1) - self.state = 1293 + self.state = 1301 self.match(HogQLParser.STRING_ESCAPE_TRIGGER) - self.state = 1294 + self.state = 1302 self.columnExpr(0) - self.state = 1295 + self.state = 1303 self.match(HogQLParser.RBRACE) pass elif token in [159]: self.enterOuterAlt(localctx, 2) - self.state = 1297 + self.state = 1305 self.match(HogQLParser.STRING_TEXT) pass else: @@ -10077,23 +10141,23 @@ def accept(self, visitor:ParseTreeVisitor): def fullTemplateString(self): localctx = HogQLParser.FullTemplateStringContext(self, self._ctx, self.state) - self.enterRule(localctx, 170, self.RULE_fullTemplateString) + self.enterRule(localctx, 172, self.RULE_fullTemplateString) self._la = 0 # Token type try: self.enterOuterAlt(localctx, 1) - self.state = 1300 + self.state = 1308 self.match(HogQLParser.QUOTE_SINGLE_TEMPLATE_FULL) - self.state = 1304 + self.state = 1312 self._errHandler.sync(self) _la = self._input.LA(1) while _la==161 or _la==162: - self.state = 1301 + self.state = 1309 self.stringContentsFull() - self.state = 1306 + self.state = 1314 self._errHandler.sync(self) _la = self._input.LA(1) - self.state = 1307 + self.state = 1315 self.match(HogQLParser.EOF) except RecognitionException as re: localctx.exception = re @@ -10139,23 +10203,23 @@ def accept(self, visitor:ParseTreeVisitor): def stringContentsFull(self): localctx = HogQLParser.StringContentsFullContext(self, self._ctx, self.state) - self.enterRule(localctx, 172, self.RULE_stringContentsFull) + self.enterRule(localctx, 174, self.RULE_stringContentsFull) try: - self.state = 1314 + self.state = 1322 self._errHandler.sync(self) token = self._input.LA(1) if token in [162]: self.enterOuterAlt(localctx, 1) - self.state = 1309 + self.state = 1317 self.match(HogQLParser.FULL_STRING_ESCAPE_TRIGGER) - self.state = 1310 + self.state = 1318 self.columnExpr(0) - self.state = 1311 + self.state = 1319 self.match(HogQLParser.RBRACE) pass elif token in [161]: self.enterOuterAlt(localctx, 2) - self.state = 1313 + self.state = 1321 self.match(HogQLParser.FULL_STRING_TEXT) pass else: @@ -10176,7 +10240,7 @@ def sempred(self, localctx:RuleContext, ruleIndex:int, predIndex:int): self._predicates = dict() self._predicates[40] = self.joinExpr_sempred self._predicates[59] = self.columnExpr_sempred - self._predicates[67] = self.tableExpr_sempred + self._predicates[68] = self.tableExpr_sempred pred = self._predicates.get(ruleIndex, None) if pred is None: raise Exception("No predicate with index:" + str(ruleIndex)) diff --git a/posthog/hogql/grammar/HogQLParserVisitor.py b/posthog/hogql/grammar/HogQLParserVisitor.py index 5d18341de87e6..58c3b4d2863d4 100644 --- a/posthog/hogql/grammar/HogQLParserVisitor.py +++ b/posthog/hogql/grammar/HogQLParserVisitor.py @@ -564,6 +564,11 @@ def visitColumnLambdaExpr(self, ctx:HogQLParser.ColumnLambdaExprContext): return self.visitChildren(ctx) + # Visit a parse tree produced by HogQLParser#hogqlxChildElement. + def visitHogqlxChildElement(self, ctx:HogQLParser.HogqlxChildElementContext): + return self.visitChildren(ctx) + + # Visit a parse tree produced by HogQLParser#HogqlxTagElementClosed. def visitHogqlxTagElementClosed(self, ctx:HogQLParser.HogqlxTagElementClosedContext): return self.visitChildren(ctx) diff --git a/posthog/hogql/parser.py b/posthog/hogql/parser.py index e58b76d1a5029..bebf6b9147a23 100644 --- a/posthog/hogql/parser.py +++ b/posthog/hogql/parser.py @@ -1131,6 +1131,9 @@ def visitColumnExprCall(self, ctx: HogQLParser.ColumnExprCallContext): expr=self.visit(ctx.columnExpr()), args=self.visit(ctx.columnExprList()) if ctx.columnExprList() else [] ) + def visitHogqlxChildElement(self, ctx: HogQLParser.HogqlxChildElementContext): + return self.visit(ctx.hogqlxTagElement() or ctx.columnExpr()) + def visitHogqlxTagElementClosed(self, ctx: HogQLParser.HogqlxTagElementClosedContext): kind = self.visit(ctx.identifier()) attributes = [self.visit(a) for a in ctx.hogqlxTagAttribute()] if ctx.hogqlxTagAttribute() else [] @@ -1143,18 +1146,15 @@ def visitHogqlxTagElementNested(self, ctx: HogQLParser.HogqlxTagElementNestedCon raise SyntaxError(f"Opening and closing HogQLX tags must match. Got {opening} and {closing}") attributes = [self.visit(a) for a in ctx.hogqlxTagAttribute()] if ctx.hogqlxTagAttribute() else [] - if ctx.hogqlxTagElement(): - source = self.visit(ctx.hogqlxTagElement()) - for a in attributes: - if a.name == "source": - raise SyntaxError(f"Nested HogQLX tags cannot have a source attribute") - attributes.append(ast.HogQLXAttribute(name="source", value=source)) - if ctx.columnExpr(): - source = self.visit(ctx.columnExpr()) + + if ctx.hogqlxChildElement(): for a in attributes: - if a.name == "source": - raise SyntaxError(f"Nested HogQLX tags cannot have a source attribute") - attributes.append(ast.HogQLXAttribute(name="source", value=source)) + if a.name == "children": + raise SyntaxError("Can't have a HogQLX tag with both children and a 'children' attribute") + children = [] + for element in ctx.hogqlxChildElement(): + children.append(self.visit(element)) + attributes.append(ast.HogQLXAttribute(name="children", value=children)) return ast.HogQLXTag(kind=opening, attributes=attributes) def visitHogqlxTagAttribute(self, ctx: HogQLParser.HogqlxTagAttributeContext): diff --git a/posthog/hogql/printer.py b/posthog/hogql/printer.py index ead58d83bf082..ee1bed311c068 100644 --- a/posthog/hogql/printer.py +++ b/posthog/hogql/printer.py @@ -1498,6 +1498,46 @@ def visit_window_frame_expr(self, node: ast.WindowFrameExpr): else: raise ImpossibleASTError(f"Invalid frame type {node.frame_type}") + def visit_hogqlx_tag(self, node: ast.HogQLXTag): + if self.dialect != "hogql": + raise QueryError("Printing HogQLX tags is only supported in HogQL queries") + + attributes = [] + children = [] + for attribute in node.attributes: + if isinstance(attribute, ast.HogQLXAttribute) and attribute.name == "children": + if isinstance(attribute.value, list): + children.extend(attribute.value) + else: + children.append(attribute.value) + else: + attributes.append(attribute) + + tag = f"<{self._print_identifier(node.kind)}" + if attributes: + tag += " " + (" ".join(self.visit(a) for a in attributes)) + if children: + children_contents = [ + self.visit(child) if isinstance(child, ast.HogQLXTag) else "{" + self.visit(child) + "}" + for child in children + ] + tag += ">" + ("".join(children_contents)) + "" + else: + tag += " />" + + return tag + + def visit_hogqlx_attribute(self, node: ast.HogQLXAttribute): + if self.dialect != "hogql": + raise QueryError("Printing HogQLX tags is only supported in HogQL queries") + if isinstance(node.value, ast.HogQLXTag): + value = self.visit(node.value) + elif isinstance(node.value, list): + value = "{[" + (", ".join(self.visit(x) for x in node.value)) + "]}" + else: + value = "{" + self.visit(node.value) + "}" + return f"{self._print_identifier(node.name)}={value}" + def _last_select(self) -> Optional[ast.SelectQuery]: """Find the last SELECT query in the stack.""" for node in reversed(self.stack): diff --git a/posthog/hogql/resolver_utils.py b/posthog/hogql/resolver_utils.py index 6ed44a9643d2a..a1d1f2be9f940 100644 --- a/posthog/hogql/resolver_utils.py +++ b/posthog/hogql/resolver_utils.py @@ -66,8 +66,11 @@ def ast_to_query_node(expr: ast.Expr | ast.HogQLXTag): if isinstance(klass, type) and issubclass(klass, schema.BaseModel) and klass.__name__ == expr.kind: attributes = expr.to_dict() attributes.pop("kind") - attributes = {key: ast_to_query_node(value) for key, value in attributes.items()} - return klass(**attributes) + # Query runners use "source" instead of "children" for their source query + if "children" in attributes and "source" in klass.model_fields: + attributes["source"] = attributes.pop("children")[0] + new_attributes = {key: ast_to_query_node(value) for key, value in attributes.items()} + return klass(**new_attributes) raise SyntaxError(f'Tag of kind "{expr.kind}" not found in schema.') else: raise SyntaxError(f'Expression of type "{type(expr).__name__}". Can\'t convert to constant.') diff --git a/posthog/hogql/test/_test_parser.py b/posthog/hogql/test/_test_parser.py index 9db5fc260b623..bd80b166da3f8 100644 --- a/posthog/hogql/test/_test_parser.py +++ b/posthog/hogql/test/_test_parser.py @@ -1769,13 +1769,17 @@ def test_visit_hogqlx_tag_nested(self): kind="OuterQuery", attributes=[ ast.HogQLXAttribute( - name="source", - value=ast.HogQLXTag( - kind="HogQLQuery", - attributes=[ - ast.HogQLXAttribute(name="query", value=ast.Constant(value="select event from events")) - ], - ), + name="children", + value=[ + ast.HogQLXTag( + kind="HogQLQuery", + attributes=[ + ast.HogQLXAttribute( + name="query", value=ast.Constant(value="select event from events") + ) + ], + ) + ], ) ], ) @@ -1795,13 +1799,17 @@ def test_visit_hogqlx_tag_nested(self): attributes=[ ast.HogQLXAttribute(name="q", value=ast.Constant(value="b")), ast.HogQLXAttribute( - name="source", - value=ast.HogQLXTag( - kind="HogQLQuery", - attributes=[ - ast.HogQLXAttribute(name="query", value=ast.Constant(value="select event from events")) - ], - ), + name="children", + value=[ + ast.HogQLXTag( + kind="HogQLQuery", + attributes=[ + ast.HogQLXAttribute( + name="query", value=ast.Constant(value="select event from events") + ) + ], + ) + ], ), ], ) @@ -1816,9 +1824,9 @@ def test_visit_hogqlx_tag_nested(self): # With mismatched closing tag with self.assertRaises(ExposedHogQLError) as e: self._select( - "select event from " + "select event from " ) - assert str(e.exception) == "Nested HogQLX tags cannot have a source attribute" + assert str(e.exception) == "Can't have a HogQLX tag with both children and a 'children' attribute" def test_visit_hogqlx_tag_alias(self): node = self._select("select event from as a") @@ -1878,7 +1886,31 @@ def test_visit_hogqlx_tag_column_source(self): kind="a", attributes=[ ast.HogQLXAttribute(name="href", value=Constant(value="https://google.com")), - ast.HogQLXAttribute(name="source", value=ast.Field(chain=["event"])), + ast.HogQLXAttribute(name="children", value=[ast.Field(chain=["event"])]), + ], + ) + + def test_visit_hogqlx_multiple_children(self): + query = """ + select {event}{'Bold!'} from events + """ + node = self._select(query) + assert isinstance(node, ast.SelectQuery) and cast(ast.HogQLXTag, node.select[0]) == ast.HogQLXTag( + kind="a", + attributes=[ + ast.HogQLXAttribute(name="href", value=Constant(value="https://google.com")), + ast.HogQLXAttribute( + name="children", + value=[ + ast.Field(chain=["event"]), + ast.HogQLXTag( + kind="b", + attributes=[ + ast.HogQLXAttribute(name="children", value=[ast.Constant(value="Bold!")]), + ], + ), + ], + ), ], ) diff --git a/posthog/hogql/visitor.py b/posthog/hogql/visitor.py index 6b763b1d0812c..3ebee8bec1c1f 100644 --- a/posthog/hogql/visitor.py +++ b/posthog/hogql/visitor.py @@ -283,7 +283,11 @@ def visit_hogqlx_tag(self, node: ast.HogQLXTag): self.visit(attribute) def visit_hogqlx_attribute(self, node: ast.HogQLXAttribute): - self.visit(node.value) + if isinstance(node.value, list): + for value in node.value: + self.visit(value) + else: + self.visit(node.value) def visit_program(self, node: ast.Program): for expr in node.declarations: @@ -654,6 +658,8 @@ def visit_hogqlx_tag(self, node: ast.HogQLXTag): return ast.HogQLXTag(kind=node.kind, attributes=[self.visit(a) for a in node.attributes]) def visit_hogqlx_attribute(self, node: ast.HogQLXAttribute): + if isinstance(node.value, list): + return ast.HogQLXAttribute(name=node.name, value=[self.visit(v) for v in node.value]) return ast.HogQLXAttribute(name=node.name, value=self.visit(node.value)) def visit_program(self, node: ast.Program): diff --git a/posthog/hogql_queries/ai/test/__snapshots__/test_traces_query_runner.ambr b/posthog/hogql_queries/ai/test/__snapshots__/test_traces_query_runner.ambr index 5ff34e6cb3808..68b436a03b792 100644 --- a/posthog/hogql_queries/ai/test/__snapshots__/test_traces_query_runner.ambr +++ b/posthog/hogql_queries/ai/test/__snapshots__/test_traces_query_runner.ambr @@ -1,38 +1,63 @@ # serializer version: 1 # name: TestTracesQueryRunner.test_field_mapping ''' - SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, - min(toTimeZone(events.timestamp, 'UTC')) AS trace_timestamp, - tuple(max(events__person.id), max(events.distinct_id), max(events__person.created_at), max(events__person.properties)) AS first_person, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, - arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events - 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) + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + 1 AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))))) + GROUP BY id) AS generations LEFT JOIN - (SELECT person.id AS id, - toTimeZone(person.created_at, 'UTC') AS created_at, - person.properties AS properties - 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), equals(events.event, '$ai_generation'), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC')))) - GROUP BY id - ORDER BY trace_timestamp DESC + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + 1 AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + ORDER BY first_timestamp DESC LIMIT 101 OFFSET 0 SETTINGS readonly=2, max_execution_time=60, @@ -45,38 +70,63 @@ # --- # name: TestTracesQueryRunner.test_pagination ''' - SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, - min(toTimeZone(events.timestamp, 'UTC')) AS trace_timestamp, - tuple(max(events__person.id), max(events.distinct_id), max(events__person.created_at), max(events__person.properties)) AS first_person, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, - arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events - 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) + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + 1 AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))))) + GROUP BY id) AS generations LEFT JOIN - (SELECT person.id AS id, - toTimeZone(person.created_at, 'UTC') AS created_at, - person.properties AS properties - 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), equals(events.event, '$ai_generation'), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC')))) - GROUP BY id - ORDER BY trace_timestamp DESC + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + 1 AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + ORDER BY first_timestamp DESC LIMIT 5 OFFSET 0 SETTINGS readonly=2, max_execution_time=60, @@ -89,38 +139,63 @@ # --- # name: TestTracesQueryRunner.test_pagination.1 ''' - SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, - min(toTimeZone(events.timestamp, 'UTC')) AS trace_timestamp, - tuple(max(events__person.id), max(events.distinct_id), max(events__person.created_at), max(events__person.properties)) AS first_person, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, - arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events - 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) + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + 1 AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))))) + GROUP BY id) AS generations LEFT JOIN - (SELECT person.id AS id, - toTimeZone(person.created_at, 'UTC') AS created_at, - person.properties AS properties - 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), equals(events.event, '$ai_generation'), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC')))) - GROUP BY id - ORDER BY trace_timestamp DESC + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + 1 AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + ORDER BY first_timestamp DESC LIMIT 5 OFFSET 5 SETTINGS readonly=2, max_execution_time=60, @@ -133,38 +208,63 @@ # --- # name: TestTracesQueryRunner.test_pagination.2 ''' - SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, - min(toTimeZone(events.timestamp, 'UTC')) AS trace_timestamp, - tuple(max(events__person.id), max(events.distinct_id), max(events__person.created_at), max(events__person.properties)) AS first_person, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, - arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events - 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) + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + 1 AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))))) + GROUP BY id) AS generations LEFT JOIN - (SELECT person.id AS id, - toTimeZone(person.created_at, 'UTC') AS created_at, - person.properties AS properties - 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), equals(events.event, '$ai_generation'), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC')))) - GROUP BY id - ORDER BY trace_timestamp DESC + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + 1 AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + ORDER BY first_timestamp DESC LIMIT 5 OFFSET 10 SETTINGS readonly=2, max_execution_time=60, @@ -175,40 +275,485 @@ max_bytes_before_external_group_by=0 ''' # --- +# name: TestTracesQueryRunner.test_properties_filter_with_multiple_events_in_group + ''' + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0)) AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS generations + LEFT JOIN + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0)) AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + HAVING ifNull(equals(filter_match, 1), 0) + ORDER BY first_timestamp DESC + LIMIT 101 + 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: TestTracesQueryRunner.test_properties_filter_with_multiple_events_in_group.1 + ''' + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'baz'), 0)) AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS generations + LEFT JOIN + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'baz'), 0)) AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + HAVING ifNull(equals(filter_match, 1), 0) + ORDER BY first_timestamp DESC + LIMIT 101 + 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: TestTracesQueryRunner.test_properties_filter_with_multiple_events_in_group.2 + ''' + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'barz'), 0)) AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS generations + LEFT JOIN + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'barz'), 0)) AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + HAVING ifNull(equals(filter_match, 1), 0) + ORDER BY first_timestamp DESC + LIMIT 101 + 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: TestTracesQueryRunner.test_trace_id_filter ''' - SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, - min(toTimeZone(events.timestamp, 'UTC')) AS trace_timestamp, - tuple(max(events__person.id), max(events.distinct_id), max(events__person.created_at), max(events__person.properties)) AS first_person, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, - sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, - round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, - arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events - 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) + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + 1 AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))), ifNull(equals(id, 'trace1'), 0))) + GROUP BY id) AS generations + LEFT JOIN + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + 1 AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))), ifNull(equals(id, 'trace1'), 0))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + ORDER BY first_timestamp DESC + LIMIT 101 + 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: TestTracesQueryRunner.test_trace_property_filter_for_event_group + ''' + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), 'runnable'), 0)) AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS generations + LEFT JOIN + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), 'runnable'), 0)) AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + HAVING ifNull(equals(filter_match, 1), 0) + ORDER BY first_timestamp DESC + LIMIT 101 + 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: TestTracesQueryRunner.test_trace_property_filter_for_event_group.1 + ''' + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0)) AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS generations + LEFT JOIN + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + max(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0)) AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + HAVING ifNull(equals(filter_match, 1), 0) + ORDER BY first_timestamp DESC + LIMIT 101 + 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: TestTracesQueryRunner.test_trace_property_filter_for_event_group.2 + ''' + SELECT generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + or(generations.filter_match, traces.filter_match) AS filter_match + FROM + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + min(toTimeZone(events.timestamp, 'UTC')) AS first_timestamp, + tuple(argMin(events__person.id, toTimeZone(events.timestamp, 'UTC')), argMin(events.distinct_id, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.created_at, toTimeZone(events.timestamp, 'UTC')), argMin(events__person.properties, toTimeZone(events.timestamp, 'UTC'))) AS first_person, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_latency'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 2) AS total_latency, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS input_tokens, + sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_tokens'), ''), 'null'), '^"|"$', ''), 'Float64')) AS output_tokens, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS input_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS output_cost, + round(accurateCastOrNull(sum(accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_total_cost_usd'), ''), 'null'), '^"|"$', ''), 'Float64')), 'Float64'), 4) AS total_cost, + arraySort(x -> x.3, groupArray(tuple(events.uuid, events.event, toTimeZone(events.timestamp, 'UTC'), events.properties))) AS events, + max(and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), 'runnable'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0))) AS filter_match + 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, + toTimeZone(person.created_at, 'UTC') AS created_at, + person.properties AS properties + 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), in(events.event, tuple('$ai_generation', '$ai_metric', '$ai_feedback')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS generations LEFT JOIN - (SELECT person.id AS id, - toTimeZone(person.created_at, 'UTC') AS created_at, - person.properties AS properties - 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), equals(events.event, '$ai_generation'), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-08 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2025-01-16 00:10:59', 6, 'UTC'))), ifNull(equals(id, 'trace1'), 0)) - GROUP BY id - ORDER BY trace_timestamp DESC + (SELECT replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_id'), ''), 'null'), '^"|"$', '') AS id, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_input_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS input_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_output_state'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS output_state, + argMin(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), toTimeZone(events.timestamp, 'UTC')) AS trace_name, + max(and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$ai_trace_name'), ''), 'null'), '^"|"$', ''), 'runnable'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, 'foo'), ''), 'null'), '^"|"$', ''), 'bar'), 0))) AS filter_match + FROM events + WHERE and(equals(events.team_id, 99999), equals(events.event, '$ai_trace'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-11-30 23:50:00', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2024-12-01 00:20:00', 6, 'UTC'))))) + GROUP BY id) AS traces ON equals(traces.id, generations.id) + HAVING ifNull(equals(filter_match, 1), 0) + ORDER BY first_timestamp DESC LIMIT 101 OFFSET 0 SETTINGS readonly=2, max_execution_time=60, diff --git a/posthog/hogql_queries/ai/test/test_traces_query_runner.py b/posthog/hogql_queries/ai/test/test_traces_query_runner.py index 030e6fba387bb..53b44eb690077 100644 --- a/posthog/hogql_queries/ai/test/test_traces_query_runner.py +++ b/posthog/hogql_queries/ai/test/test_traces_query_runner.py @@ -45,6 +45,7 @@ def _calculate_tokens(messages: str | list[InputMessage] | list[OutputMessage]) def _create_ai_generation_event( + *, input: str | list[InputMessage] = "Foo", output: str | list[OutputMessage] = "Bar", team: Team | None = None, @@ -91,6 +92,37 @@ def _create_ai_generation_event( ) +def _create_ai_trace_event( + *, + trace_id: str, + trace_name: str, + input_state: Any, + output_state: Any, + team: Team | None = None, + distinct_id: str | None = None, + properties: dict[str, Any] | None = None, + timestamp: datetime | None = None, + event_uuid: str | UUID | None = None, +): + props = { + "$ai_trace_id": trace_id, + "$ai_trace_name": trace_name, + "$ai_input_state": input_state, + "$ai_output_state": output_state, + } + if properties: + props.update(properties) + + _create_event( + event="$ai_trace", + distinct_id=distinct_id, + properties=props, + team=team, + timestamp=timestamp, + event_uuid=str(event_uuid) if event_uuid else None, + ) + + class TestTracesQueryRunner(ClickhouseTestMixin, BaseTest): def setUp(self): super().setUp() @@ -167,6 +199,8 @@ def test_field_mapping(self): "id": "trace1", "createdAt": datetime(2025, 1, 15, 0, tzinfo=UTC).isoformat(), "totalLatency": 2.0, + "inputState": None, + "outputState": None, "inputTokens": 6.0, "outputTokens": 6.0, "inputCost": 6.0, @@ -519,3 +553,151 @@ def test_model_parameters(self): self.assertEqual(len(response.results), 1) self.assertEqual(response.results[0].id, "trace1") self.assertEqual(response.results[0].events[0].properties["$ai_model_parameters"], {"temperature": 0.5}) + + def test_full_trace(self): + _create_person(distinct_ids=["person1"], team=self.team, properties={"foo": "bar"}) + _create_ai_generation_event( + distinct_id="person1", + trace_id="trace1", + team=self.team, + timestamp=datetime(2024, 12, 1, 0, 0), + ) + _create_ai_generation_event( + distinct_id="person1", + trace_id="trace1", + team=self.team, + timestamp=datetime(2024, 12, 1, 0, 10), + ) + _create_ai_trace_event( + trace_id="trace1", + trace_name="runnable", + input_state={"messages": [{"role": "user", "content": "Foo"}]}, + output_state={"messages": [{"role": "user", "content": "Foo"}, {"role": "assistant", "content": "Bar"}]}, + team=self.team, + timestamp=datetime(2024, 12, 1, 0, 11), + ) + + response = TracesQueryRunner( + team=self.team, + query=TracesQuery( + dateRange=DateRange(date_from="2024-12-01T00:00:00Z", date_to="2024-12-01T00:10:00Z"), + ), + ).calculate() + self.assertEqual(len(response.results), 1) + self.assertEqual(response.results[0].id, "trace1") + self.assertEqual(response.results[0].traceName, "runnable") + self.assertEqual(response.results[0].inputState, {"messages": [{"role": "user", "content": "Foo"}]}) + self.assertEqual( + response.results[0].outputState, + {"messages": [{"role": "user", "content": "Foo"}, {"role": "assistant", "content": "Bar"}]}, + ) + self.assertEqual(len(response.results[0].events), 2) + self.assertEqual(response.results[0].events[0].properties["$ai_trace_id"], "trace1") + self.assertEqual(response.results[0].events[1].properties["$ai_trace_id"], "trace1") + + @snapshot_clickhouse_queries + def test_properties_filter_with_multiple_events_in_group(self): + _create_person(distinct_ids=["person1"], team=self.team) + _create_ai_generation_event( + distinct_id="person1", + trace_id="trace1", + team=self.team, + timestamp=datetime(2024, 12, 1, 0, 0), + properties={"foo": "bar"}, + ) + _create_ai_generation_event( + distinct_id="person1", + trace_id="trace1", + team=self.team, + timestamp=datetime(2024, 12, 1, 0, 0), + properties={"foo": "baz"}, + ) + + response = TracesQueryRunner( + team=self.team, + query=TracesQuery( + properties=[EventPropertyFilter(key="foo", value="bar", operator=PropertyOperator.EXACT)], + dateRange=DateRange(date_from="2024-12-01T00:00:00Z", date_to="2024-12-01T00:10:00Z"), + ), + ).calculate() + self.assertEqual(len(response.results), 1) + self.assertEqual(len(response.results[0].events), 2) + + response = TracesQueryRunner( + team=self.team, + query=TracesQuery( + properties=[EventPropertyFilter(key="foo", value="baz", operator=PropertyOperator.EXACT)], + dateRange=DateRange(date_from="2024-12-01T00:00:00Z", date_to="2024-12-01T00:10:00Z"), + ), + ).calculate() + self.assertEqual(len(response.results), 1) + self.assertEqual(len(response.results[0].events), 2) + + response = TracesQueryRunner( + team=self.team, + query=TracesQuery( + properties=[EventPropertyFilter(key="foo", value="barz", operator=PropertyOperator.EXACT)], + dateRange=DateRange(date_from="2024-12-01T00:00:00Z", date_to="2024-12-01T00:10:00Z"), + ), + ).calculate() + self.assertEqual(len(response.results), 0) + + @snapshot_clickhouse_queries + def test_trace_property_filter_for_event_group(self): + _create_person(distinct_ids=["person1"], team=self.team) + _create_ai_generation_event( + distinct_id="person1", + trace_id="trace1", + team=self.team, + timestamp=datetime(2024, 12, 1, 0, 0), + ) + _create_ai_generation_event( + distinct_id="person1", + trace_id="trace1", + team=self.team, + timestamp=datetime(2024, 12, 1, 0, 2), + properties={"foo": "bar"}, + ) + _create_ai_trace_event( + trace_id="trace1", + trace_name="runnable", + input_state={"messages": [{"role": "user", "content": "Foo"}]}, + output_state={"messages": [{"role": "user", "content": "Foo"}, {"role": "assistant", "content": "Bar"}]}, + team=self.team, + timestamp=datetime(2024, 12, 1, 0, 5), + ) + + response = TracesQueryRunner( + team=self.team, + query=TracesQuery( + properties=[ + EventPropertyFilter(key="$ai_trace_name", value="runnable", operator=PropertyOperator.EXACT) + ], + dateRange=DateRange(date_from="2024-12-01T00:00:00Z", date_to="2024-12-01T00:10:00Z"), + ), + ).calculate() + self.assertEqual(len(response.results), 1) + self.assertEqual(len(response.results[0].events), 2) + + response = TracesQueryRunner( + team=self.team, + query=TracesQuery( + properties=[EventPropertyFilter(key="foo", value="bar", operator=PropertyOperator.EXACT)], + dateRange=DateRange(date_from="2024-12-01T00:00:00Z", date_to="2024-12-01T00:10:00Z"), + ), + ).calculate() + self.assertEqual(len(response.results), 1) + self.assertEqual(len(response.results[0].events), 2) + + # Shouldn't return anything because there isn't such trace + response = TracesQueryRunner( + team=self.team, + query=TracesQuery( + properties=[ + EventPropertyFilter(key="$ai_trace_name", value="runnable", operator=PropertyOperator.EXACT), + EventPropertyFilter(key="foo", value="bar", operator=PropertyOperator.EXACT), + ], + dateRange=DateRange(date_from="2024-12-01T00:00:00Z", date_to="2024-12-01T00:10:00Z"), + ), + ).calculate() + self.assertEqual(len(response.results), 0) diff --git a/posthog/hogql_queries/ai/traces_query_runner.py b/posthog/hogql_queries/ai/traces_query_runner.py index 66201f18dd6be..0cccb0ebe6943 100644 --- a/posthog/hogql_queries/ai/traces_query_runner.py +++ b/posthog/hogql_queries/ai/traces_query_runner.py @@ -64,13 +64,20 @@ def __init__(self, *args, **kwargs): def to_query(self) -> ast.SelectQuery: query = self._get_event_query() - query.where = self._get_where_clause() + if self.query.properties: + query.having = ast.CompareOperation( + left=ast.Field(chain=["filter_match"]), op=ast.CompareOperationOp.Eq, right=ast.Constant(value=1) + ) return query def calculate(self): with self.timings.measure("traces_query_hogql_execute"): query_result = self.paginator.execute_hogql_query( query=self.to_query(), + placeholders={ + "common_conditions": self._get_where_clause(), + "filter_conditions": self._get_properties_filter(), + }, team=self.team, query_type=NodeKind.TRACES_QUERY, timings=self.timings, @@ -108,7 +115,7 @@ def _map_results(self, columns: list[str], query_results: list): for result in mapped_results: # Exclude traces that are outside of the capture range. - timestamp_dt = cast(datetime, result["trace_timestamp"]) + timestamp_dt = cast(datetime, result["first_timestamp"]) if ( timestamp_dt < self._date_range.date_from_for_filtering() or timestamp_dt > self._date_range.date_to_for_filtering() @@ -125,12 +132,15 @@ def _map_trace(self, result: dict[str, Any], created_at: datetime) -> LLMTrace: "created_at": "createdAt", "person": "person", "total_latency": "totalLatency", + "input_state_parsed": "inputState", + "output_state_parsed": "outputState", "input_tokens": "inputTokens", "output_tokens": "outputTokens", "input_cost": "inputCost", "output_cost": "outputCost", "total_cost": "totalCost", "events": "events", + "trace_name": "traceName", } generations = [] @@ -143,6 +153,14 @@ def _map_trace(self, result: dict[str, Any], created_at: datetime) -> LLMTrace: "person": self._map_person(result["first_person"]), "events": generations, } + try: + trace_dict["input_state_parsed"] = orjson.loads(trace_dict["input_state"]) + except (TypeError, orjson.JSONDecodeError): + pass + try: + trace_dict["output_state_parsed"] = orjson.loads(trace_dict["output_state"]) + except (TypeError, orjson.JSONDecodeError): + pass # Remap keys from snake case to camel case trace = LLMTrace.model_validate( {TRACE_FIELDS_MAPPING[key]: value for key, value in trace_dict.items() if key in TRACE_FIELDS_MAPPING} @@ -172,52 +190,80 @@ def _map_person(self, person: tuple[UUID, UUID, datetime, str]) -> LLMTracePerso def _get_event_query(self) -> ast.SelectQuery: query = parse_select( """ + SELECT + generations.id AS id, + generations.first_timestamp AS first_timestamp, + generations.first_person AS first_person, + generations.total_latency AS total_latency, + generations.input_tokens AS input_tokens, + generations.output_tokens AS output_tokens, + generations.input_cost AS input_cost, + generations.output_cost AS output_cost, + generations.total_cost AS total_cost, + generations.events AS events, + traces.input_state AS input_state, + traces.output_state AS output_state, + traces.trace_name AS trace_name, + generations.filter_match OR traces.filter_match AS filter_match + FROM ( SELECT properties.$ai_trace_id as id, - min(timestamp) as trace_timestamp, - tuple(max(person.id), max(distinct_id), max(person.created_at), max(person.properties)) as first_person, + min(timestamp) as first_timestamp, + tuple( + argMin(person.id, timestamp), argMin(distinct_id, timestamp), + argMin(person.created_at, timestamp), argMin(person.properties, timestamp) + ) as first_person, round(toFloat(sum(properties.$ai_latency)), 2) as total_latency, sum(properties.$ai_input_tokens) as input_tokens, sum(properties.$ai_output_tokens) as output_tokens, round(toFloat(sum(properties.$ai_input_cost_usd)), 4) as input_cost, round(toFloat(sum(properties.$ai_output_cost_usd)), 4) as output_cost, round(toFloat(sum(properties.$ai_total_cost_usd)), 4) as total_cost, - arraySort(x -> x.3, groupArray(tuple(uuid, event, timestamp, properties))) as events - FROM - events - GROUP BY - id - ORDER BY - trace_timestamp DESC - """ + arraySort(x -> x.3, groupArray(tuple(uuid, event, timestamp, properties))) as events, + {filter_conditions} + FROM events + WHERE event IN ('$ai_generation', '$ai_metric', '$ai_feedback') AND {common_conditions} + GROUP BY id + ) AS generations + LEFT JOIN ( + SELECT + properties.$ai_trace_id as id, + argMin(properties.$ai_input_state, timestamp) as input_state, + argMin(properties.$ai_output_state, timestamp) as output_state, + argMin(properties.$ai_trace_name, timestamp) as trace_name, + {filter_conditions} + FROM events + WHERE event = '$ai_trace' AND {common_conditions} + GROUP BY id -- In case there are multiple trace events for the same trace ID, an unexpected but possible case + ) AS traces + ON traces.id = generations.id + ORDER BY first_timestamp DESC + """, ) return cast(ast.SelectQuery, query) - def _get_where_clause(self): - timestamp_field = ast.Field(chain=["events", "timestamp"]) + def _get_properties_filter(self): + expr: ast.Expr = ast.Constant(value=1) + if self.query.properties: + with self.timings.measure("property_filters"): + filter = ast.And(exprs=[property_to_expr(property, self.team) for property in self.query.properties]) + expr = ast.Call(name="max", args=[filter]) + return ast.Alias(alias="filter_match", expr=expr) + def _get_where_clause(self): where_exprs: list[ast.Expr] = [ - ast.CompareOperation( - left=ast.Field(chain=["event"]), - op=ast.CompareOperationOp.Eq, - right=ast.Constant(value="$ai_generation"), - ), ast.CompareOperation( op=ast.CompareOperationOp.GtEq, - left=timestamp_field, + left=ast.Field(chain=["events", "timestamp"]), right=self._date_range.date_from_as_hogql(), ), ast.CompareOperation( op=ast.CompareOperationOp.LtEq, - left=timestamp_field, + left=ast.Field(chain=["events", "timestamp"]), right=self._date_range.date_to_as_hogql(), ), ] - if self.query.properties: - with self.timings.measure("property_filters"): - where_exprs.extend(property_to_expr(property, self.team) for property in self.query.properties) - if self.query.filterTestAccounts: with self.timings.measure("test_account_filters"): for prop in self.team.test_account_filters or []: diff --git a/posthog/hogql_queries/experiments/experiment_trends_query_runner.py b/posthog/hogql_queries/experiments/experiment_trends_query_runner.py index 291047e53aed1..bf9059f671b1c 100644 --- a/posthog/hogql_queries/experiments/experiment_trends_query_runner.py +++ b/posthog/hogql_queries/experiments/experiment_trends_query_runner.py @@ -4,6 +4,7 @@ from posthog.constants import ExperimentNoResultsErrorKeys from posthog.hogql import ast from posthog.hogql_queries.experiments import CONTROL_VARIANT_KEY +from posthog.hogql_queries.experiments.types import ExperimentMetricType from posthog.hogql_queries.experiments.trends_statistics import ( are_results_significant, calculate_credible_intervals, @@ -69,6 +70,8 @@ def __init__(self, *args, **kwargs): self.stats_version = self.experiment.get_stats_config("version") or 1 + self._fix_math_aggregation() + self.prepared_count_query = self._prepare_count_query() self.prepared_exposure_query = self._prepare_exposure_query() @@ -86,6 +89,14 @@ def _uses_math_aggregation_by_user_or_property_value(self, query: TrendsQuery): math_keys.remove("sum") return any(entity.math in math_keys for entity in query.series) + def _fix_math_aggregation(self): + """ + Switch unsupported math aggregations to SUM + """ + uses_math_aggregation = self._uses_math_aggregation_by_user_or_property_value(self.query.count_query) + if uses_math_aggregation: + self.query.count_query.series[0].math = PropertyMathType.SUM + def _get_date_range(self) -> DateRange: """ Returns an DateRange object based on the experiment's start and end dates, @@ -117,6 +128,14 @@ def _get_data_warehouse_breakdown_filter(self) -> BreakdownFilter: breakdown_type="data_warehouse", ) + def _get_metric_type(self) -> ExperimentMetricType: + # Currently, we rely on the math type to determine the metric type + match self.query.count_query.series[0].math: + case PropertyMathType.SUM | "hogql": + return ExperimentMetricType.CONTINUOUS + case _: + return ExperimentMetricType.COUNT + def _prepare_count_query(self) -> TrendsQuery: """ This method takes the raw trend query and adapts it @@ -129,13 +148,6 @@ def _prepare_count_query(self) -> TrendsQuery: """ prepared_count_query = TrendsQuery(**self.query.count_query.model_dump()) - uses_math_aggregation = self._uses_math_aggregation_by_user_or_property_value(prepared_count_query) - - # Only SUM is supported now, but some earlier experiments AVG. That does not - # make sense as input for experiment analysis, so we'll swithc that to SUM here - if uses_math_aggregation: - prepared_count_query.series[0].math = PropertyMathType.SUM - prepared_count_query.trendsFilter = TrendsFilter(display=ChartDisplayType.ACTIONS_LINE_GRAPH_CUMULATIVE) prepared_count_query.dateRange = self._get_date_range() if self._is_data_warehouse_query(prepared_count_query): @@ -270,18 +282,21 @@ def run(query_runner: TrendsQueryRunner, result_key: str, is_parallel: bool): # Statistical analysis control_variant, test_variants = self._get_variants_with_base_stats(count_result, exposure_result) if self.stats_version == 2: - if self.query.count_query.series[0].math: - probabilities = calculate_probabilities_v2_continuous(control_variant, test_variants) - significance_code, p_value = are_results_significant_v2_continuous( - control_variant, test_variants, probabilities - ) - credible_intervals = calculate_credible_intervals_v2_continuous([control_variant, *test_variants]) - else: - probabilities = calculate_probabilities_v2_count(control_variant, test_variants) - significance_code, p_value = are_results_significant_v2_count( - control_variant, test_variants, probabilities - ) - credible_intervals = calculate_credible_intervals_v2_count([control_variant, *test_variants]) + match self._get_metric_type(): + case ExperimentMetricType.CONTINUOUS: + probabilities = calculate_probabilities_v2_continuous(control_variant, test_variants) + significance_code, p_value = are_results_significant_v2_continuous( + control_variant, test_variants, probabilities + ) + credible_intervals = calculate_credible_intervals_v2_continuous([control_variant, *test_variants]) + case ExperimentMetricType.COUNT: + probabilities = calculate_probabilities_v2_count(control_variant, test_variants) + significance_code, p_value = are_results_significant_v2_count( + control_variant, test_variants, probabilities + ) + credible_intervals = calculate_credible_intervals_v2_count([control_variant, *test_variants]) + case _: + raise ValueError(f"Unsupported metric type: {self._get_metric_type()}") else: probabilities = calculate_probabilities(control_variant, test_variants) significance_code, p_value = are_results_significant(control_variant, test_variants, probabilities) diff --git a/posthog/hogql_queries/experiments/test/test_experiment_trends_query_runner.py b/posthog/hogql_queries/experiments/test/test_experiment_trends_query_runner.py index d665ce227011d..6c8062b969167 100644 --- a/posthog/hogql_queries/experiments/test/test_experiment_trends_query_runner.py +++ b/posthog/hogql_queries/experiments/test/test_experiment_trends_query_runner.py @@ -1,15 +1,18 @@ from django.test import override_settings from ee.clickhouse.materialized_columns.columns import get_enabled_materialized_columns, materialize from posthog.hogql_queries.experiments.experiment_trends_query_runner import ExperimentTrendsQueryRunner +from posthog.hogql_queries.experiments.types import ExperimentMetricType from posthog.models.experiment import Experiment, ExperimentHoldout from posthog.models.feature_flag.feature_flag import FeatureFlag from posthog.schema import ( + BaseMathType, DataWarehouseNode, EventsNode, ExperimentSignificanceCode, ExperimentTrendsQuery, ExperimentTrendsQueryResponse, PersonsOnEventsMode, + PropertyMathType, TrendsQuery, ) from posthog.settings import ( @@ -27,7 +30,7 @@ flush_persons_and_events, ) from freezegun import freeze_time -from typing import cast +from typing import cast, Any from django.utils import timezone from datetime import datetime, timedelta from posthog.test.test_journeys import journeys_for @@ -2363,3 +2366,47 @@ def test_validate_event_variants_no_exposure(self): } ) self.assertEqual(cast(list, context.exception.detail)[0], expected_errors) + + def test_get_metric_type(self): + feature_flag = self.create_feature_flag() + experiment = self.create_experiment(feature_flag=feature_flag) + + # Test allowed count math types + allowed_count_math_types = [BaseMathType.TOTAL, BaseMathType.DAU, BaseMathType.UNIQUE_SESSION, None] + for math_type in allowed_count_math_types: + count_query = TrendsQuery(series=[EventsNode(event="$pageview", math=math_type)]) + experiment_query = ExperimentTrendsQuery( + experiment_id=experiment.id, + kind="ExperimentTrendsQuery", + count_query=count_query, + ) + query_runner = ExperimentTrendsQueryRunner(query=experiment_query, team=self.team) + self.assertEqual(query_runner._get_metric_type(), ExperimentMetricType.COUNT) + + # Test allowed sum math types + allowed_sum_math_types: list[Any] = [PropertyMathType.SUM, "hogql"] + for math_type in allowed_sum_math_types: + count_query = TrendsQuery( + series=[EventsNode(event="checkout completed", math=math_type, math_property="revenue")] + ) + experiment_query = ExperimentTrendsQuery( + experiment_id=experiment.id, + kind="ExperimentTrendsQuery", + count_query=count_query, + ) + query_runner = ExperimentTrendsQueryRunner(query=experiment_query, team=self.team) + self.assertEqual(query_runner._get_metric_type(), ExperimentMetricType.CONTINUOUS) + + # Test that AVG math gets converted to SUM and returns CONTINUOUS + count_query = TrendsQuery( + series=[EventsNode(event="checkout completed", math=PropertyMathType.AVG, math_property="revenue")] + ) + experiment_query = ExperimentTrendsQuery( + experiment_id=experiment.id, + kind="ExperimentTrendsQuery", + count_query=count_query, + ) + query_runner = ExperimentTrendsQueryRunner(query=experiment_query, team=self.team) + self.assertEqual(query_runner._get_metric_type(), ExperimentMetricType.CONTINUOUS) + # Verify the math type was converted to sum + self.assertEqual(query_runner.query.count_query.series[0].math, PropertyMathType.SUM) diff --git a/posthog/hogql_queries/experiments/test/test_trends_statistics_continuous.py b/posthog/hogql_queries/experiments/test/test_trends_statistics_continuous.py index d1185abd73c80..b3da850d35c57 100644 --- a/posthog/hogql_queries/experiments/test/test_trends_statistics_continuous.py +++ b/posthog/hogql_queries/experiments/test/test_trends_statistics_continuous.py @@ -14,9 +14,13 @@ from flaky import flaky -def create_variant(key: str, mean: float, exposure: float, absolute_exposure: int) -> ExperimentVariantTrendsBaseStats: - # Note: We use the count field to store the mean value for continuous metrics - return ExperimentVariantTrendsBaseStats(key=key, count=mean, exposure=exposure, absolute_exposure=absolute_exposure) +def create_variant( + key: str, total_sum: float, exposure: float, absolute_exposure: int +) -> ExperimentVariantTrendsBaseStats: + # Note: We use the count field to store the total sum for continuous metrics + return ExperimentVariantTrendsBaseStats( + key=key, count=total_sum, exposure=exposure, absolute_exposure=absolute_exposure + ) class TestExperimentTrendsStatisticsContinuous(APIBaseTest): @@ -45,11 +49,18 @@ def test_small_sample_two_variants_not_significant(self): def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): control_absolute_exposure = 100 - control = create_variant("control", mean=100.0, exposure=1, absolute_exposure=control_absolute_exposure) + control_mean = 100.0 + control = create_variant( + "control", + total_sum=control_mean * control_absolute_exposure, + exposure=1, + absolute_exposure=control_absolute_exposure, + ) test_absolute_exposure = 100 + test_mean = 105.0 test = create_variant( "test", - mean=105.0, + total_sum=test_mean * test_absolute_exposure, exposure=test_absolute_exposure / control_absolute_exposure, absolute_exposure=test_absolute_exposure, ) @@ -73,17 +84,18 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertAlmostEqual(intervals["test"][0], 80, delta=5) # Lower bound self.assertAlmostEqual(intervals["test"][1], 120, delta=5) # Upper bound else: - # Original implementation behavior for small sample - self.assertAlmostEqual(probabilities[0], 0.5, delta=0.2) - self.assertAlmostEqual(probabilities[1], 0.5, delta=0.2) - self.assertEqual(significance, ExperimentSignificanceCode.LOW_WIN_PROBABILITY) - self.assertEqual(p_value, 1) + self.assertAlmostEqual(probabilities[0], 0.0, delta=0.005) + self.assertAlmostEqual(probabilities[1], 1.0, delta=0.005) + self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) + self.assertAlmostEqual(p_value, 0.00049, delta=0.00001) - # Original implementation returns intervals as ratios/multipliers of the mean - self.assertAlmostEqual(intervals["control"][0], 1.0, delta=0.2) # Lower bound is less than mean - self.assertAlmostEqual(intervals["control"][1], 1.2, delta=0.1) # Upper bound is greater than mean - self.assertAlmostEqual(intervals["test"][0], 1.0, delta=0.2) - self.assertAlmostEqual(intervals["test"][1], 1.2, delta=0.1) + # Control: ~$100 mean with narrow interval due to old implementation + self.assertAlmostEqual(intervals["control"][0], 98, delta=3) + self.assertAlmostEqual(intervals["control"][1], 102, delta=3) + + # Test: ~$105 mean with narrow interval due to old implementation + self.assertAlmostEqual(intervals["test"][0], 103, delta=3) + self.assertAlmostEqual(intervals["test"][1], 107, delta=3) self.run_test_for_both_implementations(run_test) @@ -93,11 +105,18 @@ def test_large_sample_two_variants_significant(self): def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): control_absolute_exposure = 10000 - control = create_variant("control", mean=100.0, exposure=1, absolute_exposure=control_absolute_exposure) + control_mean = 100.0 + control = create_variant( + "control", + total_sum=control_mean * control_absolute_exposure, + exposure=1, + absolute_exposure=control_absolute_exposure, + ) test_absolute_exposure = 10000 + test_mean = 120.0 test = create_variant( "test", - mean=120.0, + total_sum=test_mean * test_absolute_exposure, exposure=test_absolute_exposure / control_absolute_exposure, absolute_exposure=test_absolute_exposure, ) @@ -121,19 +140,18 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertAlmostEqual(intervals["test"][0], 116, delta=2) # Lower bound self.assertAlmostEqual(intervals["test"][1], 122, delta=2) # Upper bound else: - # Original implementation behavior for large sample - self.assertAlmostEqual(probabilities[1], 0.75, delta=0.25) - self.assertAlmostEqual(probabilities[0], 0.25, delta=0.25) - self.assertTrue( - significance in [ExperimentSignificanceCode.HIGH_P_VALUE, ExperimentSignificanceCode.SIGNIFICANT] - ) - self.assertAlmostEqual(p_value, 0.15, delta=0.15) + self.assertAlmostEqual(probabilities[1], 1.0, delta=0.025) + self.assertAlmostEqual(probabilities[0], 0.0, delta=0.025) + self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) + self.assertEqual(p_value, 0) - # Original implementation returns intervals as ratios/multipliers of the mean - self.assertAlmostEqual(intervals["control"][0], 0.05, delta=0.05) # Lower bound less than mean - self.assertAlmostEqual(intervals["control"][1], 0.015, delta=0.005) # Upper bound greater than mean - self.assertAlmostEqual(intervals["test"][0], 0.05, delta=0.05) # Lower bound less than mean - self.assertAlmostEqual(intervals["test"][1], 0.015, delta=0.005) # Upper bound greater than mean + # Control: $100 mean with narrow interval due to large sample + self.assertAlmostEqual(intervals["control"][0], 99.8, delta=2) + self.assertAlmostEqual(intervals["control"][1], 100.2, delta=2) + + # Test: $120 mean with narrow interval due to large sample + self.assertAlmostEqual(intervals["test"][0], 119, delta=2) + self.assertAlmostEqual(intervals["test"][1], 121, delta=2) self.run_test_for_both_implementations(run_test) @@ -143,11 +161,18 @@ def test_large_sample_two_variants_strongly_significant(self): def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): control_absolute_exposure = 10000 - control = create_variant("control", mean=100.0, exposure=1, absolute_exposure=control_absolute_exposure) + control_mean = 100.0 + control = create_variant( + "control", + total_sum=control_mean * control_absolute_exposure, + exposure=1, + absolute_exposure=control_absolute_exposure, + ) test_absolute_exposure = 10000 + test_mean = 150.0 test = create_variant( "test", - mean=150.0, + total_sum=test_mean * test_absolute_exposure, exposure=test_absolute_exposure / control_absolute_exposure, absolute_exposure=test_absolute_exposure, ) @@ -164,22 +189,25 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertEqual(p_value, 0) # Control: $100 mean - self.assertAlmostEqual(intervals["control"][0], 97, delta=2) # Lower bound - self.assertAlmostEqual(intervals["control"][1], 103, delta=2) # Upper bound + self.assertAlmostEqual(intervals["control"][0], 99.8, delta=2) + self.assertAlmostEqual(intervals["control"][1], 100.2, delta=2) # Test: $150 mean, clearly higher than control - self.assertAlmostEqual(intervals["test"][0], 146, delta=3) # Lower bound - self.assertAlmostEqual(intervals["test"][1], 154, delta=3) # Upper bound + self.assertAlmostEqual(intervals["test"][0], 146, delta=3) + self.assertAlmostEqual(intervals["test"][1], 154, delta=3) else: - # Original implementation behavior for strongly significant case - self.assertTrue(probabilities[1] > 0.5) # Test variant winning - self.assertTrue(probabilities[0] < 0.5) # Control variant losing + self.assertAlmostEqual(probabilities[1], 1.0, delta=0.005) + self.assertAlmostEqual(probabilities[0], 0.0, delta=0.005) self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) - self.assertLess(p_value, 0.05) + self.assertEqual(p_value, 0) - # Original implementation returns intervals as ratios/multipliers of the mean - # For strongly significant differences, the intervals should not overlap when scaled - self.assertTrue(intervals["control"][1] * 100 < intervals["test"][0] * 150) + # Control: $100 mean + self.assertAlmostEqual(intervals["control"][0], 99.8, delta=2) + self.assertAlmostEqual(intervals["control"][1], 100.2, delta=2) + + # Test: $150 mean, clearly higher than control + self.assertAlmostEqual(intervals["test"][0], 149, delta=3) + self.assertAlmostEqual(intervals["test"][1], 151, delta=3) self.run_test_for_both_implementations(run_test) @@ -189,25 +217,34 @@ def test_many_variants_not_significant(self): def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): control_absolute_exposure = 1000 - control = create_variant("control", mean=100.0, exposure=1, absolute_exposure=control_absolute_exposure) + control_mean = 100.0 + control = create_variant( + "control", + total_sum=control_mean * control_absolute_exposure, + exposure=1, + absolute_exposure=control_absolute_exposure, + ) test_a_absolute_exposure = 1000 + test_a_mean = 98.0 test_a = create_variant( "test_a", - mean=98.0, + total_sum=test_a_mean * test_a_absolute_exposure, exposure=test_a_absolute_exposure / control_absolute_exposure, absolute_exposure=test_a_absolute_exposure, ) test_b_absolute_exposure = 1000 + test_b_mean = 102.0 test_b = create_variant( "test_b", - mean=102.0, + total_sum=test_b_mean * test_b_absolute_exposure, exposure=test_b_absolute_exposure / control_absolute_exposure, absolute_exposure=test_b_absolute_exposure, ) test_c_absolute_exposure = 1000 + test_c_mean = 101.0 test_c = create_variant( "test_c", - mean=101.0, + total_sum=test_c_mean * test_c_absolute_exposure, exposure=test_c_absolute_exposure / control_absolute_exposure, absolute_exposure=test_c_absolute_exposure, ) @@ -239,27 +276,26 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertAlmostEqual(intervals["test_c"][0], 95, delta=5) # Lower bound self.assertAlmostEqual(intervals["test_c"][1], 105, delta=5) # Upper bound else: - # Original implementation behavior for multiple variants with no clear winner - self.assertTrue(all(0.1 < p < 0.9 for p in probabilities)) - self.assertEqual(significance, ExperimentSignificanceCode.LOW_WIN_PROBABILITY) - self.assertEqual(p_value, 1) + self.assertTrue(any(p > MIN_PROBABILITY_FOR_SIGNIFICANCE for p in probabilities)) + self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) + self.assertAlmostEqual(p_value, 0, delta=0.00001) - # Original implementation returns intervals as ratios/multipliers of the mean + # Generally narrower intervals in old implementation # Control variant - self.assertAlmostEqual(intervals["control"][0], 0.085, delta=0.01) # ~8.5% - self.assertAlmostEqual(intervals["control"][1], 0.12, delta=0.01) # ~12% + self.assertAlmostEqual(intervals["control"][0], 99.8, delta=5) + self.assertAlmostEqual(intervals["control"][1], 100.2, delta=5) # Test A variant - self.assertAlmostEqual(intervals["test_a"][0], 0.085, delta=0.01) # ~8.5% - self.assertAlmostEqual(intervals["test_a"][1], 0.12, delta=0.01) # ~12% + self.assertAlmostEqual(intervals["test_a"][0], 97, delta=5) + self.assertAlmostEqual(intervals["test_a"][1], 99, delta=5) # Test B variant - self.assertAlmostEqual(intervals["test_b"][0], 0.085, delta=0.01) # ~8.5% - self.assertAlmostEqual(intervals["test_b"][1], 0.12, delta=0.01) # ~12% + self.assertAlmostEqual(intervals["test_b"][0], 101, delta=5) + self.assertAlmostEqual(intervals["test_b"][1], 103, delta=5) # Test C variant - self.assertAlmostEqual(intervals["test_c"][0], 0.085, delta=0.01) # ~8.5% - self.assertAlmostEqual(intervals["test_c"][1], 0.12, delta=0.01) # ~12% + self.assertAlmostEqual(intervals["test_c"][0], 99, delta=5) + self.assertAlmostEqual(intervals["test_c"][1], 101, delta=5) self.run_test_for_both_implementations(run_test) @@ -269,25 +305,34 @@ def test_many_variants_significant(self): def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): control_absolute_exposure = 10000 - control = create_variant("control", mean=100.0, exposure=1, absolute_exposure=control_absolute_exposure) + control_mean = 100.0 + control = create_variant( + "control", + total_sum=control_mean * control_absolute_exposure, + exposure=1, + absolute_exposure=control_absolute_exposure, + ) test_a_absolute_exposure = 10000 + test_a_mean = 105.0 test_a = create_variant( "test_a", - mean=105.0, + total_sum=test_a_mean * test_a_absolute_exposure, exposure=test_a_absolute_exposure / control_absolute_exposure, absolute_exposure=test_a_absolute_exposure, ) test_b_absolute_exposure = 10000 + test_b_mean = 150.0 test_b = create_variant( "test_b", - mean=150.0, + total_sum=test_b_mean * test_b_absolute_exposure, exposure=test_b_absolute_exposure / control_absolute_exposure, absolute_exposure=test_b_absolute_exposure, ) test_c_absolute_exposure = 10000 + test_c_mean = 110.0 test_c = create_variant( "test_c", - mean=110.0, + total_sum=test_c_mean * test_c_absolute_exposure, exposure=test_c_absolute_exposure / control_absolute_exposure, absolute_exposure=test_c_absolute_exposure, ) @@ -298,9 +343,9 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertEqual(len(probabilities), 4) if stats_version == 2: - self.assertTrue(probabilities[2] > 0.9) # test_b should be winning - self.assertTrue(probabilities[1] < 0.1) # test_a should be losing - self.assertTrue(probabilities[0] < 0.1) # control should be losing + self.assertTrue(probabilities[2] > 0.9) + self.assertTrue(probabilities[1] < 0.1) + self.assertTrue(probabilities[0] < 0.1) self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) self.assertEqual(p_value, 0) @@ -320,16 +365,27 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertAlmostEqual(intervals["test_c"][0], 108, delta=1) self.assertAlmostEqual(intervals["test_c"][1], 112, delta=1) else: - # Original implementation behavior for multiple variants with clear winner - self.assertTrue(probabilities[2] > 0.5) # test_b should be winning + self.assertTrue(probabilities[2] > 0.9) + self.assertTrue(probabilities[1] < 0.1) + self.assertTrue(probabilities[0] < 0.1) self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) - self.assertLess(p_value, 0.05) + self.assertEqual(p_value, 0) - # Original implementation returns intervals as ratios/multipliers of the mean - # Test B (150.0) should have non-overlapping intervals with others when scaled - self.assertTrue(intervals["control"][1] * 100 < intervals["test_b"][0] * 150) - self.assertTrue(intervals["test_a"][1] * 105 < intervals["test_b"][0] * 150) - self.assertTrue(intervals["test_c"][1] * 110 < intervals["test_b"][0] * 150) + # Control at $100 + self.assertAlmostEqual(intervals["control"][0], 99.0, delta=1) + self.assertAlmostEqual(intervals["control"][1], 101.0, delta=1) + + # Test A slightly higher at $105 + self.assertAlmostEqual(intervals["test_a"][0], 105, delta=1) + self.assertAlmostEqual(intervals["test_a"][1], 105, delta=1) + + # Test B clearly winning at $150 + self.assertAlmostEqual(intervals["test_b"][0], 150, delta=1) + self.assertAlmostEqual(intervals["test_b"][1], 150, delta=1) + + # Test C slightly higher at $110 + self.assertAlmostEqual(intervals["test_c"][0], 110, delta=1) + self.assertAlmostEqual(intervals["test_c"][1], 110, delta=1) self.run_test_for_both_implementations(run_test) @@ -339,11 +395,18 @@ def test_insufficient_sample_size(self): def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): control_absolute_exposure = 50 - control = create_variant("control", mean=100.0, exposure=1, absolute_exposure=control_absolute_exposure) + control_mean = 100.0 + control = create_variant( + "control", + total_sum=control_mean * control_absolute_exposure, + exposure=1, + absolute_exposure=control_absolute_exposure, + ) test_absolute_exposure = 50 + test_mean = 120.0 test = create_variant( "test", - mean=120.0, + total_sum=test_mean * test_absolute_exposure, exposure=test_absolute_exposure / control_absolute_exposure, absolute_exposure=test_absolute_exposure, ) @@ -366,17 +429,17 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertAlmostEqual(intervals["test"][0], 85, delta=10) self.assertAlmostEqual(intervals["test"][1], 140, delta=10) else: - # Original implementation behavior for insufficient sample size - self.assertAlmostEqual(probabilities[0], 0.075, delta=0.025) - self.assertAlmostEqual(probabilities[1], 0.925, delta=0.075) + self.assertAlmostEqual(probabilities[0], 0.25, delta=0.25) + self.assertAlmostEqual(probabilities[1], 0.75, delta=0.25) self.assertEqual(significance, ExperimentSignificanceCode.NOT_ENOUGH_EXPOSURE) self.assertEqual(p_value, 1.0) - # Original implementation returns intervals as ratios/multipliers of the mean - self.assertAlmostEqual(intervals["control"][0], 1.65, delta=0.15) - self.assertAlmostEqual(intervals["control"][1], 2.45, delta=0.15) - self.assertAlmostEqual(intervals["test"][0], 1.95, delta=0.15) - self.assertAlmostEqual(intervals["test"][1], 2.75, delta=0.15) + # Generally narrower intervals in old implementation + self.assertAlmostEqual(intervals["control"][0], 97, delta=3) + self.assertAlmostEqual(intervals["control"][1], 102, delta=3) + + self.assertAlmostEqual(intervals["test"][0], 117, delta=3) + self.assertAlmostEqual(intervals["test"][1], 123, delta=3) self.run_test_for_both_implementations(run_test) @@ -386,11 +449,18 @@ def test_edge_cases_zero_means(self): def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): control_absolute_exposure = 1000 - control = create_variant("control", mean=0.0, exposure=1, absolute_exposure=control_absolute_exposure) + control_mean = 0.0 + control = create_variant( + "control", + total_sum=control_mean * control_absolute_exposure, + exposure=1, + absolute_exposure=control_absolute_exposure, + ) test_absolute_exposure = 1000 + test_mean = 0.0 test = create_variant( "test", - mean=0.0, + total_sum=test_mean * test_absolute_exposure, exposure=test_absolute_exposure / control_absolute_exposure, absolute_exposure=test_absolute_exposure, ) @@ -401,8 +471,8 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertEqual(len(probabilities), 2) if stats_version == 2: - self.assertAlmostEqual(probabilities[0], 0.5, delta=0.1) # Should be close to 50/50 - self.assertAlmostEqual(probabilities[1], 0.5, delta=0.1) # Should be close to 50/50 + self.assertAlmostEqual(probabilities[0], 0.5, delta=0.1) + self.assertAlmostEqual(probabilities[1], 0.5, delta=0.1) self.assertEqual(significance, ExperimentSignificanceCode.LOW_WIN_PROBABILITY) self.assertEqual(p_value, 1) @@ -413,18 +483,17 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertAlmostEqual(intervals["test"][0], 0, delta=0.05) self.assertAlmostEqual(intervals["test"][1], 0, delta=0.05) else: - # Original implementation behavior for zero means self.assertAlmostEqual(probabilities[0], 0.5, delta=0.1) self.assertAlmostEqual(probabilities[1], 0.5, delta=0.1) self.assertEqual(significance, ExperimentSignificanceCode.LOW_WIN_PROBABILITY) self.assertEqual(p_value, 1) - # Original implementation returns intervals as ratios/multipliers of the mean - # For zero means, the intervals should still be valid ratios - self.assertAlmostEqual(intervals["control"][0], 0, delta=0.1) - self.assertAlmostEqual(intervals["control"][1], 0, delta=0.1) - self.assertAlmostEqual(intervals["test"][0], 0, delta=0.1) - self.assertAlmostEqual(intervals["test"][1], 0, delta=0.1) + # Both variants should have very small intervals near zero + self.assertAlmostEqual(intervals["control"][0], 0, delta=0.05) + self.assertAlmostEqual(intervals["control"][1], 0, delta=0.05) + + self.assertAlmostEqual(intervals["test"][0], 0, delta=0.05) + self.assertAlmostEqual(intervals["test"][1], 0, delta=0.05) self.run_test_for_both_implementations(run_test) @@ -433,18 +502,19 @@ def test_edge_cases_near_zero_means(self): """Test edge cases like near-zero means""" def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): - # Using very small positive values instead of exact zeros control_absolute_exposure = 1000 + control_mean = 0.0001 control = create_variant( "control", - mean=0.0001, + total_sum=control_mean * control_absolute_exposure, exposure=1, absolute_exposure=control_absolute_exposure, ) test_absolute_exposure = 1000 + test_mean = 0.0001 test = create_variant( "test", - mean=0.0001, + total_sum=test_mean * test_absolute_exposure, exposure=test_absolute_exposure / control_absolute_exposure, absolute_exposure=test_absolute_exposure, ) @@ -468,7 +538,6 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca self.assertAlmostEqual(intervals["test"][0], 0.0001, delta=0.00015) # Lower bound self.assertAlmostEqual(intervals["test"][1], 0.0001, delta=0.00015) # Upper bound else: - # Original implementation behavior for near-zero means self.assertAlmostEqual(probabilities[0], 0.5, delta=0.1) self.assertAlmostEqual(probabilities[1], 0.5, delta=0.1) self.assertEqual(significance, ExperimentSignificanceCode.LOW_WIN_PROBABILITY) @@ -490,11 +559,18 @@ def test_expected_loss_minimal_difference(self): def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): control_absolute_exposure = 600 - control = create_variant("control", mean=100.0, exposure=1, absolute_exposure=control_absolute_exposure) + control_mean = 100.0 + control = create_variant( + "control", + total_sum=control_mean * control_absolute_exposure, + exposure=1, + absolute_exposure=control_absolute_exposure, + ) test_absolute_exposure = 600 + test_mean = 120.0 test = create_variant( "test", - mean=120.0, # Slightly higher mean + total_sum=test_mean * test_absolute_exposure, exposure=test_absolute_exposure / control_absolute_exposure, absolute_exposure=test_absolute_exposure, ) @@ -504,13 +580,12 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca if stats_version == 2: self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) - # Expected loss should be relatively small - self.assertLess(expected_loss, 3.0) # Less than $3 expected loss - self.assertGreater(expected_loss, 0) # But still some loss + self.assertLess(expected_loss, 3.0) + self.assertGreater(expected_loss, 0) else: - # Original implementation behavior (returns p_value in expected_loss) - self.assertEqual(significance, ExperimentSignificanceCode.HIGH_P_VALUE) - self.assertAlmostEqual(expected_loss, 0.2, delta=0.1) + self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) + self.assertLess(expected_loss, 3.0) + self.assertGreater(expected_loss, 0) self.run_test_for_both_implementations(run_test) @@ -520,11 +595,18 @@ def test_expected_loss_test_variant_clear_winner(self): def run_test(stats_version, calculate_probabilities, are_results_significant, calculate_credible_intervals): control_absolute_exposure = 10000 - control = create_variant("control", mean=100.0, exposure=1, absolute_exposure=control_absolute_exposure) + control_mean = 100.0 + control = create_variant( + "control", + total_sum=control_mean * control_absolute_exposure, + exposure=1, + absolute_exposure=control_absolute_exposure, + ) test_absolute_exposure = 10000 + test_mean = 200.0 test = create_variant( "test", - mean=200.0, # Much higher mean + total_sum=test_mean * test_absolute_exposure, exposure=test_absolute_exposure / control_absolute_exposure, absolute_exposure=test_absolute_exposure, ) @@ -534,11 +616,9 @@ def run_test(stats_version, calculate_probabilities, are_results_significant, ca if stats_version == 2: self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) - # Expected loss should be very close to zero since test is clearly better - self.assertLess(expected_loss, 0.1) # Essentially zero loss + self.assertLess(expected_loss, 0.1) else: - # Original implementation behavior self.assertEqual(significance, ExperimentSignificanceCode.SIGNIFICANT) - self.assertLess(expected_loss, 0.001) + self.assertLess(expected_loss, 0.1) self.run_test_for_both_implementations(run_test) diff --git a/posthog/hogql_queries/experiments/trends_statistics_v2_continuous.py b/posthog/hogql_queries/experiments/trends_statistics_v2_continuous.py index 4931d0a07212c..359951ba38f07 100644 --- a/posthog/hogql_queries/experiments/trends_statistics_v2_continuous.py +++ b/posthog/hogql_queries/experiments/trends_statistics_v2_continuous.py @@ -254,7 +254,8 @@ def calculate_credible_intervals_v2_continuous(variants, lower_bound=0.025, uppe for variant in variants: try: # Log-transform the mean value, adding epsilon to handle zeros - log_mean = np.log(variant.count + EPSILON) # Using count field to store mean value + mean_value = variant.count / variant.absolute_exposure + log_mean = np.log(mean_value + EPSILON) # Calculate posterior parameters using absolute_exposure kappa_n = KAPPA_0 + variant.absolute_exposure @@ -306,7 +307,8 @@ def calculate_expected_loss_v2_continuous( Expected loss in mean value if choosing the target variant """ # Calculate posterior parameters for target variant - log_target_mean = np.log(target_variant.count + EPSILON) + target_mean = target_variant.count / target_variant.absolute_exposure + log_target_mean = np.log(target_mean + EPSILON) # Update parameters for target variant kappa_n_target = KAPPA_0 + target_variant.absolute_exposure @@ -323,7 +325,8 @@ def calculate_expected_loss_v2_continuous( # Draw samples from each comparison variant's posterior variant_samples = [] for variant in variants: - log_variant_mean = np.log(variant.count + EPSILON) + variant_mean = variant.count / variant.absolute_exposure + log_variant_mean = np.log(variant_mean + EPSILON) kappa_n = KAPPA_0 + variant.absolute_exposure mu_n = (KAPPA_0 * MU_0 + variant.absolute_exposure * log_variant_mean) / kappa_n diff --git a/posthog/hogql_queries/experiments/types.py b/posthog/hogql_queries/experiments/types.py new file mode 100644 index 0000000000000..17a29aef766fd --- /dev/null +++ b/posthog/hogql_queries/experiments/types.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class ExperimentMetricType(Enum): + COUNT = "count" + CONTINUOUS = "continuous" + FUNNEL = "funnel" diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel.ambr index f4f8c1a300c85..0da1479ac188a 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel.ambr @@ -80,7 +80,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -176,7 +176,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -288,7 +288,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2011-12-25 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2012-01-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$autocapture', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$autocapture', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -375,7 +375,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -483,7 +483,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-07-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -590,7 +590,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-07-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -703,7 +703,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-07-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -816,7 +816,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-07-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -898,7 +898,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -1056,7 +1056,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -1157,7 +1157,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) ARRAY + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) ARRAY JOIN prop_vals AS prop WHERE ifNull(notEquals(prop, []), isNotNull(prop) or isNotNull([])))) @@ -1257,7 +1257,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -1678,7 +1678,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -1811,7 +1811,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -1949,7 +1949,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -2087,7 +2087,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -2225,7 +2225,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_breakdowns_by_current_url.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_breakdowns_by_current_url.ambr index e01e48e4c1f7f..9f14dbe5ebec0 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_breakdowns_by_current_url.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_breakdowns_by_current_url.ambr @@ -77,7 +77,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-02 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-12 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('terminate funnel', 'watched movie'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('terminate funnel', 'watched movie'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -174,7 +174,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-02 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-12 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('terminate funnel', 'watched movie'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('terminate funnel', 'watched movie'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation.ambr index a7e5388a8e251..1ae0e4e923b69 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation.ambr @@ -66,7 +66,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -135,7 +135,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -215,7 +215,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -297,7 +297,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -419,7 +419,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -562,7 +562,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -705,7 +705,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -848,7 +848,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -949,7 +949,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -1031,7 +1031,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -1153,7 +1153,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -1296,7 +1296,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -1439,7 +1439,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -1582,7 +1582,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_actors.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_actors.ambr index cebec4f9efd4b..2a9ce6cd07207 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_actors.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_actors.ambr @@ -102,7 +102,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -309,7 +309,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed', 'insight updated'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed', 'insight updated'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -452,7 +452,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed')), ifNull(equals(e__person.properties___foo, 'bar'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed')), ifNull(equals(e__person.properties___foo, 'bar'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_actors_udf.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_actors_udf.ambr index 62ae8ccd05b01..5f243342c8b76 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_actors_udf.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_actors_udf.ambr @@ -57,7 +57,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -151,7 +151,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed', 'insight updated'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed', 'insight updated'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -246,7 +246,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed')), ifNull(equals(e__person.properties___foo, 'bar'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'insight analyzed')), ifNull(equals(e__person.properties___foo, 'bar'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_udf.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_udf.ambr index d1b1edaa350d1..e2c7f7bd7f783 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_udf.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation_udf.ambr @@ -52,7 +52,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -104,7 +104,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -167,7 +167,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -232,7 +232,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -306,7 +306,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -401,7 +401,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -496,7 +496,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -591,7 +591,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -675,7 +675,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -740,7 +740,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -814,7 +814,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -909,7 +909,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Positive'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -1004,7 +1004,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -1099,7 +1099,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up')), ifNull(equals(e__person.`properties___$browser`, 'Negative'), 0)), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons.ambr index 4c634d67842fc..f9dd62f77f9ce 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons.ambr @@ -147,7 +147,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -331,7 +331,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -515,7 +515,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons_udf.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons_udf.ambr index 742e6b15740d6..814b78bfe3485 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons_udf.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons_udf.ambr @@ -41,7 +41,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -116,7 +116,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 1), 0) @@ -191,7 +191,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(equals(step_reached, 1), 0) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict.ambr index dd6ed08f95fcc..f6f1380c7061d 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict.ambr @@ -76,7 +76,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -176,7 +176,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))))) ARRAY + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))) ARRAY JOIN prop_vals AS prop WHERE ifNull(notEquals(prop, []), isNotNull(prop) or isNotNull([])))) @@ -275,7 +275,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -627,7 +627,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -737,7 +737,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -852,7 +852,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -967,7 +967,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -1082,7 +1082,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons.ambr index c3e986c70b196..d8d50ad809031 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons.ambr @@ -107,7 +107,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -251,7 +251,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -395,7 +395,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons_udf.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons_udf.ambr index 2c3b80f8467e3..99776ac0ce47c 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons_udf.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons_udf.ambr @@ -41,7 +41,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -116,7 +116,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 1), 0) @@ -191,7 +191,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(equals(step_reached, 1), 0) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_udf.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_udf.ambr index 9b7fba2117929..ef5837eb94fe0 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_udf.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_udf.ambr @@ -57,7 +57,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -122,7 +122,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -197,7 +197,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -450,7 +450,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -527,7 +527,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE and(ifNull(greaterOrEquals(step_reached, 0), 0), ifNull(equals(arrayFlatten(array(breakdown)), arrayFlatten(array('finance'))), isNull(arrayFlatten(array(breakdown))) @@ -607,7 +607,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE and(ifNull(greaterOrEquals(step_reached, 1), 0), ifNull(equals(arrayFlatten(array(breakdown)), arrayFlatten(array('finance'))), isNull(arrayFlatten(array(breakdown))) @@ -687,7 +687,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE and(ifNull(greaterOrEquals(step_reached, 0), 0), ifNull(equals(arrayFlatten(array(breakdown)), arrayFlatten(array('technology'))), isNull(arrayFlatten(array(breakdown))) @@ -767,7 +767,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE and(ifNull(greaterOrEquals(step_reached, 1), 0), ifNull(equals(arrayFlatten(array(breakdown)), arrayFlatten(array('technology'))), isNull(arrayFlatten(array(breakdown))) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_time_to_convert.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_time_to_convert.ambr index 07ad21fd15748..a24c8620575e7 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_time_to_convert.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_time_to_convert.ambr @@ -85,7 +85,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -121,7 +121,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -154,7 +154,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -187,7 +187,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -220,7 +220,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -299,7 +299,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -335,7 +335,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -368,7 +368,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -402,7 +402,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -486,7 +486,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -517,7 +517,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -545,7 +545,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -573,7 +573,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -601,7 +601,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -661,7 +661,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -692,7 +692,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -720,7 +720,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -749,7 +749,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC')))))) + WHERE and(equals(e.team_id, 99999), and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC')))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -835,7 +835,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -878,7 +878,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -921,7 +921,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -952,7 +952,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -968,7 +968,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -984,7 +984,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1012,7 +1012,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1028,7 +1028,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1044,7 +1044,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1072,7 +1072,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1088,7 +1088,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1104,7 +1104,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1132,7 +1132,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1148,7 +1148,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1164,7 +1164,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1226,7 +1226,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1269,7 +1269,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1312,7 +1312,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps @@ -1343,7 +1343,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1359,7 +1359,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1375,7 +1375,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1403,7 +1403,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1419,7 +1419,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1435,7 +1435,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) @@ -1464,7 +1464,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1480,7 +1480,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM @@ -1496,7 +1496,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps HAVING ifNull(equals(steps, max(max_steps)), isNull(steps) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends.ambr index 1264338245f6e..2bcec242b6d2c 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends.ambr @@ -75,7 +75,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-04-30 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0)) GROUP BY aggregation_target, entrance_period_start) @@ -259,7 +259,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0)) GROUP BY aggregation_target, entrance_period_start) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_actors.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_actors.ambr index 642b651f50faf..c33a0b31cfbcd 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_actors.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_actors.ambr @@ -134,9 +134,9 @@ 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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0)) - WHERE ifNull(equals(entrance_period_start, toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), 0) + WHERE ifNull(equals(entrance_period_start, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) GROUP BY aggregation_target, entrance_period_start) WHERE ifNull(greaterOrEquals(steps_completed, 2), 0) @@ -304,9 +304,9 @@ 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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0)) - WHERE ifNull(equals(entrance_period_start, toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), 0) + WHERE ifNull(equals(entrance_period_start, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) GROUP BY aggregation_target, entrance_period_start) WHERE and(ifNull(greaterOrEquals(steps_completed, 1), 0), ifNull(less(steps_completed, 3), 0)) @@ -474,9 +474,9 @@ 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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))))) WHERE ifNull(equals(step_0, 1), 0)) - WHERE ifNull(equals(entrance_period_start, toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), 0) + WHERE ifNull(equals(entrance_period_start, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) GROUP BY aggregation_target, entrance_period_start) WHERE ifNull(greaterOrEquals(steps_completed, 3), 0) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_actors_udf.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_actors_udf.ambr index d078ffbc040d7..7ffb580102498 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_actors_udf.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_actors_udf.ambr @@ -39,10 +39,10 @@ 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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target SETTINGS date_time_output_format='iso', date_time_input_format='best_effort') - WHERE and(ifNull(equals(success_bool, 1), 0), ifNull(equals(entrance_period_start, toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), 0)) + WHERE and(ifNull(equals(success_bool, 1), 0), ifNull(equals(entrance_period_start, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) ORDER BY aggregation_target ASC SETTINGS join_algorithm='auto') AS source ORDER BY source.id ASC LIMIT 101 @@ -112,10 +112,10 @@ 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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target SETTINGS date_time_output_format='iso', date_time_input_format='best_effort') - WHERE and(ifNull(notEquals(success_bool, 1), 1), ifNull(equals(entrance_period_start, toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), 0)) + WHERE and(ifNull(notEquals(success_bool, 1), 1), ifNull(equals(entrance_period_start, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) ORDER BY aggregation_target ASC SETTINGS join_algorithm='auto') AS source ORDER BY source.id ASC LIMIT 101 @@ -185,10 +185,10 @@ 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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target SETTINGS date_time_output_format='iso', date_time_input_format='best_effort') - WHERE and(ifNull(equals(success_bool, 1), 0), ifNull(equals(entrance_period_start, toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), 0)) + WHERE and(ifNull(equals(success_bool, 1), 0), ifNull(equals(entrance_period_start, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) ORDER BY aggregation_target ASC SETTINGS join_algorithm='auto') AS source ORDER BY source.id ASC LIMIT 101 diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_udf.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_udf.ambr index 4a77813bdecd2..d91590c550850 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_udf.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_udf.ambr @@ -32,7 +32,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-04-30 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target SETTINGS date_time_output_format='iso', date_time_input_format='best_effort') AS data RIGHT OUTER JOIN @@ -138,7 +138,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-07 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target SETTINGS date_time_output_format='iso', date_time_input_format='best_effort') AS data RIGHT OUTER JOIN diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_udf.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_udf.ambr index 14b3e38b2eb64..cb94506860a5e 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_udf.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_udf.ambr @@ -47,7 +47,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -101,7 +101,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-05-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 1), 0) @@ -177,7 +177,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2011-12-25 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2012-01-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$autocapture', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$autocapture', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -259,7 +259,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -335,7 +335,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-07-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -400,7 +400,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-07-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 0), 0) @@ -467,7 +467,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-07-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 1), 0) @@ -534,7 +534,7 @@ 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 e__person ON equals(if(not(empty(e__override.distinct_id)), e__override.person_id, e.person_id), e__person.id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-07-01 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('$pageview', 'user signed up')), or(and(ifNull(ilike(e__person.properties___email, '%.com%'), 0), ifNull(equals(e__person.properties___age, '20'), 0)), or(ifNull(ilike(e__person.properties___email, '%.org%'), 0), ifNull(equals(e__person.properties___age, '28'), 0)))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE ifNull(greaterOrEquals(step_reached, 2), 0) @@ -607,7 +607,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-14 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('paid', 'user signed up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -741,7 +741,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -806,7 +806,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -881,7 +881,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -1134,7 +1134,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) GROUP BY breakdown @@ -1211,7 +1211,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE and(ifNull(greaterOrEquals(step_reached, 0), 0), ifNull(equals(arrayFlatten(array(breakdown)), arrayFlatten(array('finance'))), isNull(arrayFlatten(array(breakdown))) @@ -1291,7 +1291,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE and(ifNull(greaterOrEquals(step_reached, 1), 0), ifNull(equals(arrayFlatten(array(breakdown)), arrayFlatten(array('finance'))), isNull(arrayFlatten(array(breakdown))) @@ -1371,7 +1371,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE and(ifNull(greaterOrEquals(step_reached, 0), 0), ifNull(equals(arrayFlatten(array(breakdown)), arrayFlatten(array('technology'))), isNull(arrayFlatten(array(breakdown))) @@ -1451,7 +1451,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) GROUP BY aggregation_target HAVING ifNull(greaterOrEquals(step_reached, 0), 0)) WHERE and(ifNull(greaterOrEquals(step_reached, 1), 0), ifNull(equals(arrayFlatten(array(breakdown)), arrayFlatten(array('technology'))), isNull(arrayFlatten(array(breakdown))) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered.ambr index d33ab0746b287..fd964bca0a3e7 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered.ambr @@ -78,7 +78,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -130,7 +130,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -232,7 +232,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) ARRAY + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) ARRAY JOIN prop_vals AS prop WHERE ifNull(notEquals(prop, []), isNotNull(prop) or isNotNull([])))) @@ -291,7 +291,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) ARRAY + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))) ARRAY JOIN prop_vals AS prop WHERE ifNull(notEquals(prop, []), isNotNull(prop) or isNotNull([])))) @@ -392,7 +392,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -444,7 +444,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -1058,7 +1058,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1129,7 +1129,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1200,7 +1200,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -1312,7 +1312,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1383,7 +1383,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1454,7 +1454,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -1571,7 +1571,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1642,7 +1642,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1713,7 +1713,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -1830,7 +1830,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1901,7 +1901,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1972,7 +1972,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, @@ -2089,7 +2089,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -2160,7 +2160,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -2231,7 +2231,7 @@ WHERE and(equals(groups.team_id, 99999), equals(index, 0)) GROUP BY groups.group_type_index, groups.group_key) AS e__group_0 ON equals(e.`$group_0`, e__group_0.key) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2020-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('buy', 'play movie', 'sign up'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0)))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered_persons.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered_persons.ambr index c4529bac0f50e..ce75845b1438c 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered_persons.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered_persons.ambr @@ -97,7 +97,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -174,7 +174,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -251,7 +251,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 e__override ON equals(e.distinct_id, e__override.distinct_id) - WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-01 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-01-08 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) + WHERE and(equals(e.team_id, 99999), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0))) GROUP BY aggregation_target, steps diff --git a/posthog/hogql_queries/insights/test/__snapshots__/test_retention_query_runner.ambr b/posthog/hogql_queries/insights/test/__snapshots__/test_retention_query_runner.ambr index 77f81b89edabc..ad1b2f24023f1 100644 --- a/posthog/hogql_queries/insights/test/__snapshots__/test_retention_query_runner.ambr +++ b/posthog/hogql_queries/insights/test/__snapshots__/test_retention_query_runner.ambr @@ -200,7 +200,7 @@ count(DISTINCT actor_activity.actor_id) AS count FROM (SELECT if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id) AS actor_id, - arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('2020-06-10 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-06-21 00:00:00.000000', 6, 'UTC')))))) AS target_timestamps, + arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')))))) AS target_timestamps, arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), equals(events.event, '$pageview'))) AS returning_timestamps, arrayMap(x -> plus(toStartOfDay(assumeNotNull(parseDateTime64BestEffortOrNull('2020-06-10 00:00:00', 6, 'UTC'))), toIntervalDay(x)), range(0, 11)) AS date_range, arrayJoin(arrayFilter(x -> ifNull(greater(x, -1), 0), arrayMap((_breakdown_value, breakdown_value_timestamp) -> if(has(target_timestamps, breakdown_value_timestamp), minus(_breakdown_value, 1), -1), arrayEnumerate(date_range), date_range))) AS breakdown_values, @@ -213,7 +213,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), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('2020-06-10 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-06-21 00:00:00.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) + WHERE and(equals(events.team_id, 99999), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) GROUP BY actor_id) AS actor_activity GROUP BY start_event_matching_interval, intervals_from_base @@ -244,7 +244,7 @@ count(DISTINCT actor_activity.actor_id) AS count FROM (SELECT if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id) AS actor_id, - arraySort(groupUniqArrayIf(toStartOfMonth(toTimeZone(events.timestamp, 'UTC')), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfMonth(toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-10 00:00:00.000000', 6, 'UTC')))))) AS target_timestamps, + arraySort(groupUniqArrayIf(toStartOfMonth(toTimeZone(events.timestamp, 'UTC')), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfMonth(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')))))) AS target_timestamps, arraySort(groupUniqArrayIf(toStartOfMonth(toTimeZone(events.timestamp, 'UTC')), equals(events.event, '$pageview'))) AS returning_timestamps, arrayMap(x -> plus(toStartOfMonth(assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), toIntervalMonth(x)), range(0, 11)) AS date_range, arrayJoin(arrayFilter(x -> ifNull(greater(x, -1), 0), arrayMap((_breakdown_value, breakdown_value_timestamp) -> if(has(target_timestamps, breakdown_value_timestamp), minus(_breakdown_value, 1), -1), arrayEnumerate(date_range), date_range))) AS breakdown_values, @@ -257,7 +257,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), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfMonth(toDateTime64('2020-01-01 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-12-10 00:00:00.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) + WHERE and(equals(events.team_id, 99999), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfMonth(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) GROUP BY actor_id) AS actor_activity GROUP BY start_event_matching_interval, intervals_from_base @@ -279,7 +279,7 @@ count(DISTINCT actor_activity.actor_id) AS count FROM (SELECT if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id) AS actor_id, - arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), and(equals(events.event, 'sign up'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('2020-06-10 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-06-17 00:00:00.000000', 6, 'UTC')))))) AS target_timestamps, + arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), and(equals(events.event, 'sign up'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')))))) AS target_timestamps, arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), equals(events.event, '$some_event'))) AS returning_timestamps, arrayMap(x -> plus(toStartOfDay(assumeNotNull(parseDateTime64BestEffortOrNull('2020-06-10 00:00:00', 6, 'UTC'))), toIntervalDay(x)), range(0, 7)) AS date_range, arrayJoin(arrayFilter(x -> ifNull(greater(x, -1), 0), arrayMap((_breakdown_value, breakdown_value_timestamp) -> if(has(target_timestamps, breakdown_value_timestamp), minus(_breakdown_value, 1), -1), arrayEnumerate(date_range), date_range))) AS breakdown_values, @@ -292,7 +292,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), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('2020-06-10 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-06-17 00:00:00.000000', 6, 'UTC'))), in(events.event, tuple('$some_event', 'sign up'))) + WHERE and(equals(events.team_id, 99999), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), in(events.event, tuple('$some_event', 'sign up'))) GROUP BY actor_id) AS actor_activity GROUP BY start_event_matching_interval, intervals_from_base @@ -314,7 +314,7 @@ count(DISTINCT actor_activity.actor_id) AS count FROM (SELECT if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id) AS actor_id, - arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), and(or(and(equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'person1@test.com'), 0)), equals(events.event, 'non_matching_event')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('2020-06-10 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-06-17 00:00:00.000000', 6, 'UTC')))))) AS target_timestamps, + arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), and(or(and(equals(events.event, '$pageview'), ifNull(equals(events__person.properties___email, 'person1@test.com'), 0)), equals(events.event, 'non_matching_event')), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')))))) AS target_timestamps, arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), equals(events.event, '$pageview'))) AS returning_timestamps, arrayMap(x -> plus(toStartOfDay(assumeNotNull(parseDateTime64BestEffortOrNull('2020-06-10 00:00:00', 6, 'UTC'))), toIntervalDay(x)), range(0, 7)) AS date_range, arrayJoin(arrayFilter(x -> ifNull(greater(x, -1), 0), arrayMap((_breakdown_value, breakdown_value_timestamp) -> if(has(target_timestamps, breakdown_value_timestamp), minus(_breakdown_value, 1), -1), arrayEnumerate(date_range), date_range))) AS breakdown_values, @@ -337,7 +337,7 @@ 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), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('2020-06-10 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-06-17 00:00:00.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview', 'non_matching_event'))) + WHERE and(equals(events.team_id, 99999), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview', 'non_matching_event'))) GROUP BY actor_id) AS actor_activity GROUP BY start_event_matching_interval, intervals_from_base @@ -359,7 +359,7 @@ count(DISTINCT actor_activity.actor_id) AS count FROM (SELECT if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id) AS actor_id, - arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('2020-06-10 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-06-21 00:00:00.000000', 6, 'UTC')))))) AS target_timestamps, + arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')))))) AS target_timestamps, arraySort(groupUniqArrayIf(toStartOfDay(toTimeZone(events.timestamp, 'UTC')), equals(events.event, '$pageview'))) AS returning_timestamps, arrayMap(x -> plus(toStartOfDay(assumeNotNull(parseDateTime64BestEffortOrNull('2020-06-10 00:00:00', 6, 'UTC'))), toIntervalDay(x)), range(0, 11)) AS date_range, arrayJoin(arrayFilter(x -> ifNull(greater(x, -1), 0), arrayMap((_breakdown_value, breakdown_value_timestamp) -> if(has(target_timestamps, breakdown_value_timestamp), minus(_breakdown_value, 1), -1), arrayEnumerate(date_range), date_range))) AS breakdown_values, @@ -372,7 +372,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), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('2020-06-10 00:00:00.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-06-21 00:00:00.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) + WHERE and(equals(events.team_id, 99999), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfDay(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) GROUP BY actor_id) AS actor_activity GROUP BY start_event_matching_interval, intervals_from_base @@ -429,7 +429,7 @@ count(DISTINCT actor_activity.actor_id) AS count FROM (SELECT if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id) AS actor_id, - arraySort(groupUniqArrayIf(toStartOfWeek(toTimeZone(events.timestamp, 'UTC'), 0), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfWeek(toDateTime64('2020-06-07 00:00:00.000000', 6, 'UTC'), 0)), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-07-27 00:00:00.000000', 6, 'UTC')))))) AS target_timestamps, + arraySort(groupUniqArrayIf(toStartOfWeek(toTimeZone(events.timestamp, 'UTC'), 0), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfWeek(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'), 0)), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')))))) AS target_timestamps, arraySort(groupUniqArrayIf(toStartOfWeek(toTimeZone(events.timestamp, 'UTC'), 0), equals(events.event, '$pageview'))) AS returning_timestamps, arrayMap(x -> plus(toStartOfWeek(assumeNotNull(parseDateTime64BestEffortOrNull('2020-06-07 00:00:00', 6, 'UTC')), 0), toIntervalWeek(x)), range(0, 7)) AS date_range, arrayJoin(arrayFilter(x -> ifNull(greater(x, -1), 0), arrayMap((_breakdown_value, breakdown_value_timestamp) -> if(has(target_timestamps, breakdown_value_timestamp), minus(_breakdown_value, 1), -1), arrayEnumerate(date_range), date_range))) AS breakdown_values, @@ -442,7 +442,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), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfWeek(toDateTime64('2020-06-07 00:00:00.000000', 6, 'UTC'), 0)), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-07-27 00:00:00.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) + WHERE and(equals(events.team_id, 99999), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfWeek(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'), 0)), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) GROUP BY actor_id) AS actor_activity GROUP BY start_event_matching_interval, intervals_from_base @@ -464,7 +464,7 @@ count(DISTINCT actor_activity.actor_id) AS count FROM (SELECT if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id) AS actor_id, - arraySort(groupUniqArrayIf(toStartOfWeek(toTimeZone(events.timestamp, 'UTC'), 3), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfWeek(toDateTime64('2020-06-08 00:00:00.000000', 6, 'UTC'), 3)), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-07-27 00:00:00.000000', 6, 'UTC')))))) AS target_timestamps, + arraySort(groupUniqArrayIf(toStartOfWeek(toTimeZone(events.timestamp, 'UTC'), 3), and(equals(events.event, '$pageview'), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfWeek(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'), 3)), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')))))) AS target_timestamps, arraySort(groupUniqArrayIf(toStartOfWeek(toTimeZone(events.timestamp, 'UTC'), 3), equals(events.event, '$pageview'))) AS returning_timestamps, arrayMap(x -> plus(toStartOfWeek(assumeNotNull(parseDateTime64BestEffortOrNull('2020-06-08 00:00:00', 6, 'UTC')), 3), toIntervalWeek(x)), range(0, 7)) AS date_range, arrayJoin(arrayFilter(x -> ifNull(greater(x, -1), 0), arrayMap((_breakdown_value, breakdown_value_timestamp) -> if(has(target_timestamps, breakdown_value_timestamp), minus(_breakdown_value, 1), -1), arrayEnumerate(date_range), date_range))) AS breakdown_values, @@ -477,7 +477,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), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfWeek(toDateTime64('2020-06-08 00:00:00.000000', 6, 'UTC'), 3)), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2020-07-27 00:00:00.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) + WHERE and(equals(events.team_id, 99999), and(greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toStartOfWeek(toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'), 3)), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))), in(events.event, tuple('$pageview', '$pageview'))) GROUP BY actor_id) AS actor_activity GROUP BY start_event_matching_interval, intervals_from_base diff --git a/posthog/hogql_queries/test/__snapshots__/test_error_tracking_query_runner.ambr b/posthog/hogql_queries/test/__snapshots__/test_error_tracking_query_runner.ambr index ffeb4ef00768c..d586fa5b6d610 100644 --- a/posthog/hogql_queries/test/__snapshots__/test_error_tracking_query_runner.ambr +++ b/posthog/hogql_queries/test/__snapshots__/test_error_tracking_query_runner.ambr @@ -266,7 +266,7 @@ 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), equals(events.event, '$exception'), isNotNull(if(not(empty(events__exception_issue_override.issue_id)), events__exception_issue_override.issue_id, accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$exception_issue_id'), ''), 'null'), '^"|"$', ''), 'UUID'))), and(less(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-01-11 00:00:00.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('2022-01-10 00:00:00.000000', 6, 'UTC')), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1)), or(ifNull(greater(position(lower(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$exception_list'), ''), 'null'), '^"|"$', '')), lower('databasenot')), 0), 0), ifNull(greater(position(lower(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$exception_type'), ''), 'null'), '^"|"$', '')), lower('databasenot')), 0), 0), ifNull(greater(position(lower(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$exception_message'), ''), 'null'), '^"|"$', '')), lower('databasenot')), 0), 0))) + WHERE and(equals(events.team_id, 99999), equals(events.event, '$exception'), isNotNull(if(not(empty(events__exception_issue_override.issue_id)), events__exception_issue_override.issue_id, accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$exception_issue_id'), ''), 'null'), '^"|"$', ''), 'UUID'))), and(less(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1)), or(ifNull(greater(position(lower(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$exception_list'), ''), 'null'), '^"|"$', '')), lower('databasenot')), 0), 0), ifNull(greater(position(lower(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$exception_type'), ''), 'null'), '^"|"$', '')), lower('databasenot')), 0), 0), ifNull(greater(position(lower(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$exception_message'), ''), 'null'), '^"|"$', '')), lower('databasenot')), 0), 0))) GROUP BY if(not(empty(events__exception_issue_override.issue_id)), events__exception_issue_override.issue_id, accurateCastOrNull(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$exception_issue_id'), ''), 'null'), '^"|"$', ''), 'UUID')) LIMIT 101 OFFSET 0 SETTINGS readonly=2, diff --git a/posthog/hogql_queries/test/__snapshots__/test_sessions_timeline_query_runner.ambr b/posthog/hogql_queries/test/__snapshots__/test_sessions_timeline_query_runner.ambr index 949434348d4be..ad9ad7ce96097 100644 --- a/posthog/hogql_queries/test/__snapshots__/test_sessions_timeline_query_runner.ambr +++ b/posthog/hogql_queries/test/__snapshots__/test_sessions_timeline_query_runner.ambr @@ -43,7 +43,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), greater(timestamp, toDateTime64('2023-10-01 12:00:00.000000', 6, 'UTC')), less(timestamp, toDateTime64('2023-10-01 17:00:00.000000', 6, 'UTC'))) + WHERE and(equals(events.team_id, 99999), greater(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), less(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) ORDER BY timestamp DESC LIMIT 1001)) AS e LEFT JOIN @@ -111,7 +111,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), greater(timestamp, toDateTime64('2023-09-30 16:00:00.000000', 6, 'UTC')), less(timestamp, toDateTime64('2023-10-01 16:00:00.000000', 6, 'UTC'))) + WHERE and(equals(events.team_id, 99999), greater(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), less(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) ORDER BY timestamp DESC LIMIT 1001)) AS e LEFT JOIN @@ -179,7 +179,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), greater(timestamp, toDateTime64('2023-10-01 06:00:00.000000', 6, 'UTC')), less(timestamp, toDateTime64('2023-10-02 06:00:00.000000', 6, 'UTC'))) + WHERE and(equals(events.team_id, 99999), greater(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), less(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) ORDER BY timestamp DESC LIMIT 3)) AS e LEFT JOIN @@ -247,7 +247,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), greater(timestamp, toDateTime64('2023-10-01 06:00:00.000000', 6, 'UTC')), less(timestamp, toDateTime64('2023-10-02 06:00:00.000000', 6, 'UTC'))) + WHERE and(equals(events.team_id, 99999), greater(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), less(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) ORDER BY timestamp DESC LIMIT 1001)) AS e LEFT JOIN @@ -315,7 +315,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), greater(timestamp, toDateTime64('2023-10-01 06:00:00.000000', 6, 'UTC')), less(timestamp, toDateTime64('2023-10-02 06:00:00.000000', 6, 'UTC'))) + WHERE and(equals(events.team_id, 99999), greater(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), less(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) ORDER BY timestamp DESC LIMIT 1001)) AS e LEFT JOIN @@ -383,7 +383,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), greater(timestamp, toDateTime64('2023-10-01 06:00:00.000000', 6, 'UTC')), less(timestamp, toDateTime64('2023-10-02 06:00:00.000000', 6, 'UTC')), ifNull(equals(person_id, '00000000-0000-0000-0000-000000000000'), 0)) + WHERE and(equals(events.team_id, 99999), greater(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), less(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), ifNull(equals(person_id, '00000000-0000-0000-0000-000000000000'), 0)) ORDER BY timestamp DESC LIMIT 1001)) AS e LEFT JOIN @@ -451,7 +451,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), greater(timestamp, toDateTime64('2023-10-01 06:00:00.000000', 6, 'UTC')), less(timestamp, toDateTime64('2023-10-02 06:00:00.000000', 6, 'UTC'))) + WHERE and(equals(events.team_id, 99999), greater(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), less(timestamp, toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC'))) ORDER BY timestamp DESC LIMIT 1001)) AS e LEFT JOIN diff --git a/posthog/hogql_queries/web_analytics/stats_table.py b/posthog/hogql_queries/web_analytics/stats_table.py index b33bb0d438f8d..7ce75876fb75d 100644 --- a/posthog/hogql_queries/web_analytics/stats_table.py +++ b/posthog/hogql_queries/web_analytics/stats_table.py @@ -455,6 +455,13 @@ def calculate(self): else response.columns ) + # Add replay URL column if it doesn't exist (for session replay cross-selling) + if columns is not None: + if "context.columns.replay_url" not in columns: + # Append replay URL column to the list of columns (as Robbie suggested) + columns = [*list(columns), "context.columns.replay_url"] + results_mapped = [[*row, ""] for row in (results_mapped or [])] + return WebStatsTableQueryResponse( columns=columns, results=results_mapped, diff --git a/posthog/hogql_queries/web_analytics/test/test_web_stats_table.py b/posthog/hogql_queries/web_analytics/test/test_web_stats_table.py index 84a9f650222ad..d41cf0f946890 100644 --- a/posthog/hogql_queries/web_analytics/test/test_web_stats_table.py +++ b/posthog/hogql_queries/web_analytics/test/test_web_stats_table.py @@ -164,7 +164,7 @@ def test_no_crash_when_no_data(self): "2023-12-08", "2023-12-15", ).results - self.assertEqual([], results) + assert [] == results def test_increase_in_users(self): s1a = str(uuid7("2023-12-02")) @@ -179,13 +179,10 @@ def test_increase_in_users(self): results = self._run_web_stats_table_query("2023-12-01", "2023-12-11").results - self.assertEqual( - [ - ["/", (2, None), (2, None)], - ["/login", (1, None), (1, None)], - ], - results, - ) + assert [ + ["/", (2, None), (2, None), ""], + ["/login", (1, None), (1, None), ""], + ] == results def test_increase_in_users_on_mobile(self): s1a = str(uuid7("2023-12-02")) @@ -203,13 +200,10 @@ def test_increase_in_users_on_mobile(self): "2023-12-01", "2023-12-11", breakdown_by=WebStatsBreakdown.SCREEN_NAME ).results - self.assertEqual( - [ - ["Home", (2, None), (2, None)], - ["Login", (1, None), (1, None)], - ], - results, - ) + assert [ + ["Home", (2, None), (2, None), ""], + ["Login", (1, None), (1, None), ""], + ] == results def test_all_time(self): s1a = str(uuid7("2023-12-02")) @@ -224,14 +218,11 @@ def test_all_time(self): results = self._run_web_stats_table_query("all", "2023-12-15").results - self.assertEqual( - [ - ["/", (2, None), (2, None)], - ["/docs", (1, None), (1, None)], - ["/login", (1, None), (1, None)], - ], - results, - ) + assert [ + ["/", (2, None), (2, None), ""], + ["/docs", (1, None), (1, None), ""], + ["/login", (1, None), (1, None), ""], + ] == results def test_comparison(self): s1a = str(uuid7("2023-12-02")) @@ -248,14 +239,11 @@ def test_comparison(self): "2023-12-06", "2023-12-13", compare_filter=CompareFilter(compare=True) ).results - self.assertEqual( - [ - ["/", (1, 1), (1, 1)], - ["/docs", (1, 0), (1, 0)], - ["/login", (0, 1), (0, 1)], - ], - results, - ) + assert [ + ["/", (1, 1), (1, 1), ""], + ["/docs", (1, 0), (1, 0), ""], + ["/login", (0, 1), (0, 1), ""], + ] == results def test_filter_test_accounts(self): s1 = str(uuid7("2023-12-02")) @@ -264,10 +252,7 @@ def test_filter_test_accounts(self): results = self._run_web_stats_table_query("2023-12-01", "2023-12-03", filter_test_accounts=True).results - self.assertEqual( - [], - results, - ) + assert [] == results def test_dont_filter_test_accounts(self): s1 = str(uuid7("2023-12-02")) @@ -276,10 +261,7 @@ def test_dont_filter_test_accounts(self): results = self._run_web_stats_table_query("2023-12-01", "2023-12-03", filter_test_accounts=False).results - self.assertEqual( - [["/", (1, None), (1, None)], ["/login", (1, None), (1, None)]], - results, - ) + assert [["/", (1.0, None), (1.0, None), ""], ["/login", (1.0, None), (1.0, None), ""]] == results def test_breakdown_channel_type_doesnt_throw(self): s1a = str(uuid7("2023-12-02")) @@ -299,10 +281,7 @@ def test_breakdown_channel_type_doesnt_throw(self): breakdown_by=WebStatsBreakdown.INITIAL_CHANNEL_TYPE, ).results - self.assertEqual( - 1, - len(results), - ) + assert 1 == len(results) def test_limit(self): s1 = str(uuid7("2023-12-02")) @@ -315,23 +294,17 @@ def test_limit(self): ) response_1 = self._run_web_stats_table_query("all", "2023-12-15", limit=1) - self.assertEqual( - [ - ["/", (2, None), (2, None)], - ], - response_1.results, - ) - self.assertEqual(True, response_1.hasMore) + assert [ + ["/", (2, None), (2, None), ""], + ] == response_1.results + assert response_1.hasMore is True response_2 = self._run_web_stats_table_query("all", "2023-12-15", limit=2) - self.assertEqual( - [ - ["/", (2, None), (2, None)], - ["/login", (1, None), (1, None)], - ], - response_2.results, - ) - self.assertEqual(False, response_2.hasMore) + assert [ + ["/", (2, None), (2, None), ""], + ["/login", (1, None), (1, None), ""], + ] == response_2.results + assert response_2.hasMore is False def test_path_filters(self): s1 = str(uuid7("2023-12-02")) @@ -360,15 +333,12 @@ def test_path_filters(self): ], ).results - self.assertEqual( - [ - ["/cleaned/:id", (2, None), (2, None)], - ["/cleaned/:id/path/:id", (1, None), (1, None)], - ["/not-cleaned", (1, None), (1, None)], - ["/thing_c", (1, None), (1, None)], - ], - results, - ) + assert [ + ["/cleaned/:id", (2, None), (2, None), ""], + ["/cleaned/:id/path/:id", (1, None), (1, None), ""], + ["/not-cleaned", (1, None), (1, None), ""], + ["/thing_c", (1, None), (1, None), ""], + ] == results def test_scroll_depth_bounce_rate_one_user(self): self._create_pageviews( @@ -388,14 +358,11 @@ def test_scroll_depth_bounce_rate_one_user(self): include_bounce_rate=True, ).results - self.assertEqual( - [ - ["/a", (1, 0), (1, 0), (0, None), (0.1, None), (0, None)], - ["/b", (1, 0), (1, 0), (None, None), (0.2, None), (0, None)], - ["/c", (1, 0), (1, 0), (None, None), (0.9, None), (1, None)], - ], - results, - ) + assert [ + ["/a", (1, 0), (1, 0), (0, None), (0.1, None), (0, None), ""], + ["/b", (1, 0), (1, 0), (None, None), (0.2, None), (0, None), ""], + ["/c", (1, 0), (1, 0), (None, None), (0.9, None), (1, None), ""], + ] == results def test_scroll_depth_bounce_rate(self): self._create_pageviews( @@ -430,14 +397,11 @@ def test_scroll_depth_bounce_rate(self): include_bounce_rate=True, ).results - self.assertEqual( - [ - ["/a", (3, 0), (4, 0), (1 / 3, None), (0.5, None), (0.5, None)], - ["/b", (2, 0), (2, 0), (None, None), (0.2, None), (0, None)], - ["/c", (2, 0), (2, 0), (None, None), (0.9, None), (1, None)], - ], - results, - ) + assert [ + ["/a", (3, 0), (4, 0), (1 / 3, None), (0.5, None), (0.5, None), ""], + ["/b", (2, 0), (2, 0), (None, None), (0.2, None), (0, None), ""], + ["/c", (2, 0), (2, 0), (None, None), (0.9, None), (1, None), ""], + ] == results def test_scroll_depth_bounce_rate_with_filter(self): self._create_pageviews( @@ -473,12 +437,9 @@ def test_scroll_depth_bounce_rate_with_filter(self): properties=[EventPropertyFilter(key="$pathname", operator=PropertyOperator.EXACT, value="/a")], ).results - self.assertEqual( - [ - ["/a", (3, 0), (4, 0), (1 / 3, None), (0.5, None), (0.5, None)], - ], - results, - ) + assert [ + ["/a", (3, 0), (4, 0), (1 / 3, None), (0.5, None), (0.5, None), ""], + ] == results def test_scroll_depth_bounce_rate_path_cleaning(self): self._create_pageviews( @@ -503,14 +464,11 @@ def test_scroll_depth_bounce_rate_path_cleaning(self): ], ).results - self.assertEqual( - [ - ["/a/:id", (1, 0), (1, 0), (0, None), (0.1, None), (0, None)], - ["/b/:id", (1, 0), (1, 0), (None, None), (0.2, None), (0, None)], - ["/c/:id", (1, 0), (1, 0), (None, None), (0.9, None), (1, None)], - ], - results, - ) + assert [ + ["/a/:id", (1, 0), (1, 0), (0, None), (0.1, None), (0, None), ""], + ["/b/:id", (1, 0), (1, 0), (None, None), (0.2, None), (0, None), ""], + ["/c/:id", (1, 0), (1, 0), (None, None), (0.9, None), (1, None), ""], + ] == results def test_bounce_rate_one_user(self): self._create_pageviews( @@ -529,14 +487,11 @@ def test_bounce_rate_one_user(self): include_bounce_rate=True, ).results - self.assertEqual( - [ - ["/a", (1, 0), (1, 0), (0, None)], - ["/b", (1, 0), (1, 0), (None, None)], - ["/c", (1, 0), (1, 0), (None, None)], - ], - results, - ) + assert [ + ["/a", (1, 0), (1, 0), (0, None), ""], + ["/b", (1, 0), (1, 0), (None, None), ""], + ["/c", (1, 0), (1, 0), (None, None), ""], + ] == results def test_bounce_rate(self): self._create_pageviews( @@ -570,14 +525,11 @@ def test_bounce_rate(self): include_bounce_rate=True, ).results - self.assertEqual( - [ - ["/a", (3, 0), (4, 0), (1 / 3, None)], - ["/b", (2, 0), (2, 0), (None, None)], - ["/c", (2, 0), (2, 0), (None, None)], - ], - results, - ) + assert [ + ["/a", (3, 0), (4, 0), (1 / 3, None), ""], + ["/b", (2, 0), (2, 0), (None, None), ""], + ["/c", (2, 0), (2, 0), (None, None), ""], + ] == results def test_bounce_rate_with_property(self): self._create_pageviews( @@ -612,12 +564,9 @@ def test_bounce_rate_with_property(self): properties=[EventPropertyFilter(key="$pathname", operator=PropertyOperator.EXACT, value="/a")], ).results - self.assertEqual( - [ - ["/a", (3, 0), (4, 0), (1 / 3, None)], - ], - results, - ) + assert [ + ["/a", (3, 0), (4, 0), (1 / 3, None), ""], + ] == results def test_bounce_rate_path_cleaning(self): self._create_pageviews( @@ -641,14 +590,11 @@ def test_bounce_rate_path_cleaning(self): ], ).results - self.assertEqual( - [ - ["/a/:id", (1, 0), (1, 0), (0, None)], - ["/b/:id", (1, 0), (1, 0), (None, None)], - ["/c/:id", (1, 0), (1, 0), (None, None)], - ], - results, - ) + assert [ + ["/a/:id", (1, 0), (1, 0), (0, None), ""], + ["/b/:id", (1, 0), (1, 0), (None, None), ""], + ["/c/:id", (1, 0), (1, 0), (None, None), ""], + ] == results def test_entry_bounce_rate_one_user(self): self._create_pageviews( @@ -667,12 +613,9 @@ def test_entry_bounce_rate_one_user(self): include_bounce_rate=True, ).results - self.assertEqual( - [ - ["/a", (1, None), (3, None), (0, None)], - ], - results, - ) + assert [ + ["/a", (1, None), (3, None), (0, None), ""], + ] == results def test_entry_bounce_rate(self): self._create_pageviews( @@ -706,12 +649,9 @@ def test_entry_bounce_rate(self): include_bounce_rate=True, ).results - self.assertEqual( - [ - ["/a", (3, None), (8, None), (1 / 3, None)], - ], - results, - ) + assert [ + ["/a", (3, None), (8, None), (1 / 3, None), ""], + ] == results def test_entry_bounce_rate_with_property(self): self._create_pageviews( @@ -746,12 +686,9 @@ def test_entry_bounce_rate_with_property(self): properties=[EventPropertyFilter(key="$pathname", operator=PropertyOperator.EXACT, value="/a")], ).results - self.assertEqual( - [ - ["/a", (3, None), (4, None), (1 / 3, None)], - ], - results, - ) + assert [ + ["/a", (3, None), (4, None), (1 / 3, None), ""], + ] == results def test_entry_bounce_rate_path_cleaning(self): self._create_pageviews( @@ -775,12 +712,9 @@ def test_entry_bounce_rate_path_cleaning(self): ], ).results - self.assertEqual( - [ - ["/a/:id", (1, None), (3, None), (0, None)], - ], - results, - ) + assert [ + ["/a/:id", (1, None), (3, None), (0, None), ""], + ] == results def test_source_medium_campaign(self): d1 = "d1" @@ -824,13 +758,10 @@ def test_source_medium_campaign(self): breakdown_by=WebStatsBreakdown.INITIAL_UTM_SOURCE_MEDIUM_CAMPAIGN, ).results - self.assertEqual( - [ - ["google / (none) / (none)", (1, None), (1, None)], - ["news.ycombinator.com / referral / (none)", (1, None), (1, None)], - ], - results, - ) + assert [ + ["google / (none) / (none)", (1, None), (1, None), ""], + ["news.ycombinator.com / referral / (none)", (1, None), (1, None), ""], + ] == results def test_null_in_utm_tags(self): d1 = "d1" @@ -876,10 +807,7 @@ def test_null_in_utm_tags(self): breakdown_by=WebStatsBreakdown.INITIAL_UTM_SOURCE, ).results - self.assertEqual( - [["google", (1, None), (1, None)], [None, (1, None), (1, None)]], - results, - ) + assert [["google", (1, None), (1, None), ""], [None, (1, None), (1, None), ""]] == results def test_is_not_set_filter(self): d1 = "d1" @@ -926,10 +854,7 @@ def test_is_not_set_filter(self): properties=[EventPropertyFilter(key="utm_source", operator=PropertyOperator.IS_NOT_SET)], ).results - self.assertEqual( - [[None, (1, None), (1, None)]], - results, - ) + assert [[None, (1, None), (1, None), ""]] == results def test_same_user_multiple_sessions(self): d1 = "d1" @@ -963,7 +888,7 @@ def test_same_user_multiple_sessions(self): "2024-07-31", breakdown_by=WebStatsBreakdown.INITIAL_UTM_SOURCE, ).results - assert [["google", (1, None), (2, None)]] == results_session + assert [["google", (1, None), (2, None), ""]] == results_session # Try this with a query that uses event properties results_event = self._run_web_stats_table_query( @@ -971,13 +896,13 @@ def test_same_user_multiple_sessions(self): "2024-07-31", breakdown_by=WebStatsBreakdown.PAGE, ).results - assert [["/path", (1, None), (2, None)]] == results_event + assert [["/path", (1, None), (2, None), ""]] == results_event # Try this with a query using the bounce rate results_event = self._run_web_stats_table_query( "all", "2024-07-31", breakdown_by=WebStatsBreakdown.PAGE, include_bounce_rate=True ).results - assert [["/path", (1, 0), (2, 0), (None, None)]] == results_event + assert [["/path", (1, 0), (2, 0), (None, None), ""]] == results_event # Try this with a query using the scroll depth results_event = self._run_web_stats_table_query( @@ -987,7 +912,7 @@ def test_same_user_multiple_sessions(self): include_bounce_rate=True, include_scroll_depth=True, ).results - assert [["/path", (1, 0), (2, 0), (None, None), (None, None), (None, None)]] == results_event + assert [["/path", (1, 0), (2, 0), (None, None), (None, None), (None, None), ""]] == results_event def test_no_session_id(self): d1 = "d1" @@ -1027,7 +952,7 @@ def test_no_session_id(self): breakdown_by=WebStatsBreakdown.PAGE, ).results - assert [["/path", (1, None), (1, None)]] == results + assert [["/path", (1, None), (1, None), ""]] == results def test_cohort_test_filters(self): d1 = "d1" @@ -1089,7 +1014,7 @@ def test_cohort_test_filters(self): breakdown_by=WebStatsBreakdown.PAGE, ).results - assert results == [["/path1", (1, None), (1, None)]] + assert results == [["/path1", (1, None), (1, None), ""]] def test_language_filter(self): d1, s1 = "d1", str(uuid7("2024-07-30")) @@ -1215,10 +1140,10 @@ def test_timezone_filter_general(self): # Brasilia UTC-3, New York UTC-4, Calcutta UTC+5:30, UTC assert results == [ - [-3, (1, None), (4, None)], - [-4, (1, None), (3, None)], - [5.5, (1, None), (2, None)], - [0, (1, None), (1, None)], + [-3, (1, None), (4, None), ""], + [-4, (1, None), (3, None), ""], + [5.5, (1, None), (2, None), ""], + [0, (1, None), (1, None), ""], ] def test_timezone_filter_dst_change(self): @@ -1248,7 +1173,10 @@ def test_timezone_filter_dst_change(self): ).results # Change from UTC-2 to UTC-3 in the middle of the night - assert results == [[-3, (1, None), (4, None)], [-2, (1, None), (2, None)]] + assert results == [ + [-3.0, (1.0, None), (4.0, None), ""], + [-2.0, (1.0, None), (2.0, None), ""], + ] def test_timezone_filter_with_invalid_timezone(self): date = "2024-07-30" @@ -1354,13 +1282,14 @@ def test_conversion_goal_no_conversions(self): "2023-12-01", "2023-12-03", breakdown_by=WebStatsBreakdown.PAGE, action=action ) - assert [["https://www.example.com/foo", (1, None), (0, None), (0, None), (0, None)]] == response.results + assert [["https://www.example.com/foo", (1, None), (0, None), (0, None), (0, None), ""]] == response.results assert [ "context.columns.breakdown_value", "context.columns.visitors", "context.columns.total_conversions", "context.columns.unique_conversions", "context.columns.conversion_rate", + "context.columns.replay_url", ] == response.columns def test_conversion_goal_one_pageview_conversion(self): @@ -1391,13 +1320,14 @@ def test_conversion_goal_one_pageview_conversion(self): "2023-12-01", "2023-12-03", breakdown_by=WebStatsBreakdown.PAGE, action=action ) - assert [["https://www.example.com/foo", (1, None), (1, None), (1, None), (1, None)]] == response.results + assert [["https://www.example.com/foo", (1, None), (1, None), (1, None), (1, None), ""]] == response.results assert [ "context.columns.breakdown_value", "context.columns.visitors", "context.columns.total_conversions", "context.columns.unique_conversions", "context.columns.conversion_rate", + "context.columns.replay_url", ] == response.columns def test_conversion_goal_one_custom_event_conversion(self): @@ -1416,13 +1346,14 @@ def test_conversion_goal_one_custom_event_conversion(self): custom_event="custom_event", ) - assert [[None, (1, None), (1, None), (1, None), (1, None)]] == response.results + assert [[None, (1, None), (1, None), (1, None), (1, None), ""]] == response.results assert [ "context.columns.breakdown_value", "context.columns.visitors", "context.columns.total_conversions", "context.columns.unique_conversions", "context.columns.conversion_rate", + "context.columns.replay_url", ] == response.columns def test_conversion_goal_one_custom_action_conversion(self): @@ -1451,13 +1382,14 @@ def test_conversion_goal_one_custom_action_conversion(self): action=action, ) - assert [[None, (1, None), (1, None), (1, None), (1, None)]] == response.results + assert [[None, (1, None), (1, None), (1, None), (1, None), ""]] == response.results assert [ "context.columns.breakdown_value", "context.columns.visitors", "context.columns.total_conversions", "context.columns.unique_conversions", "context.columns.conversion_rate", + "context.columns.replay_url", ] == response.columns def test_conversion_goal_one_autocapture_conversion(self): @@ -1488,13 +1420,14 @@ def test_conversion_goal_one_autocapture_conversion(self): action=action, ) - assert [[None, (1, None), (1, None), (1, None), (1, None)]] == response.results + assert [[None, (1, None), (1, None), (1, None), (1, None), ""]] == response.results assert [ "context.columns.breakdown_value", "context.columns.visitors", "context.columns.total_conversions", "context.columns.unique_conversions", "context.columns.conversion_rate", + "context.columns.replay_url", ] == response.columns def test_conversion_rate(self): @@ -1539,8 +1472,8 @@ def test_conversion_rate(self): ) assert [ - ["https://www.example.com/foo", (2, None), (3, None), (2, None), (1, None)], - ["https://www.example.com/bar", (2, None), (0, None), (0, None), (0, None)], + ["https://www.example.com/foo", (2, None), (3, None), (2, None), (1, None), ""], + ["https://www.example.com/bar", (2, None), (0, None), (0, None), (0, None), ""], ] == response.results assert [ "context.columns.breakdown_value", @@ -1548,4 +1481,5 @@ def test_conversion_rate(self): "context.columns.total_conversions", "context.columns.unique_conversions", "context.columns.conversion_rate", + "context.columns.replay_url", ] == response.columns diff --git a/posthog/management/commands/change_team_ownership.py b/posthog/management/commands/change_team_ownership.py index 1dac8b957d191..88b02b4b7bd87 100644 --- a/posthog/management/commands/change_team_ownership.py +++ b/posthog/management/commands/change_team_ownership.py @@ -5,7 +5,7 @@ from django.core.management import CommandError from django.core.management.base import BaseCommand -from posthog.models import Organization, Team +from posthog.models import Organization, Team, Project logger = structlog.get_logger(__name__) logger.setLevel(logging.INFO) @@ -40,6 +40,9 @@ def run(options): team = Team.objects.get(pk=team_id) logger.info(f"Team {team_id} is currently in organization {team.organization_id}, named {team.organization.name}") + project = Project.objects.get(pk=team.project_id) + logger.info(f"Team {team_id} is currently in project {team.project_id}, named {project.name}") + org = Organization.objects.get(pk=organization_id) logger.info(f"Target organization {organization_id} is named {org.name}") @@ -48,7 +51,9 @@ def run(options): if live_run: team.organization_id = organization_id + project.organization_id = organization_id team.save() - logger.info("Saved team change") + project.save() + logger.info("Saved team and project changes") else: logger.info("Skipping the team change, pass --live-run to run it") diff --git a/posthog/migrations/0553_feature_flag_config.py b/posthog/migrations/0553_feature_flag_config.py new file mode 100644 index 0000000000000..9c17d14a6147a --- /dev/null +++ b/posthog/migrations/0553_feature_flag_config.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.15 on 2025-01-06 17:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0552_turn_off_all_action_webhooks"), + ] + + operations = [ + migrations.AddField( + model_name="featureflag", + name="is_remote_configuration", + field=models.BooleanField(default=False, null=True, blank=True), + ), + ] diff --git a/posthog/migrations/max_migration.txt b/posthog/migrations/max_migration.txt index fa0b2b9249701..4c0e1bd78eb69 100644 --- a/posthog/migrations/max_migration.txt +++ b/posthog/migrations/max_migration.txt @@ -1 +1 @@ -0552_turn_off_all_action_webhooks \ No newline at end of file +0553_feature_flag_config \ No newline at end of file diff --git a/posthog/models/data_color_theme.py b/posthog/models/data_color_theme.py index c13b6fb79dec4..705983f7ae428 100644 --- a/posthog/models/data_color_theme.py +++ b/posthog/models/data_color_theme.py @@ -14,3 +14,7 @@ class DataColorTheme(models.Model): def __str__(self): return self.name + + @property + def is_global(self): + return self.team_id is None diff --git a/posthog/models/feature_flag/feature_flag.py b/posthog/models/feature_flag/feature_flag.py index beca926b7fbac..c955fbfb90728 100644 --- a/posthog/models/feature_flag/feature_flag.py +++ b/posthog/models/feature_flag/feature_flag.py @@ -54,6 +54,8 @@ class FeatureFlag(models.Model): # whether a feature is sending us rich analytics, like views & interactions. has_enriched_analytics = models.BooleanField(default=False, null=True, blank=True) + is_remote_configuration = models.BooleanField(default=False, null=True, blank=True) + class Meta: constraints = [models.UniqueConstraint(fields=["team", "key"], name="unique key for team")] diff --git a/posthog/schema.py b/posthog/schema.py index 5f25e28122a04..ab7951d5cf28c 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -2255,12 +2255,15 @@ class LLMTrace(BaseModel): events: list[LLMTraceEvent] id: str inputCost: Optional[float] = None + inputState: Optional[Any] = None inputTokens: Optional[float] = None outputCost: Optional[float] = None + outputState: Optional[Any] = None outputTokens: Optional[float] = None person: LLMTracePerson totalCost: Optional[float] = None totalLatency: Optional[float] = None + traceName: Optional[str] = None class LifecycleFilter(BaseModel): diff --git a/posthog/session_recordings/queries/test/listing_recordings/__snapshots__/test_session_recording_list_from_query.ambr b/posthog/session_recordings/queries/test/listing_recordings/__snapshots__/test_session_recording_list_from_query.ambr index 2a56455b71320..314009dcd68de 100644 --- a/posthog/session_recordings/queries/test/listing_recordings/__snapshots__/test_session_recording_list_from_query.ambr +++ b/posthog/session_recordings/queries/test/listing_recordings/__snapshots__/test_session_recording_list_from_query.ambr @@ -176,24 +176,24 @@ 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), globalIn(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), globalIn(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` - HAVING hasAll(groupUniqArray(events.event), ['$pageview', 'custom-event'])))) + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score + FROM session_replay_events AS s + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), or(equals(events.event, 'custom-event'), equals(events.event, '$pageview'))) + 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 @@ -1820,30 +1820,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0))) - GROUP BY events.`$session_id` - HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), equals(events.event, '$pageview'), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), 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 @@ -1875,30 +1875,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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')), and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0))) - GROUP BY events.`$session_id` - HAVING 1))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), and(ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 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 @@ -1970,30 +1970,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0))) - GROUP BY events.`$session_id` - HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), equals(events.event, '$pageview'), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), 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 @@ -2025,30 +2025,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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')), and(ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0)), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0))) - GROUP BY events.`$session_id` - HAVING 1))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), and(ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0), ifNull(equals(nullIf(nullIf(events.`mat_$browser`, ''), 'null'), 'Chrome'), 0)), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 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 @@ -2080,30 +2080,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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'])))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -2135,30 +2135,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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'])))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -2750,30 +2750,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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(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(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)), and(ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1))) - GROUP BY events.`$session_id` - HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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(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(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)), and(ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1), 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 @@ -2845,30 +2845,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), and(ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1))) - GROUP BY events.`$session_id` - HAVING hasAll(groupUniqArray(events.event), ['$pageview'])))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0), ifNull(equals(nullIf(nullIf(events.mat_is_internal_user, ''), 'null'), 'false'), 0), ifNull(equals(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(events.properties, '$browser'), ''), 'null'), '^"|"$', ''), 'Chrome'), 0)), and(ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1), ifNull(notILike(events__person.properties___email, '%@posthog.com%'), 1), 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 @@ -3622,30 +3622,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -3677,30 +3677,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -4292,24 +4292,24 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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'])))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -4341,24 +4341,24 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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'])))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -4470,30 +4470,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -4525,30 +4525,30 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), globalIn(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))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.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 @@ -4650,19 +4650,19 @@ 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('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), globalIn(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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), notEmpty(events.`$session_id`)))), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0)) GROUP BY s.session_id HAVING 1 ORDER BY start_time DESC @@ -5409,30 +5409,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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')), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0))) - GROUP BY events.`$session_id` - HAVING 1))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 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 @@ -5504,30 +5504,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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')), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0))) - GROUP BY events.`$session_id` - HAVING 1))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 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 @@ -5599,30 +5599,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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')), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0))) - GROUP BY events.`$session_id` - HAVING 1))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 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 @@ -5694,30 +5694,30 @@ 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, + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0) AS ongoing, round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score 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), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-21 20:00:00.000000', 6, 'UTC')), 0), globalIn(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')), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 0))) - GROUP BY events.`$session_id` - HAVING 1))) + WHERE and(equals(s.team_id, 99999), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), 0), globalIn(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('explicit_redacted_timestamp.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, 'UTC'), now64(6, 'UTC')), greaterOrEquals(toTimeZone(events.timestamp, 'UTC'), toDateTime64('explicit_redacted_timestamp.000000', 6, 'UTC')), and(ifNull(equals(events__person.properties___email, 'bla'), 0), ifNull(equals(events__person.properties___email, 'bla'), 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 diff --git a/posthog/settings/__init__.py b/posthog/settings/__init__.py index e39ab1506bdb8..947d34235b5f3 100644 --- a/posthog/settings/__init__.py +++ b/posthog/settings/__init__.py @@ -24,6 +24,7 @@ from posthog.settings.async_migrations import * from posthog.settings.celery import * from posthog.settings.data_stores import * +from posthog.settings.dagster import * from posthog.settings.demo import * from posthog.settings.dynamic_settings import * from posthog.settings.ee import * diff --git a/posthog/settings/dagster.py b/posthog/settings/dagster.py new file mode 100644 index 0000000000000..bbecf7880354f --- /dev/null +++ b/posthog/settings/dagster.py @@ -0,0 +1,3 @@ +import os + +DAGSTER_S3_BUCKET = os.getenv("DAGSTER_S3_BUCKET", "posthog-dags") diff --git a/posthog/settings/data_stores.py b/posthog/settings/data_stores.py index 73ce8ed301a70..8134830e41f20 100644 --- a/posthog/settings/data_stores.py +++ b/posthog/settings/data_stores.py @@ -330,7 +330,7 @@ def _parse_kafka_hosts(hosts_string: str) -> list[str]: if not CDP_FUNCTION_EXECUTOR_API_URL: CDP_FUNCTION_EXECUTOR_API_URL = ( - "http://localhost:6738" if DEBUG else "http://ingestion-cdp-function-callbacks.posthog.svc.cluster.local" + "http://localhost:6738" if DEBUG else "http://ingestion-cdp-internal-events.posthog.svc.cluster.local" ) CACHES = { diff --git a/posthog/settings/web.py b/posthog/settings/web.py index d72f523e39615..4a1cbe9450126 100644 --- a/posthog/settings/web.py +++ b/posthog/settings/web.py @@ -251,6 +251,13 @@ ] STATICFILES_STORAGE = "whitenoise.storage.ManifestStaticFilesStorage" + +def static_varies_origin(headers, path, url): + headers["Vary"] = "Accept-Encoding, Origin" + + +WHITENOISE_ADD_HEADERS_FUNCTION = static_varies_origin + AUTH_USER_MODEL = "posthog.User" LOGIN_URL = "/login" diff --git a/posthog/tasks/scheduled.py b/posthog/tasks/scheduled.py index ae19e8532a211..9e29c5745c273 100644 --- a/posthog/tasks/scheduled.py +++ b/posthog/tasks/scheduled.py @@ -7,6 +7,7 @@ from django.conf import settings from posthog.caching.warming import schedule_warming_for_teams_task +from posthog.utils import get_instance_region from posthog.celery import app from posthog.tasks.alerts.checks import ( alerts_backlog_task, @@ -113,11 +114,20 @@ def setup_periodic_tasks(sender: Celery, **kwargs: Any) -> None: ) # Send all instance usage to the Billing service - sender.add_periodic_task( - crontab(hour="4", minute="0"), - send_org_usage_reports.s(), - name="send instance usage report", - ) + region = get_instance_region() + if region == "EU": + # Shift EU reports by 30 minutes to lighten the load + sender.add_periodic_task( + crontab(hour="3", minute="45"), + send_org_usage_reports.s(), + name="send instance usage report", + ) + else: + sender.add_periodic_task( + crontab(hour="4", minute="15"), + send_org_usage_reports.s(), + name="send instance usage report", + ) # Update local usage info for rate limiting purposes - offset by 30 minutes to not clash with the above sender.add_periodic_task( diff --git a/posthog/tasks/test/__snapshots__/test_process_scheduled_changes.ambr b/posthog/tasks/test/__snapshots__/test_process_scheduled_changes.ambr index 5883b794a5c98..d9b42b34cdc76 100644 --- a/posthog/tasks/test/__snapshots__/test_process_scheduled_changes.ambr +++ b/posthog/tasks/test/__snapshots__/test_process_scheduled_changes.ambr @@ -15,7 +15,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -140,7 +141,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -357,6 +359,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -372,6 +375,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -386,7 +390,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -667,7 +672,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE "posthog_featureflag"."id" = 99999 LIMIT 21 @@ -723,7 +729,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -940,6 +947,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -955,6 +963,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -969,7 +978,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -1385,7 +1395,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE "posthog_featureflag"."key" = 'flag-1' LIMIT 21 @@ -1504,6 +1515,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -1519,6 +1531,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -1533,7 +1546,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -1696,7 +1710,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE "posthog_featureflag"."id" = 99999 LIMIT 21 diff --git a/posthog/taxonomy/taxonomy.py b/posthog/taxonomy/taxonomy.py index ef0da98c9efa3..02e9ccf13d128 100644 --- a/posthog/taxonomy/taxonomy.py +++ b/posthog/taxonomy/taxonomy.py @@ -188,6 +188,14 @@ class CoreFilterDefinition(TypedDict): "label": "AI Generation (LLM)", "description": "A call to an LLM model. Contains the input prompt, output, model used and costs.", }, + "$ai_metric": { + "label": "AI Metric (LLM)", + "description": "An evaluation metric for a trace of a generative AI model (LLM). Contains the trace ID, metric name, and metric value.", + }, + "$ai_feedback": { + "label": "AI Feedback (LLM)", + "description": "User-provided feedback for a trace of a generative AI model (LLM).", + }, "Application Opened": { "label": "Application Opened", "description": "When a user opens the mobile app either for the first time or from the foreground.", @@ -1354,6 +1362,21 @@ class CoreFilterDefinition(TypedDict): "description": "The trace ID of the request made to the LLM API. Used to group together multiple generations into a single trace", "examples": ["c9222e05-8708-41b8-98ea-d4a21849e761"], }, + "$ai_metric_name": { + "label": "AI Metric Name (LLM)", + "description": "The name assigned to the metric used to evaluate the LLM trace", + "examples": ["rating", "accuracy"], + }, + "$ai_metric_value": { + "label": "AI Metric Value (LLM)", + "description": "The value assigned to the metric used to evaluate the LLM trace", + "examples": ["negative", "95"], + }, + "$ai_feedback_text": { + "label": "AI Feedback Text (LLM)", + "description": "The text provided by the user for feedback on the LLM trace", + "examples": ['"The response was helpful, but it did not use the provided context."'], + }, }, "numerical_event_properties": {}, "person_properties": {}, diff --git a/posthog/templates/two_factor/_base.html b/posthog/templates/two_factor/_base.html index cec5ddadc9c6e..b067b415d22a3 100644 --- a/posthog/templates/two_factor/_base.html +++ b/posthog/templates/two_factor/_base.html @@ -69,7 +69,7 @@ input:focus + label { transform: translate(0, 0) scale(1); cursor: pointer; - color: var(--primary); + color: var(--accent-primary); } input#id_understand { margin-left: 0.5rem; diff --git a/posthog/temporal/data_imports/external_data_job.py b/posthog/temporal/data_imports/external_data_job.py index dd5b725ffcb1c..dcfdd0e7427ae 100644 --- a/posthog/temporal/data_imports/external_data_job.py +++ b/posthog/temporal/data_imports/external_data_job.py @@ -239,6 +239,7 @@ async def run(self, inputs: ExternalDataWorkflowInputs): team_id=inputs.team_id, schema_id=inputs.external_data_schema_id, source_id=inputs.external_data_source_id, + billable=inputs.billable, ) job_id, incremental, source_type = await workflow.execute_activity( @@ -286,6 +287,7 @@ async def run(self, inputs: ExternalDataWorkflowInputs): run_id=job_id, schema_id=inputs.external_data_schema_id, source_id=inputs.external_data_source_id, + reset_pipeline=inputs.reset_pipeline, ) timeout_params = ( diff --git a/posthog/temporal/data_imports/pipelines/helpers.py b/posthog/temporal/data_imports/pipelines/helpers.py index d0cc153f4e11d..9be1f1111e236 100644 --- a/posthog/temporal/data_imports/pipelines/helpers.py +++ b/posthog/temporal/data_imports/pipelines/helpers.py @@ -1,7 +1,5 @@ -import uuid from posthog.warehouse.models import ExternalDataJob from django.db.models import F -from posthog.warehouse.models.external_data_source import ExternalDataSource from posthog.warehouse.util import database_sync_to_async @@ -13,10 +11,3 @@ def aget_external_data_job(team_id, job_id): @database_sync_to_async def aupdate_job_count(job_id: str, team_id: int, count: int): ExternalDataJob.objects.filter(id=job_id, team_id=team_id).update(rows_synced=F("rows_synced") + count) - - -@database_sync_to_async -def aremove_reset_pipeline(source_id: uuid.UUID): - source = ExternalDataSource.objects.get(id=source_id) - source.job_inputs.pop("reset_pipeline", None) - source.save() diff --git a/posthog/temporal/data_imports/pipelines/pipeline/pipeline.py b/posthog/temporal/data_imports/pipelines/pipeline/pipeline.py index c1a8b95bb0abe..10054464a43d9 100644 --- a/posthog/temporal/data_imports/pipelines/pipeline/pipeline.py +++ b/posthog/temporal/data_imports/pipelines/pipeline/pipeline.py @@ -19,7 +19,7 @@ from posthog.temporal.data_imports.pipelines.pipeline.hogql_schema import HogQLSchema from posthog.temporal.data_imports.pipelines.pipeline_sync import validate_schema_and_update_table_sync from posthog.temporal.data_imports.util import prepare_s3_files_for_querying -from posthog.warehouse.models import DataWarehouseTable, ExternalDataJob, ExternalDataSchema, ExternalDataSource +from posthog.warehouse.models import DataWarehouseTable, ExternalDataJob, ExternalDataSchema class PipelineNonDLT: @@ -68,9 +68,8 @@ def run(self): self._logger.debug("Deleting existing table due to reset_pipeline being set") self._delta_table_helper.reset_table() - source: ExternalDataSource = self._job.pipeline - source.job_inputs.pop("reset_pipeline", None) - source.save() + self._schema.sync_type_config.pop("reset_pipeline", None) + self._schema.save() for item in self._resource: py_table = None diff --git a/posthog/temporal/data_imports/workflow_activities/create_job_model.py b/posthog/temporal/data_imports/workflow_activities/create_job_model.py index 729ff9960ef44..b5ad95bb1376f 100644 --- a/posthog/temporal/data_imports/workflow_activities/create_job_model.py +++ b/posthog/temporal/data_imports/workflow_activities/create_job_model.py @@ -21,6 +21,7 @@ class CreateExternalDataJobModelActivityInputs: team_id: int schema_id: uuid.UUID source_id: uuid.UUID + billable: bool def get_pipeline_version() -> str: @@ -48,6 +49,12 @@ def create_external_data_job_model_activity( pipeline_version = get_pipeline_version() + # Temp until V2 is rolled out + if inputs.billable is False: + billable = False + else: + billable = pipeline_version != ExternalDataJob.PipelineVersion.V2 + job = ExternalDataJob.objects.create( team_id=inputs.team_id, pipeline_id=inputs.source_id, @@ -57,7 +64,7 @@ def create_external_data_job_model_activity( workflow_id=activity.info().workflow_id, workflow_run_id=activity.info().workflow_run_id, pipeline_version=pipeline_version, - billable=pipeline_version != ExternalDataJob.PipelineVersion.V2, + billable=billable, ) schema = ExternalDataSchema.objects.get(team_id=inputs.team_id, id=inputs.schema_id) diff --git a/posthog/temporal/data_imports/workflow_activities/import_data_sync.py b/posthog/temporal/data_imports/workflow_activities/import_data_sync.py index a9a058bb52261..0d566087520a5 100644 --- a/posthog/temporal/data_imports/workflow_activities/import_data_sync.py +++ b/posthog/temporal/data_imports/workflow_activities/import_data_sync.py @@ -2,7 +2,7 @@ import uuid from datetime import datetime from dateutil import parser -from typing import Any +from typing import Any, Optional from django.conf import settings from django.db import close_old_connections @@ -35,6 +35,7 @@ class ImportDataActivityInputs: schema_id: uuid.UUID source_id: uuid.UUID run_id: str + reset_pipeline: Optional[bool] = None def process_incremental_last_value(value: Any | None, field_type: IncrementalFieldType | None) -> Any | None: @@ -90,7 +91,16 @@ def import_data_activity_sync(inputs: ImportDataActivityInputs): _trim_source_job_inputs(model.pipeline) - reset_pipeline = model.pipeline.job_inputs.get("reset_pipeline", "False") == "True" + schema: ExternalDataSchema | None = model.schema + assert schema is not None + + if inputs.reset_pipeline is not None: + reset_pipeline = inputs.reset_pipeline + else: + reset_pipeline = schema.sync_type_config.get("reset_pipeline", False) is True + + logger.debug(f"schema.sync_type_config = {schema.sync_type_config}") + logger.debug(f"reset_pipeline = {reset_pipeline}") schema = ( ExternalDataSchema.objects.prefetch_related("source") @@ -99,24 +109,26 @@ def import_data_activity_sync(inputs: ImportDataActivityInputs): ) endpoints = [schema.name] + processed_incremental_last_value = None - if settings.TEMPORAL_TASK_QUEUE == DATA_WAREHOUSE_TASK_QUEUE_V2: - # Get the V2 last value, if it's not set yet (e.g. the first run), then fallback to the V1 value - processed_incremental_last_value = process_incremental_last_value( - schema.sync_type_config.get("incremental_field_last_value_v2"), - schema.sync_type_config.get("incremental_field_type"), - ) + if reset_pipeline is not True: + if settings.TEMPORAL_TASK_QUEUE == DATA_WAREHOUSE_TASK_QUEUE_V2: + # Get the V2 last value, if it's not set yet (e.g. the first run), then fallback to the V1 value + processed_incremental_last_value = process_incremental_last_value( + schema.sync_type_config.get("incremental_field_last_value_v2"), + schema.sync_type_config.get("incremental_field_type"), + ) - if processed_incremental_last_value is None: + if processed_incremental_last_value is None: + processed_incremental_last_value = process_incremental_last_value( + schema.sync_type_config.get("incremental_field_last_value"), + schema.sync_type_config.get("incremental_field_type"), + ) + else: processed_incremental_last_value = process_incremental_last_value( schema.sync_type_config.get("incremental_field_last_value"), schema.sync_type_config.get("incremental_field_type"), ) - else: - processed_incremental_last_value = process_incremental_last_value( - schema.sync_type_config.get("incremental_field_last_value"), - schema.sync_type_config.get("incremental_field_type"), - ) if schema.is_incremental: logger.debug(f"Incremental last value being used is: {processed_incremental_last_value}") @@ -541,7 +553,6 @@ def _run( rows_synced=F("rows_synced") + total_rows_synced ) - source = ExternalDataSource.objects.get(id=inputs.source_id) - source.job_inputs.pop("reset_pipeline", None) - - source.save() + schema = ExternalDataSchema.objects.get(id=inputs.schema_id) + schema.sync_type_config.pop("reset_pipeline", None) + schema.save() diff --git a/posthog/temporal/tests/data_imports/test_end_to_end.py b/posthog/temporal/tests/data_imports/test_end_to_end.py index 5bebcd72a5f57..a8fffef2eff6f 100644 --- a/posthog/temporal/tests/data_imports/test_end_to_end.py +++ b/posthog/temporal/tests/data_imports/test_end_to_end.py @@ -123,6 +123,7 @@ async def _run( mock_data_response: Any, sync_type: Optional[ExternalDataSchema.SyncType] = None, sync_type_config: Optional[dict] = None, + billable: Optional[bool] = None, ): source = await sync_to_async(ExternalDataSource.objects.create)( source_id=uuid.uuid4(), @@ -147,6 +148,7 @@ async def _run( team_id=team.id, external_data_source_id=source.pk, external_data_schema_id=schema.id, + billable=billable if billable is not None else True, ) await _execute_run(workflow_id, inputs, mock_data_response) @@ -172,8 +174,8 @@ async def _run( continue assert name in (res.columns or []) - await sync_to_async(source.refresh_from_db)() - assert source.job_inputs.get("reset_pipeline", None) is None + await sync_to_async(schema.refresh_from_db)() + assert schema.sync_type_config.get("reset_pipeline", None) is None return workflow_id, inputs @@ -520,8 +522,9 @@ async def test_reset_pipeline(team, stripe_balance_transaction): schema_name="BalanceTransaction", table_name="stripe_balancetransaction", source_type="Stripe", - job_inputs={"stripe_secret_key": "test-key", "stripe_account_id": "acct_id", "reset_pipeline": "True"}, + job_inputs={"stripe_secret_key": "test-key", "stripe_account_id": "acct_id"}, mock_data_response=stripe_balance_transaction["data"], + sync_type_config={"reset_pipeline": True}, ) @@ -1249,23 +1252,41 @@ async def test_delete_table_on_reset(team, stripe_balance_transaction): schema_name="BalanceTransaction", table_name="stripe_balancetransaction", source_type="Stripe", - job_inputs={"stripe_secret_key": "test-key", "stripe_account_id": "acct_id", "reset_pipeline": "True"}, + job_inputs={"stripe_secret_key": "test-key", "stripe_account_id": "acct_id"}, mock_data_response=stripe_balance_transaction["data"], + sync_type_config={"reset_pipeline": True}, ) - source = await sync_to_async(ExternalDataSource.objects.get)(id=inputs.external_data_source_id) + schema = await sync_to_async(ExternalDataSchema.objects.get)(id=inputs.external_data_schema_id) - assert source.job_inputs is not None and isinstance(source.job_inputs, dict) - source.job_inputs["reset_pipeline"] = "True" + assert schema.sync_type_config is not None and isinstance(schema.sync_type_config, dict) + schema.sync_type_config["reset_pipeline"] = True - await sync_to_async(source.save)() + await sync_to_async(schema.save)() await _execute_run(str(uuid.uuid4()), inputs, stripe_balance_transaction["data"]) mock_delta_table_delete.assert_called() mock_s3_delete.assert_called() - await sync_to_async(source.refresh_from_db)() + await sync_to_async(schema.refresh_from_db)() + + assert schema.sync_type_config is not None and isinstance(schema.sync_type_config, dict) + assert "reset_pipeline" not in schema.sync_type_config.keys() + + +@pytest.mark.django_db(transaction=True) +@pytest.mark.asyncio +async def test_billable_job(team, stripe_balance_transaction): + workflow_id, inputs = await _run( + team=team, + schema_name="BalanceTransaction", + table_name="stripe_balancetransaction", + source_type="Stripe", + job_inputs={"stripe_secret_key": "test-key", "stripe_account_id": "acct_id"}, + mock_data_response=stripe_balance_transaction["data"], + billable=False, + ) - assert source.job_inputs is not None and isinstance(source.job_inputs, dict) - assert "reset_pipeline" not in source.job_inputs.keys() + run: ExternalDataJob = await get_latest_run_if_exists(team_id=team.pk, pipeline_id=inputs.external_data_source_id) + assert run.billable is False diff --git a/posthog/temporal/tests/external_data/test_external_data_job.py b/posthog/temporal/tests/external_data/test_external_data_job.py index 43ac76f8ba564..ac09eb19b351a 100644 --- a/posthog/temporal/tests/external_data/test_external_data_job.py +++ b/posthog/temporal/tests/external_data/test_external_data_job.py @@ -173,7 +173,7 @@ def test_create_external_job_activity(activity_environment, team, **kwargs): test_1_schema = _create_schema("test-1", new_source, team) inputs = CreateExternalDataJobModelActivityInputs( - team_id=team.id, source_id=new_source.pk, schema_id=test_1_schema.id + team_id=team.id, source_id=new_source.pk, schema_id=test_1_schema.id, billable=True ) run_id, _, __ = activity_environment.run(create_external_data_job_model_activity, inputs) @@ -199,7 +199,9 @@ def test_create_external_job_activity_schemas_exist(activity_environment, team, source_id=new_source.pk, ) - inputs = CreateExternalDataJobModelActivityInputs(team_id=team.id, source_id=new_source.pk, schema_id=schema.id) + inputs = CreateExternalDataJobModelActivityInputs( + team_id=team.id, source_id=new_source.pk, schema_id=schema.id, billable=True + ) run_id, _, __ = activity_environment.run(create_external_data_job_model_activity, inputs) diff --git a/posthog/temporal/utils.py b/posthog/temporal/utils.py index fa96af6442489..23d8e406ea8c0 100644 --- a/posthog/temporal/utils.py +++ b/posthog/temporal/utils.py @@ -1,4 +1,5 @@ import dataclasses +from typing import Optional import uuid @@ -8,3 +9,5 @@ class ExternalDataWorkflowInputs: team_id: int external_data_source_id: uuid.UUID external_data_schema_id: uuid.UUID | None = None + billable: bool = True + reset_pipeline: Optional[bool] = None diff --git a/posthog/test/__snapshots__/test_feature_flag.ambr b/posthog/test/__snapshots__/test_feature_flag.ambr index 70a55d29e8020..408bf89b11165 100644 --- a/posthog/test/__snapshots__/test_feature_flag.ambr +++ b/posthog/test/__snapshots__/test_feature_flag.ambr @@ -214,7 +214,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -431,6 +432,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -446,6 +448,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -460,7 +463,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -601,7 +605,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -886,6 +891,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -901,6 +907,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -915,7 +922,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -1256,6 +1264,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -1271,6 +1280,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -1285,7 +1295,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -1426,7 +1437,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -1524,7 +1536,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -1765,6 +1778,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -1780,6 +1794,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -1794,7 +1809,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") @@ -2249,7 +2265,8 @@ "posthog_featureflag"."performed_rollback", "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", - "posthog_featureflag"."has_enriched_analytics" + "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration" FROM "posthog_featureflag" WHERE ("posthog_featureflag"."active" AND NOT "posthog_featureflag"."deleted" @@ -2540,6 +2557,7 @@ "posthog_featureflag"."ensure_experience_continuity", "posthog_featureflag"."usage_dashboard_id", "posthog_featureflag"."has_enriched_analytics", + "posthog_featureflag"."is_remote_configuration", T4."id", T4."key", T4."name", @@ -2555,6 +2573,7 @@ T4."ensure_experience_continuity", T4."usage_dashboard_id", T4."has_enriched_analytics", + T4."is_remote_configuration", T5."id", T5."key", T5."name", @@ -2569,7 +2588,8 @@ T5."performed_rollback", T5."ensure_experience_continuity", T5."usage_dashboard_id", - T5."has_enriched_analytics" + T5."has_enriched_analytics", + T5."is_remote_configuration" FROM "posthog_survey" LEFT OUTER JOIN "posthog_featureflag" ON ("posthog_survey"."linked_flag_id" = "posthog_featureflag"."id") LEFT OUTER JOIN "posthog_featureflag" T4 ON ("posthog_survey"."targeting_flag_id" = T4."id") diff --git a/posthog/test/base.py b/posthog/test/base.py index dc0760f4a3b5b..6b9157f8ff55e 100644 --- a/posthog/test/base.py +++ b/posthog/test/base.py @@ -162,7 +162,18 @@ def clean_varying_query_parts(query, replace_all_numbers): query, ) # replace explicit timestamps in cohort queries - query = re.sub(r"timestamp > '20\d\d-\d\d-\d\d \d\d:\d\d:\d\d'", r"timestamp > 'explicit_timestamp'", query) + query = re.sub( + r"timestamp > '20\d\d-\d\d-\d\d \d\d:\d\d:\d\d'", r"timestamp > 'explicit_redacted_timestamp'", query + ) + # and where the HogQL doesn't match the above + # KLUDGE we tend not to replace dates in tests so trying to avoid replacing every date here + if "equals(argMax(person_distinct_id_overrides.is_deleted" in query or "INSERT INTO cohortpeople" in query: + # those tests have multiple varying dates like toDateTime64('2025-01-08 00:00:00.000000', 6, 'UTC') + query = re.sub( + r"toDateTime64\('20\d\d-\d\d-\d\d \d\d:\d\d:\d\d(.\d+)', 6, 'UTC'\)", + r"toDateTime64('explicit_redacted_timestamp\1', 6, 'UTC')", + query, + ) # replace cohort generated conditions query = re.sub( r"_condition_\d+_level", diff --git a/posthog/test/test_middleware.py b/posthog/test/test_middleware.py index a66d26b8332bf..4f9ab8ac96bed 100644 --- a/posthog/test/test_middleware.py +++ b/posthog/test/test_middleware.py @@ -142,7 +142,7 @@ class TestAutoProjectMiddleware(APIBaseTest): @classmethod def setUpTestData(cls): super().setUpTestData() - cls.base_app_num_queries = 47 + cls.base_app_num_queries = 49 # Create another team that the user does have access to cls.second_team = create_team(organization=cls.organization, name="Second Life") diff --git a/posthog/warehouse/api/external_data_schema.py b/posthog/warehouse/api/external_data_schema.py index 56208208d5eaf..b3bcafcb45d4e 100644 --- a/posthog/warehouse/api/external_data_schema.py +++ b/posthog/warehouse/api/external_data_schema.py @@ -184,9 +184,9 @@ def update(self, instance: ExternalDataSchema, validated_data: dict[str, Any]) - sync_external_data_job_workflow(instance, create=False) if trigger_refresh: - source: ExternalDataSource = instance.source - source.job_inputs.update({"reset_pipeline": True}) - source.save() + instance.sync_type_config.update({"reset_pipeline": True}) + validated_data["sync_type_config"].update({"reset_pipeline": True}) + trigger_external_data_workflow(instance) return super().update(instance, validated_data) @@ -266,9 +266,7 @@ def resync(self, request: Request, *args: Any, **kwargs: Any): if latest_running_job and latest_running_job.workflow_id and latest_running_job.status == "Running": cancel_external_data_workflow(latest_running_job.workflow_id) - source: ExternalDataSource = instance.source - source.job_inputs.update({"reset_pipeline": True}) - source.save() + instance.sync_type_config.update({"reset_pipeline": True}) try: trigger_external_data_workflow(instance) diff --git a/posthog/warehouse/api/external_data_source.py b/posthog/warehouse/api/external_data_source.py index 2bb5680bdbc2e..6c6ca695addea 100644 --- a/posthog/warehouse/api/external_data_source.py +++ b/posthog/warehouse/api/external_data_source.py @@ -266,18 +266,22 @@ def safely_get_queryset(self, queryset): "created_by", Prefetch( "jobs", - queryset=ExternalDataJob.objects.filter(status="Completed").order_by("-created_at"), + queryset=ExternalDataJob.objects.filter(status="Completed", team_id=self.team_id).order_by( + "-created_at" + )[:1], to_attr="ordered_jobs", ), Prefetch( "schemas", - queryset=ExternalDataSchema.objects.exclude(deleted=True) + queryset=ExternalDataSchema.objects.filter(team_id=self.team_id) + .exclude(deleted=True) .select_related("table__credential", "table__external_data_source") .order_by("name"), ), Prefetch( "schemas", - queryset=ExternalDataSchema.objects.exclude(deleted=True) + queryset=ExternalDataSchema.objects.filter(team_id=self.team_id) + .exclude(deleted=True) .filter(should_sync=True) .select_related("source", "table__credential", "table__external_data_source"), to_attr="active_schemas", diff --git a/posthog/warehouse/api/test/test_external_data_schema.py b/posthog/warehouse/api/test/test_external_data_schema.py index b63f3f1dfab4f..ebdb357085936 100644 --- a/posthog/warehouse/api/test/test_external_data_schema.py +++ b/posthog/warehouse/api/test/test_external_data_schema.py @@ -183,8 +183,8 @@ def test_update_schema_change_sync_type(self): assert response.status_code == 200 mock_trigger_external_data_workflow.assert_called_once() - source.refresh_from_db() - assert source.job_inputs.get("reset_pipeline") == "True" + schema.refresh_from_db() + assert schema.sync_type_config.get("reset_pipeline") is True def test_update_schema_change_sync_type_incremental_field(self): source = ExternalDataSource.objects.create( @@ -211,10 +211,9 @@ def test_update_schema_change_sync_type_incremental_field(self): assert response.status_code == 200 mock_trigger_external_data_workflow.assert_called_once() - source.refresh_from_db() - assert source.job_inputs.get("reset_pipeline") == "True" - schema.refresh_from_db() + + assert schema.sync_type_config.get("reset_pipeline") is True assert schema.sync_type_config.get("incremental_field") == "field" assert schema.sync_type_config.get("incremental_field_type") == "integer" diff --git a/posthog/warehouse/models/external_data_schema.py b/posthog/warehouse/models/external_data_schema.py index 423468a9ce945..e48ec83fbcbb0 100644 --- a/posthog/warehouse/models/external_data_schema.py +++ b/posthog/warehouse/models/external_data_schema.py @@ -54,7 +54,7 @@ class SyncFrequency(models.TextChoices): last_synced_at = models.DateTimeField(null=True, blank=True) sync_type = models.CharField(max_length=128, choices=SyncType.choices, null=True, blank=True) - # { "incremental_field": string, "incremental_field_type": string, "incremental_field_last_value": any, "incremental_field_last_value_v2": any } + # { "incremental_field": string, "incremental_field_type": string, "incremental_field_last_value": any, "incremental_field_last_value_v2": any, "reset_pipeline": bool } sync_type_config = models.JSONField( default=dict, blank=True, diff --git a/posthog/warehouse/models/external_data_source.py b/posthog/warehouse/models/external_data_source.py index 22a46bc4cb039..d3aaaa8c3d4e1 100644 --- a/posthog/warehouse/models/external_data_source.py +++ b/posthog/warehouse/models/external_data_source.py @@ -79,8 +79,8 @@ def reload_schemas(self): from posthog.warehouse.models.external_data_schema import ExternalDataSchema for schema in ( - ExternalDataSchema.objects.exclude(deleted=True) - .filter(team_id=self.team.pk, source_id=self.id, should_sync=True) + ExternalDataSchema.objects.filter(team_id=self.team.pk, source_id=self.id, should_sync=True) + .exclude(deleted=True) .all() ): try: diff --git a/products/llm_observability/frontend/ConversationDisplay/ConversationDisplay.tsx b/products/llm_observability/frontend/ConversationDisplay/ConversationDisplay.tsx index f21ec681f7149..1e09bcd42d020 100644 --- a/products/llm_observability/frontend/ConversationDisplay/ConversationDisplay.tsx +++ b/products/llm_observability/frontend/ConversationDisplay/ConversationDisplay.tsx @@ -7,9 +7,20 @@ export function ConversationDisplay({ eventProperties }: { eventProperties: Even return ( <>
- +
- + ) } diff --git a/products/llm_observability/frontend/ConversationDisplay/ConversationMessagesDisplay.tsx b/products/llm_observability/frontend/ConversationDisplay/ConversationMessagesDisplay.tsx index a771af479434a..98e966049c7b6 100644 --- a/products/llm_observability/frontend/ConversationDisplay/ConversationMessagesDisplay.tsx +++ b/products/llm_observability/frontend/ConversationDisplay/ConversationMessagesDisplay.tsx @@ -3,62 +3,59 @@ import { LemonButton } from '@posthog/lemon-ui' import clsx from 'clsx' import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' import { JSONViewer } from 'lib/components/JSONViewer' -import { IconArrowDown, IconArrowUp, IconExclamation } from 'lib/lemon-ui/icons' +import { IconExclamation } from 'lib/lemon-ui/icons' import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' import { useState } from 'react' -import { EventType } from '~/types' - +import { LLMInputOutput } from '../LLMInputOutput' import { CompatMessage } from '../types' import { normalizeMessages } from '../utils' export function ConversationMessagesDisplay({ - eventProperties, + input, + output, + httpStatus, + bordered = false, }: { - eventProperties: EventType['properties'] + input: any + output: any + httpStatus?: number + bordered?: boolean }): JSX.Element { - const input = normalizeMessages(eventProperties.$ai_input, 'user') - const output = normalizeMessages(eventProperties.$ai_output_choices || eventProperties.$ai_output, 'assistant') - const { $ai_http_status: httpStatus } = eventProperties + const inputNormalized = normalizeMessages(input, 'user') + const outputNormalized = normalizeMessages(output, 'assistant') return ( -
-

- - Input -

- {input?.map((message, i) => ( - <> - - {i < input.length - 1 &&
/* Spacer connecting messages */} - - )) || ( -
- Missing input -
- )} -

- - Output{output && output.length > 1 ? ' (multiple choices)' : ''} -

- {output?.map((message, i) => ( - <> - - {i < output.length - 1 && ( -
/* Spacer connecting messages visually */ - )} - - )) || ( -
- - {httpStatus ? `Generation failed with HTTP status ${httpStatus}` : 'Missing output'} -
- )} -
+ ( + <> + + {i < inputNormalized.length - 1 && ( +
/* Spacer connecting messages visually */ + )} + + )) || ( +
+ No input +
+ ) + } + outputDisplay={ + outputNormalized?.map((message, i) => ) || ( +
+ + {httpStatus ? `Generation failed with HTTP status ${httpStatus}` : 'Missing output'} +
+ ) + } + outputHeading={`Output${outputNormalized && outputNormalized.length > 1 ? ' (multiple choices)' : ''}`} + bordered={bordered} + /> ) } -function MessageDisplay({ message, isOutput }: { message: CompatMessage; isOutput?: boolean }): JSX.Element { +export function LLMMessageDisplay({ message, isOutput }: { message: CompatMessage; isOutput?: boolean }): JSX.Element { const [isRenderingMarkdown, setIsRenderingMarkdown] = useState(!!message.content) const { role, content, ...additionalKwargs } = message @@ -69,26 +66,28 @@ function MessageDisplay({ message, isOutput }: { message: CompatMessage; isOutpu className={clsx( 'rounded border text-default', isOutput - ? 'bg-[var(--background-success-subtle)]' - : role === 'system' - ? 'bg-[var(--background-secondary)]' + ? 'bg-[var(--bg-fill-success-tertiary)]' : role === 'user' - ? 'bg-bg-light' - : 'bg-[var(--blue-50)] dark:bg-[var(--blue-800)]' // We don't have a semantic color using blue + ? 'bg-[var(--bg-fill-tertiary)]' + : role === 'assistant' + ? 'bg-[var(--bg-fill-info-tertiary)]' + : null // e.g. system )} >
{role} {content && ( - : } - tooltip="Toggle Markdown rendering" - onClick={() => setIsRenderingMarkdown(!isRenderingMarkdown)} - /> + <> + : } + tooltip="Toggle Markdown rendering" + onClick={() => setIsRenderingMarkdown(!isRenderingMarkdown)} + /> + + )} -
{!!content && (
@@ -102,12 +101,10 @@ function MessageDisplay({ message, isOutput }: { message: CompatMessage; isOutpu key={key} name={key} src={value} - collapseStringsAfterLength={200} - displayDataTypes={false} - // shouldCollapse limits depth shown at first. `> 4` is chosen so that we do show + // `collapsed` limits depth shown at first. 4 is chosen so that we do show // function arguments in `tool_calls`, but if an argument is an object, // its child objects are collapsed by default - shouldCollapse={({ namespace }) => namespace.length > 5} + collapsed={4} /> ))}
diff --git a/products/llm_observability/frontend/ConversationDisplay/MetadataHeader.tsx b/products/llm_observability/frontend/ConversationDisplay/MetadataHeader.tsx index 9eb2993b13ae1..d112399c67a70 100644 --- a/products/llm_observability/frontend/ConversationDisplay/MetadataHeader.tsx +++ b/products/llm_observability/frontend/ConversationDisplay/MetadataHeader.tsx @@ -1,26 +1,23 @@ -import { LemonTag, Tooltip } from '@posthog/lemon-ui' import classNames from 'classnames' -import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' import { lowercaseFirstLetter } from 'lib/utils' -import React from 'react' -import { EventType } from '~/types' +import { MetadataTag } from '../components/MetadataTag' export function MetadataHeader({ - eventProperties, + inputTokens, + outputTokens, + totalCostUsd, + model, + latency, className, }: { - eventProperties: EventType['properties'] + inputTokens?: number + outputTokens?: number + totalCostUsd?: number + model?: string + latency?: number className?: string }): JSX.Element { - const { - $ai_input_tokens: inputTokens, - $ai_output_tokens: outputTokens, - $ai_total_cost_usd: totalCostUsd, - $ai_model: model, - $ai_latency: latency, - } = eventProperties - return (
{typeof latency === 'number' && ( @@ -34,7 +31,7 @@ export function MetadataHeader({ )} {model && ( - + {model} )} @@ -44,26 +41,3 @@ export function MetadataHeader({
) } - -function MetadataTag({ - children, - label, - copyable = false, -}: { - children: string - label: string - copyable?: boolean -}): JSX.Element { - let wrappedChildren: React.ReactNode = children - if (copyable) { - wrappedChildren = ( - - {children} - - ) - } else { - wrappedChildren = {children} - } - - return {wrappedChildren} -} diff --git a/products/llm_observability/frontend/LLMInputOutput.tsx b/products/llm_observability/frontend/LLMInputOutput.tsx new file mode 100644 index 0000000000000..482aa5f14ddac --- /dev/null +++ b/products/llm_observability/frontend/LLMInputOutput.tsx @@ -0,0 +1,30 @@ +import { IconArrowDown, IconArrowUp } from 'lib/lemon-ui/icons' + +export function LLMInputOutput({ + inputDisplay, + outputDisplay, + inputHeading = 'Input', + outputHeading = 'Output', + bordered = false, +}: { + inputDisplay: JSX.Element | JSX.Element[] + outputDisplay: JSX.Element | JSX.Element[] + inputHeading?: string + outputHeading?: string + bordered?: boolean +}): JSX.Element { + return ( +
+

+ + {inputHeading} +

+ {inputDisplay} +

+ + {outputHeading} +

+ {outputDisplay} +
+ ) +} diff --git a/products/llm_observability/frontend/LLMObservabilityTraceScene.tsx b/products/llm_observability/frontend/LLMObservabilityTraceScene.tsx index cc933421b1a6e..78d1e50cdc69f 100644 --- a/products/llm_observability/frontend/LLMObservabilityTraceScene.tsx +++ b/products/llm_observability/frontend/LLMObservabilityTraceScene.tsx @@ -1,21 +1,28 @@ -import { LemonDivider, LemonTag, Link, SpinnerOverlay } from '@posthog/lemon-ui' +import { IconAIText, IconReceipt } from '@posthog/icons' +import { LemonDivider, LemonTag, LemonTagProps, Link, SpinnerOverlay, Tooltip } from '@posthog/lemon-ui' import classNames from 'classnames' -import { useValues } from 'kea' +import { BindLogic, useValues } from 'kea' +import { JSONViewer } from 'lib/components/JSONViewer' import { NotFound } from 'lib/components/NotFound' -import React, { useMemo } from 'react' +import { IconArrowDown, IconArrowUp } from 'lib/lemon-ui/icons' +import { range } from 'lib/utils' +import React from 'react' import { InsightEmptyState, InsightErrorState } from 'scenes/insights/EmptyStates' import { PersonDisplay } from 'scenes/persons/PersonDisplay' import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' -import { LLMTrace, LLMTraceEvent, TracesQueryResponse } from '~/queries/schema' +import { LLMTrace, LLMTraceEvent } from '~/queries/schema' +import { FeedbackTag } from './components/FeedbackTag' +import { MetricTag } from './components/MetricTag' import { ConversationMessagesDisplay } from './ConversationDisplay/ConversationMessagesDisplay' import { MetadataHeader } from './ConversationDisplay/MetadataHeader' import { ParametersHeader } from './ConversationDisplay/ParametersHeader' -import { getDataNodeLogicProps, llmObservabilityTraceLogic } from './llmObservabilityTraceLogic' -import { formatLLMCost, formatLLMLatency, formatLLMUsage, removeMilliseconds } from './utils' +import { LLMInputOutput } from './LLMInputOutput' +import { llmObservabilityTraceDataLogic } from './llmObservabilityTraceDataLogic' +import { llmObservabilityTraceLogic } from './llmObservabilityTraceLogic' +import { formatLLMCost, formatLLMLatency, formatLLMUsage, isLLMTraceEvent, removeMilliseconds } from './utils' export const scene: SceneExport = { component: LLMObservabilityTraceScene, @@ -23,20 +30,22 @@ export const scene: SceneExport = { } export function LLMObservabilityTraceScene(): JSX.Element { - const { traceId, query, eventId, cachedTraceResponse } = useValues(llmObservabilityTraceLogic) + const { traceId, query, cachedTraceResponse } = useValues(llmObservabilityTraceLogic) - const { response, responseLoading, responseError } = useValues( - dataNodeLogic(getDataNodeLogicProps({ traceId, query, cachedResults: cachedTraceResponse })) + return ( + + + ) +} - const traceResponse = response as TracesQueryResponse | null - const event = useMemo(() => { - const trace = traceResponse?.results?.[0] - if (!trace) { - return undefined - } - return eventId ? trace.events.find((event) => event.id === eventId) : trace.events[0] - }, [traceResponse, eventId]) +function TraceSceneWrapper(): JSX.Element { + const { eventId } = useValues(llmObservabilityTraceLogic) + const { trace, showableEvents, event, responseLoading, responseError, feedbackEvents, metricEvents } = + useValues(llmObservabilityTraceDataLogic) return ( <> @@ -44,13 +53,17 @@ export function LLMObservabilityTraceScene(): JSX.Element { ) : responseError ? ( - ) : !traceResponse || traceResponse.results.length === 0 ? ( + ) : !trace ? ( ) : ( -
- +
+
- +
@@ -59,104 +72,248 @@ export function LLMObservabilityTraceScene(): JSX.Element { ) } -function Chip({ title, children }: { title: string; children: React.ReactNode }): JSX.Element { +function Chip({ + title, + children, + icon, +}: { + title: string + children: React.ReactNode + icon?: JSX.Element +}): JSX.Element { return ( -
- {title} - {children} -
+ + + {title} + {children} + + ) } function UsageChip({ event }: { event: LLMTraceEvent | LLMTrace }): JSX.Element | null { const usage = formatLLMUsage(event) - return usage ? {usage} : null -} - -function CostChip({ cost, title }: { cost: number; title: string }): JSX.Element { - return {formatLLMCost(cost)} + return usage ? ( + }> + {usage} + + ) : null } -function TraceMetadata({ trace }: { trace: LLMTrace }): JSX.Element { +function TraceMetadata({ + trace, + metricEvents, + feedbackEvents, +}: { + trace: LLMTrace + metricEvents: LLMTraceEvent[] + feedbackEvents: LLMTraceEvent[] +}): JSX.Element { return ( -
+
{'person' in trace && ( )} - {typeof trace.inputCost === 'number' && } - {typeof trace.outputCost === 'number' && } - {typeof trace.totalCost === 'number' && } + {typeof trace.inputCost === 'number' && ( + }> + {formatLLMCost(trace.inputCost)} + + )} + {typeof trace.outputCost === 'number' && ( + }> + {formatLLMCost(trace.outputCost)} + + )} + {typeof trace.totalCost === 'number' && ( + }> + {formatLLMCost(trace.totalCost)} + + )} + {metricEvents.map((metric) => ( + + ))} + {feedbackEvents.map((feedback) => ( + + ))}
) } -function TraceSidebar({ trace, eventId }: { trace: LLMTrace; eventId?: string | null }): JSX.Element { +function TraceSidebar({ + trace, + eventId, + events, +}: { + trace: LLMTrace + eventId?: string | null + events: LLMTraceEvent[] +}): JSX.Element { return ( -