Skip to main content

Queries

Access data stored on the network.

Prerequisites


  • Installed ComposeDB client
  • A compiled composite
  • A deployed composite
tip

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 query
  • postIndex 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 in last: n to get the last n 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:

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 type PostsFiltersInput allows the postsIndex 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 than 8.

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 type PostsSortingInput 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 the title 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 value
  • isViewer: Boolean!: whether the account authenticated to the Ceramic instance matches the id
  • 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 any Node (account or document) by its id
  • 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.


  • 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