Skip to content

Conversation

@mystichronicle
Copy link

SUMMARY

This PR fixes issue #35930 where deck.gl scatterplot charts failed to create with the error 'int' object has no len(). The root cause was that deck.gl visualizations send integer column and metric IDs, but the backend validation and type system only expected strings or adhoc metric/column objects.

Changes:

Backend:

  • Updated Column and Metric type definitions to include int in addition to strings and adhoc objects
  • Added custom validate_orderby_column() validator to accept integers, replacing Marshmallow's Length validator
  • Updated _set_metrics() in QueryObject to handle integer metric IDs
  • Added integer handling in get_metric_name() and get_column_name() utility functions

Frontend:

  • Added column deduplication in deck.gl Scatter buildQuery to prevent duplicate columns when the same column is used for both latitude and longitude
  • Fixed static point size handling to only use as metric when type === 'metric', not when type === 'fix'
  • Added duplicate filter prevention in addSpatialNullFilters() for spatial columns

Tests:

  • Added unit tests for orderby and metrics field validation with integers
  • Added unit tests for get_metric_name() and get_column_name() with integer IDs

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before: Creating a deck.gl scatterplot chart resulted in error:

TypeError: object of type 'int' has no len()

After: deck.gl scatterplot charts create successfully with integer column/metric IDs.

TESTING INSTRUCTIONS

  1. Ensure Superset is running with the changes
  2. Navigate to a dataset with numeric columns
  3. Create a new deck.gl Scatterplot chart
  4. Select columns for Longitude and Latitude (using the same column for both to test deduplication)
  5. Configure point radius as either:
    • Static value (type: "fix") - should not be treated as metric
    • Dynamic metric (type: "metric") - should be included in query
  6. Click "Update Chart" or "Run"
  7. Verify chart creates successfully without errors
  8. Run backend tests:
    pytest tests/unit_tests/charts/test_schemas.py::test_chart_data_query_object_schema_orderby_validation
    pytest tests/unit_tests/charts/test_schemas.py::test_chart_data_query_object_schema_metrics_validation
    pytest tests/unit_tests/utils/test_core.py::test_get_metric_name_with_integers
    pytest tests/unit_tests/utils/test_core.py::test_get_column_name_with_integers

ADDITIONAL INFORMATION

  • Has associated issue: Fixes 6.0.0rc2 regression: deck.gl fails when point size is static. #35930
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API
@bito-code-review
Copy link
Contributor

bito-code-review bot commented Oct 31, 2025

Code Review Agent Run #643580

