{"record":{"id":"03dce13d95b7e40f","repo":"apache/druid","slug":"joining-against-a-multi-value-dimension-is-not-sup-03dce1","errorCode":null,"errorMessage":"Joining against a multi-value dimension is not supported.","messagePattern":"Joining against a multi-value dimension is not supported\\.","errorType":"exception","errorClass":"QueryUnsupportedException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcher.java","lineNumber":438,"sourceCode":"      // set outside of the supplier. Minimizing overhead is desirable since the supplier is called from a hot loop for\n      // joins.\n\n      if (selector.getValueCardinality() == DimensionDictionarySelector.CARDINALITY_UNKNOWN) {\n        // If the cardinality is unknown, then the selector does not have a \"real\" dictionary and the dimension id\n        // is not valid outside the context of a specific row. This means we cannot use a cache and must fall\n        // back to this slow code path.\n        return () -> {\n          final IndexedInts row = selector.getRow();\n\n          if (row.size() == 1) {\n            int dimensionId = row.get(0);\n            return getRowNumbers(selector.lookupName(dimensionId));\n          } else if (row.size() == 0) {\n            return getRowNumbers(null);\n          } else {\n            // Multi-valued rows are not handled by the join system right now\n            // TODO: Remove when https://github.com/apache/druid/issues/9924 is done\n            throw new QueryUnsupportedException(\"Joining against a multi-value dimension is not supported.\");\n          }\n        };\n      } else {\n        // If the cardinality is known, then the dimension id is still valid outside the context of a specific row and\n        // its mapping to row numbers can be cached.\n        return () -> {\n          final IndexedInts row = selector.getRow();\n\n          if (row.size() == 1) {\n            int dimensionId = row.get(0);\n            return getAndCacheRowNumbers(selector, dimensionId);\n          } else if (row.size() == 0) {\n            return getRowNumbers(null);\n          } else {\n            // Multi-valued rows are not handled by the join system right now\n            // TODO: Remove when https://github.com/apache/druid/issues/9924 is done\n            throw new QueryUnsupportedException(\"Joining against a multi-value dimension is not supported.\");\n          }","sourceCodeStart":420,"sourceCodeEnd":456,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/join/table/IndexedTableJoinMatcher.java#L420-L456","documentation":"The join system cannot handle multi-valued (array-like) dimension rows on the outer side of an indexed-table join. When a dimension selector returns a row with more than one value during matching, makeDimensionProcessor's matcher throws QueryUnsupportedException instead of producing wrong results.","triggerScenarios":"A join query where the outer/left side column matched in the equality is a multi-value dimension (a row containing multiple values), evaluated via makeConditionMatcher -> makeDimensionProcessor.","commonSituations":"Joining a table against an ingestion-time multi-valued string dimension (list of tags, array-like input without schema); Kafka/batch ingestion producing MV dimensions; queries mixing MV dimensions with broadcast joins.","solutions":["Unnest the multi-valued column before joining (SQL UNNEST or flattened ingestion)","Disable multi-value handling by using UNNEST or transforming with ARRAY_TO_STRING if a single value suffices","Filter or group so the join key column is single-valued per row","Track/await upstream fix for the multi-value join limitation (druid issue 9924)"],"exampleFix":"// before\n// SELECT ... FROM mvTable JOIN broadcast ON mvTable.tags = dim.tags\n// after\n// SELECT ... FROM (SELECT id, tag FROM mvTable, UNNEST(tags) AS tag) mvTable\n//   JOIN broadcast ON mvTable.tag = dim.tags","handlingStrategy":"validation","validationCode":"ColumnCapabilities caps = column.getCapabilities();\nboolean multiValue = caps != null && caps.hasMultipleValues().isTrue();","typeGuard":null,"tryCatchPattern":"try { matcher.match(forceMultiValue); } catch (QueryUnsupportedException e) { /* rewrite query with UNNEST or fail gracefully */ }","preventionTips":["Avoid joining on multi-value dimensions; unnest first","Flatten list-like inputs at ingestion time","Check hasMultipleValues() capabilities of the join column before planning"],"tags":["java","join","multi-value-dimension","query-unsupported"],"backgroundTag":"unsupported-operation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}