Query Conditions Reference
Complete reference of all query operators and conditions
JustScale's query system provides type-safe operators for every field type. Each field expression exposes only the operators that make sense for its type, ensuring compile-time safety.
Comparison Operators
Available on all fields for equality checks:
.eq(value) - Equal
import { Product } from './models';
// Equal to value
const active = await productRepo.find({
where: Product.fields.status.eq('active'),
});
// Works with all field types
Product.fields.price.eq('99.99'); // decimal
Product.fields.stock.eq(10); // int
Product.fields.featured.eq(true); // boolean.neq(value) - Not Equal
import { Product } from './models';
// Not equal to value
const notArchived = await productRepo.find({
where: Product.fields.status.neq('archived'),
});Numeric Operators
Available on numeric fields (int, decimal, float, bigint):
import { Product } from './models';
// Greater than
Product.fields.price.gt(50);
// Greater than or equal
Product.fields.price.gte(50);
// Less than
Product.fields.stock.lt(10);
// Less than or equal
Product.fields.stock.lte(10);
// Between (inclusive)
Product.fields.price.between(10, 100);
// In list
Product.fields.stock.in([0, 5, 10, 20]);
// Not in list
Product.fields.price.notIn(['0.00', '9.99']);String Operators
Available on string and text fields:
import { Product } from './models';
// LIKE with wildcards (% matches any characters)
Product.fields.name.like('%phone%');
// Case-insensitive LIKE
Product.fields.name.ilike('%PHONE%'); // Matches "iPhone", "Phone", "phone"
// Starts with prefix
Product.fields.name.startsWith('Apple');
// Ends with suffix
Product.fields.name.endsWith('Pro');
// Contains substring (wraps with %)
Product.fields.name.contains('phone'); // Same as ilike('%phone%')
// In list
Product.fields.category.in(['Electronics', 'Computers']);
// Not in list
Product.fields.category.notIn(['Archived', 'Discontinued']);Date and Time Operators
Available on timestamp and date fields:
import { Product } from './models';
const lastWeek = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
const today = new Date();
// Before date
Product.fields.createdAt.before(lastWeek);
// After date
Product.fields.createdAt.after(lastWeek);
// Between dates (inclusive)
Product.fields.createdAt.between(lastWeek, today);
// Also supports gt, gte, lt, lte
Product.fields.updatedAt.gt(lastWeek);
Product.fields.updatedAt.gte(lastWeek);
Product.fields.updatedAt.lt(today);
Product.fields.updatedAt.lte(today);Array Operators
Available on array fields:
import { Product } from './models';
// Array contains single element
Product.fields.tags.contains('featured');
// Array has any of values
Product.fields.tags.hasAny(['sale', 'new', 'featured']);
// Array has all values
Product.fields.tags.hasAll(['electronics', 'smartphone']);
// Array overlaps with values
Product.fields.tags.overlaps(['sale', 'clearance']);Reference Operators
Available on reference fields (ref and refs):
import { q } from '@justscale/models';
import { Product, Category } from './models';
// Equal to reference (accepts Reference, entity with id, or id string)
Product.fields.category.eq(categoryRef);
Product.fields.category.eq(category); // Entity with id
Product.fields.category.eq('category-id');
// Not equal
Product.fields.category.neq('category-id');
// In list
Product.fields.category.in([cat1, cat2, 'cat-id-3']);
// Has related entity matching condition (JOIN)
Product.fields.category.has(
Category.fields.name.eq('Electronics')
);
// Many-to-many refs
Product.fields.tags.hasAny([tag1, tag2]);
Product.fields.tags.hasAll([tag1, tag2]);Null Checks
Available on all fields to check for null values:
import { Product } from './models';
// IS NULL
Product.fields.deletedAt.isNull();
// IS NOT NULL
Product.fields.deletedAt.isNotNull();
// Also works with references
Product.fields.category.isNull(); // No category assigned
Product.fields.category.isNotNull(); // Has a categoryLogical Operators
Combine conditions using the q namespace:
import { q } from '@justscale/models';
import { Product } from './models';
// AND - All conditions must match
q.and(
Product.fields.status.eq('active'),
Product.fields.price.gte(10),
Product.fields.stock.gt(0),
);
// OR - Any condition must match
q.or(
Product.fields.status.eq('sale'),
Product.fields.featured.eq(true),
);
// NOT - Negate condition
q.not(
Product.fields.status.eq('archived'),
);
// Combine logical operators
q.and(
Product.fields.status.eq('active'),
q.or(
Product.fields.price.lt(20),
Product.fields.featured.eq(true),
),
);has() for Relationships
Query based on related entities using has():
import { q } from '@justscale/models';
import { Order, OrderItem, Product } from './models';
// Find orders that have items from a specific category
const orders = await orderRepo.find({
where: q.has(
Order.fields.items,
OrderItem.fields.product.has(
Product.fields.category.eq('Electronics')
)
),
});
// Find products in active categories
const products = await productRepo.find({
where: Product.fields.category.has(
Category.fields.status.eq('active')
),
});Boolean Field Helpers
Boolean fields have convenience methods:
import { Product } from './models';
// Is true
Product.fields.featured.isTrue(); // Same as .eq(true)
// Is false
Product.fields.featured.isFalse(); // Same as .eq(false)
// In list
Product.fields.active.in([true]);Order By
Order results using field methods or object syntax:
import { Product } from './models';
// Fluent syntax
await productRepo.find({
orderBy: Product.fields.price.asc(),
});
await productRepo.find({
orderBy: Product.fields.price.desc(),
});
// Object syntax
await productRepo.find({
orderBy: { price: 'asc' },
});
// Multiple fields
await productRepo.find({
orderBy: [
{ status: 'desc' },
{ price: 'asc' },
],
});
// Null ordering
Product.fields.deletedAt.asc('first'); // Nulls first
Product.fields.deletedAt.desc('last'); // Nulls lastAggregations
Perform aggregations using the q namespace:
import { q } from '@justscale/models';
import { Product } from './models';
// Count records
const total = await productRepo.aggregate(q.count());
// Average
const avgPrice = await productRepo.aggregate(
q.avg(Product.fields.price)
);
// Sum
const totalValue = await productRepo.aggregate(
q.sum(Product.fields.stock)
);
// Min/Max
const minPrice = await productRepo.aggregate(q.min(Product.fields.price));
const maxPrice = await productRepo.aggregate(q.max(Product.fields.price));Raw SQL Escape Hatch
For complex queries not covered by the type-safe API, use q.raw():
import { q } from '@justscale/models';
// Raw SQL condition (use sparingly)
const results = await productRepo.find({
where: q.and(
Product.fields.status.eq('active'),
q.raw('price * stock > ?', [1000]),
),
});