Actionable Suggestions - 0
Additional Suggestions - 10
  • tests/unit_tests/utils/test_core.py - 2
    • Import should be in type checking · Line 1175-1175
      Import `AdhocMetric` from `superset.superset_typing` should be moved into a type-checking block. Similar issue with `AdhocColumn` on line 1193.
      Code suggestion
       @@ -18,2 +18,5 @@
        from dataclasses import dataclass
      -from typing import Any, Optional
      +from typing import Any, Optional, TYPE_CHECKING
      +
      +if TYPE_CHECKING:
      +    from superset.superset_typing import AdhocMetric, AdhocColumn
    • Docstring missing ending period · Line 1168-1168
      Docstring on line 1168 should end with a period. Similar issue exists on line 1186.
      Code suggestion
       @@ -1167,2 +1167,2 @@
      -def test_get_metric_name_with_integers() -> None:
      -    """Test get_metric_name with integer IDs"""
      +def test_get_metric_name_with_integers() -> None:
      +    """Test get_metric_name with integer IDs."""
  • tests/unit_tests/charts/test_schemas.py - 3
    • Magic number used in comparison · Line 194-194
      Magic value `123` used in comparison. Consider replacing with a constant variable for better maintainability.
      Code suggestion
       @@ -186,8 +186,9 @@
            schema = ChartDataQueryObjectSchema()
       
            # Mix of different types should all pass
      +    METRIC_ID = 123
            result = schema.load({
                "datasource": {"type": "table", "id": 1},
      -        "metrics": ["count", 123, {"expressionType": "SQL", "sqlExpression": "SUM(col)", "label": "sum"}],
      +        "metrics": ["count", METRIC_ID, {"expressionType": "SQL", "sqlExpression": "SUM(col)", "label": "sum"}],
            })
            assert result["metrics"][0] == "count"
      -    assert result["metrics"][1] == 123
      +    assert result["metrics"][1] == METRIC_ID
    • Missing period in docstring first line · Line 160-160
      Docstring first line should end with a period. This issue also occurs on line 185.
      Code suggestion
       @@ -160,1 +160,1 @@
      -    """Test that orderby field handles strings, integers and objects"""
      +    """Test that orderby field handles strings, integers and objects."""
    • Line exceeds maximum length of 88 · Line 164-164
      Line too long (99 > 88 characters). This issue also occurs on line 191.
      Code suggestion
       @@ -164,1 +164,4 @@
      -    for orderby_value in ["column_name", 123, {"label": "my_metric", "sqlExpression": "SUM(col)"}]:
      +    orderby_values = [
      +        "column_name", 123, {"label": "my_metric", "sqlExpression": "SUM(col)"}
      +    ]
      +    for orderby_value in orderby_values:
  • superset/common/query_object.py - 2
    • Use specific type ignore rule codes · Line 197-197
      Replace blanket `# type: ignore` with specific rule code like `# type: ignore[index]` to make type ignoring more precise and maintainable.
      Code suggestion
       @@ -197,1 +197,1 @@
      -            x if is_str_int_or_adhoc(x) else x["label"]  # type: ignore
      +            x if is_str_int_or_adhoc(x) else x["label"]  # type: ignore[index]
    • Use union operator in isinstance call · Line 194-194
      Use `str | int` instead of `(str, int)` in `isinstance` call for modern Python union syntax. This follows PEP 604 recommendations for type checking.
      Code suggestion
       @@ -194,1 +194,1 @@
      -            return isinstance(metric, (str, int)) or is_adhoc_metric(metric)
      +            return isinstance(metric, str | int) or is_adhoc_metric(metric)
  • superset/charts/schemas.py - 1
    • Missing docstring in public function · Line 1134-1134
      Function `validate_orderby_column` is missing a docstring. Add a docstring describing the function's purpose, parameters, and return value.
      Code suggestion
       @@ -1134,2 +1134,6 @@
      -def validate_orderby_column(value: Any) -> bool:
      -    if value is None or (isinstance(value, str) and len(value) == 0):
      +def validate_orderby_column(value: Any) -> bool:
      +    """
      +    Validate that orderby column is not None or empty string.
      +    """
      +    if value is None or (isinstance(value, str) and len(value) == 0):
  • superset/superset_typing.py - 2
    • Use modern union syntax for column type · Line 109-109
      Consider using the modern `X | Y` union syntax instead of `Union[X, Y]` for the `Column` type annotation. This follows PEP 604 recommendations.
      Code suggestion
       @@ -109,1 +109,1 @@
      -Column = Union[AdhocColumn, str, int]
      +Column = AdhocColumn | str | int
    • Use modern union syntax for metric type · Line 110-110
      Consider using the modern `X | Y` union syntax instead of `Union[X, Y]` for the `Metric` type annotation. This follows PEP 604 recommendations.
      Code suggestion
       @@ -110,1 +110,1 @@
      -Metric = Union[AdhocMetric, str, int]
      +Metric = AdhocMetric | str | int
Review Details
  • Files reviewed - 8 · Commit Range: a6bcb34..ded18b1
    • superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/buildQuery.ts
    • superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.ts
    • superset/charts/schemas.py
    • superset/common/query_object.py
    • superset/superset_typing.py
    • superset/utils/core.py
    • tests/unit_tests/charts/test_schemas.py
    • tests/unit_tests/utils/test_core.py
  • Files skipped - 0
  • Tools
    • Eslint (Linter) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@dosubot dosubot bot added change:backend Requires changing the backend viz:charts:deck.gl Related to deck.gl charts labels Oct 31, 2025
