Examples for List operations
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.
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]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, _ := 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]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]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]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.
Record record = client.operate(null, key, ListOperation.removeByIndexRange("lqueue", 0, 2, ListReturnType.VALUE));// 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: [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);// 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, _ := client.Operate(nil, key, as.ListRemoveByIndexRangeCountOp("lqueue", 0, 2, as.ListReturnTypeValue),)// 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));// 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),])// 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.
Record record = client.operate(null, key, ListOperation.append("lqueue", Value.get(125)), ListOperation.removeByIndexRange("lqueue", 0, 1, ListReturnType.VALUE));// 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),])# 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);// 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, _ := client.Operate(nil, key, as.ListAppendOp("lqueue", 125), as.ListRemoveByIndexRangeCountOp("lqueue", 0, 1, as.ListReturnTypeValue),)// 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));// 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),])// 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 = 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})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);pairs := [][]interface{}{ {int64(1523474230000), 39.04}, {int64(1523474231001), 39.78}, {int64(1523474236006), 40.07}, {int64(1523474235005), 41.18}, {int64(1523474233003), 40.89}, {int64(1523474234004), 40.93},}pairList := make([]interface{}, len(pairs))for i, p := range pairs { pairList[i] = p}client.Put(nil, key, as.BinMap{"pairs": pairList})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));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 })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]. Pairs whose first element falls within this range are
returned.
Record record = client.operate(null, key, ListOperation.getByValueRange("pairs", Value.get(Arrays.asList(1523474231000L)), Value.get(Arrays.asList(1523474234000L)), ListReturnType.VALUE));List<?> list = record.getList("pairs");for (Object value : list) { System.out.println(value);}// [1523474231001, 39.78]// [1523474233003, 40.89]_, _, bins = client.operate(key, [ list_operations.list_get_by_value_range("pairs", aerospike.LIST_RETURN_VALUE, [1523474231000], [1523474234000]),])for pair in bins["pairs"]: print(pair)# [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);// [1523474231001, 39.78]// [1523474233003, 40.89]as_record_destroy(rec);as_operations_destroy(&ops);record, _ := client.Operate(nil, key, as.ListGetByValueRangeOp("pairs", []interface{}{int64(1523474231000)}, []interface{}{int64(1523474234000)}, as.ListReturnTypeValue),)for _, v := range record.Bins["pairs"].([]interface{}) { fmt.Println(v)}// [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 list = record.GetList("pairs");foreach (object value in list) { Console.WriteLine(value);}// [1523474231001, 39.78]// [1523474233003, 40.89]const result = await client.operate(key, [ lists.getByValueRange('pairs', [1523474231000], [1523474234000]) .andReturn(lists.returnType.VALUE),])for (const pair of result.bins.pairs) { console.log(pair)}// [1523474231001, 39.78]// [1523474233003, 40.89]