diff --git a/posthog/hogql/property.py b/posthog/hogql/property.py index 9536b70fa5f..e372b446b5c 100644 --- a/posthog/hogql/property.py +++ b/posthog/hogql/property.py @@ -347,54 +347,33 @@ def property_to_expr( elif property.type == "session" and scope == "replay_entity": chain = ["events", "session"] elif property.type == "data_warehouse": - if isinstance(property.key, str): + if not isinstance(property.key, str): + raise QueryError("Data warehouse property filter value must be a string") + else: split = property.key.split(".") chain = split[:-1] - property_key = split[-1] - else: - raise QueryError("Data warehouse property filter value must be a string") - - # For data warehouse properties, we split the key on dots to get the table chain and property key - # e.g. "foobars.properties.$feature/flag" becomes chain=["foobars", "properties"] and property_key="$feature/flag" - # This allows us to properly reference nested fields in data warehouse tables while maintaining consistent - # property filtering behavior. The chain represents the path to traverse through nested table structures, - # while the property_key is the actual field name we want to filter on. - if isinstance(value, list): - if len(value) == 0: - return ast.Constant(value=1) - elif len(value) == 1: - value = value[0] - else: - field = ast.Field(chain=[*chain, property_key]) - exprs = [ - _expr_to_compare_op( - expr=field, - value=v, - operator=operator, - team=team, - property=property, - is_json_field=False, - ) - for v in value - ] - if ( - operator == PropertyOperator.NOT_ICONTAINS - or operator == PropertyOperator.NOT_REGEX - or operator == PropertyOperator.IS_NOT - ): - return ast.And(exprs=exprs) - return ast.Or(exprs=exprs) - - field = ast.Field(chain=[*chain, property_key]) - return _expr_to_compare_op( - expr=field, - value=value, - operator=operator, - team=team, - property=property, - is_json_field=False, - ) + property.key = split[-1] + if isinstance(value, list) and len(value) > 1: + field = ast.Field(chain=[*chain, property.key]) + exprs = [ + _expr_to_compare_op( + expr=field, + value=v, + operator=operator, + team=team, + property=property, + is_json_field=False, + ) + for v in value + ] + if ( + operator == PropertyOperator.NOT_ICONTAINS + or operator == PropertyOperator.NOT_REGEX + or operator == PropertyOperator.IS_NOT + ): + return ast.And(exprs=exprs) + return ast.Or(exprs=exprs) elif property.type == "data_warehouse_person_property": if isinstance(property.key, str): table, key = property.key.split(".") diff --git a/posthog/hogql/test/test_property.py b/posthog/hogql/test/test_property.py index 56f342c3444..eee278e83bd 100644 --- a/posthog/hogql/test/test_property.py +++ b/posthog/hogql/test/test_property.py @@ -812,17 +812,17 @@ class TestProperty(BaseTest): ) self.assertIsInstance(expr, ast.Or) - self.assertEqual(len(expr.exprs), 2) + self.assertEqual(len(getattr(expr, 'exprs')), 2) # First expression - compare_op_1 = expr.exprs[0] + compare_op_1 = getattr(expr, 'exprs')[0] self.assertIsInstance(compare_op_1, ast.CompareOperation) self.assertIsInstance(compare_op_1.left, ast.Field) self.assertEqual(compare_op_1.left.chain, ["foobars", "properties", "$feature/test"]) self.assertEqual(compare_op_1.right.value, "control") # Second expression - compare_op_2 = expr.exprs[1] + compare_op_2 = getattr(expr, 'exprs')[1] self.assertIsInstance(compare_op_2, ast.CompareOperation) self.assertIsInstance(compare_op_2.left, ast.Field) self.assertEqual(compare_op_2.left.chain, ["foobars", "properties", "$feature/test"])