Skip to content

Shape

Shape operations transform how results are returned. Use select() to define the output structure and aggregate() for grouped/aggregated results.

The select() method defines what data to return:

const results = await store
.query()
.from("Person", "p")
.select((ctx) => ({
name: ctx.p.name,
email: ctx.p.email,
}))
.execute();
.select(selectFunction)
ParameterTypeDescription
selectFunction(ctx) => TFunction that receives a context and returns the output shape

The context provides typed access to all nodes and edges in the query via their aliases.

Return all properties as an object:

.select((ctx) => ctx.p)
// Returns: { id, kind, name, email, ... }

Return only the fields you need:

.select((ctx) => ({
id: ctx.p.id,
name: ctx.p.name,
email: ctx.p.email,
}))

Include system metadata fields:

.select((ctx) => ({
id: ctx.p.id,
kind: ctx.p.kind, // "Person"
version: ctx.p.version, // Optimistic concurrency version
createdAt: ctx.p.createdAt,
updatedAt: ctx.p.updatedAt,
}))

Select from multiple nodes in a traversal:

const results = await store
.query()
.from("Person", "p")
.traverse("worksAt", "e")
.to("Company", "c")
.select((ctx) => ({
person: ctx.p.name,
company: ctx.c.name,
role: ctx.e.role, // Edge property
}))
.execute();

Structure output with nested objects:

.select((ctx) => ({
employee: {
id: ctx.p.id,
name: ctx.p.name,
},
company: {
id: ctx.c.id,
name: ctx.c.name,
},
employment: {
role: ctx.e.role,
startDate: ctx.e.startDate,
},
}))

Rename fields in the output:

.select((ctx) => ({
personName: ctx.p.name, // Renamed from 'name'
companyName: ctx.c.name, // Renamed from 'name'
jobTitle: ctx.e.role, // Renamed from 'role'
}))

TypeScript infers the result type from your selection:

// TypeScript infers: Array<{ name: string; email: string | undefined }>
const results = await store
.query()
.from("Person", "p")
.select((ctx) => ({
name: ctx.p.name, // string (required in schema)
email: ctx.p.email, // string | undefined (optional in schema)
}))
.execute();
// Invalid property access caught at compile time:
.select((ctx) => ({
invalid: ctx.p.nonexistent, // TypeScript error!
}))

When using optionalTraverse(), accessed nodes and edges may be undefined:

const results = await store
.query()
.from("Person", "p")
.optionalTraverse("worksAt", "e")
.to("Company", "c")
.select((ctx) => ({
person: ctx.p.name,
company: ctx.c?.name, // May be undefined
role: ctx.e?.role, // May be undefined
}))
.execute();

Use aggregate() with aggregate functions for grouped queries:

import { count, sum, avg, field } from "@nicia-ai/typegraph";
const stats = await store
.query()
.from("Person", "p")
.traverse("worksAt", "e")
.to("Company", "c")
.groupBy("c", "name")
.aggregate({
companyName: field("c", "name"),
employeeCount: count("p"),
totalSalary: sum("e", "salary"),
avgSalary: avg("e", "salary"),
})
.execute();

See Aggregate for full aggregate documentation.

With recursive traversals, include path and depth:

const results = await store
.query()
.from("Category", "cat")
.traverse("parentCategory", "e")
.recursive({ path: "pathIds", depth: "depth" })
.to("Category", "ancestor")
.select((ctx) => ({
category: ctx.cat.name,
ancestor: ctx.ancestor.name,
path: ctx.pathIds, // Array of node IDs
depth: ctx.depth, // Number of hops
}))
.execute();

When using temporal queries, access validity information:

const history = await store
.query()
.from("Article", "a")
.temporal("includeEnded")
.select((ctx) => ({
title: ctx.a.title,
validFrom: ctx.a.validFrom, // When this version became valid
validTo: ctx.a.validTo, // When superseded (undefined if current)
version: ctx.a.version, // Version number
}))
.execute();

select() returns an ExecutableQuery that provides:

  • execute() - Run the query and get results
  • paginate() - Cursor-based pagination
  • stream() - Stream results for large datasets
  • first() - Get the first result or undefined
  • count() - Count matching results
  • exists() - Check if any results exist
  • toAst() - Get the query AST
  • compile() - Compile to SQL
  • Aggregate - Grouping and aggregate functions
  • Order - Ordering and limiting results
  • Execute - Running queries and pagination