Normalized search result returned by Arcana.search/2 for every mode.
All modes (:vector, :keyword, :hybrid, and graph-enhanced search)
return the same struct, so integrators never need per-mode formatters.
Fields
:id- Chunk id (UUID string):text- Chunk text:document_id- Owning document id (UUID string):chunk_index- Position of the chunk within its document:score- Final relevance score for the mode that produced it:vector_score- Vector similarity component. Only set by the single-query hybrid search on the pgvector backend,nilelsewhere.:keyword_score- Keyword relevance component. Same availability as:vector_score.:rerank_score- Score attached by rerankers that produce one (e.g.Arcana.Reranker.ColBERT),nilotherwise.:metadata- The chunk's stored metadata with string keys.
The struct implements the Access behaviour for reads, so result[:text]
keeps working for code that used the previous plain maps.
Serializing
Use to_map/1:
results |> Enum.map(&Arcana.SearchResult.to_map/1) |> JSON.encode!()The struct is deliberately not derived for Jason.Encoder or
JSON.Encoder. Deriving would make the field set part of the wire
contract, so adding or renaming a field would break everyone serializing
results. to_map/1 is the seam instead: it returns a shape this module
commits to, and lets fields be added without exposing them.
Two things not to do. Map.from_struct/1 works today but makes the
private field set your contract, so a field added later that isn't
JSON-safe breaks you at encode time. And a defimpl JSON.Encoder, for: Arcana.SearchResult in your own application is an orphan implementation
for a struct you don't own: it collides if Arcana ever ships one, and with
any other library that did the same.
Summary
Functions
Builds a result from a vector-store backend map (%{id, score, metadata}).
The result as a plain map, for encoding, logging, or handing to a model.
Types
Functions
Builds a result from a vector-store backend map (%{id, score, metadata}).
Backend metadata mixes the chunk's stored metadata with well-known keys
(:text, :chunk_index, :document_id, and hybrid score components);
the well-known keys become struct fields and the rest is kept under
:metadata with keys normalized to strings.
The result as a plain map, for encoding, logging, or handing to a model.
Fields are listed explicitly rather than taken from the struct, so this stays a contract: a field added to the struct later is not silently exposed to everything that serializes a result, and the keys here can outlive an internal rename.
iex> result = %Arcana.SearchResult{id: "abc", text: "hello", score: 0.9}
iex> map = Arcana.SearchResult.to_map(result)
iex> {map.id, map.text, map.score, map.metadata}
{"abc", "hello", 0.9, %{}}:metadata is passed through as stored, with string keys.