Skip to content

?search fans out over the resource's own columns only, so a resource whose natural search spans a relation loses it silently #93

Description

@jryannel

Found migrating subject-go's chat screen to the generated client (v0.5.0).

?search fans out over the resource's own searchable columns and only those:

for _, col := range p.model.Columns {
    if col.Searchable && !col.Hidden {
        preds = append(preds, sqlb.F(col.Name).Contains(term))
    }
}

That is right for most resources — subject-go's document library searches name and the extracted text_content, both its own columns, and ports unchanged. It is wrong for any resource whose natural search question is about something it points at.

The chat list is the clear case. Its hand-written search matches a conversation on:

  • chats.name, and
  • the name of the project the chat belongs to, and
  • the display name of any participant (participant_ids is a jsonb array of member ids).

The last one is not a nicety. A direct message has no name at all — it is named, in the UI, by whoever is in it — so "type a colleague's name to find the conversation" is the primary way the screen is used. Fanning out over the chat's own columns finds nothing for exactly those rows, and does it silently: the request is valid, the response is 200, and the chat the user was looking for is simply absent.

The natural workaround is closed on purpose, which is what makes this worth filing rather than working around: a computed column that renders the related names into one text value cannot be marked Searchablesqlb generate refuses it ("cannot be Searchable"), and FromSQL's doc comment names that refusal as deliberate.

Directions, author's call:

  • let a resource declare search predicates — the shape subject-go's own list kit uses, a raw SQL fragment per resource with the term bound into it. Most direct and most general; the cost is raw SQL in the declaration, which FromSQL has already accepted for computed columns, with the same "nothing parses it" caveat;
  • let a relation contribute a searchable column, e.g. schema.Ref("project", Project).SearchableVia("name"), compiled to an EXISTS (or a join) over the target. Declarative and keeps SQL out of the schema, but it only reaches relations the schema declares — the participant case above is a jsonb array of ids, not a Ref, so it would still be out;
  • lift the Searchable refusal for computed columns whose expression is a subquery. Smallest change and it covers both cases above, since a computed text expression can say anything. It does mean ?search can fan out into a correlated subquery per row, which is presumably why the refusal exists — so this direction is really a question about whether the refusal is protecting the database or the schema author.

Any of the three would let the chat list migrate. Meanwhile it stays on its hand-written endpoint: the alternative was a search box that quietly stopped finding direct messages.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions