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]

Reply via email to