## ----setup, include = FALSE---------------------------------------------------
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)

## ----connect------------------------------------------------------------------
# library(AstraeaDB)
# client <- astraea_connect()

## ----create-embeddings--------------------------------------------------------
# # Create nodes representing documents, each with a 4-dimensional embedding
# doc1 <- client$create_node(
#   labels     = c("Document"),
#   properties = list(title = "Graph Databases", topic = "databases"),
#   embedding  = c(0.9, 0.1, 0.2, 0.05)
# )
# 
# doc2 <- client$create_node(
#   labels     = c("Document"),
#   properties = list(title = "Vector Search", topic = "search"),
#   embedding  = c(0.1, 0.85, 0.3, 0.1)
# )
# 
# doc3 <- client$create_node(
#   labels     = c("Document"),
#   properties = list(title = "Neural Networks", topic = "ML"),
#   embedding  = c(0.15, 0.8, 0.9, 0.7)
# )
# 
# doc4 <- client$create_node(
#   labels     = c("Document"),
#   properties = list(title = "Knowledge Graphs", topic = "databases"),
#   embedding  = c(0.85, 0.2, 0.25, 0.1)
# )

## ----vector-search------------------------------------------------------------
# results <- client$vector_search(
#   query_vector = c(0.88, 0.15, 0.2, 0.08),
#   k            = 3L
# )
# 
# for (r in results) {
#   cat(sprintf("Node %d -- distance: %.4f\n", r$node_id, r$distance))
# }
# #> Node 1 -- distance: 0.0048
# #> Node 4 -- distance: 0.0129
# #> Node 2 -- distance: 0.5877

## ----hybrid-search------------------------------------------------------------
# # Find nodes that are both near doc1 in the graph AND similar to a query vector
# results <- client$hybrid_search(
#   anchor       = doc1,
#   query_vector = c(0.8, 0.3, 0.25, 0.1),
#   max_hops     = 3L,
#   k            = 5L,
#   alpha        = 0.7
# )
# 
# for (r in results) {
#   cat(sprintf("Node %d -- combined score: %.4f\n", r$node_id, r$score))
# }

## ----semantic-neighbors-------------------------------------------------------
# # First, connect the documents with edges
# client$create_edge(doc1, doc2, "RELATED_TO")
# client$create_edge(doc1, doc3, "RELATED_TO")
# client$create_edge(doc1, doc4, "RELATED_TO")
# 
# # Rank doc1's neighbors by similarity to a "search-oriented" concept
# ranked <- client$semantic_neighbors(
#   node_id   = doc1,
#   concept   = c(0.1, 0.9, 0.3, 0.1),
#   direction = "outgoing",
#   k         = 5L
# )
# 
# for (r in ranked) {
#   cat(sprintf("Neighbor %d -- distance: %.4f\n", r$node_id, r$distance))
# }

## ----semantic-walk------------------------------------------------------------
# # Walk the graph following the "ML" concept
# path <- client$semantic_walk(
#   start    = doc1,
#   concept  = c(0.1, 0.8, 0.9, 0.7),
#   max_hops = 4L
# )
# 
# cat("Walk path:", paste(path, collapse = " -> "), "\n")

## ----temporal-setup-----------------------------------------------------------
# # Create a social network with temporal edges
# alice <- client$create_node(c("Person"), list(name = "Alice"))
# bob   <- client$create_node(c("Person"), list(name = "Bob"))
# carol <- client$create_node(c("Person"), list(name = "Carol"))
# dave  <- client$create_node(c("Person"), list(name = "Dave"))
# 
# # Helper: convert date string to epoch milliseconds
# to_epoch_ms <- function(date_str) {
#   as.numeric(as.POSIXct(date_str, tz = "UTC")) * 1000
# }
# 
# # Alice knew Bob from 2020 to 2022
# client$create_edge(
#   source     = alice,
#   target     = bob,
#   edge_type  = "FRIENDS",
#   valid_from = to_epoch_ms("2020-01-01"),
#   valid_to   = to_epoch_ms("2022-01-01")
# )
# 
# # Alice has known Carol since 2021 (no end date -- still active)
# client$create_edge(
#   source     = alice,
#   target     = carol,
#   edge_type  = "FRIENDS",
#   valid_from = to_epoch_ms("2021-06-01")
# )
# 
# # Alice has known Dave since 2023
# client$create_edge(
#   source     = alice,
#   target     = dave,
#   edge_type  = "FRIENDS",
#   valid_from = to_epoch_ms("2023-01-01")
# )

## ----neighbors-at-------------------------------------------------------------
# # Who was Alice friends with on July 1, 2021?
# mid_2021 <- to_epoch_ms("2021-07-01")
# friends_2021 <- client$neighbors_at(alice, "outgoing", mid_2021)
# 
# # Result includes Bob and Carol, but not Dave (not yet friends)
# for (f in friends_2021) {
#   node <- client$get_node(f$node_id)
#   cat(node$properties$name, "\n")
# }
# #> Bob
# #> Carol