Copy link

@korbit-ai korbit-ai bot left a comment

Choose a reason for hiding this comment

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

Review by Korbit AI

Korbit automatically attempts to detect when you fix issues in new commits.
Category Issue Status
Performance Inefficient filter lookup with O(n) complexity ▹ view
Design Insufficient Type Validation in Order By Column ▹ view
Functionality Integer column ID converted to string without name resolution ▹ view
Files scanned
File Path Reviewed
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/buildQuery.ts
superset/superset_typing.py
superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.ts
superset/common/query_object.py
superset/charts/schemas.py
superset/utils/core.py

Explore our documentation to understand the languages and file types we support and the files we ignore.

Check out our docs on how you can make Korbit work best for you and your team.

Loving Korbit!? Share us on LinkedIn Reddit and X

Comment on lines +113 to +117
const existingFilterCols = new Set(
filters
.filter(filter => filter.op === 'IS NOT NULL')
.map(filter => filter.col),
);
Copy link

Choose a reason for hiding this comment

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

Inefficient filter lookup with O(n) complexity category Performance

Tell me more
What is the issue?

The code creates a new Set on every function call by filtering and mapping the entire filters array, which has O(n) time complexity where n is the number of filters.

Why this matters

This approach becomes inefficient when the filters array is large or when addSpatialNullFilters is called frequently, as it processes the entire array each time instead of using a more efficient lookup mechanism.

Suggested change ∙ Feature Preview

Consider using a Map or optimized lookup structure if this function is called frequently with large filter arrays, or cache the existingFilterCols Set if the filters don't change between calls:

const existingFilterCols = filters.reduce((acc, filter) => {
  if (filter.op === 'IS NOT NULL') {
    acc.add(filter.col);
  }
  return acc;
}, new Set<string>());
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

Comment on lines +1134 to +1137
def validate_orderby_column(value: Any) -> bool:
if value is None or (isinstance(value, str) and len(value) == 0):
raise validate.ValidationError(_("orderby column must be populated"))
return True
Copy link

Choose a reason for hiding this comment

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

Insufficient Type Validation in Order By Column category Design

Tell me more
What is the issue?

The validate_orderby_column function is too permissive in its type checking. It only validates for None and empty strings, but accepts any other type without validation.

Why this matters

This could lead to runtime errors when the validation passes but the value is of an incompatible type (like a number or boolean) that can't be used as an orderby column.

Suggested change ∙ Feature Preview
def validate_orderby_column(value: Any) -> bool:
    if not isinstance(value, str):
        raise validate.ValidationError(_('orderby column must be a string'))
    if len(value) == 0:
        raise validate.ValidationError(_('orderby column must be populated'))
    return True
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

Comment on lines +1234 to +1235
if isinstance(column, int):
return str(column)
Copy link

Choose a reason for hiding this comment

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

Integer column ID converted to string without name resolution category Functionality

Tell me more
What is the issue?

The function converts integer column IDs directly to strings without any validation or lookup, which may not provide meaningful column names for users.

Why this matters

This approach bypasses the intended column resolution logic and may result in displaying raw numeric IDs instead of human-readable column names, potentially confusing users who expect descriptive labels.

Suggested change ∙ Feature Preview

Consider implementing a lookup mechanism to resolve integer IDs to actual column names, or add documentation explaining when raw integer strings are acceptable. For example:

if isinstance(column, int):
    # TODO: Implement ID-to-name lookup when datasource context is available
    # For now, return string representation as fallback
    return str(column)
Provide feedback to improve future suggestions

Nice Catch Incorrect Not in Scope Not in coding standard Other

💬 Looking for more details? Reply to this comment to chat with Korbit.

… types and improve handling of undefined values
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:backend Requires changing the backend plugins size/L viz:charts:deck.gl Related to deck.gl charts

1 participant