Skip to content

Bin operations

For the complete documentation index see: llms.txt

All documentation pages available in markdown.

Jump to the Code block for a combined complete example.

Setup

The following examples will use the setup and record below to illustrate single record command operations in an Aerospike database.

import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Bin;
import com.aerospike.client.Key;
import com.aerospike.client.Record;
import com.aerospike.client.Value;
import com.aerospike.client.Operation;
import com.aerospike.client.cdt.ListOperation;
import com.aerospike.client.cdt.ListOrder;
import com.aerospike.client.cdt.ListPolicy;
import com.aerospike.client.cdt.ListWriteFlags;
import com.aerospike.client.cdt.MapOrder;
import com.aerospike.client.cdt.MapWriteFlags;
import com.aerospike.client.cdt.MapPolicy;
import com.aerospike.client.cdt.MapOperation;
import com.aerospike.client.cdt.MapReturnType;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
// Establishes a connection to the server
AerospikeClient client = new AerospikeClient("127.0.0.1", 3000);
// Creates a key with the namespace "sandbox", set "ufodata", and user key 5001
Key key = new Key("sandbox", "ufodata", 5001);

The record structure:

+------+----------+----------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------+
| PK | occurred | reported | posted | report | location |
+------+----------+----------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------+
| 5001 | 20220531 | 20220601 | 20220601 | MAP('{"shape":["circle", "flash", "disc"], "summary":"Large flying disc flashed in the sky above the student union. Craziest thing I've ever seen!", "city":"Ann Arbor", "state":"Michigan", "duration":"5 minutes"}') | GeoJSON('{"type":"Point","coordinates":[42.2808,83.743]}') |
+------+----------+----------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------+

Operations

Use the Aerospike Client API to perform separate operations on one or multiple bins in a record within a single transaction. This feature performs an atomic modification then returns the result. The table below shows some of the bin operations available for operate transactions.

Operations for List, Map, String, Blob/Bytes, Geospatial, and HyperLogLog data types see Scalar data types.

Data TypeOperationDescription
Allop_putUpsert (create or update) a bin. Also called write.
Allop_getRead a bin. Also called read.
Allop_touchIncrease the generation counter for a record.
Allop_deleteRemove a record from the database.
Integer
Float
op_addAdd (or subtract) a value. Used to implement counters. Also called increment.
Stringop_append
op_prepend
Modify a string. For Unicode-aware transforms, see String operations (Database 8.2.0 and later).

Single operation

All of the operations in the table above, along with CDT, Blob/Bytes, Geospatial, HyperLogLog, and String operations (Database 8.2.0 and later), can be executed as a single operation with the operate method. For the Java client’s StringOperation/StringExp surface, see String operations.

The following example creates a map policy for a KEY_ORDERED map, then applies that policy to the report bin. A KEY_ORDERED map provides faster access than the default UNORDERED map. This example passes null to use the default policy, but a write policy could be passed instead.

// Create KEY_ORDERED map policy
MapPolicy mapPolicy = new MapPolicy(MapOrder.KEY_ORDERED, MapWriteFlags.DEFAULT);
// Update the record
client.operate(null, key, MapOperation.setMapPolicy(mapPolicy, "report"));
// Close the connection to the server
client.close();

Multiple Operations

Multiple operations can be combined in a single transaction when one or more bins need to be updated or read.

The following example inserts the posted bin with value 20220602, updates the city inside the report bin to "Ypsilanti", and reads the report bin in a single transaction.

// Create posted bin to insert
Bin posted = new Bin("posted", 20220602);
// Update the record
Record record = client.operate(null, key,
Operation.put(posted),
MapOperation.put(MapPolicy.Default, "report", Value.get("city"), Value.get("Ypsilanti")),
Operation.get("report")
);
// Do something
System.out.format("Record: %s\\n", record.bins);
// Close the connection to the server
client.close();

List operations

A ListPolicy sets the order of a list and the write flags of a list write. Its constructor takes a ListOrder and the write flags, combined with |:

  • ListWriteFlags.ADD_UNIQUE rejects a value that is already in the list.
  • ListWriteFlags.NO_FAIL makes a rejected value a no-op instead of an error.
  • ListWriteFlags.PARTIAL adds the accepted values of a multi-value write even when others are rejected.

The following example appends two dates to a sightings list bin and skips dates already in the list. It then trims the list to its last five values. ListOperation.appendItems() adds each value of the list as a separate element.

// Skip values already in the list, and add the others
ListPolicy uniquePolicy = new ListPolicy(ListOrder.UNORDERED,
ListWriteFlags.ADD_UNIQUE | ListWriteFlags.NO_FAIL | ListWriteFlags.PARTIAL);
List<Value> dates = Arrays.asList(Value.get(20220601), Value.get(20220602));
Record record = client.operate(null, key,
ListOperation.appendItems(uniquePolicy, "sightings", dates),
// Keep the last five values: start at index -5 and keep 5
ListOperation.trim("sightings", -5, 5)
);
// One result per operation: the list size after the append, then the number of values trimmed
System.out.format("Results: %s%n", record.getList("sightings"));
// Close the connection to the server
client.close();

Without NO_FAIL, a value already in the list fails the command with ResultCode.ELEMENT_EXISTS.

Map operations

MapOperation.removeByKey() removes a map entry by its key. Its MapReturnType argument selects what the operation returns, such as MapReturnType.VALUE for the removed value or MapReturnType.NONE for nothing.

// Remove the duration key from the report map and return its value
Record record = client.operate(null, key,
MapOperation.removeByKey("report", Value.get("duration"), MapReturnType.VALUE)
);
System.out.format("Removed: %s%n", record.getValue("report"));
// Close the connection to the server
client.close();

MapOperation.getByRankRange() reads map entries by the rank of their values, where rank 0 is the lowest value. A negative rank counts from the highest value, so a rank of -3 with a count of 3 selects the three highest values. With MapReturnType.KEY_VALUE, the result is a List of Map.Entry, ordered from the lowest rank to the highest.

The following example reads the three highest counts from a shapeCounts map bin, such as {circle=4, disc=9, flash=2, light=6}, and prints them highest first.

// Write the shapeCounts map bin
client.put(null, key, new Bin("shapeCounts", Map.of("circle", 4, "disc", 9, "flash", 2, "light", 6)));
// Read the three entries with the highest values
Record record = client.operate(null, key,
MapOperation.getByRankRange("shapeCounts", -3, 3, MapReturnType.KEY_VALUE)
);
List<?> entries = record.getList("shapeCounts");
// The entries are lowest rank first, so read them in reverse for highest first
for (int i = entries.size() - 1; i >= 0; i--) {
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) entries.get(i);
System.out.format("%s: %s%n", entry.getKey(), entry.getValue());
}
// Close the connection to the server
client.close();

