Direct Updates
Once you have a Graph you can directly update it with the add_node() and add_edge() functions.
Adding nodes
To add a node we need a unique id to represent it and an update timestamp to specify when it was added to the graph. In the below example we are going to add node 10 at timestamp 1.
If your data doesn't have any timestamps, you can just set a constant value, such as 1, for all additions into the graph.
Printing out the graph and the returned MutableNode you can see the update was successful and the earliest and latest times have been updated. The timestamp you specified is used as the primary index of the EventTime object.
Node types
You can optionally assign a node_type to categorise nodes. This is useful for heterogeneous graphs where you have different kinds of entities. Use get_all_node_types() to retrieve all node types in the graph:
If a node was created without a type (or as a placeholder from an edge), you can set its type later using set_node_type(). Note that this can only be called once per node – the type becomes permanent after being set:
Creating nodes (fail if exists)
If you want to ensure a node doesn't already exist, use create_node() instead of add_node(). This will raise an error if the node already exists:
Adding edges
All graphs in Raphtory are directed, meaning edge additions must specify a timestamp (the same as a add_node()), the src node the edge starts from and the dst node the edge ends at.
In the example below we add an edge to the graph from 15 to 16 at timestamp 1.
Placeholder nodes
You will notice in the output that the graph has two nodes as well as the edge. Raphtory automatically creates the source and destination nodes at the same time if they do not currently exist in the graph. These auto-created nodes are placeholders – they exist to keep the graph consistent and avoid hanging edges.
Placeholder nodes only have history from their edges. If you apply a view that excludes all their edges, the placeholder nodes will also disappear unless they have their own direct updates. We'll cover views in detail in the Views & Windows section:
Accepted ID types
The add_node() and add_edge() functions will also accept strings for their id, src & dst arguments. This is useful when your node IDs are not integers. For example, node IDs could be unique strings like a person's username or a blockchain wallet hash.
In this example, we are adding two nodes to the graph User 1 and User 2 and an edge between them.
A graph can index nodes by either integers or strings, not both at the same time. This means, for example, you cannot have User 1 (a string) and 200 (an integer) as ids in the same graph.
Time input
While integer based timestamps can represent both logical time and epoch time, datasets often have their timestamps stored in human readable formats or special datetime objects. As such, add_node() and add_edge() accept TimeInput which can be integers, datetime strings, or datetime objects:
In the output we can see the History of node 10 contains the two times at which we have added it into the graph (maintained in ascending order), returned in both unix epoch (integer) and datetime format.
Internally, the history is tracked using EventTime objects which combine a timestamp with an optional event ID. See History & EventTime for more on working with temporal data.
Event ordering with event_id
When multiple updates occur at the same timestamp, you can use the event_id parameter to specify their order. This is important when the same node or edge receives multiple updates at the same time – specifying distinct event IDs ensures values aren't overwritten and affects how property values are aggregated and which value is considered "latest".
By default, Raphtory automatically assigns a monotonically increasing event ID to each update globally. You only need to specify event_id manually if this secondary ordering has meaning in your domain – for example, using the transaction index within a blockchain block.
The EventTime object stores both the timestamp and event_id, enabling sub-timestamp ordering of events.
Properties
Alongside the structural update history, Raphtory can maintain the changing value of properties associated with nodes and edges. Both the add_node() and add_edge() functions have an optional parameter properties which takes a dictionary of key value pairs to be stored at the given timestamp.
The graph itself may also have its own global properties added using the add_properties() function which takes only a timestamp and a properties dictionary.
Properties can consist of primitives (Integer, Float, String, Boolean, Datetime) and structures (Dictionary, List). This allows you to store both basic values as well as do complex hierarchical modelling depending on your use case.
In the example below, we use all of these functions to add a mixture of properties to a node, an edge, and the graph.
Please note that once a property key is associated with one of the above types for a given node/edge/graph, attempting to add a value of a different type under the same key will result in an error. For Lists the values must all be the same type and for Dictionaries the values for each key must always be the same type.
When the output is printed only the latest property values are shown. The older values haven't been lost, in fact the history of all of these different property types can be queried, explored and aggregated, as you will see in Property Queries.
Explicit typing with Prop
Raphtory is written in Rust and supports several numeric types that don't exist natively in Python (like 8-bit integers or 32-bit floats). The Prop class lets you explicitly specify these types for memory efficiency. When values are retrieved, they are automatically cast back to Python types:
| Type | Description | Python Return Type |
|---|---|---|
Prop.u8(), Prop.u16(), Prop.u32(), Prop.u64() | Unsigned integers | int |
Prop.i32(), Prop.i64() | Signed integers | int |
Prop.f32(), Prop.f64() | Floating point | float |
Prop.str() | String | str |
Prop.bool() | Boolean | bool |
Metadata
Raphtory also provides metadata associated with nodes and edges which have immutable values. These are useful when you know a value won't change or is not associated with a specific time.
You can use the add_metadata() function, which takes a single dictionary argument, to add metadata to a graph, node and edge as demonstrated below.
Updating metadata
The add_metadata() function will raise an error if you try to add a key that already exists. To update an existing metadata value, use update_metadata() instead:
Edge layers
If you have worked with other graph libraries you may be expecting two calls to add_edge() between the same nodes to generate two distinct edge objects. In Raphtory, these calls append the information together into the history of a single edge.
Edges can be exploded to interact with all updates independently and Raphtory also allows you to represent totally different relationships between the same nodes via edge layers.
The add_edge() function takes a second optional parameter, layer that allows you to name the type of relationship being added. All calls to add_edge with the same layer value will be stored together allowing them to be accessed separately or merged with other layers as required.
You can see this in the example below where we add five updates between Person 1 and Person 2 across the layers Friends, Co Workers and Family. When we query the history of the weight property on the edge we initially get all of the values back. However, by applying the layers() GraphView we can return only updates from Co Workers and Family.
Updating existing nodes and edges
You can continue to call add_node() and add_edge() on existing nodes and edges to insert additional updates – Raphtory will append the new data to their history. These functions return MutableNode and MutableEdge objects, which also have an add_updates() method that is synonymous: