Skip to content

feat: Order results by a field of their nested children with order_by_child - #7

Draft
njakobsen wants to merge 6 commits into
ci-production-solrfrom
nested-sort
Draft

njakobsen wants to merge 6 commits into
ci-production-solrfrom
nested-sort

Conversation

@njakobsen

@njakobsen njakobsen commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Adds order_by_child, which sorts parents by a field of their nested children. It takes the same block as with_child:

Sunspot.search(Project) do
  order_by_child :milestones, :started_at, :desc do
    with :name, "design"
  end
end

The sort is a {!parent} block join whose score is the children's value, over {!bool} of the block's restrictions, a filter that the field has a value, and {!func} of the field. A parent with no matching child that has a value comes last in either direction. Several children combine by the smallest value ascending and the largest descending, unless :score says otherwise.

  • Needs Solr 8 or later; the README says so. Solr 5.3.1 (bundled) and 6.6.6 reject the sort, and Solr 7.7.3 runs it but doesn't tie times before 1970 and gets a descending :score => :min sort wrong. Its specs are skipped before Solr 8 with skip_before_solr.
  • Only single-value numeric and time fields can be sorted by; a date range field raises ArgumentError. Solr compares the scores as floats, so values very close together can tie, and it reads a negative score as zero, so times before 1970 tie with each other and negative numbers with zero. A later order_by breaks a tie.

Testing

Integration and API specs for the sort, including a parent whose matching children lack the field. Locally, in both update formats, the 418 integration examples pass on solr:9.10.1 (5 pending) and on the bundled Solr 5.3.1 (12 pending: the 10 order_by_child examples and 2 that need Solr 8). The nested documents specs also pass, with the same skips, on Solr 6.6.6 and 7.7.3.

These counts are with upstream master merged in. stolo_connect's full culturecode suite, and its referral specs on all 31 clients, also pass against this branch.

Merge order

Stacked on #6 (ci-production-solr), which has upstream master merged in through #8. Merge those first.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY

njakobsen and others added 6 commits October 19, 2026 03:00
…y_child`

Issue: nested documents could filter parents by their children (`with_child`, `without_child`) but not sort by them, so "projects by when their design milestone started" wasn't expressible. Sunspot has no precedent for sorting by children, so this follows Solr's: sort by a `{!parent}` block join query whose score is the children's value.

`order_by_child :milestones, :started_at, :desc do … end` takes the same block as `with_child`, and adds a `Sort::ChildSort`. The sort is `exists(query($child_sort_N)) desc, query($child_sort_N) <direction>`, where `child_sort_N` is `{!parent … score=<min|max|avg|total>}` over `{!bool}` of the block's restrictions, a filter that the field has a value, and `{!func}` of the field (`ms()` for a time). A parent with no matching child that has a value comes last in either direction. Several children combine by the smallest value ascending and the largest descending, unless `:score` says otherwise. `SortComposite` names each child sort's parameters by its position. `DSL::Scope#build_block_join` is shared with `with_child`.

`{!bool}` needs Solr 7 or later; the README says so. Only single-value numeric and time fields can be sorted by, and Solr compares the scores as floats, so values too close together can tie.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
… negative values sort as zero

Root cause: `ChildSort` accepted any `Type::TimeType` field, and `Type::DateRangeType` is one, so `order_by_child` on a date range field sent `ms(<field>_dr)`, which Solr rejects with a 400: "A ValueSource isn't directly available from this field". Separately, Lucene reads a negative function score as zero, so every time before 1970 sorted as 1970 and tied with the others, and negative numbers tied with zero. The spec "sorts times before 1970" had one such parent, so it passed.

`ChildSort` now raises `ArgumentError` for a date range field. The README and the `order_by_child` doc say that negative values sort as zero. The pre-1970 spec now has two parents, 1950 and 1960, and shows that a later `order_by(:name)` orders them either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
…rlier versions

Root cause: the README said `order_by_child` works on Solr 7 and above, but it was only run on Solr 8.11 and 9.10.1. Solr 5.3.1, the version `sunspot_solr` bundles, and Solr 6.6.6 reject its sort with `sort param could not be parsed as a query`. Solr 7.7.3 runs the sort, but doesn't tie times before 1970 with each other, and gets a descending `:score => :min` sort wrong. CI now runs the specs on the bundled Solr as well, where all 10 `order_by_child` examples fail.

The README now says Solr 8 and above, and the `order_by_child` specs are skipped before Solr 8 with a new `skip_before_solr` helper.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TAYgm5SknHTiRNTgGA6LBY
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant