Skip to content

Predicates

Predicates are the building blocks for filtering in TypeGraph queries. Each data type has its own set of predicates optimized for that type.

Predicates are accessed through property accessors in whereNode() and whereEdge():

.whereNode("p", (p) => p.name.eq("Alice"))
// ^accessor ^predicate

The accessor provides type-safe access to the field, and returns a predicate builder with methods appropriate for that field’s type. Edge fields work the same way:

.whereEdge("e", (e) => e.role.eq("admin"))
TypePredicatesSection
All typeseq, neq, in, notIn, isNull, isNotNullCommon
Stringcontains, startsWith, endsWith, like, ilikeString
Numbergt, gte, lt, lte, betweenNumber
Boolean(common only)Boolean
Dategt, gte, lt, lte, betweenDate
Arraycontains, containsAll, containsAny, isEmpty, isNotEmpty, lengthEq/Gt/Gte/Lt/LteArray
Objectget, field, hasKey, hasPath, pathEquals, pathContains, pathIsNull, pathIsNotNullObject
EmbeddingsimilarToEmbedding
Subqueryexists, notExists, inSubquery, notInSubquerySubqueries

All predicates can be combined using logical operators:

p.status.eq("active").and(p.role.eq("admin"))
p.role.eq("admin").or(p.role.eq("moderator"))
p.status.eq("deleted").not()
p.status
.eq("active")
.and(p.role.eq("admin").or(p.role.eq("moderator")))

Parenthesization is handled automatically. Vector similarity predicates cannot be nested under OR or NOT.


These predicates are available on all field types:

PredicateDescriptionSQL
eq(value)Equals= value
neq(value)Not equals!= value
in(values[])Value is in arrayIN (...)
notIn(values[])Value is not in arrayNOT IN (...)
isNull()Is null/undefinedIS NULL
isNotNull()Is not nullIS NOT NULL

eq and neq accept param() references for prepared queries. in and notIn do not support param() because the array length must be known at compile time.


String predicates for text matching and pattern searches.

p.name.eq("Alice") // Exact match
p.name.neq("Bob") // Not equal
p.name.contains("ali") // Case-insensitive substring match
p.name.startsWith("A") // Case-insensitive prefix match
p.name.endsWith("ice") // Case-insensitive suffix match
p.email.like("%@example.com") // SQL LIKE (case-sensitive) — % = any chars, _ = single char
p.name.ilike("alice%") // Case-insensitive LIKE
p.status.in(["active", "pending"])
p.status.notIn(["archived", "deleted"])
p.email.isNull()
p.email.isNotNull()
PredicateAcceptsDescriptionSQLCase
eq(value)string | param()Exact match=sensitive
neq(value)string | param()Not equal!=sensitive
contains(str)string | param()Substring matchILIKE '%str%'insensitive
startsWith(str)string | param()Prefix matchILIKE 'str%'insensitive
endsWith(str)string | param()Suffix matchILIKE '%str'insensitive
like(pattern)string | param()SQL LIKE patternLIKEsensitive
ilike(pattern)string | param()Case-insensitive LIKEILIKEinsensitive
in(values[])string[]In arrayIN (...)sensitive
notIn(values[])string[]Not in arrayNOT IN (...)sensitive
isNull()Is nullIS NULL
isNotNull()Is not nullIS NOT NULL

Wildcard escaping: User input passed to contains, startsWith, and endsWith is automatically escaped — % and _ characters are treated as literals. Use like or ilike when you need wildcard control.


Number predicates for numeric comparisons and ranges.