## ----bfs-at-------------------------------------------------------------------
# # BFS at a point in time
# bfs_2021 <- client$bfs_at(alice, max_depth = 2L, timestamp = mid_2021)
# 
# for (entry in bfs_2021) {
#   cat(sprintf("Node %d at depth %d\n", entry$node_id, entry$depth))
# }

## ----shortest-path-at---------------------------------------------------------
# # Shortest path at a specific time
# early_2024 <- to_epoch_ms("2024-01-15")
# 
# sp <- client$shortest_path_at(
#   from_node = alice,
#   to_node   = dave,
#   timestamp = early_2024,
#   weighted  = FALSE
# )
# 
# cat("Path:", paste(sp$path, collapse = " -> "), "\n")
# cat("Hops:", sp$length, "\n")

## ----extract-subgraph---------------------------------------------------------
# # Extract a 2-hop subgraph around Alice, linearized as structured text
# sg <- client$extract_subgraph(
#   center    = alice,
#   hops      = 2L,
#   max_nodes = 20L,
#   format    = "structured"
# )
# 
# cat("Nodes:", sg$node_count, "\n")
# cat("Edges:", sg$edge_count, "\n")
# cat("\n", sg$text, "\n")

## ----graph-rag----------------------------------------------------------------
# answer <- client$graph_rag(
#   question  = "Who are Alice's current friends and what do they work on?",
#   anchor    = alice,
#   hops      = 2L,
#   max_nodes = 30L,
#   format    = "prose"
# )
# 
# cat(answer$answer, "\n")

## ----graph-rag-embedding------------------------------------------------------
# answer <- client$graph_rag(
#   question            = "What research topics are related to graph databases?",
#   question_embedding  = c(0.9, 0.1, 0.2, 0.05),
#   hops                = 3L,
#   max_nodes           = 50L,
#   format              = "structured"
# )
# 
# cat(answer$answer, "\n")

## ----algorithms-centrality----------------------------------------------------
# # PageRank
# scores <- client$run_pagerank(damping = 0.85, max_iterations = 100L)
# 
# # Degree and betweenness centrality
# deg <- client$run_degree_centrality(direction = "both")
# btw <- client$run_betweenness_centrality()

## ----algorithms-community-----------------------------------------------------
# # Louvain community detection
# louvain <- client$run_louvain()
# cat("Communities found:", louvain$num_communities, "\n")
# 
# # Connected components (weakly connected by default; strong = TRUE for SCCs)
# cc <- client$run_connected_components(strong = FALSE)
# cat("Number of components:", cc$count, "\n")

## ----traversal-dfs------------------------------------------------------------
# # Depth-first traversal (a companion to bfs())
# visited <- client$dfs(alice, max_depth = 3L)
# 
# # ...as of a point in time
# visited_past <- client$dfs_at(alice, max_depth = 3L, timestamp = 1672531200000)

## ----lookups------------------------------------------------------------------
# # All node IDs carrying a label
# people <- client$find_by_label("Person")
# 
# # All edges of a given type, each as {edge_id, source, target}
# knows_edges <- client$find_edge_by_type("KNOWS")
# 
# # Bulk-delete every node with a label (and its edges); returns the count
# removed <- client$delete_by_label("Temporary")

## ----subgraph-stats-----------------------------------------------------------
# # Raw subgraph (nodes + edges) around a center node, for visualization
# sg <- client$get_subgraph(alice, hops = 2L, max_nodes = 100L)
# 
# # Graph-wide statistics
# stats <- client$graph_stats()
# cat("Nodes:", stats$total_nodes, " Edges:", stats$total_edges, "\n")

## ----arrow-client-------------------------------------------------------------
# # install.packages("arrow")  # if not already installed
# library(AstraeaDB)
# 
# ac <- ArrowClient$new("grpc://localhost:7689")
# ac$connect()
# 
# # Execute a GQL query -- returns an Arrow Table
# table <- ac$query("MATCH (p:Person) RETURN p.name, p.age")
# 
# # Convert to data.frame
# df <- as.data.frame(table)
# 
# # Or use the convenience method
# df <- ac$query_df("MATCH (p:Person) RETURN p.name, p.age")

## ----arrow-batches------------------------------------------------------------
# ac$query_batches(
#   "MATCH (n) RETURN n",
#   callback = function(batch) {
#     cat("Received batch with", nrow(batch), "rows\n")
#     # Process each batch incrementally
#   }
# )
# 
# ac$disconnect()

## ----arrow-convenience--------------------------------------------------------
# ac <- astraea_arrow_connect("grpc://localhost:7689")
# # ... work ...
# ac$disconnect()

