# 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](https://aerospike.com/docs/graph/develop/query/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`.

Add vertices and edges (Python)

```python
# Create Alice and Bob vertices

alice = g.add_v("person").property("name", "Alice").next()

bob = g.add_v("person").property("name", "Bob").next()

# Add an edge between Alice and Bob

g.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](https://aerospike.com/docs/graph/develop/query/multi-properties).

Update properties on a vertex (Python)

```python
# Update the "age" property of an existing vertex

g.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.

Delete vertices or properties (Python)

```python
# Drop a vertex and all its edges

g.V().has("name", "Alice").drop().iterate()

# Drop the nickname property from Bob

g.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:

1.  Search match (required) – Defines how to find an existing vertex, such as by `T.id` or specific properties.
2.  `on_create` (optional) – Properties applied only when the vertex does not exist and is created.
3.  `on_match` (optional) – Properties applied only when the vertex already exists.

In this example, `person` is the [vertex label](https://aerospike.com/docs/graph/load/csv-format/#vertex-data-file-headers) 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.

Merge vertex (Python)

```python
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:

1.  Search match (required) – Defines how to find an existing edge, such as by its label and connected vertices.
2.  `on_create` (optional) – Properties applied only when the edge does not exist and is created.
3.  `on_match` (optional) – Properties applied only when the edge already exists between the vertices.

In this example, `knows` is the [edge label](https://aerospike.com/docs/graph/load/csv-format/#edge-data-file-headers) 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.

Merge edge (Python)

```python
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()`:

```python
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:

Merge edge with in/out vertex options (Java)

```java
// 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
```

::: caution
MergeE requires the Aerospike Database namespace supervisor [`nsup-period`](https://aerospike.com/docs/database/reference/config#namespace__nsup-period) to be enabled. When it is disabled (`nsup-period = 0`), an `Operation not allowed at this time` error is returned.
:::

## Performance

Merging edges on [supernodes](https://aerospike.com/docs/graph/develop/query/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](https://aerospike.com/docs/database/reference/config#service__query-threads-limit). 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:

```txt
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](https://aerospike.com/docs/graph/develop/query/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` and `mergeE` apply to all elements matching the search criteria. For example, this query updates every vertex labeled `"person"`:
    
    ```java
    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.