SemiLayerDocs

Similar — Quickstart

Three moves. If your lens already has searchable fields, you can ship similar() in under a minute.

similarproducts
products: {
  source: 'main-db',
  table: 'public.products',
  fields: {
    id:          { type: 'number', primaryKey: true },
    name:        { type: 'text',   searchable: { weight: 2 } },
    description: { type: 'text',   searchable: true },
    category:    { type: 'enum',   values: ['headphones', 'footwear', 'apparel'] },
    brand:       { type: 'text' },
  },
  grants: {
    similar: 'public',
  },
}

Three moves

Grant similar

Drop a similar entry under grants on the lens:

products: {
  // ... fields with searchable: true on the ones that matter
  grants: {
    similar: 'public',
  },
}

similar reuses the vectors search already computed from your searchable fields — nothing extra to declare, nothing extra to embed.

Push

semilayer push

No ingest re-run required if the fields were already searchable. No new embeddings — similar shares vectors with search.

Call from your app

import { beam } from './beam'

// On the product detail page
const { results } = await beam.products.similar({
  id:    currentProductId,
  limit: 6,
})

// Each result: { id, sourceRowId, score, metadata: ProductsMetadata }

React pattern

No dedicated hook — the simplest wrapper is a useEffect:

import { useEffect, useState } from 'react'
import { beam } from '@/lib/beam'

export function AlsoLike({ productId }: { productId: string }) {
  const [items, setItems] = useState<typeof beam.products.similar extends
    (...args: any) => Promise<{ results: infer R }> ? R : never
  >([])

  useEffect(() => {
    beam.products.similar({ id: productId, limit: 6 })
      .then((res) => setItems(res.results))
  }, [productId])

  return (
    <aside>
      <h4>You might also like</h4>
      {items.map((it) => (
        <a key={it.id} href={`/products/${it.sourceRowId}`}>
          {it.metadata.name}
        </a>
      ))}
    </aside>
  )
}

For feeds (paginated, live, scored), reach for useFeed with a recordVector feed instead.

Tuning

Two knobs, both optional:

  • limit — start with 6 for a sidebar panel, 10 for a dedicated page. More than 20 rarely helps; similarity scores tail off fast.
  • minScore — set to 0.7 if you want to hide "technically the nearest but still unrelated" results. The tradeoff: on sparse indexes the panel can come back empty.
await beam.products.similar({ id, limit: 10, minScore: 0.7 })

Errors

CodeMeaning
400id missing from the body
403grants.similar blocks the calling key
404Lens not found, or seed id has no stored vector (never ingested, or has no searchable fields)
409Lens is in error state — check semilayer status