Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Memory

field search (Matches / TextScore / Similarity)

bool Prop.Matches(string q) · decimal Prop.TextScore(string q) · decimal Prop.Similarity(string|Vector q)

Rank an entity's own rows by a query on an [Searchable(Entity)] field. Matches is the keyword filter (bool), TextScore is the full-text relevance score, Similarity is the semantic (meaning) score. They return values you compare, order, and weight yourself — compose your own hybrid ranking with plain arithmetic.

stable3 examples compiled by CIstoragesearchsemanticfull-text

Summary#

Field search ranks an entity's own rows by a query against one of its [Searchable(Entity)] fields (see [Searchable]). Three primitives give you the pieces, and you fuse them yourself:

  • Prop.Matches(q)bool — does the field match the keyword query? (the indexed candidate filter)
  • Prop.TextScore(q)decimal — full-text relevance (higher = better keyword match)
  • Prop.Similarity(q)decimal — semantic similarity (higher = closer in meaning)

Each returns a value, never a magic ranking. You compare, order, and weight them with ordinary expressions, so you decide what "relevant enough" means — the compose-your-own-hybrid idiom below.

Signature#

bool    Prop.Matches(string q)              // keyword predicate — use in Where
decimal Prop.TextScore(string q)            // full-text relevance score
decimal Prop.Similarity(string q)           // semantic score on a [Searchable(_, Full)] text field
decimal Prop.Similarity(Vector q)           // semantic score on a raw `Vector` field (bring your own vector)

Description#

These primitives operate on a single entity's rows through an ordinary query — they behave exactly as any other query does.

They run in the query, and only there#

All three are database index operations: Matches and TextScore work against a full-text index built over the field, and Similarity is a vector distance. None of them has an in-memory form, so they can be used only inside a query the database executes — not on a row you have already loaded, not in a computed member, and not on the client. Using one anywhere else is a compile error that says so.

Note.Where(n => n.Body.Matches(term))   // ✓ the database answers it, using the index
loadedNote.Body.Matches(term)           // ✗ compile error — there is nothing to run it against here

If you need the answer on a row you are holding, ask the query for it: filter or order by these in the query that loads the rows, and use what it returns.

Matches — the indexed keyword filter#

Prop.Matches(q) is a boolean full-text predicate: it's true when the field matches the keyword query q. It is index-backed, so it's the efficient way to narrow a large table to the candidate rows before you rank them — use it in Where.

TextScore — full-text relevance#

Prop.TextScore(q) scores how well the field matches the keyword query (higher = better). Unlike Matches, a bare score is not index-backed, so ranking by it alone would scan the whole table — filter with Matches first, then order the survivors by TextScore.

Similarity — semantic relevance#

Prop.Similarity(q) scores how close the field is to the query in meaning (higher = closer), so it finds matches with no shared keywords. On a [Searchable(_, Full)] text field you pass a string and the engine embeds it for you (once per query, never per row). On a raw Vector field you pass a vector you supply yourself.

Similarity needs a vector to compare against, so it's available only where one exists: a Full-mode searchable field or a raw Vector field. On a [Searchable(Entity, TextOnly)] field (no vector) it's a compile error — use Matches/TextScore there. Semantic ranking is active only when an embedding provider is configured; without one, Similarity contributes nothing and your search degrades to full-text (see [Searchable]).

Runtime thresholds#

Because each primitive is a value, a relevance cutoff is just a comparison — the bound can be any expression (a local, a parameter, a literal): Where(c => c.Bio.Similarity(q) > minScore).

Compose your own hybrid#

The platform hands you the pieces; you fuse them with plain arithmetic and choose the weights. The idiomatic hybrid filters with the indexed Matches, then orders by a weighted blend of semantic and lexical relevance:

Candidate
  .Where(c => c.Bio.Matches(q))                                          // indexed candidate set
  .OrderByDescending(c => 0.7 * c.Bio.Similarity(q) + 0.3 * c.Bio.TextScore(q))
  .Take(k)

For a turnkey cross-entity search that fuses these for you over the shared corpus, use using Memory (semantic search) instead; reach for field search when you want entity-local results and control over the ranking.

Examples#

using Osyrin.Memory;

entity Article {
  [Searchable] string Body;
}

List<Article> Search(string q) {
  return Article
    .Where(a => a.Body.Matches(q))                     // indexed candidate filter
    .OrderByDescending(a => a.Body.TextScore(q))       // rank the survivors
    .ToList();
}
using Osyrin.Memory;

entity Candidate {
  [Searchable] string Bio;
}

List<Candidate> Best(string q, decimal minScore, int k) {
  return Candidate
    .Where(c => c.Bio.Similarity(q) > minScore)        // threshold is any expression
    .OrderByDescending(c => c.Bio.Similarity(q))
    .Take(k)
    .ToList();
}
// Same `Candidate` as above — you decide how lexical and semantic scores are weighed.
List<Candidate> Hybrid(string q, int k) {
  return Candidate
    .Where(c => c.Bio.Matches(q))
    .OrderByDescending(c => 0.7 * c.Bio.Similarity(q) + 0.3 * c.Bio.TextScore(q))
    .Take(k)
    .ToList();
}

See also#

Related

[Searchable]

Mark a text field searchable. `[Searchable]` gives a String or Markdown property the best relevance search the app can…

using Memory (semantic search)

Opt into semantic (vector) search over your app's content. `using Memory;` adds a searchable store to the app…

SearchHit

One result of a semantic search — the matched text, how relevant it was, where it came from, when it was learned, and…