p.age.eq(30)
p.age.neq(0)
p.salary.gt(50000) // Greater than
p.salary.gte(50000) // Greater than or equal
p.age.lt(65) // Less than
p.age.lte(65) // Less than or equal
p.age.between(18, 65) // Inclusive on both bounds
p.priority.in([1, 2, 3])
p.priority.notIn([0])
p.score.isNull()
p.score.isNotNull()
PredicateAcceptsDescriptionSQL
eq(value)number | param()Equals=
neq(value)number | param()Not equals!=
gt(value)number | param()Greater than>
gte(value)number | param()Greater than or equal>=
lt(value)number | param()Less than<
lte(value)number | param()Less than or equal<=
between(lo, hi)number | param()Inclusive rangeBETWEEN lo AND hi
in(values[])number[]In arrayIN (...)
notIn(values[])number[]Not in arrayNOT IN (...)
isNull()Is nullIS NULL
isNotNull()Is not nullIS NOT NULL

Boolean fields support only the common predicates:

p.isActive.eq(true)
p.isActive.neq(false)
p.isVerified.isNull()
p.role.in(["admin", "moderator"]) // works on string enums too

No additional boolean-specific predicates are provided — eq(true) and eq(false) cover the typical cases.


Date predicates for temporal comparisons. Accepts Date objects or ISO 8601 strings.

p.createdAt.eq("2024-01-01")
p.createdAt.neq(new Date("2024-01-01"))
p.createdAt.gt("2024-01-01") // After
p.createdAt.gte("2024-01-01") // On or after
p.createdAt.lt(new Date()) // Before now
p.createdAt.lte("2024-12-31") // On or before
p.createdAt.between("2024-01-01", "2024-12-31")
p.birthday.in(["2024-01-01", "2024-07-04"])
p.deletedAt.isNull()
p.verifiedAt.isNotNull()
PredicateAcceptsDescriptionSQL
eq(value)Date | string | param()Equals=
neq(value)Date | string | param()Not equals!=
gt(value)Date | string | param()After>
gte(value)Date | string | param()On or after>=
lt(value)Date | string | param()Before<
lte(value)Date | string | param()On or before<=
between(lo, hi)Date | string | param()Inclusive rangeBETWEEN lo AND hi
in(values[])(Date | string)[]In arrayIN (...)
notIn(values[])(Date | string)[]Not in arrayNOT IN (...)
isNull()Is nullIS NULL
isNotNull()Is not nullIS NOT NULL

Array predicates for fields that contain arrays (e.g., tags: z.array(z.string())).

p.tags.contains("typescript") // Has specific value
p.tags.containsAll(["typescript", "nodejs"]) // Has ALL values
p.tags.containsAny(["typescript", "rust"]) // Has ANY value

Containment predicates (contains, containsAll, containsAny) are only available when the array element type is a scalar — string, number, boolean, or Date. They will not type-check for arrays of objects or arrays.

p.tags.isEmpty() // Empty array OR null
p.tags.isNotEmpty() // Has at least one element
p.scores.lengthEq(3) // Exactly 3 elements
p.scores.lengthGt(0) // More than 0 elements
p.scores.lengthGte(3) // 3 or more elements
p.scores.lengthLt(10) // Fewer than 10 elements
p.scores.lengthLte(5) // 5 or fewer elements
PredicateAcceptsDescriptionSQL
contains(value)THas valueJSON array contains
containsAll(values[])T[]Has all valuesAND of contains
containsAny(values[])T[]Has any valueOR of contains
isEmpty()Empty or nullIS NULL OR length = 0
isNotEmpty()Has elementsIS NOT NULL AND length > 0
lengthEq(n)numberExactly n elementsjson_array_length(col) = n
lengthGt(n)numberMore than njson_array_length(col) > n
lengthGte(n)numbern or morejson_array_length(col) >= n
lengthLt(n)numberFewer than njson_array_length(col) < n
lengthLte(n)numbern or fewerjson_array_length(col) <= n

Note: isEmpty() matches both empty arrays ([]) and null/undefined values. Use isNull() to check specifically for null.


Object predicates for JSON/object fields. Supports both fluent chaining with get() and JSON Pointer syntax for deep access.

Type-safe chaining through known keys:

p.metadata.get("theme").eq("dark")
p.settings.get("notifications").get("email").eq(true)

