Upsert data to graph
This endpoint upserts vertices and/or edges into a graph. To upsert means that if a vertex or edge does not exist, it is inserted, and if it does exist, it is updated.
Upserting vertices or edges require the user to have sufficient privileges. See Access Control Model in TigerGraph.
Parameters
The following table describes the URL parameters for the endpoint.
Name | Required | Description | ||
---|---|---|---|---|
|
No |
The value of this parameter can either be
Default value is |
||
|
No |
If |
||
|
No |
If |
||
|
No |
If |
||
|
No |
If the value is This parameter does not affect vertices.
Default value is |
||
|
No |
If the value is This parameter does not affect vertices. For non-directed edges, the behavior of this parameter is identical to This parameter is mutually exclusive with the general |
||
|
No |
If the value is This parameter does not affect vertices. For non-directed edges, the behavior of this parameter is identical to This parameter is mutually exclusive with the general |
Response
The response is the number of vertices and edges that were accepted. Additionally, if new_vertex_only
is true, the response will include two more fields:
-
skipped_vertices
: the number of vertices in the input data which already existed in the graph -
vertices_already_exist
: the id and type of the input vertices which were skipped
If vertex_must_exist
is true, the response will include two more fields:
-
skipped_edges
: the number of edges in the input data rejected because of missing endpoint vertices -
miss_vertices
: the id and type of the endpoint vertices which were missing
The example file add_id6.json
(shown in the Request Body section) upserts one User
vertex with id = "id6"
, one Liked
edge, and one Liked_By
edge. The Liked
edge is from "id1
" to "id6"
; the Liked_By
_edge is from "id6"
to _"id1"
.
Request body
The payload data should be in JSON according to the schema shown below:
{
"vertices": {
"<vertex_type>": {
"<vertex_id>": {
"<attribute>": {
"value": <value>,
"op": <opcode>
}
}
}
},
"edges": {
"<source_vertex_type>": {
"<source_vertex_id>": {
"<edge_type>": {
"<target_vertex_type>": {
"<target_vertex_id>": {
"<attribute>": {
"value": <value>,
"op": <opcode>
}
}
}
}
}
}
}
}
The fields in angle brackets (<>
) are placeholder names or values, to be replaced with actual values.
The keys in angle parentheses, such as <vertex_type>
, can be repeated to form a list of items.
The keys which are not in angle brackets are exact texts that must be used as they are.
The nested hierarchy means that vertices are grouped by type.
Edges, on the other hand, are first grouped by source vertex type, then vertex ID, then edge type.
Examples
The first example below shows two User
vertices having an attribute called age
:
{
"vertices": {
"User": {
"id6": {
"age": {
"value": 30
}
},
"id1": {
"age": {
"value": 22
}
}
}
}
}
This example starts with one User
vertex (id6
). Since id6
contains no attributes, it will remain unchanged if it already exists. If it doesn’t yet exist, the request will create a vertex with ID id6
with default attribute values. Two edges are created:
-
A
Liked
edge fromid1
toid6
. -
A
Liked_By
edge fromid6
toid1
.
{
"vertices": {
"User": {
"id6": {
}
}
},
"edges": {
"User":{
"id1": {
"Liked": {
"User": {
"id6" : {
"weight" : {
"value": 5.0
}
}
}
}
},
"id6": {
"Liked_By": {
"User": {
"id1" : {
"weight" : {
"value": 1.0
}
}
}
}
}
}
}
}
Follow the instructions in the Introduction section to format advanced data types.
For example, the following payload is used to upsert two User
vertices with an attribute coordinates
of type LIST
and an attribute measurements
of type MAP
:
{
"vertices": {
"User": {
"id4": {
"coordinates": {
"value": [51.3345, -7.2233]
},
"measurements": {
"value": {
"keyList": ["chest", "waist", "hip"]
"valueList": [35, 30, 35]
}
}
},
"id5": {
"coordinates": {
"value": [31.3245, -17.3292]
},
"measurements": {
"value": {
"keyList": ["chest", "waist", "hip"]
"valueList": [39, 35, 41]
}
}
}
}
}
}
Operation codes
Each attribute value may be accompanied by an operation (op) code, which provides very sophisticated schemes for data update or insertion:
Type | op | Meaning |
---|---|---|
1 |
|
If the vertex/edge does not exist, use the payload value to initialize the attribute; but if the vertex/edge already exists, do not change this attribute. |
2 |
|
Add the payload value to the existing value. |
3 |
|
Update to the logical AND of the payload value and the existing value. |
4 |
|
Update to the logical OR of the payload value and the existing value. |
5 |
|
Update to the higher value between the payload value and the existing value. |
6 |
|
Update to the lower value between the payload value and the existing value. |
If an attribute is not given in the payload, the attribute stays unchanged if the vertex/edge already exists, or if the vertex/edge does not exist, a new vertex/edge is created and assigned the default value for that data type. The default value is 0 for int/uint
, 0.0 for float/double
, and ""
(empty string) for string.
Upserting vertices with composite keys
If your vertex has composite keys, separate the attributes that make up the composite key with a comma (,
) in the same order as they are defined in the schema.
For example, suppose we have the following vertex definition:
CREATE VERTEX Composite_Person(id UINT, name STRING, age UINT, primary key (name, id))
CREATE VERTEX Composite_Movie (id UINT, title STRING, country STRING, year UINT, PRIMARY KEY (title,year,id))
CREATE DIRECTED EDGE Composite_Roles (from Composite_Person,to Composite_Movie, role STRING)
CREATE GRAPH Person_Movie(Composite_Person, Composite_Movie, Composite_Roles)
The following requests upserts two vertices with the defined composite key, as well as an edge of the type Composite_Roles
between Bob, 123
and Harry Potter, 1990, 1337
:
curl -X POST "localhost:14240/restpp/graph/Person_Movie" -d '
{
"vertices": {
"Composite_Person":{
"Bob,123":{
"name":{"value":"Bob"},
"id":{"value":123},
"age":{"value":25}
},
"Tom,456":{
"name":{"value":"Tom"},
"id":{"value":456},
"age":{"value":47}
}
}
},
"edges":{
"Composite_Person":{
"Bob,123":{
"Composite_Roles":{
"Composite_Movie":{
"Harry Potter,1990,1337":{
"role":{
"value":"Wizard"
}
}
}
}
}
}
}
}
'
Upserting regular edges
Regular edges do not have discriminators and must be uniquely defined by their source and target vertex IDs. To upsert a regular edge, use the following JSON format:
{
"edges": {
"<source_vertex_type>": {
"<source_vertex_id>": {
"<edge_type>": {
"<target_vertex_type>": {
"<target_vertex_id>": {
"<attribute>": {
"value": <value>
}
}
}
}
}
}
}
}
Upserting edges with discriminators
Some edge types are defined with discriminators, which allow multiple instances of the same edge type between two vertices.
Rules for upserting edges with discriminators
-
Discriminator attributes are required:
-
You must include all attributes defined in the discriminator when inserting an edge.
-
-
Discriminator attributes cannot be updated:
-
Discriminator attributes are immutable and cannot be changed once the edge is created.
-
-
Support for multi-edges with JSON arrays:
-
When upserting multiple edges with discriminators between the same source and target vertices, you can use a JSON array format..
-
Example: Edge type definition
CREATE DIRECTED EDGE Liked (
FROM User,
TO User,
DISCRIMINATOR(actionId STRING),
weight FLOAT
);
JSON Format for Edges with Discriminators
To upsert edges with discriminators, use the following JSON format:
{
"edges": {
"User": {
"id1": {
"Liked": {
"User": {
"id6": {
"actionId": {
"value": "uuid-1"
},
"weight": {
"value": 5.0
}
}
}
}
}
}
}
}
-
"actionId" is the discriminator:
-
The
actionId
uniquely identifies each edge instance of typeLiked
between the same source (id1) and target (id6) vertices. -
Discriminators are required when upserting edges of this type and must be included in the JSON payload.
-
-
Other attributes, like "weight":
-
Attributes not part of the discriminator (e.g.,
weight
) can be updated when upserting.
-
-
Nested Structure:
-
The JSON groups the edge by source vertex type (User) and source vertex ID (id1).
-
Inside, the edge type (Liked) connects the source to the target vertex type (User) and target vertex ID (id6).
-
The
value
field holds the attribute value for each edge attribute.
-
|
General rules for JSON formatting
-
Escaped quotes:
-
If the payload is enclosed in double quotes (
"
), internal double quotes must be replaced with escaped single quotes (\'
). -
Example:
-
'{"edges":{"User":{"id1":{"Liked":{"User":{"id6":{"weight":{"value":5.0}}}}}}}}'
-
Lists and Sets:
-
For attributes that store lists or sets, use the following format:
-
{
"measurements": {
"value": {
"keyList": ["chest", "waist", "hip"],
"valueList": [35, 30, 35]
}
}
}
Valid data types
The RESTPP server validates the request before updating the values. The following schema violations will cause the entire request to fail and no change will be made to a graph:
-
For vertex upsert
-
Invalid vertex type
-
Invalid attribute data type
-
-
For edge upsert:
-
Invalid source vertex type
-
Invalid edge type
-
Invalid target vertex type
-
Invalid attribute data type.
-
If an invalid attribute name is given, it is ignored.
Example
The following example submits an upsert request by using the payload data stored in add_id6.json
.
curl -X POST --data-binary @add_id6.json "http://localhost:14240/restpp/graph"
{"accepted_vertices":1,"accepted_edges":2}
If we set the value of vertex_must_exist
parameter to true
, the endpoint will only insert edges whose endpoint vertices both exist. This includes the vertices being inserted in the same request.
Therefore, inserting the content of add_id6.json
to an empty graph would cause the edges to be rejected:
curl -X POST --data-binary @add_id6.json "http://localhost:14240/restpp/graph?vertex_must_exist=true"
{
"version": {
"edition": "enterprise",
"api": "v2",
"schema": 0
},
"error": false,
"message": "",
"results": [
{
"accepted_vertices": 1,
"accepted_edges": 0,
"skipped_edges": 2,
"edge_vertices_not_exist": [
{
"v_type": "User",
"v_id": "id1"
}
]
}
],
"code": "REST-0003"
}
Atomic upsert transaction
By default, the POST /graph/{graph_name}
endpoint is not atomic. If something goes wrong during the process of the request, the request data can be partially consumed by the database.
You can append a request header gsql-atomic-level
to the request to set the request’s atomicity level.
The header parameter accepts the following values:
-
atomic
: The request is an atomic transaction. An atomic transaction means that updates to the database contained in the request are all-or-nothing: either all changes are successful, or none is successful. -
nonatomic
: The request is not atomic. This is the default behavior of the endpoint.
For example, suppose we have the following request to upsert two vertices:
curl --data-binary @vertices.json http://localhost:14240/restpp/graph/social
Content of vertices.json
is:
{
"vertices": {
"person": {
"Velma": {
"age": {
"value": 30
}
},
"Kelly": {
"age": {
"value": 22
}
}
}
}
}
With the request above, if the vertex Kelly
fails to be upserted due to a machine failure, it is still possible that the vertex Velma
is upserted to the database.
If you add the gsql-atomic-level
header to the request URL and set its value to atomic
, the request becomes atomic and if any part of the request body fails to be upserted, nothing will be upserted:
# This is an atomic request
curl -X POST -H 'gsql-atomic-level:atomic' --data-binary @vertices.json http://localhost:14240/restpp/graph/social