Database
Filtering and Querying
This guide covers techniques for selecting, comparing, and transforming documents using AQL's filter and ValueProxy capabilities.
import { Schema } from "@antelopejs/interface-database";
const schema = Schema.get("myapp")!;
const users = schema.instance().table("users");
Basic Filtering
Use the filter method with a predicate function. The callback receives a ValueProxy<T> representing each document.
const active = await users.filter((user) =>
user.key("status").eq("active"),
);
Comparison Operators
Equality and Inequality
const admins = await users.filter((u) => u.key("role").eq("admin"));
const nonAdmins = await users.filter((u) => u.key("role").ne("admin"));
Numeric Comparisons
const adults = await users.filter((u) => u.key("age").ge(18));
const seniors = await users.filter((u) => u.key("age").gt(65));
const young = await users.filter((u) => u.key("age").lt(18));
const discounted = await users.filter((u) => u.key("age").le(25));
Logical Operators
Combine conditions with and(), or(), and not():
// AND
const verified = await users.filter((u) =>
u.key("status").eq("active").and(u.key("emailVerified").eq(true)),
);
// OR
const important = await users.filter((u) =>
u.key("role").eq("admin").or(u.key("plan").eq("premium")),
);
// NOT
const standard = await users.filter((u) =>
u.key("plan").eq("premium").not(),
);
// Complex combinations
const target = await users.filter((u) =>
u.key("status").eq("active")
.and(
u.key("plan").eq("premium")
.or(u.key("totalPurchases").gt(500)),
),
);
String Operations
// Case-insensitive match
const bobs = await users.filter((u) =>
u.key("name").downcase().eq("bob"),
);
// Regex matching
const gmailUsers = await users.filter((u) =>
u.key("email").match("@gmail\\.com$"),
);
// String concatenation in filter
const fullNameMatch = await users.filter((u) =>
u.key("firstName").concat(u.key("lastName")).eq("JohnSmith"),
);
Date Filtering
Date Comparisons
const newUsers = await users.filter((u) =>
u.key("createdAt").gt(new Date("2024-01-01")),
);
const q1Users = await users.filter((u) =>
u.key("createdAt").ge(new Date("2024-01-01"))
.and(u.key("createdAt").lt(new Date("2024-04-01"))),
);
Date Components
const thisYear = await users.filter((u) =>
u.key("createdAt").year().eq(2024),
);
const morningLogins = await users.filter((u) =>
u.key("lastLogin").hours().lt(12),
);
Array Operations
// Check array contents
const jsPosts = await schema.instance().table("posts").filter((post) =>
post.key("tags").includes("javascript"),
);
// Check array length
const wellTagged = await schema.instance().table("posts").filter((post) =>
post.key("tags").count().gt(3),
);
// Check if empty
const untagged = await schema.instance().table("posts").filter((post) =>
post.key("tags").isempty(),
);
Default Values in Filters
Use default() to handle potentially null fields:
const activeUsers = await users.filter((u) =>
u.key("active").default(false).eq(true),
);
const frequentVisitors = await users.filter((u) =>
u.key("visits").default(0).gt(10),
);
Ordering and Pagination
// Sort ascending
const byName = await users.orderBy("name", "asc");
// Sort descending
const newest = await users.orderBy("createdAt", "desc");
// Paginate
const page1 = await users.orderBy("createdAt").slice(0, 10);
const page2 = await users.orderBy("createdAt").slice(10, 10);
Performance Tips
- Use
getAll()andbetween()with indexes instead offilter()when possible. Index-based lookups are significantly faster than full scans. - Filter early in the query chain to reduce the dataset before applying transformations or aggregations.
- Limit result sets with
slice()to avoid transferring unnecessary data. - Avoid complex computations inside filter predicates when simpler alternatives exist.
// Slower: full table scan with filter
const slow = await users.filter((u) => u.key("email").eq("[email protected]"));
// Faster: index-based lookup
const fast = await users.getAll("[email protected]", "email");
Index Management
Indexes improve query performance by enabling the database to locate documents quickly based on indexed fields. In AQL, indexes are defined as part of the schema definition.
Lookup
The lookup method performs a foreign key join, replacing a field in your documents with the referenced data from another table. It is available on both Stream<T> and Datum<T>.