SemiLayerDocs

Query — Predicates

The where clause accepts a Record<string, unknown>. At the top level, keys AND together; per-field values can be a literal (equality) or an object with one of the supported operators.

Equality

The simplest shape. Direct equality on one or more fields:

await beam.products.query({
  where: { category: 'footwear', in_stock: true },
})
// Postgres equivalent: WHERE category = 'footwear' AND in_stock = true

Top-level keys always AND.

Operators

Wrap a value in an object to use a range or set operator. The grammar uses Mongo-style $ prefixes:

OperatorMeaningExample
$eqEqual (rarely needed — bare value works){ status: { $eq: 'open' } }
$neNot equal{ status: { $ne: 'closed' } }
$inValue is one of an array{ status: { $in: ['open', 'triaged'] } }
$gt, $gteGreater than, greater-or-equal{ rating: { $gte: 4 } }
$lt, $lteLess than, less-or-equal{ price: { $lt: 100 } }
$ilike, $likeCase-insensitive / case-sensitive substring (% and _ wildcards){ title: { $ilike: '%curry%' } }
$existsField is non-null{ deleted_at: { $exists: false } }

Combine operators on one field with an object:

await beam.products.query({
  where: {
    price: { $gte: 20, $lt: 100 },     // 20 ≤ price < 100
    category: { $in: ['footwear', 'apparel'] },
    in_stock: true,
  },
})

Logical composition — $or, $and, $not

Top-level keys still AND together by default. To compose more complex predicates, drop in $or, $and, or $not:

// "Failed orders OR orders over $500"
await beam.orders.query({
  where: {
    $or: [
      { status: 'failed' },
      { total_cents: { $gte: 50_000 } },
    ],
  },
})

// AND'd inside an OR — typical "tag filter" shape
await beam.products.query({
  where: {
    in_stock: true,
    $or: [
      { $and: [{ category: 'footwear' }, { price: { $lte: 100 } }] },
      { category: 'accessories' },
    ],
  },
})

// NOT
await beam.orders.query({
  where: {
    $not: { status: { $in: ['cancelled', 'refunded'] } },
  },
})

The grammar is shared across query, count, analyze.candidates.where, analyze drill-down search, and the streaming exports — one operator vocabulary, every read surface.

⚠️

Bridge capabilities differ. Postgres / MySQL / SQLite / Mongo / ClickHouse / Redis / DynamoDB / Firestore / Elasticsearch / Cassandra all support the full operator set as of @semilayer/bridge-resolver@1.5.0. Older bridge versions or community bridges may reject $or / $ilike — the service catches the bridge-side UnsupportedOperatorError and returns 400 unsupported_operator with the offending operator + bridge name in the body.

Full example

queryreviews
reviews: {
  source: 'main-db',
  table: 'public.reviews',
  fields: {
    id:            { type: 'number',  primaryKey: true },
    product_id:    { type: 'number' },
    author:        { type: 'text' },
    rating:        { type: 'number' },
    verified:      { type: 'boolean' },
    helpful_count: { type: 'number' },
  },
  grants: { query: 'public' },
}

orderBy

A single rule or an array (applied in order):

// Single
orderBy: { field: 'placed_at', dir: 'desc' }

// Multiple — first tiebreaker first
orderBy: [
  { field: 'priority',  dir: 'desc' },
  { field: 'placed_at', dir: 'asc' },
]

Default direction is 'asc'. Default ordering is whatever the bridge returns if you omit orderBy entirely — which is not guaranteed stable for pagination. Always set orderBy if you're going to paginate.

select

Restrict the projection to specific fields. Cheaper over the wire and often cheaper to compute.

await beam.products.query({
  where: { category: 'footwear' },
  select: ['id', 'name', 'price'],     // only these fields in the result
  limit: 50,
})

Omit select to get all fields declared on the lens. Selecting a field not declared on the lens is a validation error (422).

Beyond: joining across lenses

For "products with their recent reviews", reach for include:

await beam.products.query({
  where: { category: 'footwear' },
  include: {
    reviews: {
      where:   { rating: { $gte: 4 } },
      orderBy: { field: 'helpful_count', dir: 'desc' },
      limit:   3,
    },
  },
})

The full join grammar lives in Joins → Include Syntax.

Next: Pagination — handling more rows than fit in a single response.