netbox icon indicating copy to clipboard operation
netbox copied to clipboard

Align GraphQL lookup types for booleans & numerics (replace `FilterLookup`)

Open pheus opened this issue 2 months ago • 1 comments

NetBox version

v4.4.4

Feature type

Change to existing functionality

Proposed functionality

To keep GraphQL filter inputs consistent and aligned with Strawberry‑Django, I’d like to suggest:

  • Booleans:

    Option A (preferred): use a plain bool on all boolean filter fields
    This keeps input shapes simple and ergonomic for typical use.

    # v2 boolean filters – applied consistently across all fields
    is_active: bool | None = strawberry_django.filter_field()
    

    Option B (alternative): use BaseFilterLookup[bool] on all boolean filter fields
    Choose this if we want to expose exact, isNull, and inList uniformly for tri‑state scenarios.

    from strawberry_django import BaseFilterLookup
    
    # v2 boolean filters – applied consistently across all fields
    is_active: BaseFilterLookup[bool] | None = strawberry_django.filter_field()
    

    The intent is consistency: adopt Option A (recommended) or Option B across the entire v2 schema, but don’t mix them by field.

  • Numerics (e.g., counts/lengths): adopt ComparisonFilterLookup[...] so v2 clients can use exact/gt/gte/lt/lte/range.

    from strawberry_django import ComparisonFilterLookup
    
    device_bay_count: ComparisonFilterLookup[int] | None = strawberry_django.filter_field()
    
  • Strings: continue using FilterLookup[str] where substring/regex matching is desired.

Use case

  • With Option A (preferred), boolean queries remain compact and easy to read:

    # exact match using plain bool
    query {
      device_type_list(filters: { is_full_depth: true }) { id }
    }
    
  • If Option B is selected, nullable/tri‑state patterns remain idiomatic:

    # exact true
    query {
      device_type_list(filters: { is_full_depth: { exact: true } }) { id }
    }
    
    # explicit null check
    query {
      device_type_list(filters: { is_full_depth: { is_null: true } }) { id }
    }
    
    # exclude nulls (generic builder pattern)
    query {
      device_type_list(filters: { is_full_depth: { in_list: [true, false] } }) { id }
    }
    
  • Numeric comparisons become first‑class:

    query {
      device_list(filters: { device_bay_count: { gte: 1, lte: 4 } }) { count }
    }
    

This keeps boolean filters uniform across v2 and exposes the intended comparison operators for numeric fields, aligning with Strawberry‑Django’s lookup families.

Database changes

None.

External dependencies

None.

Additional context

  • Related: #20603

pheus avatar Oct 17 '25 13:10 pheus

blocked by #20603

arthanson avatar Oct 23 '25 17:10 arthanson