mirror of
https://github.com/PostHog/posthog.git
synced 2024-11-21 21:49:51 +01:00
fix: cohort negation (#25327)
This commit is contained in:
parent
fafeeaae1d
commit
cbf1ffecff
@ -126,6 +126,7 @@ class TestFilters(PGTestFilters):
|
||||
{
|
||||
"key": "id",
|
||||
"value": cohort.pk,
|
||||
"negation": False,
|
||||
"type": "precalculated-cohort",
|
||||
}
|
||||
],
|
||||
@ -141,6 +142,7 @@ class TestFilters(PGTestFilters):
|
||||
"values": [
|
||||
{
|
||||
"key": "id",
|
||||
"negation": False,
|
||||
"value": cohort.pk,
|
||||
"type": "precalculated-cohort",
|
||||
}
|
||||
@ -158,7 +160,7 @@ class TestFilters(PGTestFilters):
|
||||
{
|
||||
"properties": {
|
||||
"type": "AND",
|
||||
"values": [{"type": "static-cohort", "key": "id", "value": cohort.pk}],
|
||||
"values": [{"type": "static-cohort", "negation": False, "key": "id", "value": cohort.pk}],
|
||||
}
|
||||
},
|
||||
)
|
||||
@ -172,7 +174,7 @@ class TestFilters(PGTestFilters):
|
||||
{
|
||||
"properties": {
|
||||
"type": "AND",
|
||||
"values": [{"type": "cohort", "key": "id", "value": cohort.pk}],
|
||||
"values": [{"type": "cohort", "negation": False, "key": "id", "value": cohort.pk}],
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -482,7 +482,8 @@ def property_to_expr(
|
||||
return ast.CompareOperation(
|
||||
left=ast.Field(chain=["id" if scope == "person" else "person_id"]),
|
||||
op=ast.CompareOperationOp.NotInCohort
|
||||
if property.operator == PropertyOperator.NOT_IN.value
|
||||
# Kludge: negation is outdated but still used in places
|
||||
if property.negation or property.operator == PropertyOperator.NOT_IN.value
|
||||
else ast.CompareOperationOp.InCohort,
|
||||
right=ast.Constant(value=cohort.pk),
|
||||
)
|
||||
|
@ -1,3 +1,6 @@
|
||||
from posthog.schema import PropertyOperator
|
||||
|
||||
|
||||
def clean_global_properties(properties: dict | list[dict] | None):
|
||||
if properties is None or len(properties) == 0:
|
||||
# empty properties
|
||||
@ -90,8 +93,15 @@ def clean_property(property: dict):
|
||||
cleaned_property["type"] = "cohort"
|
||||
|
||||
# fix invalid property key for cohorts
|
||||
if cleaned_property.get("type") == "cohort" and cleaned_property.get("key") != "id":
|
||||
cleaned_property["key"] = "id"
|
||||
if cleaned_property.get("type") == "cohort":
|
||||
if cleaned_property.get("key") != "id":
|
||||
cleaned_property["key"] = "id"
|
||||
if cleaned_property.get("operator") is None:
|
||||
cleaned_property["operator"] = (
|
||||
PropertyOperator.NOT_IN.value if cleaned_property.get("negation", False) else PropertyOperator.IN_.value
|
||||
)
|
||||
if "negation" in cleaned_property:
|
||||
del cleaned_property["negation"]
|
||||
|
||||
# set a default operator for properties that support it, but don't have an operator set
|
||||
if is_property_with_operator(cleaned_property) and cleaned_property.get("operator") is None:
|
||||
@ -112,7 +122,7 @@ def clean_property(property: dict):
|
||||
|
||||
|
||||
def is_property_with_operator(property: dict):
|
||||
return property.get("type") not in ("cohort", "hogql")
|
||||
return property.get("type") not in ("hogql",)
|
||||
|
||||
|
||||
# old style dict properties e.g. {"utm_medium__icontains": "email"}
|
||||
|
@ -40,7 +40,7 @@ class TestCleanGlobalProperties(BaseTest):
|
||||
"values": [
|
||||
{
|
||||
"type": "AND",
|
||||
"values": [{"key": "id", "type": "cohort", "value": 636}],
|
||||
"values": [{"key": "id", "type": "cohort", "operator": "in", "value": 636}],
|
||||
}
|
||||
],
|
||||
},
|
||||
@ -61,7 +61,33 @@ class TestCleanGlobalProperties(BaseTest):
|
||||
"values": [
|
||||
{
|
||||
"type": "AND",
|
||||
"values": [{"key": "id", "type": "cohort", "value": 850}],
|
||||
"values": [{"key": "id", "type": "cohort", "operator": "in", "value": 850}],
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
def test_handles_cohort_negation(self):
|
||||
properties = {
|
||||
"type": "AND",
|
||||
"values": [
|
||||
{
|
||||
"type": "AND",
|
||||
"values": [{"key": "id", "type": "cohort", "value": 850, "operator": None, "negation": True}],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
result = clean_global_properties(properties)
|
||||
|
||||
self.assertEqual(
|
||||
result,
|
||||
{
|
||||
"type": "AND",
|
||||
"values": [
|
||||
{
|
||||
"type": "AND",
|
||||
"values": [{"key": "id", "type": "cohort", "operator": "not_in", "value": 850}],
|
||||
}
|
||||
],
|
||||
},
|
||||
@ -82,7 +108,7 @@ class TestCleanGlobalProperties(BaseTest):
|
||||
"values": [
|
||||
{
|
||||
"type": "AND",
|
||||
"values": [{"key": "id", "type": "cohort", "value": 850}],
|
||||
"values": [{"key": "id", "type": "cohort", "operator": "in", "value": 850}],
|
||||
}
|
||||
],
|
||||
},
|
||||
|
@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Any, Literal, TypeVar, cast
|
||||
|
||||
from posthog.constants import PropertyOperatorType
|
||||
from posthog.models.property import GroupTypeIndex, PropertyGroup
|
||||
from posthog.schema import PropertyOperator
|
||||
|
||||
if TYPE_CHECKING: # Avoid circular import
|
||||
from posthog.models import Property, Team
|
||||
@ -112,7 +113,9 @@ class SimplifyFilterMixin:
|
||||
# :TODO: Handle non-existing resource in-query instead
|
||||
return PropertyGroup(type=PropertyOperatorType.AND, values=[property])
|
||||
|
||||
return simplified_cohort_filter_properties(cohort, team, property.negation)
|
||||
return simplified_cohort_filter_properties(
|
||||
cohort, team, property.negation or property.operator == PropertyOperator.NOT_IN.value
|
||||
)
|
||||
|
||||
# PropertyOperatorType doesn't really matter here, since only one value.
|
||||
return PropertyGroup(type=PropertyOperatorType.AND, values=[property])
|
||||
|
Loading…
Reference in New Issue
Block a user