Skip to content

Query your entities

Before going further be sure you completed Getting started

Learn how to query Torii gRPC

Torii gRPC query & clause documentation

Using the QueryBuilder

The QueryBuilder provides a fluent interface to construct queries for your entities. It allows you to organize your queries by namespace and entity, making it easy to build complex queries in a type-safe manner.

Basic Usage

Here's a simple example of how to use the QueryBuilder:

import { ToriiQueryBuilder, MemberClause } from "@dojoengine/sdk";
 
// Basic query for a player with specific ID and name
const query = new ToriiQueryBuilder()
    .withClause(
        MemberClause("world-Player", "id", "Eq", "1")
            .and(MemberClause("world-Player", "name", "Eq", "Alice"))
            .build()
    )
    .build();
 
// Execute the query
const entities = await sdk.getEntities(query);

Query Operators

The QueryBuilder supports various operators for filtering entities:

  • Eq - Equality comparison
  • Neq - Not equal comparison
  • Gt - Greater than comparison
  • Gte - Greater than or equal comparison
  • Lt - Less than comparison
  • Lte - Less than or equal comparison
  • In - Value is in a list
  • NotIn - Value is not in a list

Complex Queries

You can build complex queries using AND/OR combinations:

import { ToriiQueryBuilder, MemberClause, AndComposeClause, OrComposeClause } from "@dojoengine/sdk";
 
// Complex query with AND/OR combinations
const query = new ToriiQueryBuilder()
    .withClause(
        AndComposeClause([
            // First condition: score > 100
            MemberClause("world-Player", "score", "Gt", 100),
            // Second condition: name is either "Bob" or "Alice"
            OrComposeClause([
                MemberClause("world-Player", "name", "Eq", "Bob"),
                MemberClause("world-Player", "name", "Eq", "Alice")
            ]),
            // Third condition: durability < 50
            MemberClause("world-Item", "durability", "Lt", 50)
        ]).build()
    )
    .build();
 
const entities = await sdk.getEntities(query);

Subscribing to Entity Changes

To subscribe to entity changes, you need to first query your model and then subscribe using the entityIds:

import { ToriiQueryBuilder, MemberClause, AndComposeClause } from "@dojoengine/sdk";
 
// Subscribe to item updates with specific conditions
const [initialEntities, subscription] = await sdk.subscribeEntities({
    query: new ToriiQueryBuilder()
        .withClause(
            AndComposeClause([
                MemberClause("world-Item", "type", "Eq", "sword"),
                MemberClause("world-Item", "durability", "Eq", 5)
            ]).build()
        )
        // IMPORTANT: Include hashedKeys to be able to subscribe to changes
        // as we need entityIds to subscribe to changes.
        // If you provide a KeysClause, this will be used to subscribe to changes.
        // so that you can subscribe to keys patterns instead of entityIds.
        .includeHashedKeys(),
    callback: ({ data, error }) => {
        if (data) {
            console.log("Updated entities:", data);
        }
        if (error) {
            console.error("Subscription error:", error);
        }
    }
});

Pagination and Ordering

You can add pagination and ordering to your queries:

import { ToriiQueryBuilder, MemberClause } from "@dojoengine/sdk";
 
const query = new ToriiQueryBuilder()
    .withClause(
        MemberClause("world-Player", "score", "Gt", 0).build()
    )
    .limit(10)  // Limit results to 10 entities
    .offset(0)  // Start from the first result
    .orderBy("world-Player", "score", "Desc")  // Order by score in descending order
    .build();
 
const entities = await sdk.getEntities(query);

Best Practices

  1. Type Safety: Always use TypeScript to ensure type safety in your queries
  2. Query Organization: Group related conditions using AND/OR combinations
  3. Performance: Use pagination for large datasets
  4. Error Handling: Always handle potential errors in subscriptions
  5. Model Names: Use the correct model names with namespace prefix (e.g., "world-Player")
  6. Query Optimization: Use appropriate operators for your use case (e.g., Eq for exact matches)

Common Use Cases

Querying Players in a Specific Area

const query = new ToriiQueryBuilder()
    .withClause(
        AndComposeClause([
            MemberClause("world-Position", "x", "Gte", 0),
            MemberClause("world-Position", "x", "Lt", 100),
            MemberClause("world-Position", "y", "Gte", 0),
            MemberClause("world-Position", "y", "Lt", 100)
        ]).build()
    )
    .build();

Finding Active Players with Available Moves

const query = new ToriiQueryBuilder()
    .withClause(
        AndComposeClause([
            MemberClause("world-Moves", "remaining", "Gt", 0),
            MemberClause("world-Moves", "can_move", "Eq", true)
        ]).build()
    )
    .build();

React Integration

When using the QueryBuilder with React, you can use the provided hooks:

import { useEntityQuery, useModels, useModel } from "@dojoengine/sdk/react";
 
// Inside your React component
function GameComponent() {
    // Query entities and automatically subscribe to changes + binding to store
    useEntityQuery(
        new ToriiQueryBuilder()
            .withClause(
                MemberClause("world-Item", "durability", "Eq", 2).build()
            )
            .includeHashedKeys()
    );
 
    // Get all items
    const items = useModels("world-Item");
 
    // Get a single item by ID
    const entityId = useEntityId(1);
    const item = useModel(entityId, "world-Item");
 
    return (
        // Your component JSX
    );
}

This documentation provides a clear overview of the QueryBuilder's functionality, including:

  • Basic usage
  • Query operators
  • Complex queries
  • Subscribing to entity changes
  • Pagination and ordering
  • Best practices
  • Common use cases
  • React integration