Filters
Use WHERE operators, logical groups, related-table filters, JSON operators, and geo filters.
Filter shape
GraphJin filters live inside an inline where object. Keep the object shape in the query and use variables as leaf values:
query Products($min: Float!, $max: Float!, $ids: [Int!]) {
products(
where: {
price: { gte: $min, lte: $max }
id: { in: $ids }
}
order_by: { id: asc }
) {
id
name
price
}
}{ "min": 10, "max": 100, "ids": [1, 2, 3] }Do not pass the entire filter as where: $where. Whole-object filters are rejected so saved queries and allow-list review keep a stable operation shape.
Scalar and list operators
| Family | Operators | Notes |
|---|---|---|
| Equality | eq, neq | Use real booleans/numbers, not strings like "true" or "50". |
| Ordered comparisons | gt, gte, lt, lte | Aliases such as lteq, lesser_or_equals, and greater_or_equals are covered by tests. |
| Lists | in, nin | Values must be arrays, even for one value. |
| Nulls | is_null | Use true for null checks and combine with not for not-null checks. |
| Text | like, ilike, regex, iregex, similar | Pattern semantics are dialect-specific; ilike needs % wildcards for partial matches. |
query {
products(
where: { price: { gt: 10, lt: 100 } }
order_by: { id: asc }
limit: 3
) {
id
name
price
}
}Common operators include eq, neq, gt, gte, lt, lte, in, nin, is_null, and iregex.
Example_queryWithWhereGreaterThanOrLesserThan
tests/query_test.go:471Example_queryWithWhereIn
tests/query_test.go:411Logical groups
and, or, and not can wrap column filters, relationship filters, and spatial filters.
query {
products(where: {
and: [
{ not: { id: { is_null: true } } },
{ price: { gt: 10 } }
]
}) {
id
}
}Example_queryWithWhereNotIsNullAndGreaterThan
tests/query_test.go:438Text search filters
query Products($name: String!) {
products(
where: {
id: [3, 34]
or: {
name: { iregex: $name }
description: { iregex: $name }
}
}
order_by: { id: asc }
) {
id
}
}{ "name": "Product 3" }The id: [3, 34] shorthand is useful for primary-key lists, while the explicit operator form is clearer when the filter will be reused in saved queries.
Example_queryWithWhere1
tests/query_test.go:377Related-table filters
query ($user_id: ID!) {
products(
where: { owner: { id: { or: [{ eq: $user_id }, { eq: 3 }] } } }
order_by: { id: asc }
limit: 2
) {
id
owner { email }
}
}Example_queryWithWhereOnRelatedTable
tests/query_test.go:504Relationship filters compile through the same schema relationship graph as nested selections. Do not guess relationship names in agentic workflows; search gj_catalog for relationship rows first.
JSON filters and path fields
JSON columns can use key operators and path-derived fields.
query {
products(
where: { metadata: { has_key_any: ["foo", "bar"] } }
order_by: { id: asc }
limit: 3
) {
id
}
}query {
products(
where: { metadata_foo: { eq: true } }
order_by: { id: asc }
limit: 10
) {
id
metadata_foo
}
}Underscore notation maps to JSON path access for supported JSON columns. Key operators include has_key, has_key_any, has_key_all, contains, and contained_in.
Example_queryJSONPathOperations
tests/query_test.go:96Example_queryJSONPathOperationsAlternativeSyntax
tests/query_test.go:129Example_queryWithWhereHasAnyKey
tests/query_test.go:2007Geo filters
Spatial filters are available when the dialect and deployment support spatial types. The shared examples cover PostGIS, MySQL 8+, MariaDB, SQLite with SpatiaLite, SQL Server, Oracle Spatial, and MongoDB where the operation maps cleanly.
Distance
query {
locations(
where: {
geom: {
st_dwithin: {
point: [-122.4, 37.8]
distance: 10000
}
}
}
order_by: { id: asc }
) {
id
name
}
}Example_queryWithGeoFilter
tests/geo_test.go:10Distance can include units and variables:
query Nearby($loc: JSON!, $radius: Float!) {
locations(where: {
geom: { st_dwithin: { point: $loc, distance: $radius } }
}) {
id
name
}
}query {
locations(where: {
geom: { st_dwithin: { point: [-122.4194, 37.7749], distance: 5, unit: "miles" } }
}) {
id
name
}
}TestGeoStDWithinVariable
core/internal/qcode/geo_test.go:63TestGeoStDWithinWithUnit
core/internal/qcode/geo_test.go:42Shape relationships
query {
locations(where: {
geom: {
st_within: {
polygon: [[-122.5, 37.7], [-122.3, 37.7], [-122.3, 37.9], [-122.5, 37.9], [-122.5, 37.7]]
}
}
}) {
id
}
}query {
parcels(where: {
geom: {
st_intersects: {
geometry: {
type: "Polygon"
coordinates: [[[-122.5, 37.7], [-122.3, 37.7], [-122.3, 37.9], [-122.5, 37.9], [-122.5, 37.7]]]
}
}
}
}) {
id
}
}Supported spatial operator names include st_within, st_contains, st_intersects, st_coveredby, st_covers, st_touches, and st_overlaps.
Example_queryWithGeoContains
tests/geo_test.go:46TestGeoStIntersectsGeoJSON
core/internal/qcode/geo_test.go:133TestGeoStTouches
core/internal/qcode/geo_test.go:198TestGeoStOverlaps
core/internal/qcode/geo_test.go:219TestGeoStCoveredBy
core/internal/qcode/geo_test.go:242TestGeoStCovers
core/internal/qcode/geo_test.go:265MongoDB near
query {
locations(where: {
geom: { near: { point: [-122.4194, 37.7749], maxDistance: 5000 } }
}) {
id
name
}
}TestGeoNear
core/internal/qcode/geo_test.go:156Wrong / Right examples
| Wrong | Right | Why |
|---|---|---|
where: $where | where: { label: { eq: $label } } | Whole-object filters break saved-query review and are rejected. |
where: { id: { in: 1 } } | where: { id: { in: [1] } } | in and nin require arrays. |
where: { price: { gt: "50" } } | where: { price: { gt: 50 } } | Numeric operators need numeric values. |
where: { active: { eq: "true" } } | where: { active: { eq: true } } | Boolean values are true and false. |
where: { name: { ilike: "phone" } } | where: { name: { ilike: "%phone%" } } | SQL LIKE partial matching needs wildcards. |
These same mistakes are exposed to AI clients through serv/mcp_syntax.go so MCP users receive the same guidance.