DocsGuidesLive Queries

Live Queries

TopGun provides a powerful reactive query system that allows you to subscribe to changes in your data. Unlike traditional databases where you have to poll for updates, TopGun pushes updates to your client in real-time.

Real-time Updates

Changes are pushed to subscribers immediately as they happen, no polling required.

Powerful Filters

Use simple equality matching or complex predicates with logical operators.

React Integration

First-class React hooks for seamless integration with your UI components.

Basic Usage

To create a live query, use the client.query() method. This returns a QueryHandle that you can subscribe to.

src/queries/todos.ts
const query = client.query('todos', {
  where: { completed: false },
  sort: { createdAt: 'desc' }
});

const unsubscribe = query.subscribe((results) => {
  console.log('Active todos:', results);
});

// Later, when you're done:
unsubscribe();

Query Filters

The QueryFilter object supports several options:

  • where: Simple equality matching
  • predicate: Complex conditions using logic operators
  • sort: Sort order
  • limit: Max number of results (coming soon)

Complex Predicates

For more complex filtering, use the predicate field:

src/queries/products.ts
import { Predicates } from '@topgunbuild/client';

const query = client.query('products', {
  predicate: Predicates.and(
    Predicates.greaterThan('price', 100),
    Predicates.equal('category', 'electronics')
  )
});

Available Predicate Methods

MethodDescriptionExample
equal(attr, value)Exact matchPredicates.equal('status', 'active')
notEqual(attr, value)Not equalPredicates.notEqual('type', 'draft')
greaterThan(attr, value)Greater thanPredicates.greaterThan('price', 100)
greaterThanOrEqual(attr, value)Greater or equalPredicates.greaterThanOrEqual('stock', 0)
lessThan(attr, value)Less thanPredicates.lessThan('age', 18)
lessThanOrEqual(attr, value)Less or equalPredicates.lessThanOrEqual('priority', 5)
like(attr, pattern)SQL-like pattern (% = any, _ = single char)Predicates.like('name', '%john%')
regex(attr, pattern)Regular expressionPredicates.regex('email', '^.*@gmail\\.com$')
between(attr, from, to)Range (inclusive)Predicates.between('price', 10, 100)
and(...predicates)Logical ANDPredicates.and(p1, p2, p3)
or(...predicates)Logical ORPredicates.or(p1, p2)
not(predicate)Logical NOTPredicates.not(p1)

Change Tracking

TopGun tracks not just the current state, but what changed. This enables:

  • Efficient UI updates (only re-render changed items)
  • Add/remove animations (framer-motion, react-spring)
  • Notifications (“New item added”)
  • Optimistic UI rollback on specific keys
const query = client.query('todos');

// Subscribe to changes
const unsubscribe = query.onChanges((changes) => {
  for (const change of changes) {
    console.log(`${change.type}: ${change.key}`, change.value);
    // change.type: 'add' | 'update' | 'remove'
    // change.previousValue: available for update/remove
  }
});

// Get pending changes without subscribing
const pending = query.getPendingChanges();

// Consume and clear changes
const consumed = query.consumeChanges();

React Integration

If you are using React, we recommend using the useQuery hook which handles subscription management automatically.

See the React Hooks reference for detailed documentation on useQuery (including change tracking), useMutation, and other hooks.

Distributed Live Queries (Cluster Mode)

In clustered environments, live queries automatically work across all nodes. When you subscribe to a query, you receive real-time updates for matching documents regardless of which node owns the data.

Cluster-Wide Updates

Changes on any cluster node automatically push to all relevant subscribers.

Transparent Coordination

The node you connect to becomes the coordinator, aggregating updates from all nodes.

How It Works

Cluster Live Query
// Live queries work seamlessly in clustered environments
// The client connects to any node - queries see data from ALL nodes

const client = new TopGunClient({
  serverUrl: 'ws://node1:8080'  // Connect to any cluster node
});

// This subscription receives updates from all cluster nodes
const query = client.query('orders', {
  predicate: Predicates.greaterThan('total', 100)
});

// Updates are pushed automatically when:
// - New matching documents are added on ANY node
// - Existing documents are modified to match/unmatch on ANY node
// - Documents are removed from ANY node
query.subscribe((results) => {
  console.log('All matching orders across cluster:', results.length);
});

Architecture

When a client subscribes to a live query in a cluster:

  1. Registration Phase: The connected node (coordinator) broadcasts the subscription to all cluster nodes
  2. Initial Results: Each node evaluates the query against its local data and sends results
  3. Result Merging: The coordinator deduplicates and merges results from all nodes
  4. Live Updates: When data changes on any node, that node evaluates affected subscriptions and sends targeted updates to the coordinator
  5. Client Delivery: The coordinator forwards updates to the client
Client ──QUERY_SUB──▶ Coordinator ──CLUSTER_SUB_REGISTER──▶ All Nodes
                            │                                    │
                            │◀───────────────────────────────────┘
                            │         Initial results + ACKs


                     Merge & Send to Client

              ┌─────────────┼─────────────┐
              │             │             │
         On change:    On change:    On change:
         Node A        Node B        Node C
              │             │             │
              └─────────────┼─────────────┘

                    CLUSTER_SUB_UPDATE


                  Forward to Client

Delta Updates

Instead of resending full result sets, nodes send targeted delta updates:

Update TypeDescription
ENTERDocument now matches the query predicate
UPDATEDocument still matches but its value changed
LEAVEDocument no longer matches the query predicate

This approach minimizes network traffic and enables efficient UI updates.

Automatic failover: If a cluster node disconnects, the coordinator automatically removes its results from the subscription state. When the node rejoins, its data becomes visible again through normal query updates.