Skip to content

BE: add 'in' operator to MetadataFilter for categorical filtering - #1745

Merged
ikondrat merged 50 commits into
mainfrom
kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p4
Jul 27, 2026
Merged

BE: add 'in' operator to MetadataFilter for categorical filtering#1745
ikondrat merged 50 commits into
mainfrom
kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p4

Conversation

@ikondrat

@ikondrat ikondrat commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Stacked on p3.

Adds an in operator to MetadataFilter so the frontend can request samples matching any of a set of categorical values (including null/missing):

  • Validates the list is non-empty and homogeneous (all str, all bool, or [None] / [str | None])
  • Builds an OR predicate via a standard SQL IN clause for non-null values
  • When None is included, uses a LEFT OUTER JOIN so samples with no metadata entry for the field are also matched

Also includes a mypy # type: ignore[no-untyped-call] fix for query.compile(dialect=postgresql.dialect()) in test_metadata_filter.py, matching the pattern used in other test files.

Test plan

  • make static-checks passes
  • make test-ci passes (new tests in test_metadata_filter.py and test_sample_metadata_filtering.py, plus additional API tests in test_metadata.py)

Part of LIG-9585.

Summary by CodeRabbit

  • New Features

    • Added support for categorical metadata filtering with the in operator.
    • Filters can match multiple string or boolean values, including samples with missing metadata.
    • Invalid empty in filter lists now return a validation error.
  • Tests

    • Added coverage for valid and invalid categorical filters, missing metadata, and special-character metadata keys.

ikondrat and others added 2 commits July 22, 2026 18:03
Adds weather (skewed string), reviewed (boolean), camera_id (25 values,
exercises the "Other" bucket), and split (~20% missing, exercises the
"Missing" bucket) alongside the existing numeric fields. location now
uses weighted sampling instead of uniform.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ikondrat
ikondrat requested a review from a team as a code owner July 22, 2026 16:17
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fd451be5-30ec-407f-a9fd-d0ced6beb1c0

📥 Commits

Reviewing files that changed from the base of the PR and between b5b97e0 and ce881ab.

📒 Files selected for processing (5)
  • lightly_studio/src/lightly_studio/resolvers/metadata_resolver/metadata_filter.py
  • lightly_studio/src/lightly_studio/resolvers/metadata_resolver/sample/__init__.py
  • lightly_studio/tests/api/routes/api/test_metadata.py
  • lightly_studio/tests/resolvers/metadata_resolvers/sample/test_sample_metadata_filtering.py
  • lightly_studio/tests/resolvers/metadata_resolvers/test_metadata_filter.py

📝 Walkthrough

Walkthrough

Changes

Metadata filtering

Layer / File(s) Summary
Filter operator contract and validation
lightly_studio/src/lightly_studio/resolvers/metadata_resolver/metadata_filter.py, lightly_studio/tests/resolvers/metadata_resolvers/test_metadata_filter.py, lightly_studio/tests/api/routes/api/test_metadata.py
Adds the in operator, validates non-empty homogeneous string or boolean lists with optional nulls, and returns HTTP 422 for invalid values.
SQL predicate construction
lightly_studio/src/lightly_studio/resolvers/metadata_resolver/metadata_filter.py, lightly_studio/src/lightly_studio/resolvers/metadata_resolver/sample/__init__.py, lightly_studio/tests/resolvers/metadata_resolvers/test_metadata_filter.py
Builds categorical IN predicates, normalizes booleans, handles null metadata through outer joins and IS NULL, and preserves special-character keys.
Endpoint and sample filtering coverage
lightly_studio/tests/api/routes/api/test_metadata.py, lightly_studio/tests/resolvers/metadata_resolvers/sample/test_sample_metadata_filtering.py
Verifies resolver calls and filtering for concrete values, booleans, missing metadata, and special-character metadata keys.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant APIClient
  participant MetadataValueCountsAPI
  participant MetadataFilter
  participant apply_metadata_filters
  participant PostgreSQL
  APIClient->>MetadataValueCountsAPI: submit metadata/value-counts request
  MetadataValueCountsAPI->>MetadataFilter: validate filter
  MetadataValueCountsAPI->>apply_metadata_filters: apply validated filters
  apply_metadata_filters->>PostgreSQL: execute IN or NULL predicate
  PostgreSQL-->>MetadataValueCountsAPI: return matching rows
Loading

Possibly related PRs

Suggested reviewers: gabrielfruet, horatiualmasan, igorsusmelj

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: adding an "in" operator to MetadataFilter for categorical filtering.
Description check ✅ Passed The description covers the change and test plan, but it should add the changelog checkbox section to match the template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p4

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lightly-fast-track-bot

lightly-fast-track-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

❌  Fast Track: checks did not pass

