Queries
Access data stored on the network.
Prerequisites
- Installed ComposeDB client
- A compiled composite
- A deployed composite
The ComposeDB Client automatically generates a GraphQL Schema from your compiled composite.
Basic queries
For our model, let’s say we have a Post
Data Model:
# Post model
type Post @createModel(accountRelation: LIST, description: "A simple text post") {
author: DID! @documentAccount
title: String! @string(minLength: 10, maxLength: 100)
text: String! @string(maxLength: 500)
}
Now, query for posts:
# Get first 2 posts
query{
postIndex(first:2){
edges{
node{
title
text
id
}
}
}
}
Where:
query
instructs GraphQL to perform a querypostIndex
is a built-in binding of your runtime composite. This binding enables us to look through the indexed data for a specific model and retrieve it.(first: 2)
grabs the first 2 items that are indexed. You can also pass inlast: n
to get the lastn
number of items.- The remainder of the query is standard GraphQL to retrieve the desired fields
Querying relations
In Relations you learned how to write models with relationships to other models or accounts. Here we demonstrate how to query those relations. Unlike basic queries, when querying relations allows you to specify fields in a related model to be returned as fields of the original model.
Querying model-to-model relations
For our models, let’s say we have a Post
model and we extend it by adding a Comments
model.
# Post model
type Post @createModel(accountRelation: LIST, description: "A simple text post") {
author: DID! @documentAccount
title: String! @string(minLength: 10, maxLength: 100)
text: String! @string(maxLength: 500)
}
# Extend post model with comment relations
type Comment @loadModel(id: "...") {
id
}
type Post @loadModel(id: "kjzl6hvfrbw6c99mdfpjx1z3fue7sesgua6gsl1vu97229lq56344zu9bawnf96") {
comments: [Comment] @relationFrom(model: "Comment", property: "postID")
}
Query for comments on posts:
# Get first 5 posts
# Get first 5 comments per post
query {
postIndex(first: 5) {
edges {
node {
title
text
commentsCount
commentsIndex(first: 5) {
edges {
node {
text
}
}
}
}
}
}
}
Querying account-to-model relations
# $id references the account DID string
query ($id: ID!) {
node(id: $id) {
...on CeramicAccount {
postConnection (first: 5) {
edges {
node {
id
title
text
}
}
}
}
}
}
Querying model-to-account relations
# $id references a Post document stream ID
query ($id: ID!) {
node(id: $id) {
...on Post {
author {
id
postConnection (first: 5) {
edges {
node {
id
title
text
}
}
}
}
}
}
}
Query filtering and sorting
Query filtering and sorting allows you to filter the query results based on specific fields and sort the results in a specified order.
Query filtering
When building your application you might want to filter the query results based on a specific condition. There are two types of conditions you can define for filtering the documents:
- value conditions - simple conditions that apply to a single field in a document
- logical conditions - multiple conditions that create more complex filtering
Create indices
If you want a specific field in your data model to be used as a filter, you have to create an index for that specific field. This can be done using using @createIndex
directive in your data model graphql schema.
For example, let’s say we would like to be able to filter the documents stored in the Posts
model by fields title
, tag
, ranking
and created_at
. To define the indices for these fields we are going to update the model schema definition as follows:
type Posts @createModel(accountRelation: LIST, description: "A simple Post")
@createIndex(fields: [{path: "title"}])
@createIndex(fields: [{path: "tag"}])
@createIndex(fields: [{path: "ranking"}])
@createIndex(fields: [{path: "created_at"}])
{
title: String! @string(minLength: 1, maxLength: 100)
body: String! @string(minLength: 1, maxLength: 100)
tag: String! @string(minLength: 1, maxLength: 100)
ranking: Int!
created_at: DateTime!
}
We can then create some example data with different inputs by running a series of mutations like the following:
Query to create a post:
mutation CreatePosts($input: CreatePostsInput!) {
createPosts(input: $input) {
document {
title
body
tag
ranking
created_at
}
}
}
Input variable with content for the post:
{
"input": {
"content": {
"title": "Let's talk about web3",
"body": "This is my first tutorial covering web3",
"tag": "tutorial",
"ranking": 10,
"created_at": "2023-07-11T10:02:08.190Z"
}
}
}
With indices defined and some data available we can now perform various queries with filters applied to specific fields.
Filter documents based on value conditions
ComposeDB enables you to filter documents based on specific value conditions applied to a specific field. Below is an example query that filters posts based on the post ranking. Key things to understand about the query in the example below:
- variable
input
of a typePostsFiltersInput
allows thepostsIndex
to be filtered by a specified condition - the
input
variable below defines the condition that is used to filter the documents. In this specific example, the query will return the documents that have a ranking greater than8
.
Query:
query MyQuery($input:PostsFiltersInput!) {
postsIndex(filters:$input, first:5) {
edges {
node {
title
body
created_at
ranking
tag
}
}
}
}
Filtering condition:
{
"input": {
"where": {
"ranking": {
"greaterThan": 8
}
}
}
}
Let’s look at a few more examples of what queries with value conditions you can write. We will reuse the same query called MyQuery
as above and will only modify the query conditions.
Return all documents where the field tag
is not null:
{
"input": {
"where": {
"tag": {
"isNull": false
}
}
}
}
Return all documents where the field tag has a value tutorial
or guide
:
{
"input": {
"where": {
"tag": {
"in": ["tutorial", "guide"]
}
}
}
}
Return documents that were created after a specific date:
{
"input": {
"where": {
"created_at": {
"greaterThan":"2023-05-12"
}
}
}
}
Check out the reference for a full list of available value conditions.
Filter documents using logical conditions
In addition to filtering documents based on values of a specific field, you might want to write more complex queries that would include logical conditions like and
, or
or not
.
This can be achieved by defining logical conditions like the example below. Here, we are using or
logical condition in the input variable. This query will return the documents that have a tag update
or were created before the date 2023-08-10:
Query:
query MyQuery($input:PostsFiltersInput!) {
postsIndex(filters:$input, first:5) {
edges {
node {
title
body
created_at
ranking
tag
}
}
}
}
Filtering condition:
{
"input": {
"or": [
{
"where": {
"tag": {
"equalTo":"update"
}
}
},
{
"where": {
"created_at": {
"lessThan":"2023-08-10"
}
}
}
]
}
}
Similarly, you can filter the documents based on a range of values. For example, let's say you would like to return the documents created between 2023-04-01
and 2023-08-01
:
{
"input": {
"and": [
{
"where": {
"created_at": {
"greaterThenOrEqualTo":"2023-04-01"
}
}
},
{
"where": {
"created_at": {
"lessThanOrEqualTo":"2023-08-01"
}
}
}
]
}
}
Note: while writing the queries in a verbose way enables you to avoid ambiguity, there is a possibility to write shorter queries that imply the expected logic. For example, the query above that is querying and filtering the documents based on a range of dates can also be written in a less verbose way as shown below. Under the hood, this filtering condition will use the same and logic as the example written above.
{
"input": {
"where": {
"created_at": {
"greaterThenOrEqualTo":"2023-04-01",
"lessThanOrEqualTo":"2023-08-01"
}
}
}
}
Similarly, you can have multiple fields in a single where
statement with implicit and
logic. The query below will return the documents that were created
within a specified date range and have either tutorial
or guide
tag:
{
"input": {
"where": {
"created_at": {
"greaterThanOrEqualTo":"2023-04-01",
"lessThanOrEqualTo":"2023-08-01"
},
"tag": {
"in": ["tutorial", "guide"]
}
}
}
}
Check out the reference for more examples of how you can write queries with logical conditions.
Sorting
Sorting allows you to sort the returned documents by a specified order - ascending, specified as ASC
, or descending, specified as DESC
. For a field to be used for sorting the returned documents, it needs to have an index created just like we specified in Create Indices section of this guide.
Below is an example query that returns the document stored in a Posts model, filtered by a specified date and ordered in an ascending order based on the title
field. Key things to understand about the query defined below:
- variable
sortinput
of a typePostsSortingInput
allows the postsIndex to be sorted using a specified ordering option. - the
sortinput
variable below defines the field that will be used for sorting and the sorting order. In this example, the results will be sorted in an ascending order based on thetitle
field.
Query:
query MyQuery($input:PostsFiltersInput!, $sortinput:PostsSortingInput!) {
postsIndex(filters:$input, sorting:$sortinput, first:5) {
edges {
node {
title
body
created_at
ranking
tag
}
}
}
}
Filtering and sorting condition:
{
"input": {
"where": {
"created_at": {
"greaterThan":"2023-05-12"
}
}
},
"sortinput": {
"title":"ASC"
}
}
Sorting documents by multiple fields is technically supported by ComposeDB, however, we don’t advice sorting documents by multiple fields as the order of which fields will be used for sorting cannot be guaranteed.
Check out the reference for more details about sorting.
Things to Know
ComposeDB client automatically generates a GraphQL Schema from a compiled composite. It includes two objects: a CeramicAccount
object and a root Query
object used as an entrypoint to access the graph.
CeramicAccount
Object
The CeramicAccount
object replaces DID
scalars in your composite with the following fields:
id: ID!
: the DID string valueisViewer: Boolean!
: whether the account authenticated to the Ceramic instance matches theid
- Other fields will be generated based on the models present in the definition
Query
Object
The Query
object provides entrypoints for accessing data in the graph, using the following fields:
node(id: ID!): Node
: loads anyNode
(account or document) by itsid
viewer: CeramicAccount
: the account attached to the Ceramic instance, if authenticated- Other fields will be generated based on the models present in the definition, providing entry-points by querying the index
Next Steps
Head to the next section, Mutations, to learn how to write or modify data on the network.
Related Guides
- For additional context on the types of account and model relations, see the Relations guide
- To learn how to compile and deploy a composite, see Composites