Read and reset metadata

Operation.getHeader() reads the record’s metadata without its bins, and Operation.touch() resets the record’s time to live to the write policy’s expiration. For an example that combines them, and for the Record fields that hold the metadata, see Reset the time to live.

Record not found behavior

operate() handles a missing record differently depending on whether the call includes a write operation:

  • A read-only operate() call (only read-type operations, such as Operation.get() or MapOperation.getByKey()) behaves like client.get() and returns null if the record does not exist.
  • An operate() call that includes a write-type operation (such as Operation.put() or a CDT write) follows normal write semantics instead of returning null: with the default RecordExistsAction.UPDATE, it creates the record if it does not exist. It throws an AerospikeException with ResultCode.KEY_NOT_FOUND_ERROR only when the record is required to exist, for example with RecordExistsAction.UPDATE_ONLY/REPLACE_ONLY, or Operation.touch().

Check the return value for null when an operate() call is read-only, and catch AerospikeException when a write-type call requires the record to already exist.

Returning from operate()

It’s important to understand how the operate method returns data, since operate accepts a variety of operations and deals with both reads and writes in a single call.

Examples of operations and results:

  • A basic put or write operation does not return anything by default, though it can be set to return null through a write policy.
  • A map or list modify operation returns the map or list size for the bin_name.
  • A get or read operation returns the value for the bin_name.

See Bitwise operations, List operations, Map operations, String operations, HLL operations, and Expressions for more information about return values.

The operate method takes the results of all the operations executed within the call and groups them together by bin_name. If a specific bin_name has multiple results, such as an operate call containing multiple operations that return results for a single bin_name, a list is created containing the results of each operation. If a bin_name has a single value, the bin_name value is returned as expected.

When grouped by bin_name, the way results are accessed will vary based on what operations are performed.

Code block

Expand this section for a single code block to create a transaction operation
import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Bin;
import com.aerospike.client.Key;
import com.aerospike.client.Record;
import com.aerospike.client.Value;
import com.aerospike.client.Operation;
import com.aerospike.client.cdt.MapOrder;
import com.aerospike.client.cdt.MapWriteFlags;
import com.aerospike.client.cdt.MapPolicy;
import com.aerospike.client.cdt.MapOperation;
// Establishes a connection to the server
AerospikeClient client = new AerospikeClient("127.0.0.1", 3000);
// Creates a key with the namespace "sandbox", set "ufodata", and user key 5001
Key key = new Key("sandbox", "ufodata", 5001);
// Create posted bin to insert
Bin posted = new Bin("posted", 20220602);
// Update the record
Record record = client.operate(null, key,
Operation.put(posted),
MapOperation.put(MapPolicy.Default, "report", Value.get("city"), Value.get("Ypsilanti")),
Operation.get("report")
);
// Do something
System.out.format("Record: %s\\n", record.bins);
// Close the connection to the server
client.close();