adityamparikh opened a new pull request, #185:
URL: https://github.com/apache/solr-mcp/pull/185
Closes #182.
## The bug
A faceted search whose query matched nothing failed with
`ClassCastException: ArrayList cannot be cast to NamedList` instead of
returning an empty
result. Zero matches is an ordinary search outcome, so this is not an edge
case — it fires
on properly-typed fields whenever a filter happens to match no documents.
Same collection, same field, only the filter differs:
```java
// works
search("shows", "*:*", null, List.of("platform"), ...)
-> facets = {platform={Netflix=20, HBO Max=7, ...}}
// throws
search("shows", "platform:NoSuchPlatform", null, List.of("platform"), ...)
-> ClassCastException: ArrayList cannot be cast to NamedList
```
## Cause
Solr writes a facet field as a flat array under `json.nl=flat`, and writes
an empty one as
`[]`. `JsonResponseParser.isFlatNamedList` rejects zero-length arrays:
```java
if (size == 0 || size % 2 != 0)
return false;
```
so the value fell through to the plain-list branch, and SolrJ's
`QueryResponse.getFacetFields()` — which casts to `NamedList` — threw.
## Why the one-line version is wrong
Allowing `size == 0` in that heuristic looks like the fix and isn't. A bare
`[]` is genuinely
ambiguous:
| Response key | Empty form | Must decode as | Because |
|---|---|---|---|
| `facet_counts.facet_fields.<field>` | `[]` | `NamedList` |
`QueryResponse.getFacetFields()` casts to it |
| `collections` | `[]` | `List` | `CollectionService.listCollections()`
casts to `List<String>` |
Treating every empty array as a NamedList just moves the
`ClassCastException` to
`list-collections` against an empty cluster — a first-run experience, not an
obscure path.
## The fix
Shape cannot distinguish them, so use the enclosing key. Arrays directly
inside
`facet_fields`, `facet_queries` and `facet_intervals` are flat NamedLists by
definition,
empty or not. Every other array keeps the existing heuristic untouched.
`json.nl=map` was considered and rejected: `ResponseParser` exposes no hook
for query params
and `HttpJdkSolrClient.Builder` has no default-params method, so it would
mean wrapping every
request — and `json.nl` is global, silently collapsing the duplicate keys a
`NamedList`
permits. (I'd originally suggested it on the issue before reading the code;
noting the
correction here.)
**Known gap, documented in the class javadoc:** `facet_ranges` nests its
flat list one level
deeper under a `counts` key, so an empty range facet would still decode as a
`List`. The
`search` tool does not expose range faceting, so nothing reaches that path
today — flagged
rather than silently left.
## Tests
Written first, each watched failing for the right reason before the fix went
in:
- **`JsonResponseParserFacetTest`** (new) — empty facet decodes as
`NamedList`; populated
facet still does; empty non-facet array stays a `List`. That last one is
the regression
guard for the trap above. Before the fix: 1 failed, `expected: <NamedList>
but was:
<java.util.ArrayList>`; the other two passed, confirming they pin existing
behaviour.
-
**`SearchServiceIntegrationTest.facetingAQueryThatMatchesNothingReturnsEmptyFacets`**
—
end-to-end against real Solr via Testcontainers. Before the fix:
`java.lang.ClassCastException`.
Full `./gradlew build`: **376 tests, 0 failures, 7 skipped.**
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]