diff --git a/posthog/api/test/test_survey.py b/posthog/api/test/test_survey.py index e90ebea3b41b1..e164cc89f256c 100644 --- a/posthog/api/test/test_survey.py +++ b/posthog/api/test/test_survey.py @@ -748,6 +748,34 @@ def test_survey_targeting_flag_validation(self): assert updated_survey_deletes_targeting_flag.status_code == status.HTTP_200_OK + def test_survey_targeting_flag_numeric_validation(self): + survey_with_targeting = self.client.post( + f"/api/projects/{self.team.id}/surveys/", + data={ + "name": "survey with numeric targeting", + "type": "popover", + "targeting_flag_filters": { + "groups": [ + { + "variant": None, + "rollout_percentage": None, + "properties": [ + { + "key": "$browser_version", + "value": "10", + "operator": "gt", + "type": "person", + } + ], + } + ] + }, + "conditions": {"url": ""}, + }, + format="json", + ) + assert survey_with_targeting.status_code == status.HTTP_201_CREATED + def test_updating_survey_to_send_none_linked_flag_removes_linking(self): linked_flag = FeatureFlag.objects.create(team=self.team, key="early-access", created_by=self.user) diff --git a/posthog/models/feature_flag/flag_matching.py b/posthog/models/feature_flag/flag_matching.py index b53e10a4342a0..9a8961dd4a862 100644 --- a/posthog/models/feature_flag/flag_matching.py +++ b/posthog/models/feature_flag/flag_matching.py @@ -503,10 +503,7 @@ def condition_eval(key, condition): # in properties_to_q, in empty_or_null_with_value_q # These need to come in before the expr so they're available to use inside the expr. # Same holds for the group queries below. - type_property_annotations = { - prop_key: Func(F(prop_field), function="JSONB_TYPEOF", output_field=CharField()) - for prop_key, prop_field in properties_with_math_operators - } + type_property_annotations = _get_property_type_annotations(properties_with_math_operators) person_query = person_query.annotate( **type_property_annotations, **{ @@ -525,10 +522,7 @@ def condition_eval(key, condition): group_query, group_fields, ) = group_query_per_group_type_mapping[feature_flag.aggregation_group_type_index] - type_property_annotations = { - prop_key: Func(F(prop_field), function="JSONB_TYPEOF", output_field=CharField()) - for prop_key, prop_field in properties_with_math_operators - } + type_property_annotations = _get_property_type_annotations(properties_with_math_operators) group_query = group_query.annotate( **type_property_annotations, **{ @@ -1096,15 +1090,25 @@ def check_flag_evaluation_query_is_ok(feature_flag: FeatureFlag, team_id: int) - team_id, property_list, ) + properties_with_math_operators = get_all_properties_with_math_operators(property_list, {}, team_id) + type_property_annotations = _get_property_type_annotations(properties_with_math_operators) base_query = base_query.annotate( + **type_property_annotations, **{ key: ExpressionWrapper( expr if expr else RawSQL("true", []), output_field=BooleanField(), ), - } + }, ) query_fields.append(key) values = base_query.values(*query_fields)[:10] return len(values) > 0 + + +def _get_property_type_annotations(properties_with_math_operators): + return { + prop_key: Func(F(prop_field), function="JSONB_TYPEOF", output_field=CharField()) + for prop_key, prop_field in properties_with_math_operators + }