Filtering
This is the documentation of the GraphQL Library version 6. For the long-term support (LTS) version 5, refer to GraphQL Library version 5 LTS. |
When querying for data, a number of operators are available for the types in the where
argument of a query or mutation, allowing you to filter query results or specify the set of objects a mutation applies to.
Operators can either be standalone operators (see Boolean operators) or they are appended to field names (for example, String comparison).
All operators can be combined using the Boolean operators AND
, OR
, and NOT
.
Operators
Boolean operators
As standalone operators, Boolean operators accept an array argument with items of the same format as the where
argument.
This way, they can be nested to form complex Boolean expressions.
For example, if you want to match all actors either by the name of "Keanu" or not belonging to the "Pantoliano" family, who played in "The Matrix" movie, here is how you query that:
query {
actors(where: {
AND: [
{
OR: [
{ name_CONTAINS: "Keanu" },
{ NOT: { name_ENDS_WITH: "Pantoliano" } }
]
},
{
movies_SOME: { title_EQ: "The Matrix" }
}
]}
) {
name
movies {
title
}
}
}
name_CONTAINS
and name_ENDS_WITH
are String comparisons while movies_SOME
is a relationship filter.
Equality operators
All types can be tested for equality or non-equality.
For example:
query {
users(where: { name_EQ: "John" })
id
name
}
For non-equality, you must use the NOT
logical operator.
query {
users(where: { NOT: { name_EQ: "John" }})
id
name
}
For the |
Numerical operators
-
_LT
-
_LTE
-
_GT
-
_GTE
Here is an example of how to use them:
query {
users(where: {age_LT: 50 }) {
id
name
age
}
}
Spatial types use numerical filtering differently and they also have additional options. See Filtering spatial types for more information.
Spatial type filtering
Both the Point
and the CartesianPoint
types use numerical operators and have an additional _DISTANCE
filter.
Here is a list of what each filter does for the two types:
-
_LT
: checks if a point is less than the distance in thedistance
field away (in meters) from the point specified by thepoint
field. -
_LTE
: checks if a point is less than or equal to the distance in thedistance
field away (in meters) from the point specified by thepoint
field. -
_DISTANCE
: checks if a point is the exact distance in thedistance
field away (in meters) from the point specified by thepoint
field. -
_GT
: checks if a point is greater than the distance in thedistance
field away (in meters) from the point specified by thepoint
field. -
_GTE
: checks if a point is greater than or equal to the distance in thedistance
field away (in meters) from the point specified by thepoint
field.
For a Point
type, all filters take the following type as an argument:
input PointDistance {
point: Point!
distance: Float!
}
In practice, you can construct queries like the following, which finds all users within a 5km (5000m) radius of a Point
:
query CloseByUsers($longitude: Float!, $latitude: Float!) {
users(where: { location_LTE: { point: { longitude: $longitude, latitude: $latitude }, distance: 5000 } }) {
name
location {
longitude
latitude
}
}
}
Similarly, for a CartesianPoint
type, all filters take the following type as an argument:
input CartesianPointDistance {
point: CartesianPoint!
distance: Float!
}
The same query for a CartesianPoint
:
query CloseByUsers($x: Float!, $y: Float!) {
users(where: { location_LTE: { point: { x: $x, y: $y }, distance: 5000 } }) {
name
location {
x
y
}
}
}
Type comparison
String comparison
The following case-sensitive comparison operators are available for String
and ID
types:
-
_STARTS_WITH
-
_ENDS_WITH
-
_CONTAINS
Here is an example of how to use them:
query {
users(where: { name_STARTS_WITH: "J" }) {
id
name
}
}
Additionally, numerical operators can be used for String comparisons.
They are disabled default.
To enable them, add them in the filters
features options for String
:
const { Neo4jGraphQL } = require("@neo4j/graphql");
const neo4j = require("neo4j-driver");
const typeDefs = `
type User @node {
name: String
}
`;
const driver = neo4j.driver(
"bolt://localhost:7687",
neo4j.auth.basic("username", "password")
);
const features = {
filters: {
String: {
LT: true,
GT: true,
LTE: true,
GTE: true
}
}
};
const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver });
RegEx matching
The filter _MATCHES
is available for comparison of String
and ID
types.
It accepts RegEx strings as an argument and returns any matches.
Note that RegEx matching filters are disabled by default. This is because, on an unprotected API, they could potentially be used to execute a ReDoS attack against the backing Neo4j database.
If you want to enable RegEx matching, update the features
configuration object.
For String
:
const features = {
filters: {
String: {
MATCHES: true,
}
}
};
const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver });
For ID
:
const features = {
filters: {
ID: {
MATCHES: true,
}
}
};
const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver });
For both String
and ID
:
const features = {
filters: {
String: {
MATCHES: true,
},
ID: {
MATCHES: true,
}
}
};
const neoSchema = new Neo4jGraphQL({ features, typeDefs, driver });
Array comparison
Consider the following type definitions:
type Movie @node {
id: ID!
title: String!
genres: [String!]
year: Int!
actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN)
}
type Actor @node {
id: ID!
name: String!
movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
}
The _IN
operator is available on non-array fields, and accepts an array argument:
query {
movies(where: { year_IN: [1999, 2000, 2001] }) {
title
year
}
}
The query returns all movies released in the years 1999, 2000 and 2001.
Conversely, the _INCLUDES
operator is available on array fields, and accepts a single argument:
query {
movies(where: { genres_INCLUDES: "Action" }) {
title
genres
}
}
The query returns all movies which have "Action" as one of their genres.
_IN
and _INCLUDES
are available for all types except Boolean
.
Interface filtering
You can use the typename_IN
filter to filter interfaces.
Refer to Type definitions → Type → Interface for more details and an example.
Relationship filtering
Relationship filtering depends on the type of relationship:
-
n..1
: the filtering is done on equality or inequality of the related nodes by specifying a filter onfield
. -
n..m
: the filtering is done on the list of related nodes and is based on the list predicates available in Cypher:
For example, take these type definitions:
type User @node {
id: ID!
name: String
posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT)
}
type Post @node {
id: ID!
content: String
author: User! @relationship(type: "HAS_POST", direction: IN)
likes: [User!]! @relationship(type: "LIKES", direction: IN)
}
n..1
relationships
In the type definitions example, an author
represents an n..1
relationship on Post
, where a given Post
is authored by one, and only one, author
.
The available filter is author
.
For example:
query {
posts(where: { author: { id_EQ: "7CF1D9D6-E527-4ACD-9C2A-207AE0F5CB8C" } }) {
content
}
}
NOT
by an undesired authorquery {
posts(where: { NOT: { author: { id_EQ: "7CF1D9D6-E527-4ACD-9C2A-207AE0F5CB8C" } } }) {
content
}
}
n..m
relationships
In the type definitions example, posts
represents an n..m
relationship on User
, where a given User
can have any number of posts
.
For example:
"neo4j"
query {
users(where: { posts_ALL: { content_CONTAINS: "neo4j" } }) {
name
}
}
"cypher"
query {
users(where: { posts_NONE: { content_CONTAINS: "cypher" } }) {
name
}
}
"graphql"
query {
users(where: { posts_SOME: { content_CONTAINS: "graphql" } }) {
name
}
}
"graph"
query {
users(where: { posts_SINGLE: { content_CONTAINS: "graph" } }) {
name
}
}
Aggregation filtering
The Neo4j GraphQL Library offers an aggregation key inside the where
argument of each relationship.
You can use it both on the node
and edge
of a relationship.
Here are some examples on how to apply this kind of filtering:
-
Find posts where the number of likes are greater than 5
Schema exampletype User @node { name: String } type Post @node { content: String likes: [User!]! @relationship(type: "LIKES", direction: IN) }
Queryquery { posts(where: { likesAggregate: { count_GT: 5 } }) { content } }
-
Find flights where the average age of passengers is greater than or equal to 18
Schema exampletype Passenger @node { name: String age: Int } type Flight @node { code: String passengers: [Passenger!]! @relationship(type: "FLYING_ON", direction: IN) }
Queryquery { flights(where: { passengersAggregate: { node: { age_AVERAGE_GTE: 18 } } }) { code } }
-
Find movies where the shortest actor screen time is less than 10 minutes
Schema exampletype Movie @node { title: String actors: [Person!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn") } type Person @node { name: String } type ActedIn @relationshipProperties { screenTime: Int }
Queryquery { movies(where: { actorsAggregate: { edge: { screenTime_MIN_LT: 10 } } }) { title } }
With operators
Aggregation filtering can also be done with operators.
They provide autogenerated filters available for each type on the node
and edge
of the specified relationship.
Field type | Description | Operators | Example |
---|---|---|---|
|
A special 'top level' key inside the |
|
|
|
These operators are calculated against the length of each string. |
|
|
|
Used in the case of |
|
|
|
Used in the case of |
|
Type definitions
Query
|
|
Description. |
|
Type definitions
Query
|
|
No aggregation filters are available for ID. |
- |
- |