Skip to main content

Overview

The map() method queries nodes using MongoDB-style filters with support for sorting, pagination, and real-time subscriptions. It’s the primary method for searching and monitoring data.

Signature

await db.map(
  ...args: [
    options?: QueryOptions,
    callback?: (event: NodeEvent) => void
  ]
): Promise<{ results: Node[], unsubscribe?: () => void }>
The order of arguments doesn’t matter - you can pass the callback first or the options first.

Parameters

options
object
Query configuration object.
callback
function
Optional callback for real-time updates.Callback receives an event object with:
  • id (string): Node ID
  • value (object): Node content (null for ‘removed’ action)
  • edges (array): Connected relationships
  • timestamp (object): HLC timestamp
  • action (string): Event type - 'initial', 'added', 'updated', 'removed'

Return Value

results
Node[]
Array of nodes matching the query. Each node contains { id, value, edges, timestamp }.
unsubscribe
function
Function to stop listening for updates (only present when callback is provided).

Examples

Basic Query - All Nodes

const { results } = await db.map()

console.log("All nodes:", results)

Filter by Type

const { results } = await db.map({
  query: { type: "user" }
})

console.log("All users:", results)

Sorting

const { results } = await db.map({
  query: { type: "user" },
  field: "name",
  order: "asc"
})

console.log("Users sorted by name:", results)

Limit Results

const { results } = await db.map({
  query: { type: "post" },
  field: "createdAt",
  order: "desc",
  $limit: 10
})

console.log("Latest 10 posts:", results)

Real-Time Updates

// Listen to all nodes in real-time
const { unsubscribe } = await db.map(({ id, value, action }) => {
  if (action === "initial") {
    console.log(`[INITIAL DATA] ID: ${id}`, value)
  }
  if (action === "added") {
    console.log(`[NODE ADDED] ID: ${id}`, value)
  }
  if (action === "updated") {
    console.log(`[NODE UPDATED] ID: ${id}`, value)
  }
  if (action === "removed") {
    console.log(`[NODE REMOVED] ID: ${id}`)
  }
})

// Stop listening
// unsubscribe()

Real-Time with Filters

const { unsubscribe } = await db.map(
  { query: { type: "task", status: "pending" } },
  ({ id, value, action }) => {
    console.log(`Task ${id} ${action}:`, value)
  }
)

Live Task List

class TaskList {
  constructor() {
    this.tasks = new Map()
    this.unsubscribe = null
  }
  
  async init() {
    const { unsubscribe } = await db.map(
      { query: { type: "task" } },
      ({ id, value, action }) => {
        if (action === "initial" || action === "added" || action === "updated") {
          this.tasks.set(id, value)
        } else if (action === "removed") {
          this.tasks.delete(id)
        }
        this.render()
      }
    )
    
    this.unsubscribe = unsubscribe
  }
  
  render() {
    const html = Array.from(this.tasks.values())
      .map(task => `<li>${task.text}</li>`)
      .join("")
    
    document.getElementById("tasks").innerHTML = html
  }
  
  destroy() {
    if (this.unsubscribe) this.unsubscribe()
  }
}

Pagination - After

// Get first page
const page1 = await db.map({
  query: { type: "post" },
  field: "createdAt",
  order: "desc",
  $limit: 10
})

console.log("Page 1:", page1.results)

// Get next page
const lastId = page1.results[page1.results.length - 1].id

const page2 = await db.map({
  query: { type: "post" },
  field: "createdAt",
  order: "desc",
  $limit: 10,
  $after: lastId
})

console.log("Page 2:", page2.results)

Pagination - Before

const { results } = await db.map({
  query: { type: "post" },
  $limit: 10,
  $before: "post-123"
})

console.log("Previous page:", results)

Complex Filters with Operators

// Users with age > 18
const { results } = await db.map({
  query: {
    type: "user",
    age: { $gt: 18 }
  }
})

// Posts with specific tags
const { results } = await db.map({
  query: {
    type: "post",
    tags: { $in: ["javascript", "react"] }
  }
})

// Items within price range
const { results } = await db.map({
  query: {
    type: "product",
    price: { $between: [10, 100] }
  }
})

Graph Traversal with $edge

// Find all Files inside a Documents folder
const { results } = await db.map({
  query: {
    type: "Folder",
    name: "Documents",
    $edge: {
      type: "File"
    }
  }
})

console.log("Files in Documents:", results)

Nested Graph Traversal

// Find all Files > 1MB in Documents folder
const { results } = await db.map({
  query: {
    type: "Folder",
    name: "Documents",
    $edge: {
      type: "File",
      $or: [
        { extension: "jpg" },
        { size: { $gt: 1024 } }
      ]
    }
  }
})

Multiple Independent Queries

// Run in parallel
const [users, posts, tasks] = await Promise.all([
  db.map({ query: { type: "user" } }),
  db.map({ query: { type: "post" } }),
  db.map({ query: { type: "task" } })
])

console.log("Users:", users.results.length)
console.log("Posts:", posts.results.length)
console.log("Tasks:", tasks.results.length)

Real-Time Event Actions

initial
Fired for each node that matches the query when the subscription is first created. Provides the initial dataset.
added
Fired when a new node is created that matches the query.
updated
Fired when an existing node matching the query is modified.
removed
Fired when a node matching the query is deleted. The value will be null.

Best Practices

Use Destructuring: Extract only the properties you need from the event object:
// ✅ Recommended
await db.map(({ id, value, action }) => {
  console.log(id, action)
})

// ❌ Avoid
await db.map((event) => {
  console.log(event.id, event.action)
})
Always Unsubscribe: Prevent memory leaks by calling unsubscribe() when done:
const { unsubscribe } = await db.map(callback)

// Later, when component unmounts
unsubscribe()
Initial Data: The callback receives action: 'initial' events for existing data, making separate handling of the results array often unnecessary for real-time UI updates.

Build docs developers (and LLMs) love