Guardrail Result Message
dummy Always passes.
frontend/complexity 0 file(s) checked.
backend/complexity 5 file(s) checked, no violations.
backend/coverage Guardrail threw: Command failed: uv run pytest tests/resolvers/metadata_resolvers/test_metadata_filter.py --cov=src/lightly_studio/resolvers/metadata_resolver --cov-report=json:coverage.json -q --no-header ImportError while loading conftest '/home/runner/work/lightly-studio/lightly-studio/lightly_studio/tests/conftest.py'. tests/conftest.py:18: in from lightly_studio.api import features src/lightly_studio/init.py:18: in from lightly_studio.core.start_gui import ( src/lightly_studio/core/start_gui.py:12: in from lightly_studio.api.server import Server src/lightly_studio/api/server.py:11: in from lightly_studio.api.app import app src/lightly_studio/api/app.py:16: in from lightly_studio.api.routes import ( src/lightly_studio/api/routes/webapp.py:19: in raise RuntimeError(f"Directory '{webapp_dir}' does not exist in '{project_root}'") E RuntimeError: Directory '/home/runner/work/lightly-studio/lightly-studio/lightly_studio/src/lightly_studio/dist_lightly_studio_view_app' does not exist in '/home/runner/work/lightly-studio/lightly-studio/lightly_studio/src/lightly_studio'
diff-size PR adds 227 line(s), which exceeds the limit of 215.
frontend/coverage 0 file(s) checked.

View the guardrail run

To run the guardrails locally, from fast_track/ run make install once, then make run-guardrails (or GUARDRAILS=<name1>,<name2> make run-guardrails for some guardrails).

Reflects ce881ab.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 55ec580ab7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lightly_studio/src/lightly_studio/resolvers/metadata_resolver/metadata_filter.py Outdated
Comment thread lightly_studio/tests/resolvers/metadata_resolvers/test_metadata_filter.py Outdated
ikondrat and others added 9 commits July 22, 2026 18:22
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an 'in' operator that accepts a list of string/boolean/null values
and builds an OR predicate, using a LEFT OUTER JOIN when null is present
to match samples with no metadata entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ikondrat
ikondrat force-pushed the kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p4 branch from 55ec580 to cf40cb2 Compare July 22, 2026 17:06
@ikondrat
ikondrat force-pushed the kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p3 branch from ebfc8d8 to 1a22eff Compare July 22, 2026 17:06
@ikondrat
ikondrat changed the base branch from kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p3 to kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p2 July 22, 2026 17:11
@ikondrat

Copy link
Copy Markdown
Contributor Author

/review

ikondrat and others added 11 commits July 22, 2026 19:13
Split the combined test into focused cases for string values vs booleans,
use keyword arguments for clarity, and tighten resolver call assertions
with assert_called_once_with.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a function and its module file share the same name, mypy resolves the
attribute as the module (not callable). Add a __init__.pyi stub for
metadata_resolver to declare the four exported functions explicitly, and
use explicit re-exports (as) in both __init__.py files to signal intent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed section on AI Team Workflow and unnecessary blank lines.
- Assert collection_id is forwarded to the resolver in the route test
- Use keyword arguments in all _create_sample calls
- Reduce other_collection seed to 1 sample (sufficient to prove isolation)
- Add active.other_count assertion to complete the boolean field checks
- Add docstrings to clarify own-key-exclusion filter semantics, known-fields
  behaviour under a no-match filter, and the literal-key test purpose

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t-filter-ui-8-sp.p2' into kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p4
ikondrat and others added 22 commits July 23, 2026 16:23
…t-filter-ui-8-sp.p0' into kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p1
…t-filter-ui-8-sp.p1' into kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p1.p2
…t-filter-ui-8-sp.p1.p2' into kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p1.p3
- Remove redundant response_model from route decorator
- Make request body required (filters nullable inside the model)
- Expand docstring with top-20 cap, other_count, missing_count, and faceted-search semantics

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…t-filter-ui-8-sp.p1.p3' into kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p2
…t-filter-ui-8-sp.p1.p2' into kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p2
…t-filter-ui-8-sp.p1.p2' into kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p2
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…t-filter-ui-8-sp.p1.p2' into kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p2
…t-filter-ui-8-sp.p2' into kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p4
Base automatically changed from kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p2 to main July 27, 2026 09:01
@ikondrat
ikondrat enabled auto-merge July 27, 2026 09:10
@ikondrat
ikondrat added this pull request to the merge queue Jul 27, 2026
Merged via the queue into main with commit 86ea268 Jul 27, 2026
24 checks passed
@ikondrat
ikondrat deleted the kondrat-lig-9585-frontend-categorical-metadata-bar-chart-filter-ui-8-sp.p4 branch July 27, 2026 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants