Skip to main content

Filtering

Hugr provides powerful filtering capabilities for querying data with complex conditions. Filters can be applied to scalar fields, relations, nested objects, and combined using boolean logic.

Basic Filters

Scalar Field Filters

Filter by simple field comparisons:

query {
customers(filter: { country: { eq: "USA" } }) {
id
name
country
}
}

Multiple Conditions (Implicit AND)

Multiple filters at the same level are combined with AND:

query {
products(filter: {
price: { gte: 10.0 }
category: { eq: "electronics" }
in_stock: { eq: true }
}) {
id
name
price
category
}
}

Equivalent to SQL:

WHERE price >= 10.0 AND category = 'electronics' AND in_stock = true

Filter Operators by Type

Numeric Filters (Int, Float, BigInt)

query {
products(filter: {
price: {
eq: 50.0 # Equal to
gt: 10.0 # Greater than
gte: 10.0 # Greater than or equal
lt: 100.0 # Less than
lte: 100.0 # Less than or equal
in: [10.0, 20.0, 30.0] # In list
is_null: false # Is NULL / NOT NULL
}
}) {
id
name
price
}
}

String Filters

query {
customers(filter: {
name: {
eq: "John Doe" # Exact match
in: ["John", "Jane"] # In list
like: "John%" # Pattern match (% wildcard)
ilike: "john%" # Case-insensitive pattern match
regex: "^[A-Z][a-z]+" # Regular expression
is_null: false # Is NULL / NOT NULL
}
}) {
id
name
}
}

Pattern examples:

  • "John%" - Starts with "John"
  • "%Doe" - Ends with "Doe"
  • "%John%" - Contains "John"
  • "J_hn" - Matches "John", "Jahn" (single character wildcard)

Boolean Filters

query {
products(filter: {
in_stock: { eq: true }
is_featured: { is_null: false }
}) {
id
name
in_stock
}
}

Date and Timestamp Filters

query {
orders(filter: {
created_at: {
gte: "2024-01-01T00:00:00Z" # After or equal
lt: "2024-02-01T00:00:00Z" # Before
}
shipped_date: {
is_null: false # Has been shipped
}
}) {
id
created_at
shipped_date
}
}

JSON/JSONB Filters

Filter by JSON field values:

query {
events(filter: {
metadata: {
eq: { "user_id": 123, "status": "active" } # Exact match
contains: { "user_id": 123 } # Contains key-value (like PostgreSQL @>)
has: "transaction_id" # Has key
has_all: ["user_id", "transaction_id"] # Has all specified keys
is_null: false # Is NULL / NOT NULL
}
}) {
id
metadata
}
}

Geometry Filters

Filter by spatial relationships between geometries:

query {
parcels(filter: {
boundary: {
eq: "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))" # Exact geometry match (WKT)
intersects: {
type: "Polygon"
coordinates: [[[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]]
} # Intersects with geometry (GeoJSON)
contains: "POINT(5 5)" # Contains geometry (WKT)
is_null: false # Is NULL / NOT NULL
}
}) {
id
parcel_number
boundary
}
}

Geometry input formats:

  • WKT (Well-Known Text): "POINT(1 2)", "POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))"
  • GeoJSON: { "type": "Point", "coordinates": [1, 2] }

Spatial operators:

  • eq: Exact geometry match
  • intersects: Geometries share any portion of space
  • contains: First geometry completely contains second geometry
  • is_null: Check for NULL geometry

Intersects Examples

Find parcels that intersect with a polygon:

query {
parcels(filter: {
boundary: {
intersects: "POLYGON((0 0, 0 100, 100 100, 100 0, 0 0))"
}
}) {
id
parcel_number
area
}
}

Find roads crossing a specific area (using GeoJSON):

query {
roads(filter: {
geometry: {
intersects: {
type: "Polygon"
coordinates: [
[
[-74.01, 40.70],
[-74.01, 40.72],
[-73.99, 40.72],
[-73.99, 40.70],
[-74.01, 40.70]
]
]
}
}
}) {
id
name
road_type
}
}

Contains Examples

Find regions containing a specific point:

query {
regions(filter: {
boundary: {
contains: "POINT(-73.98 40.75)"
}
}) {
id
region_name
area
}
}

Find areas that contain a building footprint:

query {
zones(filter: {
boundary: {
contains: {
type: "Polygon"
coordinates: [
[
[-74.00, 40.71],
[-74.00, 40.712],
[-73.998, 40.712],
[-73.998, 40.71],
[-74.00, 40.71]
]
]
}
}
}) {
id
zone_name
}
}

Combining Geometry Filters

Combine spatial and attribute filters:

query {
buildings(filter: {
_and: [
{
footprint: {
intersects: "POLYGON((0 0, 0 100, 100 100, 100 0, 0 0))"
}
}
{ building_type: { eq: "residential" } }
{ height: { gte: 20 } }
]
}) {
id
name
building_type
height
}
}

Note: For more advanced spatial queries including distance-based filtering and spatial aggregations, see Spatial Queries.

Boolean Logic

AND Conditions

Combine multiple conditions that must all be true:

query {
orders(filter: {
_and: [
{ status: { eq: "completed" } }
{ total: { gte: 100.0 } }
{ created_at: { gte: "2024-01-01" } }
]
}) {
id
status
total
}
}

OR Conditions

Match any of the specified conditions:

query {
customers(filter: {
_or: [
{ country: { eq: "USA" } }
{ country: { eq: "Canada" } }
{ vip_status: { eq: true } }
]
}) {
id
name
country
}
}

NOT Conditions

Negate a filter condition:

query {
products(filter: {
_not: {
category: { in: ["discontinued", "archived"] }
}
}) {
id
name
category
}
}

Complex Boolean Logic

Combine AND, OR, and NOT:

query {
orders(filter: {
_and: [
{
_or: [
{ status: { eq: "pending" } }
{ status: { eq: "processing" } }
]
}
{
_or: [
{ priority: { eq: "high" } }
{ total: { gte: 1000.0 } }
]
}
{
_not: {
customer: {
status: { eq: "suspended" }
}
}
}
]
}) {
id
status
priority
total
}
}

Equivalent to SQL:

WHERE (status = 'pending' OR status = 'processing')
AND (priority = 'high' OR total >= 1000.0)
AND NOT (customer.status = 'suspended')

Important: Filtering by related objects only works for relations defined with @field_references or @references directives (foreign key relationships). Predefined joins created with @join or @table_function_call_join directives cannot be used in filter conditions. To restrict records based on these joins, use the inner: true parameter instead.

One-to-One Relations

Filter by fields in related objects:

query {
orders(filter: {
customer: {
country: { eq: "USA" }
vip_status: { eq: true }
}
}) {
id
customer {
name
country
}
}
}

Nested Relation Filters

Filter through multiple relation levels:

query {
orders(filter: {
customer: {
address: {
city: { eq: "New York" }
}
category: {
name: { ilike: "%premium%" }
}
}
}) {
id
customer {
name
address {
city
}
}
}
}

Deep Relation Filters

Filter up to 4 levels deep:

query {
order_details(filter: {
order: {
customer: {
category: {
description: { ilike: "%enterprise%" }
}
}
}
}) {
id
order {
customer {
name
category {
description
}
}
}
}
}

Filtering by List Relations

For one-to-many and many-to-many relations, use list operators:

any_of - At Least One Matches

query {
customers(filter: {
orders: {
any_of: {
status: { eq: "pending" }
}
}
}) {
id
name
orders(filter: { status: { eq: "pending" } }) {
id
status
}
}
}

Finds customers who have at least one pending order.

all_of - All Must Match

query {
customers(filter: {
orders: {
all_of: {
status: { eq: "completed" }
}
}
}) {
id
name
}
}

Finds customers where all their orders are completed.

none_of - None Can Match

query {
customers(filter: {
orders: {
none_of: {
status: { eq: "cancelled" }
}
}
}) {
id
name
}
}

Finds customers with no cancelled orders.

Complex List Filters

Combine multiple conditions:

query {
products(filter: {
categories: {
any_of: {
_and: [
{ name: { ilike: "%electronics%" } }
{ active: { eq: true } }
]
}
}
}) {
id
name
categories {
name
}
}
}

Nested List Filters

Filter through nested list relations:

query {
customers(filter: {
orders: {
any_of: {
order_details: {
any_of: {
product: {
category: { eq: "electronics" }
}
quantity: { gte: 5 }
}
}
}
}
}) {
id
name
}
}

Finds customers who ordered 5+ units of electronics.

Filtering Nested Objects

For fields with nested object types:

# Schema with nested object
type customer_profile {
preferences: UserPreferences # Nested object
settings: AccountSettings # Nested object
}

type UserPreferences {
theme: String
language: String
notifications_enabled: Boolean
}

Filter by nested object fields:

query {
customer_profiles(filter: {
preferences: {
theme: { eq: "dark" }
notifications_enabled: { eq: true }
}
}) {
id
preferences {
theme
notifications_enabled
}
}
}

Deep Nested Object Filters

query {
customers(filter: {
profile: {
settings: {
privacy: {
share_email: { eq: false }
}
}
}
}) {
id
name
}
}

Filtering with NULL Values

Finding NULL Values

query {
customers(filter: {
phone: { is_null: true }
}) {
id
name
phone
}
}

Finding Non-NULL Values

query {
customers(filter: {
phone: { is_null: false }
}) {
id
name
phone
}
}

NULL in Relations

query {
orders(filter: {
shipped_date: { is_null: true }
customer: { is_null: false }
}) {
id
status
}
}

Array and Vector Filters

Array Contains

query {
products(filter: {
tags: {
contains: ["featured", "sale"] # Contains all specified values
}
}) {
id
name
tags
}
}

Array Intersects

query {
products(filter: {
tags: {
intersects: ["featured", "new"] # Has any of specified values
}
}) {
id
name
tags
}
}

Array Equality

query {
products(filter: {
tags: {
eq: ["sale", "clearance"] # Exact match of array
}
}) {
id
name
tags
}
}

Vector fields support similarity search using embeddings for semantic search. Vector similarity search is not a filter but a separate query argument that ranks results by distance.

Note: Vector fields can only be filtered with is_null. For semantic search, similarity ranking, and distance-based queries, see Vector Similarity Search.

query {
documents(
# Vector similarity search
similarity: {
name: "embedding"
vector: [0.1, 0.2, 0.3, ...]
distance: Cosine
limit: 10
}
# Combined with filters
filter: {
published: { eq: true }
language: { eq: "en" }
}
) {
id
title
}
}

See Vector Similarity Search for comprehensive documentation on:

  • Distance metrics (Cosine, L2, Inner product)
  • Calculated distance fields
  • Combining vector search with filters
  • Text-to-vector search with @embeddings
  • Performance optimization
  • Use cases (semantic search, recommendations, duplicate detection)

Filter Reuse with Variables

Define reusable filters using variables:

query GetActiveCustomers($countryFilter: customers_filter!) {
customers(filter: $countryFilter) {
id
name
country
}
}

Variables:

{
"countryFilter": {
"_and": [
{ "country": { "in": ["USA", "Canada"] } },
{ "status": { "eq": "active" } }
]
}
}

Required Filters

Some fields may require filters using @filter_required:

# Schema definition
type time_series_data @table(name: "time_series") {
timestamp: Timestamp! @filter_required
sensor_id: Int!
value: Float!
}

Queries must include the required filter:

# Valid
query {
time_series_data(filter: {
timestamp: { gte: "2024-01-01", lt: "2024-02-01" }
}) {
timestamp
sensor_id
value
}
}

# Invalid - will return error
query {
time_series_data {
timestamp
value
}
}

Performance Optimization

1. Filter Early

Apply filters at the highest level to reduce data volume:

# Good - Filter at top level
query {
customers(filter: { country: { eq: "USA" } }) {
id
orders {
id
}
}
}

# Less efficient - Filter after join
query {
customers {
id
orders(filter: { customer: { country: { eq: "USA" } } }) {
id
}
}
}

2. Use Indexes

Ensure database indexes exist on:

  • Frequently filtered fields
  • Foreign key fields used in relation filters
  • Fields used in ORDER BY

3. Combine Filters Efficiently

# Good - Single filter with multiple conditions
query {
orders(filter: {
_and: [
{ status: { in: ["pending", "processing"] } }
{ total: { gte: 100 } }
]
}) {
id
}
}

# Avoid - Separate queries
query {
pending: orders(filter: { status: { eq: "pending" } }) { id }
processing: orders(filter: { status: { eq: "processing" } }) { id }
}

4. Use IN Instead of Multiple OR

# Good
query {
customers(filter: {
country: { in: ["USA", "Canada", "Mexico"] }
}) {
id
}
}

# Avoid
query {
customers(filter: {
_or: [
{ country: { eq: "USA" } }
{ country: { eq: "Canada" } }
{ country: { eq: "Mexico" } }
]
}) {
id
}
}

Common Filter Patterns

Date Ranges

query OrdersThisMonth {
orders(filter: {
created_at: {
gte: "2024-03-01T00:00:00Z"
lt: "2024-04-01T00:00:00Z"
}
}) {
id
created_at
}
}

Search by Name

query SearchCustomers($query: String!) {
customers(filter: {
_or: [
{ name: { ilike: $query } }
{ email: { ilike: $query } }
{ phone: { like: $query } }
]
}) {
id
name
email
}
}

Variables:

{
"query": "%john%"
}

Active Records Only

query {
products(filter: {
_and: [
{ deleted_at: { is_null: true } }
{ active: { eq: true } }
{ in_stock: { eq: true } }
]
}) {
id
name
}
}

Exclude Specific Values

query {
orders(filter: {
status: {
_not: { in: ["cancelled", "refunded"] }
}
}) {
id
status
}
}

Next Steps