Skip to content

Commit 72b8595

Browse files
authored
fix(surveys): support gt/lt operator in event filters (#2777)
## Problem web sdk supports triggering surveys from events with property filters, but it's missing support for lt/gt <!-- Who are we building for, what are their needs, why is this important? --> ## Changes adds support for lt/gt <!-- What is changed and what information would be useful to a reviewer? --> ## Release info Sub-libraries affected ### Libraries affected <!-- Please mark which libraries will require a version bump. --> - [ ] All of them - [x] posthog-js (web) - [ ] posthog-js-lite (web lite) - [ ] posthog-node - [ ] posthog-react-native - [ ] @posthog/react - [ ] @posthog/ai - [ ] @posthog/nextjs-config - [ ] @posthog/nuxt - [ ] @posthog/rollup-plugin - [ ] @posthog/webpack-plugin ## Checklist - [x] Tests for new code - [x] Accounted for the impact of any changes across different platforms - [x] Accounted for backwards compatibility of any changes (no breaking changes!) - [x] Took care not to unnecessarily increase the bundle size ### If releasing new changes - [x] Ran `pnpm changeset` to generate a changeset file - [x] Added the "release" label to the PR to indicate we're publishing new versions for the affected packages <!-- For more details check RELEASING.md -->
1 parent 6c67818 commit 72b8595

File tree

4 files changed

+52
-4
lines changed

4 files changed

+52
-4
lines changed

.changeset/plenty-things-listen.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'posthog-js': minor
3+
---
4+
5+
support lt/gt operator in survey event property filters

packages/browser/src/__tests__/utils/survey-event-receiver.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,36 @@ describe('survey-event-receiver', () => {
363363
registeredHook('purchase', createEventPayload('purchase', { any_prop: 'any_value' }))
364364
expect(surveyEventReceiver.getSurveys()).toContain('no-filters-test')
365365
})
366+
367+
it('activates survey with gt (greater than) numeric property match', () => {
368+
const survey = createSurveyWithPropertyFilters('gt-test', 'purchase', {
369+
amount: { values: ['100'], operator: 'gt' },
370+
})
371+
372+
const surveyEventReceiver = new SurveyEventReceiver(instance)
373+
surveyEventReceiver.register([survey])
374+
const registeredHook = mockAddCaptureHook.mock.calls[0][0]
375+
376+
;(instance.getSurveys as jest.Mock).mockImplementation((callback) => callback([survey]))
377+
378+
registeredHook('purchase', createEventPayload('purchase', { amount: 150 }))
379+
expect(surveyEventReceiver.getSurveys()).toContain('gt-test')
380+
})
381+
382+
it('activates survey with lt (less than) numeric property match', () => {
383+
const survey = createSurveyWithPropertyFilters('lt-test', 'purchase', {
384+
amount: { values: ['100'], operator: 'lt' },
385+
})
386+
387+
const surveyEventReceiver = new SurveyEventReceiver(instance)
388+
surveyEventReceiver.register([survey])
389+
const registeredHook = mockAddCaptureHook.mock.calls[0][0]
390+
391+
;(instance.getSurveys as jest.Mock).mockImplementation((callback) => callback([survey]))
392+
393+
registeredHook('purchase', createEventPayload('purchase', { amount: 50 }))
394+
expect(surveyEventReceiver.getSurveys()).toContain('lt-test')
395+
})
366396
})
367397

368398
describe('action based surveys', () => {

packages/browser/src/posthog-surveys-types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ export enum SurveyEventType {
1111
Cancellation = 'cancelEvents',
1212
}
1313

14+
// Extended operator type to include numeric operators not in PropertyMatchType
15+
export type PropertyOperator = PropertyMatchType | 'gt' | 'lt'
16+
1417
export type PropertyFilters = {
1518
[propertyName: string]: {
1619
values: string[]
17-
operator: PropertyMatchType
20+
operator: PropertyOperator
1821
}
1922
}
2023

packages/browser/src/utils/property-utils.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { isNull, isUndefined } from '@posthog/core'
22
import { jsonStringify } from '../request'
3-
import { PropertyFilters } from '../posthog-surveys-types'
4-
import type { Properties, PropertyMatchType } from '../types'
3+
import { PropertyFilters, PropertyOperator } from '../posthog-surveys-types'
4+
import type { Properties } from '../types'
55
import { isMatchingRegex } from './regex-utils'
66

77
export function getPersonPropertiesHash(
@@ -12,7 +12,7 @@ export function getPersonPropertiesHash(
1212
return jsonStringify({ distinct_id, userPropertiesToSet, userPropertiesToSetOnce })
1313
}
1414

15-
export const propertyComparisons: Record<PropertyMatchType, (targets: string[], values: string[]) => boolean> = {
15+
export const propertyComparisons: Record<PropertyOperator, (targets: string[], values: string[]) => boolean> = {
1616
exact: (targets, values) => values.some((value) => targets.some((target) => value === target)),
1717
is_not: (targets, values) => values.every((value) => targets.every((target) => value !== target)),
1818
regex: (targets, values) => values.some((value) => targets.some((target) => isMatchingRegex(value, target))),
@@ -21,6 +21,16 @@ export const propertyComparisons: Record<PropertyMatchType, (targets: string[],
2121
values.map(toLowerCase).some((value) => targets.map(toLowerCase).some((target) => value.includes(target))),
2222
not_icontains: (targets, values) =>
2323
values.map(toLowerCase).every((value) => targets.map(toLowerCase).every((target) => !value.includes(target))),
24+
gt: (targets, values) =>
25+
values.some((value) => {
26+
const numValue = parseFloat(value)
27+
return !isNaN(numValue) && targets.some((t) => numValue > parseFloat(t))
28+
}),
29+
lt: (targets, values) =>
30+
values.some((value) => {
31+
const numValue = parseFloat(value)
32+
return !isNaN(numValue) && targets.some((t) => numValue < parseFloat(t))
33+
}),
2434
}
2535

2636
const toLowerCase = (v: string): string => v.toLowerCase()

0 commit comments

Comments
 (0)