Skip to content

Filters

IdentityScribe supports four filter formats for queries. Filters are auto-detected, so you can use whichever format fits your use case.

Filters are detected in this order:

FormatDetectionBest For
JSONStarts with { or [Programmatic queries, SDKs
LDAPStarts with ( and ends with )Legacy systems, LDAP clients
FleXTried nextInteractive queries, config files
SCIMFallbackREST APIs, SCIM clients
OperationFleXJSONSCIMLDAP
Equalitycn = John{"cn": "John"}cn eq "John"(cn=John)
Not equalcn != John{"cn": {"ne": "John"}}cn ne "John"(!(cn=John))
Starts withcn ^= J{"cn": {"sw": "J"}}cn sw "J"(cn=J*)
Containscn %= oh{"cn": {"co": "oh"}}cn co "oh"(cn=*oh*)
Existscn?{"cn": {"pr": true}}cn pr(cn=*)
ANDa=1 b=2{"a": "1", "b": "2"}a eq "1" and b eq "2"(&(a=1)(b=2))
ORa=1 or b=2{"_or_": [...]}a eq "1" or b eq "2"(|(a=1)(b=2))
Attr groupa|b = x{"a|b": "x"}

When filtering on timestamp attributes (createTimestamp, modifyTimestamp, verifiedTimestamp), you can use flexible temporal references instead of absolute timestamps. This feature works with all filter formats (FleX, JSON, SCIM, LDAP).

FormatExampleDescription
nowmodifyTimestamp ge nowCurrent time
now-durationmodifyTimestamp ge now-1h1 hour ago
now+durationverifiedTimestamp lt now+1d1 day from now
DurationcreateTimestamp ge 7d7 days ago
ISO durationmodifyTimestamp ge P30D30 days ago
Partial datecreateTimestamp ge 2024Start of 2024
Day keywordsmodifyTimestamp ge todayMidnight UTC
Period keywordscreateTimestamp ge this_weekStart of period
Relative periodsmodifyTimestamp ge last 3 daysN units ago

Day keywords: yesterday, today, tomorrow

Period keywords: this week, last week, this month, last month, this year, last year (Also: this_week, thisweek, last-month, etc.)

Relative periods: last N units where units can be days, hours, minutes, weeks, etc.

# FleX
modifyTimestamp ge today
createTimestamp ge last 3 days
# SCIM
modifyTimestamp ge "today"
createTimestamp ge "last 3 days"
# LDAP
(modifyTimestamp>=today)
(createTimestamp>=last 3 days)
# JSON
{"modifyTimestamp": {"ge": "today"}}
{"createTimestamp": {"ge": "last 3 days"}}

See Temporal references for the complete list of supported formats.


FleX is a flexible, human-readable query language. The name: Flexible Filter Expressions. Whitespace is lenient, and natural language operators make filters self-documenting.

Primary use case: Runtime query parsing for REST/GraphQL filter parameters, interactive search, and monitoring rules.

Secondary benefit: Cleaner syntax for HOCON configuration files compared to LDAP/SCIM/JSON formats.

# Simple equality
status = active
status is active
# Multiple conditions (implicit AND)
result=ok duration<=50ms
# OR conditions
result=ok or result=warn
result=ok, result=warn
# Natural language
duration is at most 100ms
name starts with John
OperatorAliasesDescription
=, ==eq, is, equalsEquality
!=, !==ne, isnt, is notNot equal
>gt, after, greater thanGreater than
>=ge, min, at leastGreater or equal
<lt, before, less thanLess than
<=le, max, at mostLess or equal
OperatorAliasesDescription
^=sw, starts with, prefixStarts with
$=ew, ends with, suffixEnds with
%=co, contains, hasContains substring
*=matches, like, globWildcard/glob pattern
~=approx, sounds likeApproximate match (phonetic)
OperatorAliasesDescription
?exists, present, prAttribute exists
OperatorAliasesDescription
inone ofValue in set
not in!in, not one ofValue not in set

Whitespace between conditions implies AND:

result=ok duration<=50ms
result=ok and duration<=50ms
result=ok & duration<=50ms
result=ok && duration<=50ms

Use or, ,, |, or ||:

result=ok or result=warn
result=ok, result=warn
result=ok | result=warn
result=ok || result=warn

AND binds tighter than OR. Use parentheses to override:

# Parses as: (channel=LDAP AND op=Search) OR (channel=REST AND op=Search)
channel=LDAP op=Search or channel=REST op=Search
# Use parentheses to override
channel=LDAP (op=Search or op=Bind)

FleX uses forgiving whitespace handling:

# These are all equivalent
age != 5
age ! = 5 # space after ! is OK
age !eq 5 # attached to word operator
age ! eq 5 # space after ! is OK
# Natural language prefixes
age is greater than 5
age is not greater than 5 # negated with "is not"
age isn't greater than 5 # contraction form
age isnt greater than 5 # without apostrophe
# "not" prefix works with all operators
name not contains foo
path not starts with /tmp
email not exists

Negate operators with ! or not:

result != ok
result !eq ok
result not eq ok
cn !starts with 'Test'
cn not starts with 'Test'
email !exists
email not exists

Negate entire expressions with ! or not():

!(result = ok)
not(result = ok)

Simple values without spaces or special characters:

status = active
priority = 42
enabled = true
duration <= 50ms

Use backticks (preferred), single quotes, or double quotes for values with spaces:

cn = `John Doe`
cn = 'John Doe'
cn = "John Doe"
value = null # Treated as "not exists"
value = nil # Same as null
value = true # Boolean
value = false # Boolean

To match the literal string “null”, quote it:

value = `null` # Matches the string "null"

FleX recognizes these unquoted value formats:

  • Numbers: 42, 3.14, 1_000_000, 1.5e-3
  • Hex/octal/binary: 0xFF, 0o755, 0b1010
  • Durations: 50ms, 5s, 1m, 2h, 1d, PT1M30S
  • Timestamps: 2024-01-15, 2024-01-15T10:30:00Z
  • UUIDs: 550e8400-e29b-41d4-a716-446655440000
  • IPs: 192.168.1.1, ::1
  • Emails: user@example.com

Apply the same condition to multiple attributes:

# Pipe syntax (no whitespace)
sn|givenName|name contains 'admin'
# Brace syntax (whitespace allowed)
{sn, givenName, name} contains 'admin'
# Both expand to:
sn contains 'admin' or givenName contains 'admin' or name contains 'admin'

Use in with parentheses or brackets:

status in (active, pending, review)
status in [active, pending, review]
role not in (admin, superuser)

Empty sets check presence:

status in () # Same as: status exists
status not in () # Same as: status not exists

For DN-based matching (LDAP :dn modifier):

member:dn := "cn=John,ou=users,dc=example,dc=com"
ou:dn := Engineering
monitoring {
rules = [
{ action = exclude, where = "scribe.result=ok duration.seconds<=50ms" }
{ action = exclude, name = "Hints.*", where = "duration.seconds is at most 100ms" }
{ action = include, where = "scribe.result != ok, duration.seconds >= 5s" }
]
}
transcribes {
ldap {
filter = "objectClass=Person employeeStatus!=terminated"
filter = "objectClass=Person (employeeType=FTE or employeeType=Contractor)"
}
}

JSON filters provide a structured, programmatic way to build queries. Ideal for SDKs, code generation, and type-safe query building.

// Simple equality
{"cn": "john"}
// Multiple conditions (AND)
{"cn": "john", "sn": "doe"}
// Array of values (OR)
{"cn": ["john", "jane"]}
// Operator with value
{"cn": {"sw": "A"}}
// Explicit logical operators
{"_or_": [{"cn": "a"}, {"cn": "b"}]}
{"_not_": {"disabled": "true"}}

JSON filters can have two root types:

  • Object {...}: Keys are combined with AND
  • Array [...]: Elements are combined with AND
// Object root - all conditions ANDed
{"cn": "john", "sn": "doe", "active": "true"}
// Array root - all elements ANDed
[{"cn": "john"}, {"sn": "doe"}]
OperatorAliasesMeaning
eqequals, is, =Equals
nenotEquals, !=Not equals
cocontains, %Contains substring
swstartsWith, ^Starts with
ewendsWith, $Ends with
prexistsAttribute present
gt>, afterGreater than
gegte, >=, minGreater or equal
lt<, beforeLess than
lelte, <=, maxLess or equal
approx~, soundsLikeApproximate match
matches*Wildcard pattern
includesin, oneOfValue in set
excludesnotIn, notOneOfValue not in set

Use _and_, _or_, and _not_ (or without underscores) for explicit logic:

// OR - any condition matches
{"_or_": [{"status": "active"}, {"status": "pending"}]}
// AND - all conditions match (explicit)
{"_and_": [{"role": "admin"}, {"active": "true"}]}
// NOT - negate condition
{"_not_": {"disabled": "true"}}
// Nested logic
{"_and_": [
{"type": "user"},
{"_or_": [{"role": "admin"}, {"role": "operator"}]}
]}

JSON values are coerced to strings:

JSON TypeResult
StringUsed as-is
NumberPlain string (no scientific notation)
Boolean"true" or "false"
NullTreated as “not exists”
{"age": 25} // age = "25"
{"active": true} // active = "true"
{"deleted": null} // deleted not exists

Apply the same condition to multiple attributes using pipe syntax:

// Search across multiple name fields
{"sn|givenName|cn": {"co": "admin"}}
// Equivalent to:
{"_or_": [
{"sn": {"co": "admin"}},
{"givenName": {"co": "admin"}},
{"cn": {"co": "admin"}}
]}
// Complex query with mixed operators
{
"objectClass": "user",
"cn": {"sw": "A"},
"department": ["Engineering", "Product"],
"_not_": {"status": "terminated"}
}
// Array operator (IN)
{"status": {"in": ["active", "pending", "review"]}}
// Multiple operators on same attribute
{"age": {"ge": "18", "lt": "65"}}

SCIM 2.0 filter syntax (RFC 7644) for REST-native queries. Basic SCIM filters work in FleX mode, though advanced features like complex value filters (emails[type eq "work"]), dotted paths, and URN-qualified attributes are not supported.

cn eq "john"
cn sw "A" and sn pr
not (disabled eq "true")
userName eq "john" or email co "@example.com"
OperatorDescription
eqEquals
neNot equals
coContains
swStarts with
ewEnds with
prPresent (attribute exists)
gtGreater than
geGreater or equal
ltLess than
leLess or equal
userName eq "john" and active eq true
userName eq "john" or userName eq "jane"
not (disabled eq "true")

Use parentheses to group conditions:

userName sw "a" and (role eq "admin" or role eq "operator")

SCIM operators are valid FleX syntax. FleX adds additional aliases:

SCIMFleX Alternatives
eq=, equals, is
ne!=, <>, is not
co%=, contains
sw^=, starts with
ew$=, ends with
pr?, exists, present
gt>, greater than, after
ge>=, at least, since
lt<, less than, before
le<=, at most, until
andimplicit (whitespace), &, &&
or,, |, ||
not (...)!(...), != value

Standard LDAP filter syntax (RFC 4515) for compatibility with existing LDAP infrastructure.

(cn=john)
(cn=john*)
(&(objectClass=user)(cn=john*))
(|(cn=john)(cn=jane))
(!(disabled=true))
PatternDescription
(attr=value)Equality
(attr=*)Presence (attribute exists)
(attr=prefix*)Starts with
(attr=*suffix)Ends with
(attr=*substring*)Contains
(attr~=value)Approximate match
(attr>=value)Greater or equal
(attr<=value)Less or equal
// AND - all must match
(&(objectClass=user)(status=active)(department=Engineering))
// OR - any must match
(|(cn=john)(cn=jane)(cn=bob))
// NOT - negate
(!(disabled=true))
// Nested
(&(objectClass=user)(|(role=admin)(role=operator)))

DN-component matching:

(member:dn:=cn=John,ou=users,dc=example,dc=com)
(ou:dn:=Engineering)
LDAPFleX
(cn=John)cn = John
(&(a=1)(b=2))a=1 b=2
(|(a=1)(b=2))a=1 or b=2
(!(a=1))a != 1 or !(a=1)
(cn=*)cn exists or cn?
(cn=John*)cn ^= John or cn starts with John
(cn=*John)cn $= John or cn ends with John
(cn=*John*)cn %= John or cn contains John