Summary#
SearchHit is a single result of a semantic search: the text that matched, a relevance Score, the id of the
entity the text came from, and the raw Distance. Memory.Search returns a List<SearchHit> ranked
most-relevant-first. It is available under using Memory; and cannot be redeclared by the app.
Signature#
class SearchHit {
// what matched
string Content; // the human-readable text that matched
decimal Score; // relevance — higher is more relevant
decimal Distance; // raw vector distance to the query — lower is closer
MemoryKind Kind; // a property chunk, a document section, a chat turn
string? Via; // the link's words, when `related:` reached this hit — null for a direct hit
// which record
Guid SourceEntityId; // the entity instance this came from (a value, not a live reference)
string SourceEntityType; // the NAME of the entity type that id belongs to
string? SourceLabel; // that record in words — its `semantic` render
string? SourceDocument; // the FILE the words are in, when it came from one
// where, exactly
string? Section; // the heading this text sits under
int? PageFrom; // first page, when the source has pages
int? PageTo; // last page — "45-46" when it straddles a break
int? SourceOffset; // where this text starts in the source it was extracted from
int? MatchOffset; // where inside Content the match is
int? MatchLength; // how much of Content matched
// when, and on whose say-so
DateTime? RememberedAt; // when this was learned
MemoryOrigin Origin; // Derived (the record restated) or Authored (somebody chose to remember it)
Guid? AuthoredBy; // who — null for Derived, always
// what it connects to
MemoryLink[] Links; // what this record links to, and why
int? LinksElided; // how many links were left out for length
}Description#
A SearchHit is a plain in-memory value describing why a piece of content matched, so you can rank, filter, or
display results without re-fetching the source:
Content— the text that matched the query.Score— relevance, higher is more relevant. When akeywordis supplied to the search, the score is the fused (hybrid) relevance; otherwise it is the semantic similarity.SourceEntityType— the NAME of the entity typeSourceEntityIdbelongs to. You need both: the id alone says which row a hit came from but not which table, so it cannot be loaded, grouped or linked on its own.Kind— what kind of memory matched: a chunk of a[Searchable]property, a section of a document, a conversation turn. Worth reading when you build a prompt from hits — a recalled conversation turn and a stored field are different kinds of claim.SourceEntityId— the id of the entity the content came from. It is a plainGuidvalue, not a live entity reference — use it to load the full entity when you need it.Distance— the raw vector distance to the query embedding; lower is closer.Scoreis the value to rank on;Distanceis exposed for tuning athreshold.Via— why this hit is here at all, whenMemory.Search'srelated:reached it by following a stated link instead of by being about one of the entities you named. It carries the link's words read in the direction you travelled, so a hit found through your deal reads "superseded by — renegotiated after the Q2 review" rather than the sentence the other end would read. Two hops read as a path ("… → …"), and a hit several links reached carries each of their reasons. It is empty for a direct hit, which is what lets you tell the two apart — and the hop is never folded intoScore, because "similar to your query" and "linked to what you asked about" are different claims and one number cannot carry both.
Saying where it came from#
A quoted fact nobody can check is not much better than an unquoted one, so a hit carries everything needed to attribute it:
SourceLabel— the record in words, rendered from itssemantictemplate: "Contract MSA-2024-11", beside theSourceEntityIda machine can act on. It is read fresh on every search, so renaming a record renames its citations immediately — nothing is re-indexed and nothing goes stale.SourceDocument— the file the words are in, when the memory came from one. You usually want this andSourceLabel: "Order SO-1001, p. 45" cannot be checked when the order has four attachments.Section— the heading the text sits under, when the source had headings.PageFrom/PageTo— the pages, when the source has pages. A range, because text straddles page breaks.SourceOffset— where the returned text begins in the source it was extracted from. The general form of the same question, for sources whose structure is not pages — a transcript's timeline, a source file's lines.MatchOffset/MatchLength— where insideContentthe match actually is.Content.Substring(MatchOffset, MatchLength)is the sentence that matched, which is what you want to quote or highlight whenContentis a whole page. Empty when the hit is short enough to be its own match.
When, and on whose say-so#
RememberedAt— when this was learned, not when a row was written. Two memories often disagree, and the later one is usually right; without a date you cannot tell which is later.Origin—Derivedmeans the record restating itself (a[Searchable]property's text, a document section);Authoredmeans somebody chose to remember it (a conversation turn, a file, a stated link). Worth reading before you present a hit as fact: "the contract says" and "somebody said" carry different weight.AuthoredBy— who, for authored memory. Null forDerived— asking who wrote a property's chunk is a category error, not a missing value.
What it connects to#
Links carries what the hit's record is connected to, and why — each with the target's type, id and label, the
relationship read in the right direction, and the link's own stated words. It is an ANNOTATION, not a second
search: it describes what was already found, so it never changes the ranking and never crowds out a result. That is
the difference between it and related:, which genuinely widens the search and is opt-in for that reason.
A record with hundreds of links does not empty them into an answer — the list is capped, and LinksElided says
how many were left out. A link whose target you are not allowed to read is simply absent, and is never counted.
SearchHit is provided by the platform under using Memory;. Because Memory.Search's return
contract depends on its exact shape, an app cannot declare its own type named SearchHit while the capability is
in use — doing so is a compile error.
Examples#
using Osyrin.Memory;
string BestMatch(string q) {
var hits = Memory.Search(q, limit: 3); // already ranked best-first
if (hits.Count == 0) { return ""; }
return hits[0].Content;
}using Osyrin.Memory;
string Cite(string question) {
var hits = Memory.Search(question, limit: 1);
if (hits.Count == 0) { return "nothing on file"; }
var hit = hits[0];
var where = hit.SourceLabel;
if (hit.Section != null) { where = where + " - " + hit.Section; }
if (hit.PageFrom != null) { where = where + ", p. " + hit.PageFrom; }
return hit.Content + " (" + where + ")";
}using Osyrin.Memory;
string SupersededBy(string question) {
var hits = Memory.Search(question, limit: 1);
if (hits.Count == 0) { return ""; }
foreach (var link in hits[0].Links) {
if (link.Label == "superseded by") { return link.TargetLabel + ": " + link.Reason; }
}
return "";
}See also#
- using Memory (semantic search) — the
Memory.Searchcall that returns these hits - Memory.Link / Memory.Unlink — how a link's words are stated, and why each end gets its own
- How retrieval works — why a hit's text is a whole passage and the match is a range inside it