GrafeoDB¶
The main database class. All query methods return Promise<QueryResult>.
Constructor¶
// In-memory database
const db = GrafeoDB.create();
// Persistent database
const db = GrafeoDB.create('./my_graph.db');
// Open existing database
const db = GrafeoDB.open('./my_graph.db');
// For tests: rows of queries without ORDER BY in random order
const db = GrafeoDB.create(undefined, { shuffleUnordered: true });
// Keep a week of history for time-travel reads
const db = GrafeoDB.create('./my_graph.db', { historyRetention: 7 * 24 * 3600 * 1000 });
Parameters¶
| Method | Parameters | Description |
|---|---|---|
create(path?, options?) | path: string \| undefined, options: { shuffleUnordered?: boolean, historyRetention?: 'off' \| 'forever' \| number } | Create a database (in-memory if no path). shuffleUnordered returns the rows of every query without ORDER BY in random order, to find code that relies on an order that is unspecified. historyRetention sets how much history the database keeps for time-travel reads: 'off' (the default), 'forever' or a number of milliseconds; a database file stores it |
open(path) | path: string | Open an existing database |
Query Methods¶
All query methods accept an optional params object for parameterized queries.
execute()¶
Execute a GQL (ISO standard) query.
const result = await db.execute(
'MATCH (p:Person) WHERE p.age > $minAge RETURN p.name',
{ minAge: 25 }
);
executeCypher()¶
Execute a Cypher query. Requires the cypher feature.
executeGremlin()¶
Execute a Gremlin query. Requires the gremlin feature.
executeGraphql()¶
Execute a GraphQL query. Requires the graphql feature.
executeSparql()¶
Execute a SPARQL query against the RDF triple store. Requires the sparql feature.
executeSql()¶
Execute a SQL/PGQ query (SQL:2023 GRAPH_TABLE). Requires the sql-pgq feature.
const result = await db.executeSql(
'SELECT * FROM GRAPH_TABLE (MATCH (p:Person) COLUMNS (p.name AS name))'
);
Node Operations¶
createNode()¶
Create a node with labels and optional properties.
const node = db.createNode(['Person'], { name: 'Alix', age: 30 });
console.log(node.id); // 0
console.log(node.labels); // ['Person']
getNode()¶
Get a node by ID. Returns null if not found.
getNodeEdges()¶
The edges of a node as [neighborId, edgeId] pairs, without a query. direction is 'out' (the default), 'in' or 'both', which lists a self-loop once; edgeTypes keeps only edges of those types. A node that does not exist has none. A GraphHandle has the same method for its graph.
getNodeEdges(id: number, options?: { direction?: 'out' | 'in' | 'both'; edgeTypes?: string[] }): number[][]
deleteNode()¶
Delete a node by ID. Returns true if the node existed.
A node that still has edges throws: delete them first, or use detachDeleteNode().
detachDeleteNode()¶
Delete a node with all its edges, as DETACH DELETE does. Returns true if the node existed; the node and its edges are one change.
setNodeProperty()¶
Set a property on a node.
removeNodeProperty()¶
Remove a property from a node. Returns true if the property existed.
addNodeLabel()¶
Add a label to an existing node. Returns true if the label was added.
removeNodeLabel()¶
Remove a label from a node. Returns true if the label was removed.
getNodeLabels()¶
Get all labels for a node. Returns null if the node doesn't exist.
Edge Operations¶
createEdge()¶
Create an edge between two nodes with a type and optional properties.
const edge = db.createEdge(0, 1, 'KNOWS', { since: 2024 });
console.log(edge.edgeType); // 'KNOWS'
console.log(edge.sourceId); // 0
console.log(edge.targetId); // 1
getEdge()¶
Get an edge by ID. Returns null if not found.
deleteEdge()¶
Delete an edge by ID. Returns true if the edge existed.
setEdgeProperty()¶
Set a property on an edge.
removeEdgeProperty()¶
Remove a property from an edge. Returns true if the property existed.
Properties¶
| Property | Type | Description |
|---|---|---|
nodeCount | number | Number of nodes in the database |
edgeCount | number | Number of edges in the database |
Transaction Methods¶
beginTransaction()¶
Start a new transaction with an optional isolation level.
Isolation levels: "read_committed", "snapshot" (default); "serializable" is refused for now: snapshot isolation is the strongest level until queries record their reads.
Named Graphs¶
graph()¶
A handle on a named graph of the current schema, to work in it without switching the current graph. Throws when the graph does not exist; once it is dropped, every call through the handle throws.
A GraphHandle has the query and direct API of GrafeoDB, working in its graph: execute(), executeLanguage(), beginTransaction(), the node and edge operations above, batchCreateNodesWithProps(), batchCreateEdges(), upsertNodes() and upsertEdges(), and its name. Each call is its own transaction; beginTransaction() groups several.
await db.execute('CREATE GRAPH trips');
const trips = db.graph('trips');
const prague = trips.createNode(['City'], { name: 'Prague' });
const result = await trips.execute('MATCH (c:City) RETURN c.name');
Time Travel¶
A point in time is a bigint (an epoch), a Date (the last commit at or before it) or { ago: milliseconds }. A plain number throws a TypeError: it could be either an epoch or a time. A point before the history the database keeps throws ("not retained").
asOf()¶
A read-only view of the database at a point in time. It keeps the history of its epoch until close().
An AsOfView has epoch (a bigint), execute(query, params?) (a Promise<QueryResult>), getNode(id), getEdge(id), getNodeProperty(id, key) and close(). A query that writes through it is rejected.
const view = db.asOf({ ago: 5 * 60 * 1000 });
const result = await view.execute('MATCH (p:Person) RETURN count(p) AS n');
view.close();
executeAsOf()¶
Run one GQL query at a point in time, without keeping a view.
executeAsOf(query: string, point: bigint | Date | { ago: number }, params?: object): Promise<QueryResult>
epochAsOf() / currentEpoch()¶
epochAsOf(point) is the epoch a point reads at, currentEpoch() the epoch of the last commit, both as a bigint.
Vector Search¶
createVectorIndex()¶
Create an HNSW vector similarity index on a node property.
async createVectorIndex(
label: string,
property: string,
dimensions?: number,
metric?: string, // 'cosine' (default), 'euclidean', 'dot'
m?: number, // connections per node (default: 16)
efConstruction?: number // build quality (default: 128)
): Promise<void>
dropVectorIndex()¶
Drop a vector index. Returns true if the index existed.
rebuildVectorIndex()¶
Rebuild a vector index by rescanning all matching nodes. Preserves the original index configuration.
Auto-sync: rebuild is rarely needed
Vector indexes auto-sync when you call setNodeProperty(), batchCreateNodes(), or batchCreateNodesWithProps() with vector data. You only need rebuildVectorIndex() after importing data through non-standard paths or to compact the index after many deletions.
vectorSearch()¶
Search for the k nearest neighbors of a query vector. Returns [[nodeId, distance], ...] sorted by distance ascending (lower distance = more similar). The distance scale depends on the metric configured at index creation: cosine [0, 2], euclidean [0, inf), dot_product (negated), manhattan [0, inf).
async vectorSearch(
label: string,
property: string,
query: number[],
k: number,
ef?: number,
filters?: Record<string, any>
): Promise<number[][]>
const results = await db.vectorSearch('Document', 'embedding', queryVec, 10);
for (const [nodeId, distance] of results) {
console.log(`Node ${nodeId}: distance ${distance}`);
}
// With metadata filter
const filtered = await db.vectorSearch(
'Document', 'embedding', queryVec, 10, undefined, { user_id: 1 }
);
batchCreateNodes()¶
Bulk-insert nodes with vector properties. Returns an array of node IDs.
batchCreateNodesWithProps()¶
Create one node per properties object, each with labels (a label or a list of labels), in one transaction. Returns an array of node IDs, in input order.
async batchCreateNodesWithProps(
labels: string | string[],
propertiesList: Record<string, any>[]
): Promise<number[]>
batchCreateEdges()¶
Create edges, each with its own type and properties, in one transaction: if an edge names a node that does not exist or breaks the schema, none is created. Returns an array of edge IDs, in input order.
async batchCreateEdges(
edges: { src: number; dst: number; type: string; properties?: Record<string, any> }[]
): Promise<number[]>
const [alix, gus] = await db.batchCreateNodesWithProps('Person', [{ name: 'Alix' }, { name: 'Gus' }]);
await db.batchCreateEdges([
{ src: alix, dst: gus, type: 'KNOWS', properties: { since: 2020 } },
{ src: gus, dst: alix, type: 'KNOWS' },
]);
upsertNodes() / upsertEdges()¶
Create or update nodes and edges by a key property, many rows in one statement. See the Python upsert_nodes() and upsert_edges() for the rules; the options object takes the same settings in camelCase (key, replace, endpointKey, endpointLabels, srcField, dstField).
async upsertNodes(labels: string[], rows: Record<string, any>[], options?: { key?: string; replace?: boolean }): Promise<UpsertSummary>
async upsertEdges(edgeType: string, rows: Record<string, any>[], options?: UpsertEdgesOptions): Promise<UpsertSummary>
// UpsertSummary: { created: number; updated: number; skipped: number; skippedRows: number[] }
batchVectorSearch()¶
Batch search for nearest neighbors of multiple query vectors.
async batchVectorSearch(
label: string,
property: string,
queries: number[][],
k: number,
ef?: number,
filters?: Record<string, any>
): Promise<number[][][]>
mmrSearch()¶
Search for diverse nearest neighbors using Maximal Marginal Relevance. Returns [[nodeId, distance], ...] in MMR selection order. The distance values are identical to those returned by vectorSearch() for the same nodes (lower = more similar). The ordering reflects MMR's relevance-diversity balance, not pure distance sorting.
async mmrSearch(
label: string,
property: string,
query: number[],
k: number,
fetchK?: number,
lambdaMult?: number, // diversity vs relevance (0 = max diversity, 1 = max relevance)
ef?: number,
filters?: Record<string, any>
): Promise<number[][]>
Text Search¶
createTextIndex()¶
Create a BM25 text index on a node property for full-text search. The index is automatically kept in sync as nodes are created, updated, or deleted. You do not need to call rebuildTextIndex() after normal write operations.
async createTextIndex(
label: string,
property: string,
options?: {
k1?: number, // BM25 term frequency saturation, >= 0 (default 1.2)
b?: number, // BM25 length normalization, 0 to 1 (default 0.75)
tokenizer?: string, // 'simple' (default), 'standard' or 'cjk_bigram'
stopWords?: string[] // in place of the tokenizer's own
}
): Promise<void>
The database keeps the options with the index: reopening it, recovering after a crash and rebuildTextIndex() use them again. An unknown tokenizer, or k1 or b out of range, rejects with GRAFEO-V001.
dropTextIndex()¶
Drop a text index. Returns true if the index existed.
rebuildTextIndex()¶
Rebuild a text index by rescanning all matching nodes. Text indexes auto-sync on normal writes; you only need this after importing data through non-standard paths.
textSearch()¶
Search a text index using BM25 scoring. Returns [[nodeId, score], ...] sorted by descending relevance (higher score = more relevant). BM25 scores are unbounded positive floats.
async textSearch(
label: string,
property: string,
query: string,
k: number,
filters?: Record<string, any> // as vectorSearch(): {city: 'Berlin'}, {rank: {$gt: 19}}
): Promise<number[][]>
With filters, only matching nodes are searched, so up to k of them come back, scored as without the filters.
hybridSearch()¶
Combine text (BM25) and vector similarity search. For best results, create both a text index (createTextIndex()) and a vector index (createVectorIndex()). If either index is missing, that source is silently omitted from fusion. With neither (no text index, and no vector index or no queryVector), it rejects with an error naming the indexes.
Returns [[nodeId, score], ...] sorted by fused score descending (higher = more relevant). These are fusion scores, not distances.
Score convention differs from vectorSearch
hybridSearch() returns fusion scores where higher = better. vectorSearch() returns distances where lower = better. For temporal decay, multiply fusion scores but divide distances.
async hybridSearch(
label: string,
textProperty: string,
vectorProperty: string,
queryText: string,
k: number,
queryVector?: number[],
fusion?: string, // 'weighted' for weighted fusion
weights?: number[], // [textWeight, vectorWeight], default [0.5, 0.5]
filters?: Record<string, any> // as vectorSearch(): {city: 'Berlin'}, {rank: {$gt: 19}}
): Promise<number[][]>
With filters, both the text and the vector search keep only the matching nodes before fusion, so up to k matching nodes come back.
Embedding (opt-in)¶
These methods require the embed feature flag.
registerEmbeddingModel()¶
Register an ONNX embedding model for text-to-vector conversion.
async registerEmbeddingModel(
name: string,
modelPath: string,
tokenizerPath: string,
batchSize?: number
): Promise<void>
embedText()¶
Generate embeddings for a list of texts. Returns one float array per input text.
vectorSearchText()¶
Search a vector index using a text query, generating the embedding on-the-fly.
async vectorSearchText(
label: string,
property: string,
modelName: string,
queryText: string,
k: number,
ef?: number
): Promise<number[][]>
Change Data Capture¶
These methods require the cdc feature flag.
nodeHistory()¶
Returns the full change history for a node.
edgeHistory()¶
Returns the full change history for an edge.
nodeHistorySince()¶
Returns change events for a node since a given epoch.
changesBetween()¶
Returns all change events across entities in an epoch range.
Each ChangeEvent is a JSON object:
{
entity_id: number;
entity_type: 'node' | 'edge';
kind: 'create' | 'update' | 'delete';
epoch: number;
timestamp: number;
before: Record<string, any> | null;
after: Record<string, any> | null;
}
Admin Methods¶
The objects these methods return gain keys only: a patch release can add a key, never remove or rename one, so read the keys you need rather than comparing whole objects.
info()¶
Returns high-level database information as a JSON object.
schema()¶
Returns schema information: the labels, edge types and property keys the data holds, each sorted by name, as { mode: 'lpg', labels, edge_types, property_keys }. A label is { name, count, properties }, an edge type { name, count, properties, endpoints }, a property { name, types } (types such as ["STRING"]) and an endpoint { source, target, count }. A label, edge type or property key nothing holds any more is left out. It reads the whole graph.
version()¶
Returns the Grafeo engine version string.
compact()¶
Compacts the database: writes a checkpoint of a persistent database (an in-memory or read-only one writes none), drops the old versions no open transaction can see any more, compresses the property values held decoded in memory, and returns what it did: { checkpointed, versions_collected, rows_compressed, duration_ms }. Since 0.6.0 the database keeps one store: compact() no longer builds a separate columnar one (see Compact Store). Throws if the checkpoint fails, or after close().
compact(): { checkpointed: boolean, versions_collected: number, rows_compressed: number, duration_ms: number }
const db = GrafeoDB.create();
await db.execute("INSERT (:Person {name: 'Alix', age: 30})");
const report = db.compact(); // { checkpointed: false, versions_collected: 0, ... }
const result = await db.execute("MATCH (p:Person) RETURN p.name");
close()¶
Close the database and release resources.