SemiLayerDocs

Similar

search takes a query string. feed takes a context + a ranking recipe. similar takes a single row id and returns the nearest neighbors by stored vector. No query text, no scorers — the simplest "more like this" primitive in SemiLayer. The vectors come from the same searchable fields search uses; declaring similar on a lens doesn't embed anything new.

similarrecipes
recipes: {
  source: 'main',
  table: 'recipes',
  fields: {
    id:          { type: 'number', primaryKey: true },
    title:       { type: 'text',   searchable: { weight: 2 } },
    description: { type: 'text',   searchable: true },
    cuisine:     { type: 'enum',   values: ['thai', 'japanese', 'italian'] },
  },
  grants: {
    similar: 'public',
  },
}
ℹ️

Play with similar against real data at demo.semilayer.com/similar — click a recipe, see its nearest neighbors.

What similar does

Given a seed row id, the server:

  1. Looks up the seed's stored vector (indexed read, scoped to the environment).
  2. Runs an ANN search against the lens's embeddings.
  3. Returns the closest rows by cosine similarity, excluding the seed itself.

Zero embedding API calls at request time. The seed's vector was computed at ingest; the similarity call just reads it.

The config

grants: {
  similar: 'public',       // or 'authenticated', or a claim check, or a function
}

One line. similar rides the searchable fields already declared on the lens — same vectors search uses, no extra embedding. Pass limit per-call.

That's it. No scorers, no pagination, no evolution.

The call

interface SimilarParams {
  id:        string              // required — the seed record id
  limit?:    number              // default: 10
  minScore?: number              // floor for cosine similarity, 0–1
  fields?:   string[]            // post-mapping projection for the response
  include?:  IncludeSpec         // expand related lenses onto each result
}

id is the only required argument. minScore is the shortcut you reach for when you want to drop distant neighbors without post-filtering on the client.

How it compares

searchsimilarfeed w/ recordVector
Seed typeQuery stringRow idRow id
Embedding cost at requestOne call (or cached)ZeroZero
RankingSimilarity onlySimilarity onlyComposable (sim + rec + engagement + diversify)
PaginationCursor / offsetSingle responseFull cursor system
EvolutionWebSocket ticks
Right forDiscovery / chat groundingPanel widgets, detail-page "more like this"Ranked related streams

Rule of thumb: reach for similar when the UX is a small, unchanging list (4–10 items) on a detail page. Reach for a feed with recordVector when you want popularity weighting, diversity, or pagination.

Access rules

grants.similar is a standard AccessRule. If you don't set it, similar() is still callable from sk_ keys but returns 403 for pk_ keys — safe by default for public-facing apps.

grants: {
  similar: 'public',         // open to pk_ keys
  // similar: 'authenticated',
  // similar: (claims) => claims.org_id === lens.orgId,  // custom
}

Where to start

  • Quickstart — declare + call in under 3 minutes.
  • Recipes — product catalog "you might also like", docs "related articles", support "similar tickets".