expr()The expr() function creates type-safe SurrealQL expressions using standalone operator functions, providing an alternative to writing raw SurrealQL strings.
Import:
import { expr, eq, eeq, ne, gt, gte, lt, lte, and, or, not, contains, containsAny, containsAll, containsNone, inside, outside, intersects, matches, knn, add, sub, mul, div, raw } from 'surrealdb';
Source: utils/expr.ts
function expr(expression: ExprLike): BoundQuery
| Parameter | Type | Description |
|---|---|---|
expression required | ExprLike | An expression created using operator functions. |
BoundQuery - Compiled query with bindings
eq(field, value)Equality comparison (=).
const adults = expr(eq('age', 18)); await db.select(new Table('users')).where(adults); // WHERE age = 18
eeq(field, value)Exact equality comparison (==).
const exact = expr(eeq('count', 0)); // WHERE count == 0
ne(field, value)Not equal comparison (!=).
const notAdmin = expr(ne('role', 'admin')); // WHERE role != 'admin'
gt(field, value), gte(field, value)Greater than (>) and greater than or equal (>=).
const adults = expr(gt('age', 17)); const adultsInclusive = expr(gte('age', 18));
lt(field, value), lte(field, value)Less than (<) and less than or equal (<=).
const young = expr(lt('age', 30)); const youngInclusive = expr(lte('age', 29));
and(...conditions)Logical AND - all conditions must be true.
const premiumAdults = expr(and( eq('tier', 'premium'), gte('age', 18) )); // WHERE tier = 'premium' AND age >= 18
or(...conditions)Logical OR - at least one condition must be true.
const adminOrModerator = expr(or( eq('role', 'admin'), eq('role', 'moderator') )); // WHERE role = 'admin' OR role = 'moderator'
not(condition)Logical NOT - inverts the condition.
const notBanned = expr(not(eq('status', 'banned'))); // WHERE NOT status = 'banned'
contains(field, value)Check if field contains value (CONTAINS).
const hasTag = expr(contains('tags', 'featured')); // WHERE tags CONTAINS 'featured'
containsAny(field, values)Check if field contains any of the values (CONTAINSANY).
const hasAnyTag = expr(containsAny('tags', ['new', 'featured', 'trending'])); // WHERE tags CONTAINSANY ['new', 'featured', 'trending']
containsAll(field, values)Check if field contains all values (CONTAINSALL).
const hasAllTags = expr(containsAll('tags', ['verified', 'premium'])); // WHERE tags CONTAINSALL ['verified', 'premium']
containsNone(field, values)Check if field contains none of the values (CONTAINSNONE).
const noBadTags = expr(containsNone('tags', ['spam', 'banned'])); // WHERE tags CONTAINSNONE ['spam', 'banned']
inside(field, geometry)Check if geometry is inside another (INSIDE).
const inRegion = expr(inside('location', regionPolygon)); // WHERE location INSIDE $regionPolygon
outside(field, geometry)Check if geometry is outside another (OUTSIDE).
const outsideZone = expr(outside('location', restrictedZone)); // WHERE location OUTSIDE $restrictedZone
intersects(field, geometry)Check if geometries intersect (INTERSECTS).
const overlaps = expr(intersects('area', otherArea)); // WHERE area INTERSECTS $otherArea
matches(field, query, ref?)Full-text search match (@@ or @ref@).
// Basic match const searchResults = expr(matches('content', 'searchTerm')); // WHERE content @@ 'searchTerm' // With reference number const searchWithRef = expr(matches('content', 'searchTerm', 1)); // WHERE content @1@ 'searchTerm'
knn(field, vector, k, distance?)K-nearest neighbors vector search.
const similar = expr(knn('embedding', [0.1, 0.2, 0.3], 10, 'cosine')); // WHERE embedding <|10,COSINE|> [0.1, 0.2, 0.3]
add(a, b)Addition (+).
const totalPrice = expr(add('price', 'tax')); // price + tax
sub(a, b)Subtraction (-).
const discount = expr(sub('original_price', 'sale_price')); // original_price - sale_price
mul(a, b)Multiplication (*).
const total = expr(mul('price', 'quantity')); // price * quantity
div(a, b)Division (/).
const average = expr(div('total', 'count')); // total / count
raw(sql)Create raw SurrealQL expressions.
WarningOnly use
raw()when no other operator is applicable. Incorrect use risks SQL injection.
const custom = expr(raw('custom_function()'));
import { expr, eq, gte } from 'surrealdb'; // Single condition const active = expr(eq('status', 'active')); const users = await db.select(new Table('users')).where(active); // Multiple conditions with AND const premiumAdults = expr(and( eq('tier', 'premium'), gte('age', 18), eq('active', true) )); const results = await db.select(new Table('users')).where(premiumAdults);
// Nested OR and AND const eligibleUsers = expr(or( and( eq('tier', 'premium'), gte('age', 18) ), and( eq('role', 'admin'), eq('verified', true) ) )); const users = await db.select(new Table('users')).where(eligibleUsers);
import { DateTime, Duration } from 'surrealdb'; const cutoffDate = DateTime.now().minus(Duration.parse('30d')); const recentUsers = expr(gte('created_at', cutoffDate)); const users = await db.select(new Table('users')).where(recentUsers);
// Check if user has specific tags const hasFeaturedTag = expr(contains('tags', 'featured')); // Check if has any of these tags const hasPromotedTags = expr(containsAny('tags', ['featured', 'trending', 'new'])); // Must have all required tags const fullyVerified = expr(containsAll('badges', ['email-verified', 'phone-verified'])); // Must not have any bad tags const cleanContent = expr(containsNone('flags', ['spam', 'inappropriate']));
import { GeometryPoint } from 'surrealdb'; const searchArea = new GeometryPolygon([/* ... */]); // Find locations inside area const nearby = expr(inside('location', searchArea)); const locations = await db.select(new Table('stores')).where(nearby); // Find areas that intersect const overlapping = expr(intersects('coverage_area', searchArea)); const zones = await db.select(new Table('zones')).where(overlapping);
// Basic text search const searchQuery = 'javascript tutorial'; const articles = await db.select(new Table('articles')) .where(expr(matches('content', searchQuery))); // With reference number for multi-field search const multiField = expr(or( matches('title', searchQuery, 1), matches('content', searchQuery, 1) ));
// Find similar items using KNN const queryVector = [0.1, 0.2, 0.3, /* ... */]; const similar = expr(knn('embedding', queryVector, 10, 'cosine')); const results = await db.select(new Table('items')).where(similar);
// Define reusable filters const activeFilter = expr(eq('active', true)); const verifiedFilter = expr(eq('verified', true)); const premiumFilter = expr(eq('tier', 'premium')); // Combine as needed const premiumActive = expr(and(activeFilter, premiumFilter)); const verifiedActive = expr(and(activeFilter, verifiedFilter)); // Use in queries const users1 = await db.select(new Table('users')).where(premiumActive); const users2 = await db.select(new Table('users')).where(verifiedActive);
const condition = expr(and( eq('status', 'pending'), lt('created_at', DateTime.now().minus(Duration.parse('1h'))) )); const updated = await db.update(new Table('orders')) .merge({ status: 'expired' }) .where(condition);
const oldInactive = expr(and( eq('active', false), lt('last_login', DateTime.now().minus(Duration.parse('90d'))) )); const deleted = await db.delete(new Table('users')).where(oldInactive);
// Good: Type-safe and reusable const condition = expr(and( gte('age', 18), eq('verified', true) )); // Avoid: Raw strings (no type safety) const condition = 'age >= 18 AND verified = true';
// Good: Compose small expressions const isAdult = expr(gte('age', 18)); const isVerified = expr(eq('verified', true)); const isActive = expr(eq('active', true)); const eligibleUsers = expr(and(isAdult, isVerified, isActive)); // You can reuse components const premiumEligible = expr(and(isAdult, isVerified));
raw() When Possible// Good: Use typed operators const condition = expr(gte('score', 80)); // Avoid: Raw SQL (SQL injection risk) const condition = expr(raw(`score >= ${userInput}`));
// Good: Values are automatically parameterized const minAge = getUserInput(); const condition = expr(gte('age', minAge)); // Safe: minAge is bound as a parameter, not concatenated
function buildUserFilter(options: { minAge?: number; tier?: string; active?: boolean; }) { const conditions: ExprLike[] = []; if (options.minAge !== undefined) { conditions.push(gte('age', options.minAge)); } if (options.tier) { conditions.push(eq('tier', options.tier)); } if (options.active !== undefined) { conditions.push(eq('active', options.active)); } return conditions.length > 0 ? expr(and(...conditions)) : null; } // Usage const filter = buildUserFilter({ minAge: 18, tier: 'premium' }); if (filter) { const users = await db.select(new Table('users')).where(filter); }
function searchProducts(criteria: { minPrice?: Decimal; maxPrice?: Decimal; categories?: string[]; inStock?: boolean; }) { const conditions: ExprLike[] = []; if (criteria.minPrice) { conditions.push(gte('price', criteria.minPrice)); } if (criteria.maxPrice) { conditions.push(lte('price', criteria.maxPrice)); } if (criteria.categories?.length) { conditions.push(containsAny('categories', criteria.categories)); } if (criteria.inStock !== undefined) { conditions.push(eq('in_stock', criteria.inStock)); } return expr(and(...conditions)); }