## ----unified-client-----------------------------------------------------------
# uc <- UnifiedClient$new(
#   host       = "127.0.0.1",
#   port       = 7687L,
#   flight_uri = "grpc://localhost:7689"
# )
# uc$connect()
# 
# # Check which transports are active
# uc$is_arrow_enabled()
# #> [1] TRUE
# 
# # CRUD operations go through JSON/TCP
# node_id <- uc$create_node(c("Person"), list(name = "Grace", age = 42))
# 
# # Queries go through Arrow Flight (or fall back to JSON/TCP)
# df <- uc$query_df("MATCH (p:Person) RETURN p.name, p.age")
# 
# # All other operations are available as usual
# uc$neighbors(node_id, direction = "outgoing")
# uc$vector_search(c(0.5, 0.5, 0.5, 0.5), k = 3L)
# 
# uc$disconnect()

## ----auth-token---------------------------------------------------------------
# # AstraeaClient with authentication
# client <- AstraeaClient$new(
#   host       = "127.0.0.1",
#   port       = 7687L,
#   auth_token = "my-secret-token"
# )
# client$connect()
# 
# # All operations now carry the token
# client$ping()
# client$create_node(c("Person"), list(name = "Secured"))
# client$disconnect()

## ----auth-connect-------------------------------------------------------------
# client <- astraea_connect(auth_token = "my-secret-token")
# # ... work ...
# client$disconnect()

## ----auth-unified-------------------------------------------------------------
# uc <- UnifiedClient$new(
#   host       = "127.0.0.1",
#   port       = 7687L,
#   auth_token = "my-secret-token"
# )
# uc$connect()
# # ... work ...
# uc$disconnect()

## ----auth-error---------------------------------------------------------------
# tryCatch(
#   {
#     client <- astraea_connect(auth_token = "wrong-token")
#     client$create_node(c("Test"), list(x = 1))
#   },
#   error = function(e) {
#     message("Auth error: ", conditionMessage(e))
#   }
# )

## ----full-workflow------------------------------------------------------------
# library(AstraeaDB)
# client <- astraea_connect()
# on.exit(client$disconnect(), add = TRUE)
# 
# # --- Build the graph ---
# ml   <- client$create_node(c("Topic"), list(name = "Machine Learning"),
#                             embedding = c(0.1, 0.8, 0.9, 0.7))
# nlp  <- client$create_node(c("Topic"), list(name = "NLP"),
#                             embedding = c(0.2, 0.9, 0.7, 0.6))
# kg   <- client$create_node(c("Topic"), list(name = "Knowledge Graphs"),
#                             embedding = c(0.85, 0.2, 0.25, 0.1))
# rag  <- client$create_node(c("Topic"), list(name = "RAG"),
#                             embedding = c(0.6, 0.7, 0.5, 0.4))
# 
# to_ms <- function(d) as.numeric(as.POSIXct(d, tz = "UTC")) * 1000
# 
# client$create_edge(ml,  nlp, "RELATED_TO", weight = 0.9,
#                    valid_from = to_ms("2018-01-01"))
# client$create_edge(nlp, rag, "ENABLES",    weight = 0.8,
#                    valid_from = to_ms("2022-01-01"))
# client$create_edge(kg,  rag, "ENABLES",    weight = 0.85,
#                    valid_from = to_ms("2020-01-01"))
# client$create_edge(ml,  kg,  "RELATED_TO", weight = 0.7,
#                    valid_from = to_ms("2015-01-01"))
# 
# # --- Vector search ---
# cat("== Vector Search ==\n")
# vs <- client$vector_search(c(0.15, 0.85, 0.8, 0.65), k = 2L)
# for (r in vs) {
#   cat(sprintf("  Node %d (dist %.4f)\n", r$node_id, r$distance))
# }
# 
# # --- Hybrid search ---
# cat("\n== Hybrid Search ==\n")
# hs <- client$hybrid_search(
#   anchor = ml, query_vector = c(0.6, 0.7, 0.5, 0.4),
#   max_hops = 2L, k = 3L, alpha = 0.5
# )
# for (r in hs) {
#   cat(sprintf("  Node %d\n", r$node_id))
# }
# 
# # --- Temporal query ---
# cat("\n== Temporal Query (2019) ==\n")
# nbrs_2019 <- client$neighbors_at(ml, "outgoing", to_ms("2019-06-01"))
# for (n in nbrs_2019) {
#   node <- client$get_node(n$node_id)
#   cat(sprintf("  %s\n", node$properties$name))
# }
# # Only "NLP" and "Knowledge Graphs" -- RAG edge did not exist in 2019
# 
# # --- GraphRAG ---
# cat("\n== GraphRAG ==\n")
# answer <- client$graph_rag(
#   question  = "How are ML and RAG connected?",
#   anchor    = ml,
#   hops      = 2L,
#   max_nodes = 20L,
#   format    = "prose"
# )
# cat(answer$answer, "\n")

