Title: Clarify or fix composite filter boolean semantics · Issue #23 · lightdash/python-sdk · GitHub
Open Graph Title: Clarify or fix composite filter boolean semantics · Issue #23 · lightdash/python-sdk
X Title: Clarify or fix composite filter boolean semantics · Issue #23 · lightdash/python-sdk
Description: Problem The SDK currently exposes filter composition with &, |, and repeated .filter() calls, but the behavior for mixed boolean logic does not preserve the semantics users would reasonably expect from the Python expression. This came up...
Open Graph Description: Problem The SDK currently exposes filter composition with &, |, and repeated .filter() calls, but the behavior for mixed boolean logic does not preserve the semantics users would reasonably expect ...
X Description: Problem The SDK currently exposes filter composition with &, |, and repeated .filter() calls, but the behavior for mixed boolean logic does not preserve the semantics users would reasonably exp...
Opengraph URL: https://github.com/lightdash/python-sdk/issues/23
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Clarify or fix composite filter boolean semantics","articleBody":"## Problem\n\nThe SDK currently exposes filter composition with `\u0026`, `|`, and repeated `.filter()` calls, but the behavior for mixed boolean logic does not preserve the semantics users would reasonably expect from the Python expression.\n\nThis came up while reviewing #22. That PR correctly enables multiple rules on the same field for flat same-field filters, but it also made the broader filter API easier to trust for range and cohort workflows. The remaining issue is that mixed AND/OR expressions can silently serialize differently from the expression a data scientist wrote.\n\n## Repro cases\n\n### Repeated `.filter()` calls after an OR composite\n\nThe query docs say multiple `.filter()` calls are combined with AND logic:\n\n```python\nquery = (\n model.query()\n .filter((model.dimensions.status == \"active\") | (model.dimensions.status == \"pending\"))\n .filter(model.dimensions.status != \"deleted\")\n)\n```\n\nExpected logical shape:\n\n```text\n(status = active OR status = pending) AND status != deleted\n```\n\nCurrent behavior preserves the existing `or` aggregation when appending a single filter, producing the equivalent of:\n\n```text\nstatus = active OR status = pending OR status != deleted\n```\n\nThe relevant path is `Query.filter()` when `self._filters` already exists and the new filter is a `DimensionFilter`.\n\n### Nested boolean expressions flatten precedence\n\nThe docs currently show a complex filter example like:\n\n```python\nf = (\n (model.dimensions.country == \"USA\") \u0026\n ((model.dimensions.amount \u003e 1000) | (model.dimensions.priority == \"high\"))\n)\n```\n\nExpected logical shape:\n\n```text\ncountry = USA AND (amount \u003e 1000 OR priority = high)\n```\n\nCurrent behavior flattens the filters into a single aggregation, so the nested precedence is not represented in the serialized payload.\n\n## Why this matters\n\nFor exploratory analysis and notebook workflows, users often write cohort filters, date windows, and exclusion rules in Python expressions. If the SDK silently changes OR/AND semantics, a query can return a materially different population without an obvious failure. That is especially risky for data scientists using this SDK to build analysis datasets.\n\n## Possible resolutions\n\n1. Implement nested composite serialization if the Lightdash API supports nested boolean filter groups.\n2. If the API only supports flat `and`/`or` groups, reject mixed nested expressions early with a clear error.\n3. Update docs to avoid advertising unsupported complex combinations until the behavior is implemented.\n4. Fix repeated `.filter()` calls so they are actually AND-ed, or document the exact current behavior if preserving aggregation is intentional.\n\n## Acceptance criteria\n\n- Mixed `AND`/`OR` filter expressions either serialize with correct precedence or raise an explicit unsupported-operation error.\n- Multiple `.filter()` calls always behave as documented, especially when the first filter is an OR composite.\n- Tests cover:\n - `(a | b).filter(c)` style chaining through `Query.filter()`.\n - `a \u0026 (b | c)` nested expression behavior.\n - The documented complex combination example.\n- `docs/SDK_GUIDE.md` matches the actual supported filter semantics.","author":{"url":"https://github.com/jpetey75","@type":"Person","name":"jpetey75"},"datePublished":"2026-06-16T19:51:56.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/23/python-sdk/issues/23"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:d6e475e0-2919-6fc5-5d9a-4c439cbb22c9 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | EB54:2279D0:483E74:6393FB:6A4DC83E |
| html-safe-nonce | 4711b6cc531b640b682d3df810e56ef45e2313472f0833947fe7002d5a2045c5 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJFQjU0OjIyNzlEMDo0ODNFNzQ6NjM5M0ZCOjZBNERDODNFIiwidmlzaXRvcl9pZCI6IjU5MTM4NzgyOTI4NTE0NDM3NzQiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 96a16346d1fe571d98ce2ad2b43a06116ca5ecdb84f34808e906a9b9bcc6a856 |
| hovercard-subject-tag | issue:4677419508 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/lightdash/python-sdk/23/issue_layout |
| twitter:image | https://opengraph.githubassets.com/6ad00277c6321d9869d42dc57a208b429bb13990d1315f010a6d9ec4cbd528bb/lightdash/python-sdk/issues/23 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/6ad00277c6321d9869d42dc57a208b429bb13990d1315f010a6d9ec4cbd528bb/lightdash/python-sdk/issues/23 |
| og:image:alt | Problem The SDK currently exposes filter composition with &, |, and repeated .filter() calls, but the behavior for mixed boolean logic does not preserve the semantics users would reasonably expect ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | jpetey75 |
| hostname | github.com |
| expected-hostname | github.com |
| None | 06b8a6144231bf3a234f1c2e9993861e07ce98a905912b114aa386c2d7e84b33 |
| turbo-cache-control | no-preview |
| go-import | github.com/lightdash/python-sdk git https://github.com/lightdash/python-sdk.git |
| octolytics-dimension-user_id | 65912827 |
| octolytics-dimension-user_login | lightdash |
| octolytics-dimension-repository_id | 932695849 |
| octolytics-dimension-repository_nwo | lightdash/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 932695849 |
| octolytics-dimension-repository_network_root_nwo | lightdash/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 1d344bdb7547fe6bca17a59bb2b8aac3dc9532a0 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width