mirror of
https://github.com/umami-software/umami.git
synced 2026-02-06 21:57:16 +01:00
implement UTM filters and fields
This commit is contained in:
parent
7514af4236
commit
49adaa32d0
11 changed files with 336 additions and 55 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { Button, Column, Grid, List, ListItem } from '@umami/react-zen';
|
||||
import { Button, Column, Grid, List, ListItem, ListSection } from '@umami/react-zen';
|
||||
import { useState } from 'react';
|
||||
import { useFields, useMessages } from '@/components/hooks';
|
||||
import { type FieldGroup, useFields, useMessages } from '@/components/hooks';
|
||||
|
||||
export function FieldSelectForm({
|
||||
selectedFields = [],
|
||||
|
|
@ -13,7 +13,7 @@ export function FieldSelectForm({
|
|||
}) {
|
||||
const [selected, setSelected] = useState(selectedFields);
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { fields } = useFields();
|
||||
const { fields, groupLabels } = useFields();
|
||||
|
||||
const handleChange = (value: string[]) => {
|
||||
setSelected(value);
|
||||
|
|
@ -24,17 +24,38 @@ export function FieldSelectForm({
|
|||
onClose();
|
||||
};
|
||||
|
||||
const groupedFields = fields
|
||||
.filter(field => field.name !== 'event')
|
||||
.reduce(
|
||||
(acc, field) => {
|
||||
const group = field.group;
|
||||
if (!acc[group]) {
|
||||
acc[group] = [];
|
||||
}
|
||||
acc[group].push(field);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<FieldGroup, typeof fields>,
|
||||
);
|
||||
|
||||
return (
|
||||
<Column gap="6">
|
||||
<List value={selected} onChange={handleChange} selectionMode="multiple">
|
||||
{fields.map(({ name, label }) => {
|
||||
return (
|
||||
<ListItem key={name} id={name}>
|
||||
{label}
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
<Column gap="3" overflowY="auto" maxHeight="400px">
|
||||
<List value={selected} onChange={handleChange} selectionMode="multiple">
|
||||
{groupLabels.map(({ key: groupKey, label }) => {
|
||||
const groupFields = groupedFields[groupKey];
|
||||
return (
|
||||
<ListSection key={groupKey} title={label}>
|
||||
{groupFields.map(field => (
|
||||
<ListItem key={field.name} id={field.name}>
|
||||
{field.filterLabel}
|
||||
</ListItem>
|
||||
))}
|
||||
</ListSection>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Column>
|
||||
<Grid columns="1fr 1fr" gap>
|
||||
<Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>
|
||||
<Button onPress={handleApply} variant="primary">
|
||||
|
|
|
|||
|
|
@ -4,10 +4,14 @@ import {
|
|||
AppWindow,
|
||||
Cpu,
|
||||
Earth,
|
||||
Fingerprint,
|
||||
Globe,
|
||||
KeyRound,
|
||||
Landmark,
|
||||
Languages,
|
||||
Laptop,
|
||||
Layers,
|
||||
Link2,
|
||||
LogIn,
|
||||
LogOut,
|
||||
MapPin,
|
||||
|
|
@ -15,9 +19,11 @@ import {
|
|||
Monitor,
|
||||
Network,
|
||||
Search,
|
||||
Send,
|
||||
Share2,
|
||||
SquareSlash,
|
||||
Tag,
|
||||
Target,
|
||||
Type,
|
||||
} from '@/components/icons';
|
||||
import { Lightning } from '@/components/svg';
|
||||
|
|
@ -154,6 +160,41 @@ export function WebsiteExpandedMenu({
|
|||
},
|
||||
].filter(filterExcluded),
|
||||
},
|
||||
{
|
||||
label: formatMessage(labels.utm),
|
||||
items: [
|
||||
{
|
||||
id: 'utmSource',
|
||||
label: formatMessage(labels.source),
|
||||
path: updateParams({ view: 'utmSource' }),
|
||||
icon: <Link2 />,
|
||||
},
|
||||
{
|
||||
id: 'utmMedium',
|
||||
label: formatMessage(labels.medium),
|
||||
path: updateParams({ view: 'utmMedium' }),
|
||||
icon: <Send />,
|
||||
},
|
||||
{
|
||||
id: 'utmCampaign',
|
||||
label: formatMessage(labels.campaign),
|
||||
path: updateParams({ view: 'utmCampaign' }),
|
||||
icon: <Target />,
|
||||
},
|
||||
{
|
||||
id: 'utmContent',
|
||||
label: formatMessage(labels.content),
|
||||
path: updateParams({ view: 'utmContent' }),
|
||||
icon: <Layers />,
|
||||
},
|
||||
{
|
||||
id: 'utmTerm',
|
||||
label: formatMessage(labels.term),
|
||||
path: updateParams({ view: 'utmTerm' }),
|
||||
icon: <KeyRound />,
|
||||
},
|
||||
].filter(filterExcluded),
|
||||
},
|
||||
{
|
||||
label: formatMessage(labels.other),
|
||||
items: [
|
||||
|
|
@ -173,7 +214,7 @@ export function WebsiteExpandedMenu({
|
|||
id: 'distinctId',
|
||||
label: formatMessage(labels.distinctId),
|
||||
path: updateParams({ view: 'distinctId' }),
|
||||
icon: <Tag />,
|
||||
icon: <Fingerprint />,
|
||||
},
|
||||
{
|
||||
id: 'tag',
|
||||
|
|
|
|||
|
|
@ -88,6 +88,31 @@ export function CompareTables({ websiteId }: { websiteId: string }) {
|
|||
label: formatMessage(labels.events),
|
||||
path: renderPath('event'),
|
||||
},
|
||||
{
|
||||
id: 'utmSource',
|
||||
label: formatMessage(labels.utmSource),
|
||||
path: renderPath('utmSource'),
|
||||
},
|
||||
{
|
||||
id: 'utmMedium',
|
||||
label: formatMessage(labels.utmMedium),
|
||||
path: renderPath('utmMedium'),
|
||||
},
|
||||
{
|
||||
id: 'utmCampaign',
|
||||
label: formatMessage(labels.utmCampaign),
|
||||
path: renderPath('utmCampaign'),
|
||||
},
|
||||
{
|
||||
id: 'utmContent',
|
||||
label: formatMessage(labels.utmContent),
|
||||
path: renderPath('utmContent'),
|
||||
},
|
||||
{
|
||||
id: 'utmTerm',
|
||||
label: formatMessage(labels.utmTerm),
|
||||
path: renderPath('utmTerm'),
|
||||
},
|
||||
{
|
||||
id: 'hostname',
|
||||
label: formatMessage(labels.hostname),
|
||||
|
|
|
|||
|
|
@ -1,24 +1,142 @@
|
|||
import { useMessages } from './useMessages';
|
||||
|
||||
export type FieldGroup = 'url' | 'sources' | 'location' | 'environment' | 'utm' | 'other';
|
||||
|
||||
export interface Field {
|
||||
name: string;
|
||||
filterLabel: string;
|
||||
label: string;
|
||||
group: FieldGroup;
|
||||
}
|
||||
|
||||
export function useFields() {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
const fields = [
|
||||
{ name: 'path', type: 'string', label: formatMessage(labels.path) },
|
||||
{ name: 'query', type: 'string', label: formatMessage(labels.query) },
|
||||
{ name: 'title', type: 'string', label: formatMessage(labels.pageTitle) },
|
||||
{ name: 'referrer', type: 'string', label: formatMessage(labels.referrer) },
|
||||
{ name: 'browser', type: 'string', label: formatMessage(labels.browser) },
|
||||
{ name: 'os', type: 'string', label: formatMessage(labels.os) },
|
||||
{ name: 'device', type: 'string', label: formatMessage(labels.device) },
|
||||
{ name: 'country', type: 'string', label: formatMessage(labels.country) },
|
||||
{ name: 'region', type: 'string', label: formatMessage(labels.region) },
|
||||
{ name: 'city', type: 'string', label: formatMessage(labels.city) },
|
||||
{ name: 'hostname', type: 'string', label: formatMessage(labels.hostname) },
|
||||
{ name: 'distinctId', type: 'string', label: formatMessage(labels.distinctId) },
|
||||
{ name: 'tag', type: 'string', label: formatMessage(labels.tag) },
|
||||
{ name: 'event', type: 'string', label: formatMessage(labels.event) },
|
||||
const fields: Field[] = [
|
||||
{
|
||||
name: 'path',
|
||||
filterLabel: formatMessage(labels.path),
|
||||
label: formatMessage(labels.path),
|
||||
group: 'url',
|
||||
},
|
||||
{
|
||||
name: 'query',
|
||||
filterLabel: formatMessage(labels.query),
|
||||
label: formatMessage(labels.query),
|
||||
group: 'url',
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
filterLabel: formatMessage(labels.pageTitle),
|
||||
label: formatMessage(labels.pageTitle),
|
||||
group: 'url',
|
||||
},
|
||||
{
|
||||
name: 'referrer',
|
||||
filterLabel: formatMessage(labels.referrer),
|
||||
label: formatMessage(labels.referrer),
|
||||
group: 'sources',
|
||||
},
|
||||
{
|
||||
name: 'country',
|
||||
filterLabel: formatMessage(labels.country),
|
||||
label: formatMessage(labels.country),
|
||||
group: 'location',
|
||||
},
|
||||
{
|
||||
name: 'region',
|
||||
filterLabel: formatMessage(labels.region),
|
||||
label: formatMessage(labels.region),
|
||||
group: 'location',
|
||||
},
|
||||
{
|
||||
name: 'city',
|
||||
filterLabel: formatMessage(labels.city),
|
||||
label: formatMessage(labels.city),
|
||||
group: 'location',
|
||||
},
|
||||
{
|
||||
name: 'browser',
|
||||
filterLabel: formatMessage(labels.browser),
|
||||
label: formatMessage(labels.browser),
|
||||
group: 'environment',
|
||||
},
|
||||
{
|
||||
name: 'os',
|
||||
filterLabel: formatMessage(labels.os),
|
||||
label: formatMessage(labels.os),
|
||||
group: 'environment',
|
||||
},
|
||||
{
|
||||
name: 'device',
|
||||
filterLabel: formatMessage(labels.device),
|
||||
label: formatMessage(labels.device),
|
||||
group: 'environment',
|
||||
},
|
||||
{
|
||||
name: 'utmSource',
|
||||
filterLabel: formatMessage(labels.source),
|
||||
label: formatMessage(labels.utmSource),
|
||||
group: 'utm',
|
||||
},
|
||||
{
|
||||
name: 'utmMedium',
|
||||
filterLabel: formatMessage(labels.medium),
|
||||
label: formatMessage(labels.utmMedium),
|
||||
group: 'utm',
|
||||
},
|
||||
{
|
||||
name: 'utmCampaign',
|
||||
filterLabel: formatMessage(labels.campaign),
|
||||
label: formatMessage(labels.utmCampaign),
|
||||
group: 'utm',
|
||||
},
|
||||
{
|
||||
name: 'utmContent',
|
||||
filterLabel: formatMessage(labels.content),
|
||||
label: formatMessage(labels.utmContent),
|
||||
group: 'utm',
|
||||
},
|
||||
{
|
||||
name: 'utmTerm',
|
||||
filterLabel: formatMessage(labels.term),
|
||||
label: formatMessage(labels.utmTerm),
|
||||
group: 'utm',
|
||||
},
|
||||
{
|
||||
name: 'hostname',
|
||||
filterLabel: formatMessage(labels.hostname),
|
||||
label: formatMessage(labels.hostname),
|
||||
group: 'other',
|
||||
},
|
||||
{
|
||||
name: 'distinctId',
|
||||
filterLabel: formatMessage(labels.distinctId),
|
||||
label: formatMessage(labels.distinctId),
|
||||
group: 'other',
|
||||
},
|
||||
{
|
||||
name: 'tag',
|
||||
filterLabel: formatMessage(labels.tag),
|
||||
label: formatMessage(labels.tag),
|
||||
group: 'other',
|
||||
},
|
||||
{
|
||||
name: 'event',
|
||||
filterLabel: formatMessage(labels.event),
|
||||
label: formatMessage(labels.event),
|
||||
group: 'other',
|
||||
},
|
||||
];
|
||||
|
||||
return { fields };
|
||||
const groupLabels: { key: FieldGroup; label: string }[] = [
|
||||
{ key: 'url', label: formatMessage(labels.url) },
|
||||
{ key: 'sources', label: formatMessage(labels.sources) },
|
||||
{ key: 'location', label: formatMessage(labels.location) },
|
||||
{ key: 'environment', label: formatMessage(labels.environment) },
|
||||
{ key: 'utm', label: formatMessage(labels.utm) },
|
||||
{ key: 'other', label: formatMessage(labels.other) },
|
||||
];
|
||||
|
||||
return { fields, groupLabels };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ export function useFilterParameters() {
|
|||
tag,
|
||||
hostname,
|
||||
distinctId,
|
||||
utmSource,
|
||||
utmMedium,
|
||||
utmCampaign,
|
||||
utmContent,
|
||||
utmTerm,
|
||||
page,
|
||||
pageSize,
|
||||
search,
|
||||
|
|
@ -45,6 +50,11 @@ export function useFilterParameters() {
|
|||
tag,
|
||||
hostname,
|
||||
distinctId,
|
||||
utmSource,
|
||||
utmMedium,
|
||||
utmCampaign,
|
||||
utmContent,
|
||||
utmTerm,
|
||||
search,
|
||||
segment,
|
||||
cohort,
|
||||
|
|
@ -66,6 +76,11 @@ export function useFilterParameters() {
|
|||
tag,
|
||||
hostname,
|
||||
distinctId,
|
||||
utmSource,
|
||||
utmMedium,
|
||||
utmCampaign,
|
||||
utmContent,
|
||||
utmTerm,
|
||||
page,
|
||||
pageSize,
|
||||
search,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ import {
|
|||
Icon,
|
||||
List,
|
||||
ListItem,
|
||||
ListSection,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuSection,
|
||||
MenuTrigger,
|
||||
Popover,
|
||||
Row,
|
||||
|
|
@ -15,7 +17,7 @@ import { endOfDay, subMonths } from 'date-fns';
|
|||
import type { Key } from 'react';
|
||||
import { Empty } from '@/components/common/Empty';
|
||||
import { FilterRecord } from '@/components/common/FilterRecord';
|
||||
import { useFields, useMessages, useMobile } from '@/components/hooks';
|
||||
import { type FieldGroup, useFields, useMessages, useMobile } from '@/components/hooks';
|
||||
import { Plus } from '@/components/icons';
|
||||
|
||||
export interface FieldFiltersProps {
|
||||
|
|
@ -27,11 +29,25 @@ export interface FieldFiltersProps {
|
|||
|
||||
export function FieldFilters({ websiteId, value, exclude = [], onChange }: FieldFiltersProps) {
|
||||
const { formatMessage, messages } = useMessages();
|
||||
const { fields } = useFields();
|
||||
const { fields, groupLabels } = useFields();
|
||||
const startDate = subMonths(endOfDay(new Date()), 6);
|
||||
const endDate = endOfDay(new Date());
|
||||
const { isMobile } = useMobile();
|
||||
|
||||
const groupedFields = fields
|
||||
.filter(({ name }) => !exclude.includes(name))
|
||||
.reduce(
|
||||
(acc, field) => {
|
||||
const group = field.group;
|
||||
if (!acc[group]) {
|
||||
acc[group] = [];
|
||||
}
|
||||
acc[group].push(field);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<FieldGroup, typeof fields>,
|
||||
);
|
||||
|
||||
const updateFilter = (name: string, props: Record<string, any>) => {
|
||||
onChange(value.map(filter => (filter.name === name ? { ...filter, ...props } : filter)));
|
||||
};
|
||||
|
|
@ -66,32 +82,44 @@ export function FieldFilters({ websiteId, value, exclude = [], onChange }: Field
|
|||
onAction={handleAdd}
|
||||
style={{ maxHeight: 'calc(100vh - 2rem)', overflowY: 'auto' }}
|
||||
>
|
||||
{fields
|
||||
.filter(({ name }) => !exclude.includes(name))
|
||||
.map(field => {
|
||||
const isDisabled = !!value.find(({ name }) => name === field.name);
|
||||
return (
|
||||
<MenuItem key={field.name} id={field.name} isDisabled={isDisabled}>
|
||||
{field.label}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
{groupLabels.map(({ key: groupKey, label }) => {
|
||||
const groupFields = groupedFields[groupKey];
|
||||
return (
|
||||
<MenuSection key={groupKey} title={label}>
|
||||
{groupFields.map(field => {
|
||||
const isDisabled = !!value.find(({ name }) => name === field.name);
|
||||
return (
|
||||
<MenuItem key={field.name} id={field.name} isDisabled={isDisabled}>
|
||||
{field.filterLabel}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</MenuSection>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</Popover>
|
||||
</MenuTrigger>
|
||||
</Row>
|
||||
<Column display={{ xs: 'none', md: 'flex' }} border="right" paddingRight="3" marginRight="6">
|
||||
<List onAction={handleAdd}>
|
||||
{fields
|
||||
.filter(({ name }) => !exclude.includes(name))
|
||||
.map(field => {
|
||||
const isDisabled = !!value.find(({ name }) => name === field.name);
|
||||
return (
|
||||
<ListItem key={field.name} id={field.name} isDisabled={isDisabled}>
|
||||
{field.label}
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
{groupLabels.map(({ key: groupKey, label }) => {
|
||||
const groupFields = groupedFields[groupKey];
|
||||
if (!groupFields || groupFields.length === 0) return null;
|
||||
|
||||
return (
|
||||
<ListSection key={groupKey} title={label}>
|
||||
{groupFields.map(field => {
|
||||
const isDisabled = !!value.find(({ name }) => name === field.name);
|
||||
return (
|
||||
<ListItem key={field.name} id={field.name} isDisabled={isDisabled}>
|
||||
{field.filterLabel}
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</ListSection>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Column>
|
||||
<Column overflow="auto" gapY="4" style={{ contain: 'layout' }}>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export const labels = defineMessages({
|
|||
joinTeam: { id: 'label.join-team', defaultMessage: 'Join team' },
|
||||
settings: { id: 'label.settings', defaultMessage: 'Settings' },
|
||||
owner: { id: 'label.owner', defaultMessage: 'Owner' },
|
||||
url: { id: 'label.url', defaultMessage: 'URL' },
|
||||
teamOwner: { id: 'label.team-owner', defaultMessage: 'Team owner' },
|
||||
teamManager: { id: 'label.team-manager', defaultMessage: 'Team manager' },
|
||||
teamMember: { id: 'label.team-member', defaultMessage: 'Team member' },
|
||||
|
|
@ -245,6 +246,16 @@ export const labels = defineMessages({
|
|||
device: { id: 'label.device', defaultMessage: 'Device' },
|
||||
pageTitle: { id: 'label.pageTitle', defaultMessage: 'Page title' },
|
||||
tag: { id: 'label.tag', defaultMessage: 'Tag' },
|
||||
source: { id: 'label.source', defaultMessage: 'Source' },
|
||||
medium: { id: 'label.medium', defaultMessage: 'Medium' },
|
||||
campaign: { id: 'label.campaign', defaultMessage: 'Campaign' },
|
||||
content: { id: 'label.content', defaultMessage: 'Content' },
|
||||
term: { id: 'label.term', defaultMessage: 'Term' },
|
||||
utmSource: { id: 'label.utm-source', defaultMessage: 'UTM source' },
|
||||
utmMedium: { id: 'label.utm-medium', defaultMessage: 'UTM medium' },
|
||||
utmCampaign: { id: 'label.utm-campaign', defaultMessage: 'UTM campaign' },
|
||||
utmContent: { id: 'label.utm-content', defaultMessage: 'UTM content' },
|
||||
utmTerm: { id: 'label.utm-term', defaultMessage: 'UTM term' },
|
||||
segment: { id: 'label.segment', defaultMessage: 'Segment' },
|
||||
cohort: { id: 'label.cohort', defaultMessage: 'Cohort' },
|
||||
minute: { id: 'label.minute', defaultMessage: 'Minute' },
|
||||
|
|
@ -311,9 +322,7 @@ export const labels = defineMessages({
|
|||
channel: { id: 'label.channel', defaultMessage: 'Channel' },
|
||||
channels: { id: 'label.channels', defaultMessage: 'Channels' },
|
||||
sources: { id: 'label.sources', defaultMessage: 'Sources' },
|
||||
medium: { id: 'label.medium', defaultMessage: 'Medium' },
|
||||
campaigns: { id: 'label.campaigns', defaultMessage: 'Campaigns' },
|
||||
content: { id: 'label.content', defaultMessage: 'Content' },
|
||||
terms: { id: 'label.terms', defaultMessage: 'Terms' },
|
||||
direct: { id: 'label.direct', defaultMessage: 'Direct' },
|
||||
referral: { id: 'label.referral', defaultMessage: 'Referral' },
|
||||
|
|
|
|||
|
|
@ -44,6 +44,11 @@ export const EVENT_COLUMNS = [
|
|||
'event',
|
||||
'tag',
|
||||
'hostname',
|
||||
'utmSource',
|
||||
'utmMedium',
|
||||
'utmCampaign',
|
||||
'utmContent',
|
||||
'utmTerm',
|
||||
];
|
||||
|
||||
export const SESSION_COLUMNS = [
|
||||
|
|
@ -83,6 +88,11 @@ export const FILTER_COLUMNS = {
|
|||
event: 'event_name',
|
||||
tag: 'tag',
|
||||
eventType: 'event_type',
|
||||
utmSource: 'utm_source',
|
||||
utmMedium: 'utm_medium',
|
||||
utmCampaign: 'utm_campaign',
|
||||
utmContent: 'utm_content',
|
||||
utmTerm: 'utm_term',
|
||||
};
|
||||
|
||||
export const COLLECTION_TYPE = {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ export const filterParams = {
|
|||
distinctId: z.string().optional(),
|
||||
language: z.string().optional(),
|
||||
event: z.string().optional(),
|
||||
utmSource: z.string().optional(),
|
||||
utmMedium: z.string().optional(),
|
||||
utmCampaign: z.string().optional(),
|
||||
utmContent: z.string().optional(),
|
||||
utmTerm: z.string().optional(),
|
||||
segment: z.uuid().optional(),
|
||||
cohort: z.uuid().optional(),
|
||||
eventType: z.coerce.number().int().positive().optional(),
|
||||
|
|
@ -94,6 +99,11 @@ export const fieldsParam = z.enum([
|
|||
'distinctId',
|
||||
'language',
|
||||
'event',
|
||||
'utmSource',
|
||||
'utmMedium',
|
||||
'utmCampaign',
|
||||
'utmContent',
|
||||
'utmTerm',
|
||||
]);
|
||||
|
||||
export const reportTypeParam = z.enum([
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
|
|||
${joinSessionQuery}
|
||||
where website_event.website_id = {{websiteId::uuid}}
|
||||
and website_event.created_at between {{startDate}} and {{endDate}}
|
||||
and website_event.event_type != 2
|
||||
${filterQuery}
|
||||
group by time
|
||||
order by 1
|
||||
|
|
@ -59,8 +60,10 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
|
|||
count(distinct session_id) as value
|
||||
from website_event
|
||||
${cohortQuery}
|
||||
${excludeBounceQuery}
|
||||
where website_id = {websiteId:UUID}
|
||||
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
||||
and event_type != 2
|
||||
${filterQuery}
|
||||
group by time
|
||||
order by time
|
||||
|
|
@ -75,6 +78,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
|
|||
${excludeBounceQuery}
|
||||
where website_id = {websiteId:UUID}
|
||||
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
||||
and event_type != 2
|
||||
${filterQuery}
|
||||
group by time
|
||||
order by time
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ async function relationalQuery(
|
|||
website_event.session_id, website_event.visit_id
|
||||
) as t
|
||||
group by ${parseFieldsByName(fields)}
|
||||
order by 1 desc, 2 desc
|
||||
order by 2 desc, 1 desc
|
||||
limit 500
|
||||
`,
|
||||
queryParams,
|
||||
|
|
@ -119,7 +119,7 @@ async function clickhouseQuery(
|
|||
session_id, visit_id
|
||||
) as t
|
||||
group by ${parseFieldsByName(fields)}
|
||||
order by 1 desc, 2 desc
|
||||
order by 2 desc, 1 desc
|
||||
limit 500
|
||||
`,
|
||||
queryParams,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue