Skip to content

Map operations examples

Aerospike maps can be used to implement use cases such as:

  • Event History Containers
  • Document Store
  • Leaderboards

Modeling concepts

Nested lists and maps may be combined to model complex use cases. The examples below use the map operations available in each Aerospike language client.

Developers familiar with other NoSQL document stores will see the flexibility in applying map and list operations on bins with nested CDTs. Combined with (operate) single record transactions, Aerospike provides powerful functionality for modeling a wide range of use cases.

Examples

Event containers with unique timestamps

In this example we want to store and query user event data. Each record contains the recent N events of a specific user, keyed by that user’s unique identifier.

Assuming that events will not occur at the same millisecond, we’ll use millisecond timestamps as map keys for distinct events. Each event’s data will be a tuple [ event-type, { attr1: v1, attr2: v2, ... } ].

Our sample data will be the following events of a single user:

{
1523474230000: ['fav', {'sku':1, 'b':2}],
1523474231001: ['comment', {'sku':2, 'b':22}],
1523474236006: ['viewed', {'foo':'bar', 'sku':3, 'zz':'top'}],
1523474235005: ['comment', {'sku':1, 'c':1234}],
1523474233003: ['viewed', {'sku':3, 'z':26}],
1523474234004: ['viewed', {'sku':1, 'ff':'hhhl'}]
}

Retrieving data for specific event types

We’ll retrieve all the events of a specific event type, using a get_all_by_value map operation. The argument for the operation is the tuple ['comment', *]. The wildcard singleton (*) matches any remaining elements in the tuple from that position onward, so all tuples starting with 'comment' as their first element are matched.

// events = {1523474230000: ["fav", {sku: 1, b: 2}],
// 1523474231001: ["comment", {sku: 2, b: 22}],
// 1523474233003: ["viewed", {sku: 3, z: 26}],
// 1523474234004: ["viewed", {sku: 1, ff: "hhhl"}],
// 1523474235005: ["comment", {sku: 1, c: 1234}],
// 1523474236006: ["viewed", {foo: "bar", sku: 3, zz: "top"}]}
List<Value> pattern = Arrays.asList(Value.get("comment"), Value.WILDCARD);
Record record = client.operate(null, key,
MapOperation.getByValue("events", Value.get(pattern), MapReturnType.KEY_VALUE)
);
// {1523474231001: ["comment", {sku: 2, b: 22}],
// 1523474235005: ["comment", {sku: 1, c: 1234}]}

Expanding on that example, we will retrieve all the events for multiple event types using get_all_by_value_list:

List<Value> commentPattern = Arrays.asList(Value.get("comment"), Value.WILDCARD);
List<Value> favPattern = Arrays.asList(Value.get("fav"), Value.WILDCARD);
List<Value> patterns = Arrays.asList(Value.get(commentPattern), Value.get(favPattern));
Record record = client.operate(null, key,
MapOperation.getByValueList("events", patterns, MapReturnType.KEY_VALUE)
);
// {1523474230000: ["fav", {sku: 1, b: 2}],
// 1523474231001: ["comment", {sku: 2, b: 22}],
// 1523474235005: ["comment", {sku: 1, c: 1234}]}

Counting events

We will get a count of a specific event type against the sample data above by specifying a returnType=count. The default behavior would be that of returnType=KeyValue:

List<Value> viewedPattern = Arrays.asList(Value.get("viewed"), Value.WILDCARD);
Record viewedCount = client.operate(null, key,
MapOperation.getByValue("events", Value.get(viewedPattern), MapReturnType.COUNT)
);
// 3
List<Value> commentPattern = Arrays.asList(Value.get("comment"), Value.WILDCARD);
Record commentCount = client.operate(null, key,
MapOperation.getByValue("events", Value.get(commentPattern), MapReturnType.COUNT)
);
// 2

Trimming the map

Often we want to cap the number of events captured within a map. We will use the remove_by_index_range map operation to keep the last 1000 events. The INVERTED flag removes everything outside the specified range — effectively keeping only the 1000 highest-indexed (most recent) entries and discarding the rest.

client.operate(null, key,
MapOperation.removeByIndexRange("events", -1000, 1000,
MapReturnType.NONE | MapReturnType.INVERTED)
);

Event containers with unique UUIDs

In some use cases a timestamp would result in frequent collisions. We can model using a unique identifier as the map key.

In this example a conversation thread is stored in a single record. The map keys are message UUIDs, and the map values are list tuples [timestamp, msg-string, username].

Our sample data will be the messages in a single conversation thread:

{
'0edf5b73-535c-4be7-b653-c0513dc79fb4': [1523474230, "Billie Jean is not my lover", "MJ"],
'29342a0b-e20f-4676-9ecf-dfdf02ef6683': [1523474241, "She's just a girl who", "MJ"],
'31a8ba1b-8415-aab7-0ecc-56ee659f0a83': [1523474245, "claims that I am the one", "MJ"],
'9f54b4f8-992e-427f-9fb3-e63348cd6ac9': [1523474249, "...", "Tito"],
'1ae56b18-7a3c-4f64-adb7-2e845eb5094e': [1523474257, "But the kid is not my son", "MJ"],
'08785e96-eb1b-4a74-a767-7b56e8f13ea9': [1523474306, "ok...", "Tito"],
'319fa1a6-0640-4354-a426-10c4d3459f0a': [1523474316, "Hee-hee!", "MJ"]
}

We will retrieve all the messages in a range of timestamps, using the get_by_value_interval map operation.

The arguments for the operation are minimum and maximum tuples of [timestamp, nil]. The NIL singleton is lower in value than a string. The get_by_value_interval checks if each map value (a list) is between the two list arguments of the operation.

// Retrieve messages with timestamps in [1523474240, 1523474246)
List<Value> rangeStart = Arrays.asList(Value.get(1523474240), Value.getAsNull());
List<Value> rangeEnd = Arrays.asList(Value.get(1523474246), Value.getAsNull());
Record record = client.operate(null, key,
MapOperation.getByValueRange("msgs", Value.get(rangeStart), Value.get(rangeEnd),
MapReturnType.KEY_VALUE)
);
// {29342a0b-...: [1523474241, "She's just a girl who", "MJ"],
// 31a8ba1b-...: [1523474245, "claims that I am the one", "MJ"]}

By the interval comparison rules the following is evaluated:

Terminal window
[1523474240, nil] ≰ [1523474230, "Billie Jean is not my lover", "MJ"] < [1523474246, nil] (false)
[1523474240, nil] ≤ [1523474241, "She's just a girl who", "MJ"] < [1523474246, nil] (true)
[1523474240, nil] ≤ [1523474245, "claims that I am the one", "MJ"] < [1523474246, nil] (true)
[1523474240, nil] ≤ [1523474249, "...", "Tito"] ≮ [1523474246, nil] (false)
[1523474240, nil] ≤ [1523474257, "But the kid is not my son", "MJ"] ≮ [1523474246, nil] (false)
[1523474240, nil] ≤ [1523474306, "ok...", "Tito"] ≮ [1523474246, nil] (false)
[1523474240, nil] ≤ [1523474316, "Hee-hee!", "MJ"] ≮ [1523474246, nil] (false)
Feedback

Was this page helpful?

What type of feedback are you giving?

What would you like us to know?

+Capture screenshot

Can we reach out to you?