Null & None
Filter by null values, absent fields, and defined status
isNull
Checks whether a @nullable field has a null value. Only available on fields decorated with @nullable.
// Schema: deletedAt Date @nullable
// Find records where deletedAt is null
const softDeleted = await client.db.User.findMany({
where: { deletedAt: { isNull: true } },
});
// Find records where deletedAt is NOT null
const deleted = await client.db.User.findMany({
where: { deletedAt: { isNull: false } },
});eq: null and neq: null
You can also use eq and neq with null directly on @nullable fields:
// Equivalent to isNull: true
await client.db.User.findMany({
where: { deletedAt: { eq: null } },
});
// Equivalent to isNull: false
await client.db.User.findMany({
where: { deletedAt: { neq: null } },
});isNone
Checks whether an optional (?) field is absent (NONE) on the record. Only available on fields marked with ?.
// Schema: bio String?
// Find records where bio is absent
const noBio = await client.db.User.findMany({
where: { bio: { isNone: true } },
});
// Find records where bio exists (has any value, including null if @nullable)
const hasBio = await client.db.User.findMany({
where: { bio: { isNone: false } },
});isDefined
A semantic alias for isNone with inverted logic. Only available on optional (?) fields.
// Equivalent to isNone: false — field is present
await client.db.User.findMany({
where: { bio: { isDefined: true } },
});
// Equivalent to isNone: true — field is absent
await client.db.User.findMany({
where: { bio: { isDefined: false } },
});Use whichever reads more naturally for your intent — isDefined: true is often clearer than isNone: false.
NONE vs null vs Value
SurrealDB distinguishes between three field states: absent (NONE), null, and having a value. This is different from most databases where absent and null are the same thing.
For fields with both ? and @nullable (e.g., bio String? @nullable), there are three distinct states:
| State | Field exists? | Has value? | isNone: true | isNull: true | isDefined: true |
|---|---|---|---|---|---|
| NONE | No | No | ✅ | ❌ | ❌ |
null | Yes | No (null) | ❌ | ✅ | ✅ |
'hello' | Yes | Yes | ❌ | ❌ | ✅ |
Fields with only ? have two states (value or NONE). Fields with only @nullable have two states (value or null).
// Schema: bio String? @nullable
// Create records in each state
await client.db.User.create({ data: { name: 'Alice' } });
// Stored: { name: 'Alice' } — bio is NONE (absent)
await client.db.User.create({ data: { name: 'Bob', bio: null } });
// Stored: { name: 'Bob', bio: null }
await client.db.User.create({ data: { name: 'Carol', bio: 'Hello!' } });
// Stored: { name: 'Carol', bio: 'Hello!' }Querying the differences:
// Only Alice — bio field is absent
await client.db.User.findMany({ where: { bio: { isNone: true } } });
// Only Bob — bio is null
await client.db.User.findMany({ where: { bio: { isNull: true } } });
// Bob and Carol — bio field exists (regardless of value)
await client.db.User.findMany({ where: { bio: { isNone: false } } });
// Alice and Carol — bio is not null (NONE is also not null)
await client.db.User.findMany({ where: { bio: { isNull: false } } });Related
- NONE vs null — Understanding the difference between absent fields and null values
- Special Operators — Range queries with
betweenand combining filters