Exploring the graph¶
fs.graph is the fluent read/write surface over the graph. Reads return handles
that wrap the underlying Pydantic models and add navigation methods, so you can hop
around the structure without writing queries.
Reading¶
fs.graph.shelves(facet="dietary_patterns") # list[ShelfHandle]
fs.graph.shelf("s-med").themes() # list[ThemeHandle]
fs.graph.shelf("s-med").chunks() # list[Chunk]
fs.graph.shelf("s-med").parent() # ShelfHandle | None
fs.graph.shelf("s-med").children() # list[ShelfHandle]
fs.graph.theme("t-olive").shelves() # back-references to shelves
fs.graph.theme("t-olive").card().cited_chunks()
fs.graph.summary() # {"shelves": ..., "themes": ...}
A handle exposes the model’s fields directly (shelf.label, shelf.chunk_count,
theme.discovery_pass) and adds traversal methods (.parent(), .children(),
.themes(), .chunks(), .card()). Reach the raw Pydantic model any time via
handle.model.
Writing¶
The same surface builds the graph by hand — handy in tests and notebooks:
fs.graph.add_shelf(shelf_id="s-med", label="Mediterranean diet",
facet="dietary_patterns", depth=1)
fs.graph.attach_chunks(["c1", "c2"], shelf="s-med") # auto-denormalizes shelf_ids
fs.graph.add_theme(theme_id="t-olive", label="Olive oil", shelf_ids=["s-med"],
discovered_by="leiden", discovery_version="v0",
facet="dietary_patterns", discovery_pass="global_similarity")
attach_chunks requires exactly one target — shelf= or theme=, not both — and
keeps the Elasticsearch denormalization (shelf_ids / theme_ids) in lockstep with the
Neo4j edges automatically.
Retrieval¶
fs.graph.search("olive oil", shelf="s-med", k=5) # hybrid BM25 + kNN, shelf-filtered
This is the retrieval path from Architecture: hybrid search over Elasticsearch, optionally scoped to a shelf or theme.
Tip
Everything here works identically on the in-memory backend, so you can prototype graph
walks against FoodScholar.in_memory() with no services running.