This vignette walks through every major operation supported by the AstraeaDB R package. Each section contains a self-contained code example that you can copy into an R session once you have a running AstraeaDB server.
Before you can connect from R you need a running AstraeaDB instance. The easiest way to start one locally is from the AstraeaDB source tree:
The default ports are 7687 (JSON/TCP), 7688 (gRPC), and 7689 (Arrow Flight).
There are two ways to create a client and connect to the server.
The simplest approach is the astraea_connect() helper,
which creates a client and calls connect() in one step:
If you need more control (for example, to set an authentication token), you can construct and connect the client separately:
library(AstraeaDB)
client <- AstraeaClient$new(
host = "127.0.0.1",
port = 7687L,
auth_token = "my-secret-token"
)
client$connect()In either case, you can verify the connection with a health-check ping:
A node requires at least one label (character vector) and a set of properties (named list). You may optionally provide an embedding (numeric vector) for vector similarity search.
# Basic node
alice_id <- client$create_node(
labels = c("Person"),
properties = list(name = "Alice", age = 30, city = "San Francisco")
)
# Node with multiple labels
bob_id <- client$create_node(
labels = c("Person", "Engineer"),
properties = list(name = "Bob", age = 25, city = "New York")
)
# Node with an embedding vector
carol_id <- client$create_node(
labels = c("Person"),
properties = list(name = "Carol", age = 35),
embedding = c(0.12, 0.87, 0.45, 0.33)
)create_node() returns the integer ID assigned to the new
node by the server.
Retrieve a node by its ID. The result is a list with
labels and properties:
update_node() uses merge semantics:
existing properties that are not mentioned in the update are preserved.
New properties are added and existing ones are overwritten.
Deleting a node also removes all edges connected to it:
An edge connects a source node to a target node with a typed relationship. Optional parameters include properties, weight, and temporal validity bounds.
# Simple edge
edge1 <- client$create_edge(
source = alice_id,
target = bob_id,
edge_type = "KNOWS",
properties = list(context = "work")
)
# Weighted edge
edge2 <- client$create_edge(
source = bob_id,
target = carol_id,
edge_type = "FOLLOWS",
weight = 0.7
)
# Temporal edge with validity window (milliseconds since epoch)
# Valid from 2023-01-01 to 2024-01-01
edge3 <- client$create_edge(
source = alice_id,
target = carol_id,
edge_type = "MENTORS",
properties = list(topic = "graph databases"),
weight = 1.0,
valid_from = 1672531200000,
valid_to = 1704067200000
)create_edge() returns the integer ID of the new edge.
The valid_from and valid_to parameters are
numeric scalars representing milliseconds since the Unix epoch. When
set, the edge is only visible in temporal queries whose timestamp falls
within the window.
Retrieve direct neighbors of a node. You can filter by direction
("outgoing", "incoming", or
"both") and by edge type:
# Outgoing neighbors (default)
out <- client$neighbors(alice_id, direction = "outgoing")
# Incoming neighbors
inc <- client$neighbors(bob_id, direction = "incoming")
# Both directions, filtered by edge type
knows <- client$neighbors(alice_id, direction = "both", edge_type = "KNOWS")Each entry in the result contains at least node_id and
edge_id.
BFS explores the graph outward from a starting node up to a maximum depth:
Find the shortest path between two nodes. Set
weighted = TRUE to use edge weights (Dijkstra’s algorithm)
instead of hop count:
AstraeaDB supports a subset of Graph Query Language (GQL). You can
run queries with MATCH, WHERE,
RETURN, ORDER BY, and LIMIT
clauses:
# Find all Person nodes
result <- client$query("MATCH (p:Person) RETURN p.name, p.age")
# Filter with WHERE
result <- client$query(
"MATCH (p:Person) WHERE p.age > 25 RETURN p.name, p.city"
)
# Edges in the pattern
result <- client$query(
"MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a.name, b.name"
)
# Ordering and limiting
result <- client$query(
"MATCH (p:Person) RETURN p.name, p.age ORDER BY p.age LIMIT 10"
)The query() method returns the raw server response data.
For tabular results you may want to use the
UnifiedClient$query_df() method (see the Advanced Features
vignette for Arrow Flight support).
When you need to create or delete many nodes or edges at once, batch methods are more convenient than calling single-item methods in a loop.
# Create multiple nodes
node_ids <- client$create_nodes(list(
list(labels = c("City"), properties = list(name = "San Francisco", state = "CA")),
list(labels = c("City"), properties = list(name = "New York", state = "NY")),
list(labels = c("City"), properties = list(name = "Austin", state = "TX"))
))
# Create multiple edges
edge_ids <- client$create_edges(list(
list(source = alice_id, target = node_ids[1], edge_type = "LIVES_IN"),
list(source = bob_id, target = node_ids[2], edge_type = "LIVES_IN"),
list(source = carol_id, target = node_ids[3], edge_type = "LIVES_IN",
weight = 0.8)
))# Delete multiple nodes (also removes their edges)
deleted_count <- client$delete_nodes(node_ids)
cat(deleted_count, "nodes deleted\n")
# Delete multiple edges
deleted_edges <- client$delete_edges(edge_ids)Batch delete operations silently skip any IDs that do not exist or have already been deleted, and return the count of successful deletions.
The package provides import and export helpers that bridge between R data frames and the AstraeaDB graph.
people_df <- data.frame(
label = c("Person", "Person", "Person"),
name = c("Dave", "Eve", "Frank"),
age = c(40, 28, 55),
city = c("Chicago", "Boston", "Denver"),
stringsAsFactors = FALSE
)
new_ids <- client$import_nodes_df(people_df, label_col = "label")
new_ids
#> [1] 10 11 12The label_col parameter identifies which column holds
the node label. All other columns become node properties. You can also
specify embedding_cols if some columns should be assembled
into an embedding vector:
edges_df <- data.frame(
source = new_ids[c(1, 2)],
target = new_ids[c(2, 3)],
type = c("KNOWS", "MENTORS"),
since = c(2018, 2021),
stringsAsFactors = FALSE
)
edge_ids <- client$import_edges_df(
edges_df,
source_col = "source",
target_col = "target",
type_col = "type"
)Additional column-mapping parameters are available:
weight_col, valid_from_col, and
valid_to_col. All columns not mapped to a structural role
become edge properties.
df <- client$export_nodes_df(new_ids)
df
#> node_id labels name age city
#> 1 10 Person Dave 40 Chicago
#> 2 11 Person Eve 28 Boston
#> 3 12 Person Frank 55 DenverThe exported data frame contains a node_id column, a
comma-separated labels column, and one column per property.
Nodes with different property sets are aligned; missing properties are
filled with NA.
bfs_df <- client$export_bfs_df(alice_id, max_depth = 2L)
bfs_df
#> node_id depth labels name age city
#> 1 1 0 Person Alice 30 San Francisco
#> 2 2 1 Person Bob 25 New York
#> 3 3 2 Person Carol 35 <NA>This runs a BFS internally, then fetches each visited node and
flattens the results into a data frame with an additional
depth column.
Always disconnect when you are done. This closes the underlying TCP socket:
A good pattern for scripts is to use on.exit() to
guarantee cleanup:
All client methods signal R errors when the server returns an error
response or the connection fails. Use tryCatch() to handle
errors gracefully:
tryCatch(
{
node <- client$get_node(999999L)
},
error = function(e) {
message("Operation failed: ", conditionMessage(e))
}
)Typical error scenarios include:
auth_token.You can check whether the server is reachable before attempting to connect:
This vignette covered the core workflow for interacting with AstraeaDB from R:
For vector search, temporal queries, GraphRAG, and Arrow Flight
transport, see vignette("advanced-features").