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

eq-example.tsTypeScript
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

neq-example.tsTypeScript
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):

numeric-operators.tsTypeScript
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:

string-operators.tsTypeScript
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:

date-operators.tsTypeScript
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:

array-operators.tsTypeScript
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):

reference-operators.tsTypeScript
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:

null-checks.tsTypeScript
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 category

Logical Operators

Combine conditions using the q namespace:

logical-operators.tsTypeScript
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():

has-operator.tsTypeScript
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:

boolean-helpers.tsTypeScript
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:

order-by.tsTypeScript
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 last

Aggregations

Perform aggregations using the q namespace:

aggregations.tsTypeScript
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():

raw-sql.tsTypeScript
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]),
  ),
});