get() returns a typed field builder — if the nested field is a string you get string predicates, if it’s a number you get number predicates, and so on.

Access nested fields by JSON Pointer path:

p.config.field("/settings/theme").eq("dark")
p.config.field(["settings", "theme"]).eq("dark") // Array form

Like get(), field() returns a typed field builder for the resolved path. Use field() when you need to reach deeply nested paths in a single call.

p.metadata.hasKey("theme") // Has top-level key
p.config.hasPath("/nested/key") // Has nested path
p.config.pathEquals("/settings/theme", "dark") // Value at path equals scalar
p.config.pathContains("/tags", "featured") // Array at path contains value
p.config.pathIsNull("/optional") // Value at path is null
p.config.pathIsNotNull("/required") // Value at path is not null
PredicateAcceptsDescription
get(key)string (key name)Access nested field, returns typed field builder
field(pointer)string | string[] (JSON Pointer)Access field by path, returns typed field builder
hasKey(key)stringHas top-level key
hasPath(pointer)string | string[]Has nested path
pathEquals(pointer, value)pointer + string | number | boolean | DateValue at path equals scalar
pathContains(pointer, value)pointer + string | number | boolean | DateArray at path contains value
pathIsNull(pointer)string | string[]Value at path is null
pathIsNotNull(pointer)string | string[]Value at path is not null

JSON Pointer syntax: Use /key/nested/value string form or ["key", "nested", "value"] array form. pathEquals only works on scalar values (not objects or arrays). pathContains requires the path to point to an array.


Embedding predicates for vector similarity search on embedding fields.

Find similar vectors using distance metrics:

p.embedding.similarTo(queryEmbedding, 10) // Top 10 similar (cosine)
p.embedding.similarTo(queryEmbedding, 10, {
metric: "cosine", // "cosine" | "l2" | "inner_product"
minScore: 0.8, // Minimum similarity threshold
})
PredicateAcceptsDescription
similarTo(embedding, k)number[], numberTop k most similar vectors (cosine)
similarTo(embedding, k, opts)number[], number, SimilarToOptionsTop k with custom metric and threshold
MetricDescriptionRangeDefaultBest For
cosineCosine similarity0–1 (1 = identical)YesNormalized embeddings, semantic similarity
l2Euclidean distance0–∞ (0 = identical)Absolute distances, unnormalized vectors
inner_productInner product (PostgreSQL only)-∞ to ∞Maximum Inner Product Search (MIPS)
const similar = await store
.query()
.from("Document", "d")
.whereNode("d", (d) =>
d.embedding.similarTo(queryEmbedding, 20, {
metric: "cosine",
minScore: 0.7,
})
)
.select((ctx) => ({
id: ctx.d.id,
title: ctx.d.title,
content: ctx.d.content,
}))
.execute();

Limitations: Results are automatically ordered by similarity (most similar first). similarTo cannot be nested under OR or NOT. SQLite does not support embeddings — vector search requires PostgreSQL with pgvector.


Use param(name) to create a named placeholder for prepared queries.

import { param } from "@nicia-ai/typegraph";
const prepared = store
.query()
.from("Person", "p")
.whereNode("p", (p) => p.name.eq(param("name")))
.select((ctx) => ctx.p)
.prepare();
const results = await prepared.execute({ name: "Alice" });
PositionSupportedExample
Scalar comparisons (eq, neq, gt, gte, lt, lte)Yesp.age.gt(param("minAge"))
between boundsYesp.age.between(param("lo"), param("hi"))
String operations (contains, startsWith, endsWith, like, ilike)Yesp.name.contains(param("search"))
in / notInNoArray length must be known at compile time
Array predicatesNo
Subquery predicatesNo

See Prepared Queries for full usage and performance details.

  • Filter — Using predicates in queries
  • Subqueriesexists(), notExists(), inSubquery(), notInSubquery()
  • Overview — Query builder categories