From e12960b68b89a05d2e3e3b36042e1c8179febab3 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Thu, 21 Nov 2024 03:15:41 -0800 Subject: [PATCH] fix(hogql): Support parsing dw properties with nested values (#26321) --- posthog/hogql/property.py | 28 +++++++++++++++++++ posthog/hogql/test/test_property.py | 42 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/posthog/hogql/property.py b/posthog/hogql/property.py index 10eb991ea8f..e372b446b5c 100644 --- a/posthog/hogql/property.py +++ b/posthog/hogql/property.py @@ -346,6 +346,34 @@ def property_to_expr( chain = ["events", "properties"] elif property.type == "session" and scope == "replay_entity": chain = ["events", "session"] + elif property.type == "data_warehouse": + 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] + + 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 fb2f2f23097..5efa8f221da 100644 --- a/posthog/hogql/test/test_property.py +++ b/posthog/hogql/test/test_property.py @@ -785,3 +785,45 @@ class TestProperty(BaseTest): ), self._parse_expr("person.extended_properties.bool_prop = true"), ) + + def test_data_warehouse_property_with_list_values(self): + credential = DataWarehouseCredential.objects.create( + team=self.team, access_key="_accesskey", access_secret="_secret" + ) + DataWarehouseTable.objects.create( + team=self.team, + name="foobars", + columns={ + "event": {"hogql": "StringDatabaseField", "clickhouse": "String"}, + "properties": {"hogql": "JSONField", "clickhouse": "String"}, + }, + credential=credential, + url_pattern="", + ) + + expr = self._property_to_expr( + Property( + type="data_warehouse", + key="foobars.properties.$feature/test", + value=["control", "test"], + operator="exact", + ), + self.team, + ) + + self.assertIsInstance(expr, ast.Or) + self.assertEqual(len(expr.exprs), 2) + + # First expression + compare_op_1 = 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] + 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"]) + self.assertEqual(compare_op_2.right.value, "test")