Examples for List operations
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
Use Aerospike lists to implement use cases when working with any of the following:
- Queues
- Unique ordered data
- Time series containers
- Leaderboards
- Membership lists for user profiles
Each one of the list operations has a detailed description with code examples.
Modeling concepts
An Aerospike list can be used to express a many-to-one relationship in a single record. By comparison in a relational database, such a relationship would span multiple rows in two tables.
For example, a user and her vehicles would likely be expressed in a relational database as a users table and a vehicles table, with a vehicles.user_id column indexed and declared to be a foreign key to users.
In Aerospike a Users set would contain user records. Each user’s vehicles
could be expressed as a list of map elements, with each map element containing
the information of one vehicle. Users without any vehicles would not have a
vehicles bin at all. In Aerospike, sets have no schema, and records can vary
from one another in their structure. There is no necessity to place-hold
vehicles with something like a NULL in a relational database.
Examples
Lists as queues
Starting from an unordered list in the lqueue bin:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]Enqueue two elements
Enqueue two new elements at the same time, using a single atomic
operate call with two list_append operations.
After this call, the list has 16 elements.
try (RecordStream rs = session.upsert(key) .bin("lqueue").listAppend(377) .bin("lqueue").listAppend(477) .execute()) { Record rec = rs.next().recordOrThrow();}// lqueue: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]stream = (session.upsert(key) .bin("lqueue").list_append(377) .bin("lqueue").list_append(477) .execute())# lqueue: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]// Requires: use aerospike::operations::lists::{self, ListPolicy};let policy = ListPolicy::default();client.operate(&WritePolicy::default(), &key, &[ lists::append(&policy, "lqueue", as_val!(377)), lists::append(&policy, "lqueue", as_val!(477)),]).await?;// lqueue: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]Record record = client.Operate(null, key, ListOperation.Append("lqueue", Value.Get(377)), ListOperation.Append("lqueue", Value.Get(477)));// lqueue: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]// Requires: import as "github.com/aerospike/aerospike-client-go/v8"_, err := client.Operate(nil, key, as.ListAppendOp("lqueue", 377), as.ListAppendOp("lqueue", 477),)// lqueue: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]const Aerospike = await import("aerospike");const lists = Aerospike.lists;
const result = await client.operate(key, [ lists.append('lqueue', 377), lists.append('lqueue', 477),])// lqueue: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]as_operations ops;as_operations_inita(&ops, 2);as_operations_list_append(&ops, "lqueue", NULL, NULL, (as_val*)as_integer_new(377));as_operations_list_append(&ops, "lqueue", NULL, NULL, (as_val*)as_integer_new(477));as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);// lqueue: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]as_record_destroy(rec);as_operations_destroy(&ops);Record record = client.operate(null, key, ListOperation.append("lqueue", Value.get(377)), ListOperation.append("lqueue", Value.get(477)));// lqueue: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]from aerospike_helpers.operations import list_operations
_, _, bins = client.operate(key, [ list_operations.list_append("lqueue", 377), list_operations.list_append("lqueue", 477),])# lqueue: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]Dequeue two elements
Dequeue the first two values with one atomic list operation. The
remove_by_index_range operation removes elements starting at index 0, with a
count of 2.
try (RecordStream rs = session.upsert(key) .bin("lqueue").onListIndexRange(0, 2).removeAnd().getValues() .execute()) { List<?> removed = rs.next().recordOrThrow().getList("lqueue"); // removed: [0, 1]}// lqueue: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]from aerospike_sdk import ListReturnType
stream = (session.upsert(key) .bin("lqueue").on_list_index_range(0, 2) .remove(return_type=ListReturnType.VALUE) .execute())removed = stream.first_or_raise().record_or_raise().bins["lqueue"]# removed: [0, 1]# lqueue: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]// Requires: use aerospike::operations::lists::{self, ListReturnType};let record = client.operate(&WritePolicy::default(), &key, &[ lists::remove_by_index_range_count("lqueue", 0, 2, ListReturnType::Values),]).await?;let removed = record.bins.get("lqueue");// removed: [0, 1]// lqueue: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]Record record = client.Operate(null, key, ListOperation.RemoveByIndexRange("lqueue", 0, 2, ListReturnType.VALUE));IList removed = record.GetList("lqueue");// removed: [0, 1]// lqueue: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]// Requires: import as "github.com/aerospike/aerospike-client-go/v8"record, err := client.Operate(nil, key, as.ListRemoveByIndexRangeCountOp("lqueue", 0, 2, as.ListReturnTypeValue),)removed := record.Bins["lqueue"]// removed: [0, 1]// lqueue: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]const result = await client.operate(key, [ lists.removeByIndexRange('lqueue', 0, 2) .andReturn(lists.returnType.VALUE),])const removed = result.bins.lqueue// removed: [0, 1]// lqueue: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]as_operations ops;as_operations_inita(&ops, 1);as_operations_list_remove_by_index_range(&ops, "lqueue", NULL, 0, 2, AS_LIST_RETURN_VALUE);as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);as_list* removed = as_record_get_list(rec, "lqueue");// removed: [0, 1]// lqueue: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]as_record_destroy(rec);as_operations_destroy(&ops);Record record = client.operate(null, key, ListOperation.removeByIndexRange("lqueue", 0, 2, ListReturnType.VALUE));List<?> removed = record.getList("lqueue");// removed: [0, 1]// lqueue: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]_, _, bins = client.operate(key, [ list_operations.list_remove_by_index_range("lqueue", 0, aerospike.LIST_RETURN_VALUE, 2),])removed = bins["lqueue"]# removed: [0, 1]# lqueue: [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477]Enqueue and dequeue atomically
Use a single operate call to enqueue a new element while dequeuing the first
element in the queue.
try (RecordStream rs = session.upsert(key) .bin("lqueue").listAppend(125) .bin("lqueue").onListIndexRange(0, 1).removeAnd().getValues() .execute()) { List<?> removed = rs.next().recordOrThrow().operationResult(1).getList(); // removed: [1]}// lqueue: [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477, 125]stream = (session.upsert(key) .bin("lqueue").list_append(125) .bin("lqueue").on_list_index_range(0, 1) .remove(return_type=ListReturnType.VALUE) .execute())removed = stream.first_or_raise().record_or_raise().operation_result(1)# removed: [1]# lqueue: [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477, 125]// Requires: use aerospike::{operations::lists::{self, ListPolicy, ListReturnType}, Value};let policy = ListPolicy::default();let record = client.operate(&WritePolicy::default(), &key, &[ lists::append(&policy, "lqueue", as_val!(125)), lists::remove_by_index_range_count("lqueue", 0, 1, ListReturnType::Values),]).await?;// Two operations on one bin come back as a MultiResult, one entry per operation.if let Some(Value::MultiResult(results)) = record.bins.get("lqueue") { let removed = &results[1]; // removed: [1]}// lqueue: [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477, 125]Record record = client.Operate(null, key, ListOperation.Append("lqueue", Value.Get(125)), ListOperation.RemoveByIndexRange("lqueue", 0, 1, ListReturnType.VALUE));IList removed = (IList)record.GetList("lqueue")[1];// removed: [1]// lqueue: [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477, 125]// Requires: import as "github.com/aerospike/aerospike-client-go/v8"record, err := client.Operate(nil, key, as.ListAppendOp("lqueue", 125), as.ListRemoveByIndexRangeCountOp("lqueue", 0, 1, as.ListReturnTypeValue),)removed := record.Bins["lqueue"].(as.OpResults)[1]// removed: [1]// lqueue: [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477, 125]const result = await client.operate(key, [ lists.append('lqueue', 125), lists.removeByIndexRange('lqueue', 0, 1) .andReturn(lists.returnType.VALUE),])// With two operations on one bin, the bin holds the last one's result.const removed = result.bins.lqueue// removed: [1]// lqueue: [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477, 125]as_operations ops;as_operations_inita(&ops, 2);as_operations_list_append(&ops, "lqueue", NULL, NULL, (as_val*)as_integer_new(125));as_operations_list_remove_by_index_range(&ops, "lqueue", NULL, 0, 1, AS_LIST_RETURN_VALUE);as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);// Each operation has its own bin entry, in order: entries[1] is the dequeue.as_list* removed = as_list_fromval((as_val*)as_bin_get_value(&rec->bins.entries[1]));// removed: [1]// lqueue: [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477, 125]as_record_destroy(rec);as_operations_destroy(&ops);Record record = client.operate(null, key, ListOperation.append("lqueue", Value.get(125)), ListOperation.removeByIndexRange("lqueue", 0, 1, ListReturnType.VALUE));List<?> removed = (List<?>) record.getList("lqueue").get(1);// removed: [1]// lqueue: [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477, 125]_, _, bins = client.operate(key, [ list_operations.list_append("lqueue", 125), list_operations.list_remove_by_index_range("lqueue", 0, aerospike.LIST_RETURN_VALUE, 1),])# With two operations on one bin, the bin holds the last one's result.removed = bins["lqueue"]# removed: [1]# lqueue: [2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 477, 125]Lists as containers for time series data
A record contains a list of [timestamp, value] pairs in the pairs bin:
[ [1523474230000, 39.04], [1523474231001, 39.78], [1523474236006, 40.07], [1523474235005, 41.18], [1523474233003, 40.89], [1523474234004, 40.93]]First, insert the record:
List<List<Object>> pairs = List.of( List.of(1523474230000L, 39.04), List.of(1523474231001L, 39.78), List.of(1523474236006L, 40.07), List.of(1523474235005L, 41.18), List.of(1523474233003L, 40.89), List.of(1523474234004L, 40.93));try (RecordStream rs = session.upsert(key).bin("pairs").setTo(pairs).execute()) { rs.next().recordOrThrow();}pairs = [ [1523474230000, 39.04], [1523474231001, 39.78], [1523474236006, 40.07], [1523474235005, 41.18], [1523474233003, 40.89], [1523474234004, 40.93],]session.upsert(key).bin("pairs").set_to(pairs).execute()let pairs = as_list!( as_list!(1523474230000i64, 39.04), as_list!(1523474231001i64, 39.78), as_list!(1523474236006i64, 40.07), as_list!(1523474235005i64, 41.18), as_list!(1523474233003i64, 40.89), as_list!(1523474234004i64, 40.93));client.put(&WritePolicy::default(), &key, &[as_bin!("pairs", pairs)]).await?;var pairs = new List<Value> { Value.Get(new List<Value> { Value.Get(1523474230000L), Value.Get(39.04) }), Value.Get(new List<Value> { Value.Get(1523474231001L), Value.Get(39.78) }), Value.Get(new List<Value> { Value.Get(1523474236006L), Value.Get(40.07) }), Value.Get(new List<Value> { Value.Get(1523474235005L), Value.Get(41.18) }), Value.Get(new List<Value> { Value.Get(1523474233003L), Value.Get(40.89) }), Value.Get(new List<Value> { Value.Get(1523474234004L), Value.Get(40.93) }),};client.Put(null, key, new Bin("pairs", pairs));// Requires: import as "github.com/aerospike/aerospike-client-go/v8"pairs := []any{ []any{int64(1523474230000), 39.04}, []any{int64(1523474231001), 39.78}, []any{int64(1523474236006), 40.07}, []any{int64(1523474235005), 41.18}, []any{int64(1523474233003), 40.89}, []any{int64(1523474234004), 40.93},}err := client.Put(nil, key, as.BinMap{"pairs": pairs})const pairs = [ [1523474230000, 39.04], [1523474231001, 39.78], [1523474236006, 40.07], [1523474235005, 41.18], [1523474233003, 40.89], [1523474234004, 40.93],]await client.put(key, { pairs })as_arraylist pairs;as_arraylist_init(&pairs, 6, 0);int64_t ts[] = {1523474230000, 1523474231001, 1523474236006, 1523474235005, 1523474233003, 1523474234004};double vs[] = {39.04, 39.78, 40.07, 41.18, 40.89, 40.93};for (int i = 0; i < 6; i++) { as_arraylist* pair = as_arraylist_new(2, 0); as_arraylist_append_int64(pair, ts[i]); as_arraylist_append_double(pair, vs[i]); as_arraylist_append(&pairs, (as_val*)pair);}as_record rec;as_record_inita(&rec, 1);as_record_set_list(&rec, "pairs", (as_list*)&pairs);aerospike_key_put(&as, &err, NULL, &key, &rec);as_record_destroy(&rec);List<List<Object>> pairs = Arrays.asList( Arrays.asList(1523474230000L, 39.04), Arrays.asList(1523474231001L, 39.78), Arrays.asList(1523474236006L, 40.07), Arrays.asList(1523474235005L, 41.18), Arrays.asList(1523474233003L, 40.89), Arrays.asList(1523474234004L, 40.93));client.put(null, key, new Bin("pairs", pairs));pairs = [ [1523474230000, 39.04], [1523474231001, 39.78], [1523474236006, 40.07], [1523474235005, 41.18], [1523474233003, 40.89], [1523474234004, 40.93],]client.put(key, {"pairs": pairs})Then retrieve the pairs within a specified range of timestamp values using
get_by_value_range. The range lower bound is [1523474231000] and upper bound
is [1523474234000]. The lower bound is inclusive and the upper bound is
exclusive, so the pairs returned are those whose first element is at least
1523474231000 and less than 1523474234000.
try (RecordStream rs = session.query(key) .bin("pairs").onListValueRange(List.of(1523474231000L), List.of(1523474234000L)).getValues() .execute()) { List<?> inRange = rs.next().recordOrThrow().getList("pairs"); // inRange: [[1523474231001, 39.78], [1523474233003, 40.89]]}stream = (session.query(key) .bin("pairs").on_list_value_range([1523474231000], [1523474234000]).get_values() .execute())in_range = stream.first_or_raise().record_or_raise().bins["pairs"]# in_range: [[1523474231001, 39.78], [1523474233003, 40.89]]let record = client.operate(&WritePolicy::default(), &key, &[ lists::get_by_value_range("pairs", as_list!(1523474231000i64), as_list!(1523474234000i64), ListReturnType::Values),]).await?;let in_range = record.bins.get("pairs");// in_range: [[1523474231001, 39.78], [1523474233003, 40.89]]Record record = client.Operate(null, key, ListOperation.GetByValueRange("pairs", Value.Get(new List<Value> { Value.Get(1523474231000L) }), Value.Get(new List<Value> { Value.Get(1523474234000L) }), ListReturnType.VALUE));IList inRange = record.GetList("pairs");// inRange: [[1523474231001, 39.78], [1523474233003, 40.89]]// Requires: import as "github.com/aerospike/aerospike-client-go/v8"record, err := client.Operate(nil, key, as.ListGetByValueRangeOp("pairs", []any{int64(1523474231000)}, []any{int64(1523474234000)}, as.ListReturnTypeValue),)inRange := record.Bins["pairs"]// inRange: [[1523474231001, 39.78], [1523474233003, 40.89]]const result = await client.operate(key, [ lists.getByValueRange('pairs', [1523474231000], [1523474234000]) .andReturn(lists.returnType.VALUE),])const inRange = result.bins.pairs// inRange: [[1523474231001, 39.78], [1523474233003, 40.89]]as_arraylist begin;as_arraylist_init(&begin, 1, 0);as_arraylist_append_int64(&begin, 1523474231000);as_arraylist end;as_arraylist_init(&end, 1, 0);as_arraylist_append_int64(&end, 1523474234000);
as_operations ops;as_operations_inita(&ops, 1);as_operations_list_get_by_value_range(&ops, "pairs", NULL, (as_val*)&begin, (as_val*)&end, AS_LIST_RETURN_VALUE);as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);as_list* in_range = as_record_get_list(rec, "pairs");// in_range: [[1523474231001, 39.78], [1523474233003, 40.89]]as_record_destroy(rec);as_operations_destroy(&ops);Record record = client.operate(null, key, ListOperation.getByValueRange("pairs", Value.get(Arrays.asList(1523474231000L)), Value.get(Arrays.asList(1523474234000L)), ListReturnType.VALUE));List<?> inRange = record.getList("pairs");// inRange: [[1523474231001, 39.78], [1523474233003, 40.89]]_, _, bins = client.operate(key, [ list_operations.list_get_by_value_range("pairs", aerospike.LIST_RETURN_VALUE, [1523474231000], [1523474234000]),])in_range = bins["pairs"]# in_range: [[1523474231001, 39.78], [1523474233003, 40.89]]