Skip to content

Rich Filters

FraiseQL’s rich filters provide sophisticated query operators for 49 semantic scalar types, enabling domain-specific filtering beyond basic string matching and numeric comparisons.

Rich filters are automatically-generated GraphQL query operators that specialize in semantic domains like emails, geographic locations, financial codes, and more. Each semantic type generates:

  • Standard operators: eq, neq, contains, isnull
  • Domain-specific operators: Email domain matching, geographic distance, financial validation, etc.

All operators compile to optimized SQL at build time — zero runtime overhead.

query {
# Find users at a specific company domain
users(where: { email: { domainEq: "example.com" } }) {
id
email
}
# Find restaurants within 5km
restaurants(where: {
location: {
distanceWithin: {
latitude: 40.7128
longitude: -74.0060
radiusKm: 5
}
}
}) {
name
}
}

FraiseQL supports 49 semantic scalar types organized into 9 categories:

EmailAddress · PhoneNumber · URL · DomainName · Hostname

IBAN · CUSIP · ISIN · SEDOL · LEI · MIC · CurrencyCode · Money · ExchangeCode · ExchangeRate · StockSymbol

PostalCode · Latitude · Longitude · Coordinates · Timezone · LocaleCode · LanguageCode · CountryCode

Slug · SemanticVersion · HashSHA256 · APIKey · LicensePlate · VIN · TrackingNumber · ContainerNumber

IPAddress · IPv4 · IPv6 · MACAddress · CIDR · Port

AirportCode · PortCode · FlightNumber

Markdown · HTML · MimeType · Color · Image · File

DateRange · Duration · Percentage

Plus additional types for UUIDs, hashes, encodings, and more.

See Semantic Scalars Reference for complete documentation of all 49 types.

Execute queries with rich type filters in Apollo Sandbox below:

Rich Filters Example

Try filtering by email domain, geographic proximity, VIN, or other semantic types. Modify the query to explore different operators!

Loading Apollo Sandbox...

This sandbox uses Apollo Sandbox (the same GraphQL IDE as fraiseql serve). Your queries execute against the endpoint below. No data is sent to Apollo. Learn more about privacy →

Find all users at a specific company:

query {
users(where: { email: { domainEq: "acme.com" } }) {
id
email
name
}
}

Generates efficient database-specific SQL:

SELECT data FROM v_user
WHERE SUBSTRING(data->>'email' FROM POSITION('@' IN data->>'email') + 1) = 'acme.com'

Find restaurants within 5 kilometers:

query {
restaurants(where: {
location: {
distanceWithin: {
latitude: 40.7128
longitude: -74.0060
radiusKm: 5
}
}
}) {
id
name
location { latitude longitude }
}
}

Works across all databases with database-specific optimizations:

SELECT data FROM v_restaurant
WHERE ST_DWithin(
ST_Point(json_extract(data, '$.location.longitude'), json_extract(data, '$.location.latitude'))::geography,
ST_Point(-74.0060, 40.7128)::geography,
5000 -- 5km in meters
)

Query by international bank account number:

query {
accounts(where: {
iban: { ibanCountryEq: "DE" }
}) {
id
iban
balance
}
}

Find projects lasting at least 90 days:

query {
projects(where: {
timeline: {
durationGte: 90
overlaps: {
start: "2024-06-01T00:00:00Z"
end: "2024-08-31T23:59:59Z"
}
}
}) {
id
name
}
}

Find devices on a specific network:

query {
devices(where: {
ipAddress: { cidrContains: "192.168.0.0/24" }
}) {
id
hostname
ipAddress
}
}
TypePostgreSQLMySQLSQLiteSQL Server
EmailAddress✅ Native✅ Native✅ Native✅ Native
PhoneNumber✅ Regex✅ REGEXP✅ GLOB✅ LIKE
Coordinates✅ PostGIS✅ ST_Distance⚠️ Approx✅ Geography
DateRange✅ Intervals✅ DATEDIFF✅ julianday✅ DATEDIFF
Duration✅ INTERVAL✅ Parse✅ Parse✅ Parse

Geographic queries on PostgreSQL require the PostGIS extension:

Terminal window
# Enable PostGIS
CREATE EXTENSION IF NOT EXISTS postgis;
# Create spatial index for performance
CREATE INDEX locations_idx ON users USING GIST (location);

When you compile your schema with fraiseql compile:

@fraiseql.type
class User:
email: EmailAddress
location: Coordinates

FraiseQL automatically generates:

input EmailAddressWhereInput {
eq: String
neq: String
contains: String
domainEq: String
domainIn: [String!]
domainEndswith: String
}

The compiler embeds database-specific SQL templates in the compiled schema:

{
"operators": {
"domainEq": {
"postgres": "SUBSTRING(email FROM POSITION('@' IN email) + 1) = $1",
"mysql": "SUBSTRING_INDEX(email, '@', -1) = %s",
"sqlite": "SUBSTR(email, INSTR(email, '@') + 1) = ?",
"sqlserver": "SUBSTRING(email, CHARINDEX('@', email) + 1, LEN(email)) = @p1"
}
}
}

Rich filters are resolved at compile time, not runtime:

  • ✅ No reflection or dynamic generation
  • ✅ No string parsing
  • ✅ No database queries for metadata
  • ✅ All operators pre-compiled

Reference data is embedded in schema.compiled.json:

{
"lookup_data": {
"countries": {
"US": { "name": "United States", "continent": "North America" },
"FR": { "name": "France", "continent": "Europe" }
},
"currencies": {
"USD": { "symbol": "$", "decimal_places": 2 },
"EUR": { "symbol": "€", "decimal_places": 2 }
}
}
}

This ensures:

  • Fast lookups with no database queries
  • Consistent validation across instances
  • Deterministic compilation

Rich filters integrate with FraiseQL’s validation system to pre-validate parameters before SQL execution:

fraiseql.toml
[fraiseql.validation]
# Email domain must be valid format
email_domain_eq = { pattern = "^[a-z0-9]([a-z0-9-]*\\.)*[a-z0-9]([a-z0-9-]*[a-z0-9])?$" }
# IBAN must pass MOD-97 checksum
iban_country_eq = { checksum = "mod97" }
# Distance must be reasonable
distance_within_radius_km = { numeric_range = { min: 0, max: 40075 } }

See TOML Configuration - Validation for complete validation documentation.

Rich filters generate optimized SQL:

  1. Index-friendly: Use GIST/BRIN indexes for geospatial queries
  2. String-optimized: Prefix indexes for domain matching
  3. Cached lookups: Reference data cached in schema, not queried

This query:

users(where: { email: { domainEq: "example.com" } })

Becomes a simple string comparison with good index support:

WHERE SUBSTRING(...) = 'example.com'

This query:

restaurants(where: {
location: { distanceWithin: { latitude: 40.7128, longitude: -74.0060, radiusKm: 5 } }
})

Uses PostGIS efficiently:

WHERE ST_DWithin(location::geography, ST_Point(...)::geography, 5000)