The standard representation of an astrological chart is a list of planets with floating-point longitudes. Ask most libraries for a chart and you get back something like a dictionary of planet names to degree values. This is not wrong — it is just profoundly incomplete.
A chart is not a list. It is a network of relationships: planets in signs, signs ruling houses, planets aspecting each other, lords of houses placed in other houses. The relationships are the point. They are what a practitioner reads, what a dasha system navigates, what a yoga rule tests. Flattening this network into a struct loses most of the semantically interesting structure before the application ever sees it.
The graph model
A ChartGraph has 10 node types and 13 edge types. The nodes are:
:ChartRoot node — metadata, ayanamsha, house system, epoch.
:PlanetAny graha: Sun, Moon, Mars, Mercury, Jupiter, Venus, Saturn, Rahu, Ketu, plus Ascendant.
:SignOne of 12 rasis. Carries modality, element, direction, ruler.
:HouseOne of 12 bhavas. Carries cusp longitude, natural karakatwa.
:NakshatraOne of 27 lunar mansions. Carries pada, lord, deity, shakti.
:AspectA computed aspect event with orb, exactness, applying/separating flag.
:YogaA detected yoga rule with its component planets and activation status.
:DashaA dasha period — mahadasha, antardasha, or deeper sub-period.
:DignityShadbala component — exaltation, moolatrikona, own, friend, neutral, enemy, debilitation.
:VargaA divisional chart — D1 through D60 — as a subgraph of the root chart.
The 13 edge types
Edges carry the relationships that practitioners actually reason about. Most of the interesting chart interpretation is a path traversal over these edges.
PLACED_IN_SIGNPLACED_IN_HOUSEOCCUPIES_NAKSHATRARULESLORDSASPECTSASPECTS_HOUSECONJOINSACTIVATES_YOGAHAS_DIGNITYPART_OF_DASHADISPOSITOR_OFVARGA_PLACEMENTFour emitters
A ChartGraph is an in-memory structure. Getting it into a useful target is the job of the emitters.
use vedaksha::graph::*;
let graph = compute_chart_graph(jd, lat, lng, &ChartConfig::vedic());
// Neo4j / any Cypher-compatible graph DB
let cypher = graph.emit_cypher();
// SurrealDB
let surreal = graph.emit_surrealql();
// JSON-LD for semantic web / RAG pipelines
let jsonld = graph.emit_jsonld();
// Pre-chunked text for vector embedding
let chunks = graph.emit_embedding_text();Why this matters for AI agents
When an AI agent receives a flat list of planet-degree pairs, it has to reconstruct the chart's relational structure from scratch. The agent effectively re-implements a simplified version of the chart interpretation layer to answer a question like "which planets aspect the 7th house lord?" This is fragile, slow, and inaccurate.
With a property graph emitter, the agent can run a Cypher query:
MATCH (p:Planet)-[:LORDS]->(h:House {number: 7}))
MATCH (aspector:Planet)-[:ASPECTS]->(p)
RETURN aspector.name, aspector.sign, p.name
The graph model also enables multi-chart queries. Synastry (comparing two charts) is a join between two subgraphs. A transit lookup is an intersection between a natal graph and a transiting planet graph. These queries are awkward with flat arrays; they are natural with a graph schema.
Deterministic node IDs
Every node in the graph has a deterministic ID derived from its content — not a random UUID, not a database sequence. A Planet node ID is a hash of the chart ID plus the planet name. The Chart node ID is derived from the Julian Day, coordinates, and configuration.
This means two identical chart computations produce identical node IDs, which means upserts are idempotent. Load the same chart twice into Neo4j and you get the same graph. This also allows safe merging of multiple charts into a single database without ID collisions.
Embedding text for RAG pipelines
The embedding text emitter generates pre-chunked natural language descriptions of each node and its immediate neighborhood. Each chunk is designed to be the right size for a vector embedding — not the full chart as a wall of text, but semantically coherent fragments like:
"Mars is placed in Scorpio in the 8th house at 14°22'. It is the lord of the 1st and 8th houses. Mars aspects the 2nd house (4th drishti), the 3rd house (7th drishti), and the Ascendant (8th drishti). Mars is in its own sign, giving it strength. It activates the Ruchaka Mahapurusha Yoga."
Every chunk includes the relevant node IDs so the vector store result can be linked back to the graph for follow-up queries. This enables a hybrid retrieval pattern: semantic search finds the relevant chart region, graph traversal answers the precise relational question.
The flat struct representation of a chart is a compression artifact — it drops the relationships to fit into a simpler data model. A property graph is not an elaborate overengineering; it is the natural shape of the data. Everything else — the emitters, the deterministic IDs, the embedding chunks — follows from that starting point.