Weβre overhauling Dgraphβs docs to make them clearer and more approachable. If
you notice any issues during this transition or have suggestions, please
let us know.
The @search directive tells Dgraph what search to build into your GraphQL API.
When a type contains an @search directive, Dgraph constructs a search input
type and a query in the GraphQL Query type. For example, if the schema
contains
then Dgraph constructs a queryPost GraphQL query for querying posts. The
@search directives in the Post type control how Dgraph builds indexes and
what kinds of search it builds into queryPost. If the type contains
type Post {
...
datePublished: DateTime @search
}
then itβs possible to filter posts with a date-time search like:
query {
queryPost(filter: { datePublished: { ge: "2020-06-15" }}) {
...
}
}
If the type tells Dgraph to build search capability based on a term (word) index
for the title field
type Post {
...
title: String @search(by: [term])
}
then, the generated GraphQL API will allow search by terms in the title.
query {
queryPost(filter: { title: { anyofterms: "GraphQL" }}) {
...
}
}
Dgraph also builds search into the fields of each type, so searching is
available at deep levels in a query. For example, if the schema contained these
types
type Post {
...
title: String @search(by: [term])
}
type Author {
name: String @search(by: [hash])
posts: [Post]
}
then Dgraph builds GraphQL search such that a query can, for example, find an
author by name (from the hash search on name) and return only their posts that
contain the term βGraphQLβ.
queryAuthor(filter: { name: { eq: "Diggy" } } ) {
}
}
Dgraph can build search types with the ability to search between a range. For
example, with the preceding Post type with the datePublished field, a query
can find publish dates within a range.
query {
queryPost(filter: { datePublished: { between: { min: "2020-06-15", max: "2020-06-16" }}}) {
...
}
}
Dgraph can also build GraphQL search ability to find match a value from a list.
For example with the preceding Author type with the name field, a query can
return the Authors that match a list
queryAuthor(filter: { name: { in: ["Diggy", "Jarvis"] } } ) {
...
}
Thereβs different search possible for each type as explained below.
Int, float and dateTime
| argument | constructed filter |
|---|
| none | lt, le, eq, in, between, ge, and gt |
Search for fields of types Int, Float and dateTime is enabled by adding
@search to the field with no arguments. For example, if a schema contains:
type Post {
...
numLikes: Int @search
}
Dgraph generates search into the API for numLikes in two ways: a query for
posts and field search on any post list.
A field queryPost is added to the Query type of the schema.
type Query {
...
queryPost(filter: PostFilter, order: PostOrder, first: Int, offset: Int): [Post]
}
PostFilter will contain less than lt, less than or equal to le, equal
eq, in list in, between range between, greater than or equal to ge, and
greater than gt search on numLikes. Allowing for example:
query {
queryPost(filter: { numLikes: { gt: 50 }}) {
...
}
}
Also, any field with a type of list of posts has search options added to it. For
example, if the input schema also contained:
type Author {
...
posts: [Post]
}
Dgraph would insert search into posts, with
type Author {
...
posts(filter: PostFilter, order: PostOrder, first: Int, offset: Int): [Post]
}
That allows search within the GraphQL query. For example, to find Diggyβs posts
with more than 50 likes.
queryAuthor(filter: { name: { eq: "Diggy" } } ) {
...
posts(filter: { numLikes: { gt: 50 }}) {
title
text
}
}
dateTime
| argument | constructed filters |
|---|
year, month, day, or hour | lt, le, eq, in, between, ge, and gt |
As well as @search with no arguments, DateTime also allows specifying how
the search index should be built: by year, month, day or hour. @search
defaults to year, but once you understand your data and query patterns, you
might want to changes that like @search(by: [day]).
Boolean fields
| argument | constructed filter |
|---|
| none | true and false |
Boolean fields can only be tested for true or false. If
isPublished: Boolean @search is in the schema, then the search allows
filter: { isPublished: true }
and
filter: { isPublished: false }
Strings allow a wider variety of search options than other types. For strings,
you have the following options as arguments to @search.
| argument | constructed searches |
|---|
hash | eq and in |
exact | lt, le, eq, in, between, ge, and gt (lexicographically) |
regexp | regexp (regular expressions) |
term | allofterms and anyofterms |
fulltext | alloftext and anyoftext |
ngram | ngram |
- Schema rule:
hash and exact canβt be used together.
String exact and hash search
Exact and hash search has the standard lexicographic meaning.
query {
queryAuthor(filter: { name: { eq: "Diggy" } }) { ... }
}
And for exact search
query {
queryAuthor(filter: { name: { gt: "Diggy" } }) { ... }
}
to find users with names lexicographically after βDiggy.β
String regular expression search
Search by regular expression requires bracketing the expression with / and
/. For example, query for βDiggyβ and anyone else with βiggyβ in their name:
query {
queryAuthor(filter: { name: { regexp: "/.*iggy.*/" } }) { ... }
}
String term and fulltext search
If the schema has
type Post {
title: String @search(by: [term])
text: String @search(by: [fulltext])
...
}
then
query {
queryPost(filter: { title: { `allofterms: "GraphQL tutorial"` } } ) { ... }
}
anyofterms: "GraphQL tutorial" would match posts with either βGraphQLβ or
example, to find posts that talk about fantastic GraphQL tutorials:
query {
queryPost(filter: { title: { `alloftext: "fantastic GraphQL tutorials"` } } ) { ... }
}
String ngram search
The ngram index tokenizes a string into contiguous sequences of n words, with
support for stop word removal and stemming. N-gram search matches if the indexed
string contains the given sequence of terms.
If the schema has
type Post {
title: String @search(by: [ngram])
...
}
then
query {
queryPost(filter: { title: { ngram: "quick brown fox" } } ) { ... }
}
will match all posts that contain the contiguous sequence βquick brown foxβ in
the title.
Strings with multiple searches
It is possible to add multiple string indexes to a field. For example to search
for authors by eq and regular expressions, add both options to the type
definition, as follows.
type Author {
...
name: String! @search(by: [hash, regexp])
}
| argument | constructed searches |
|---|
| none | eq and in |
hash | eq and in |
exact | lt, le, eq, in, between, ge, and gt (lexicographically) |
regexp | regexp (regular expressions) |
enum fields are serialized in Dgraph as strings. @search with no arguments is
the same as @search(by: [hash]) and provides eq and in searches. Also
available for enums are exact and regexp. For hash and exact search on
enums, the literal enum value, without quotes "...", is used, for regexp,
strings are required. For example:
enum Tag {
GraphQL
Database
Question
...
}
type Post {
...
tags: [Tag!]! @search
}
would allow
query {
queryPost(filter: { tags: { eq: GraphQL } } ) { ... }
}
Which would find any post with the GraphQL tag.
While @search(by: [exact, regexp] would also admit lt etc. and
query {
queryPost(filter: { tags: { regexp: "/.*aph.*/" } } ) { ... }
}
which is helpful for example if the enums are something like product codes where
regular expressions can match a number of values.
Geolocation
There are 3 Geolocation types: Point, Polygon and MultiPolygon. All of
them are searchable.
The following table lists the generated filters for each type when you include
@search on the corresponding field:
| type | constructed searches |
|---|
Point | near, within |
Polygon | near, within, contains, intersects |
MultiPolygon | near, within, contains, intersects |
Example
Take for example a Hotel type that has a location and an area:
type Hotel {
id: ID!
name: String!
location: Point @search
area: Polygon @search
}
The near filter matches all entities where the location given by a field is
within a distance meters from a coordinate.
queryHotel(filter: {
location: {
near: {
coordinate: {
latitude: 37.771935,
longitude: -122.469829
},
distance: 1000
}
}
}) {
name
}
The within filter matches all entities where the location given by a field is
within a defined polygon.
queryHotel(filter: {
location: {
within: {
polygon: {
coordinates: [{
points: [{
latitude: 11.11,
longitude: 22.22
}, {
latitude: 15.15,
longitude: 16.16
}, {
latitude: 20.20,
longitude: 21.21
}, {
latitude: 11.11,
longitude: 22.22
}]
}],
}
}
}
}) {
name
}
Contains
The contains filter matches all entities where the Polygon or MultiPolygon
field contains another given point or polygon.
Only one point or polygon can be taken inside the ContainsFilter at a
time.
A contains example using point:
queryHotel(filter: {
area: {
contains: {
point: {
latitude: 0.5,
longitude: 2.5
}
}
}
}) {
name
}
A contains example using polygon:
queryHotel(filter: {
area: {
contains: {
polygon: {
coordinates: [{
points:[{
latitude: 37.771935,
longitude: -122.469829
}]
}],
}
}
}
}) {
name
}
Intersects
The intersects filter matches all entities where the Polygon or
MultiPolygon field intersects another given polygon or multiPolygon.
Only one polygon or multiPolygon can be given inside the
IntersectsFilter at a time.
queryHotel(filter: {
area: {
intersects: {
multiPolygon: {
polygons: [{
coordinates: [{
points: [{
latitude: 11.11,
longitude: 22.22
}, {
latitude: 15.15,
longitude: 16.16
}, {
latitude: 20.20,
longitude: 21.21
}, {
latitude: 11.11,
longitude: 22.22
}]
}, {
points: [{
latitude: 11.18,
longitude: 22.28
}, {
latitude: 15.18,
longitude: 16.18
}, {
latitude: 20.28,
longitude: 21.28
}, {
latitude: 11.18,
longitude: 22.28
}]
}]
}, {
coordinates: [{
points: [{
latitude: 91.11,
longitude: 92.22
}, {
latitude: 15.15,
longitude: 16.16
}, {
latitude: 20.20,
longitude: 21.21
}, {
latitude: 91.11,
longitude: 92.22
}]
}, {
points: [{
latitude: 11.18,
longitude: 22.28
}, {
latitude: 15.18,
longitude: 16.18
}, {
latitude: 20.28,
longitude: 21.28
}, {
latitude: 11.18,
longitude: 22.28
}]
}]
}]
}
}
}
}) {
name
}
Unions can be queried only as a field of a type. Union queries canβt be ordered,
but you can filter and paginate them.
Union queries donβt support the order argument. The results will be ordered
by the UID of each node in ascending order.
For example, the following schema will enable to query the members union field
in the Home type with filters and pagination.
union HomeMember = Dog | Parrot | Human
type Home {
id: ID!
address: String
members(filter: HomeMemberFilter, first: Int, offset: Int): [HomeMember]
}
# Not specifying a field in the filter input will be considered as a null value for that field.
input HomeMemberFilter {
# `homeMemberTypes` is used to specify which types to report back.
homeMemberTypes: [HomeMemberType]
# specifying a null value for this field means query all dogs
dogFilter: DogFilter
# specifying a null value for this field means query all parrots
parrotFilter: ParrotFilter
# note that there is no HumanFilter because the Human type wasn't filterable
}
enum HomeMemberType {
dog
parrot
human
}
input DogFilter {
id: [ID!]
category: Category_hash
breed: StringTermFilter
and: DogFilter
or: DogFilter
not: DogFilter
}
input ParrotFilter {
id: [ID!]
category: Category_hash
and: ParrotFilter
or: ParrotFilter
not: ParrotFilter
}
Not specifying any filter at all or specifying any of the null values for a
filter will query all members.
The same example, but this time with filter and pagination arguments:
query {
queryHome {
address
members(
filter: {
homeMemberTypes: [dog, parrot] # means we don't want to query humans
dogFilter: {
# means in Dogs, we only want to query "German Shepherd" breed
breed: { allofterms: "German Shepherd" }
}
# not specifying any filter for parrots means we want to query all parrots
}
first: 5
offset: 10
) {
... on Animal {
category
}
... on Dog {
breed
}
... on Parrot {
repeatsWords
}
... on HomeMember {
name
}
}
}
}
Vector embedding
The @search directive is used in conjunction with @embedding directive to
define the HNSW index on vector embeddings. These vector embeddings are obtained
from external Machine Learning models.
type User {
userID: ID!
name: String!
name_v: [Float!]
@embedding
@search(by: ["hnsw(metric: euclidean, exponent: 4)"])
}
In this schema, the field name_v is an embedding on which the HNSW algorithm
is used to create a vector search index.
The metric used to compute the distance between vectors (in this example) is
Euclidean distance. Other possible metrics are cosine and dotproduct.
The directive, @embedding, designates one or more fields as vector embeddings.
The exponent value is used to set reasonable defaults for HNSW internal tuning
parameters. It is an integer representing an approximate number for the vectors
expected in the index, in terms of power of 10. Default is β4β (10^4 vectors).