Query usage
This page describes how to use mutation queries such as add, property, drop, and merge operations. It explains general usage patterns for all mutation queries, and then highlights specific performance considerations. For details on how data consistency settings affect these queries, see transactions.
Basic mutation query usage
Aerospike Graph Service (AGS) supports several mutation operations that directly add, update, or remove data. Each call to these operations applies the change unconditionally, so repeating them adds new data, modifies properties again, or attempts to delete elements that may already be gone.
AddV and AddE queries
- Insert a new vertex or edge every time the query runs. To prevent duplicates, enforce uniqueness using identifiers such as
T.id
.
# Create Alice and Bob verticesalice = g.add_v("person").property("name", "Alice").next()bob = g.add_v("person").property("name", "Bob").next()
# Add an edge between Alice and Bobg.add_e("knows").from_(alice).to(bob).iterate()
Property queries
- Update properties on an existing vertex or edge.
- Overwrite property values when called repeatedly, unless using multi-properties.
# Update the "age" property of an existing vertexg.V().has("name", "Alice").property("age", 31).iterate()
Drop queries
- Remove vertices, edges, or properties from the graph.
- Determine what gets removed based on the final step in the traversal:
- If the traversal ends on a vertex or edge, remove that element.
- If it ends on a property, remove only that property.
- Delete all edges connected to a vertex when that vertex is dropped.
# Drop a vertex and all its edgesg.V().has("name", "Alice").drop().iterate()
# Drop the nickname property from Bobg.V().has("name", "Bob").properties("nickname").drop().iterate()
Use drop queries with caution and consider dependencies when removing vertices.
Merge queries
Merge operations such as mergeV
and mergeE
create a vertex or edge if it does not already exist, and update it if an on_match
conditional is provided.
MergeV query
A merge vertex query consists of three parts:
- Search match (required) – Defines how to find an existing vertex, such as by
T.id
or specific properties. on_create
(optional) – Properties applied only when the vertex does not exist and is created.on_match
(optional) – Properties applied only when the vertex already exists.
In this example, person
is the vertex label and name
and age
are properties. The search criteria use T.id
, the first conditional creates the vertex with the name
property, or the second conditional updates the age
property when the vertex already exists.
g.merge_v({T.id: "v1"}) .option(Merge.on_create, {T.label: "person", "name": "Alice"}) .option(Merge.on_match, {"age": 30}) .iterate()
Calling the same query multiple times does not create duplicate vertices. Conditionals defined in on_match
are applied only to existing vertices.
MergeE query
A merge edge query also has three parts:
- Search match (required) – Defines how to find an existing edge, such as by its label and connected vertices.
on_create
(optional) – Properties applied only when the edge does not exist and is created.on_match
(optional) – Properties applied only when the edge already exists between the vertices.
In this example, knows
is the edge label and since
and weight
are edge properties. The search criteria use the edge label and vertex directions, the first conditional creates the edge with the since
property, or the second conditional adds or updates the weight
property when the edge already exists.
g.merge_e({T.label: "knows", Direction.IN: "v1", Direction.OUT: "v2"}) .option(Merge.on_create, {"since": 2020}) .option(Merge.on_match, {"weight": 5}) .iterate()
When running mergeE
, terminate with iterate()
. Using next()
will read the adjacent vertices again to return the edge. If you only need the edge ID, use id().next()
:
g.merge_e({T.label: "knows", Direction.IN: "v1", Direction.OUT: "v2"}) .option(Merge.on_create, {"since": 2020}) .option(Merge.on_match, {"weight": 5}) .id() .next()
mergeE
also supports Merge.inV
and Merge.outV
options to set which vertices the edge should connect, which is helpful when the vertices are chosen earlier in the traversal instead of being provided directly:
// Earlier step: find Alice and label her as "a"g.V().has("name", "Alice").as("a")// Earlier step: find Bob and label him as "b" .V().has("name", "Bob").as("b")// Merge an edge with Alice as OUT and Bob as IN .mergeE(asMap(T.label, "knows", Direction.OUT, Merge.outV, Direction.IN, Merge.inV)) .option(Merge.outV, select("a")) // Later step: use Alice as OUT vertex .option(Merge.inV, select("b")) // Later step: use Bob as IN vertex
Performance
Merging edges on supernodes (vertices with many connected edges) triggers secondary index lookups. These lookups consume query threads and may impact performance. This is most relevant when the search uses only one direction (IN
or OUT
) rather than both (IN
and OUT
).
By default, Aerospike has 128 query threads. Each index query uses 4 threads limiting you to 32 parallel index queries (128 divided by 4). Exceeding this limit results in the following error:
Operation not allowed at this time
Reduce merges on supernodes, or limit the number of parallel merges, to stay within the available query threads. For more information, see supernodes.
Reliability
Certain conditions can affect how merges behave:
-
If the identifiers used for merging, such as vertex label or property value, are not unique, merges can create duplicates or update the wrong element.
-
mergeV
andmergeE
apply to all elements matching the search criteria. For example, this query updates every vertex labeled"person"
:g.mergeV(asMap(T.label, "person")).option(Merge.on_match, asMap("updated", true)).iterate()Include unique identifiers in the search match to avoid unintended bulk updates.