Skip to main content

Reading and Updating Maps

For an interactive Jupyter notebook experience: Binder

Last updated: June 22, 2021

This notebook demonstrates Java Aerospike CRUD operations (Create, Read, Update, Delete) for maps of data, focusing on server-side read and update operations.

Aerospike stores records by association with a key. Maps contain key:value pairs. This notebook makes use of the word mapkey to distinguish from a record key.

This Jupyter Notebook requires the Aerospike Database running locally with Java kernel and Aerospike Java Client. To create a Docker container that satisfies the requirements and holds a copy of these notebooks, visit the Aerospike Notebooks Repo.

Notebook Setup

Run these first to initialize Jupyter, download the Java Client, and make sure the Aerospike Database is running.

Import Jupyter Java Integration

Make it easier to work with Java in Jupyter.

import io.github.spencerpark.ijava.IJava;
import io.github.spencerpark.jupyter.kernel.magic.common.Shell;

IJava.getKernelInstance().getMagics().registerMagics(Shell.class);

Start Aerospike

Ensure Aerospike Database is running locally.

%sh asd

Download the Aerospike Java Client

Ask Maven to download and install the project object model (POM) of the Aerospike Java Client.

%%loadFromPOM
<dependencies>
<dependency>
<groupId>com.aerospike</groupId>
<artifactId>aerospike-client</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>

Start the Aerospike Java Client and Connect

Create an instance of the Aerospike Java Client, and connect to the demo cluster.

The default cluster location for the Docker container is localhost port 3000. If your cluster is not running on your local machine, modify localhost and 3000 to the values for your Aerospike cluster.

import com.aerospike.client.AerospikeClient;

AerospikeClient client = new AerospikeClient("localhost", 3000);
System.out.println("Initialized the client and connected to the cluster.");

Output:

Initialized the client and connected to the cluster.

CREATING Maps in Aerospike

Create and Print Map Data

Create a string map representing fish metadata. Create an integer map containing timestamped fish observation locations.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

HashMap <String, String> mapFish = new HashMap <String, String>();
mapFish.put("name", "Annette");
mapFish.put("fruit", "Pineapple");
mapFish.put("color", "Aquamarine");
mapFish.put("tree", "Redwood");
System.out.println("Fish Map: " + mapFish);


HashMap <Integer, HashMap> mapObs = new HashMap <Integer, HashMap>();
HashMap <String, Integer> mapCoords0 = new HashMap <String, Integer>();
mapCoords0.put("lat", -85);
mapCoords0.put("long", -130);
HashMap <String, Integer> mapCoords1 = new HashMap <String, Integer>();
mapCoords1.put("lat", -25);
mapCoords1.put("long", -50);
HashMap <String, Integer> mapCoords2 = new HashMap <String, Integer>();
mapCoords2.put("lat", 35);
mapCoords2.put("long", 30);

mapObs.put(13456, mapCoords1);
mapObs.put(14567, mapCoords2);
mapObs.put(12345, mapCoords0);
System.out.println("Observations Map:" + mapObs);

Output:

Fish Map: {color=Aquamarine, fruit=Pineapple, name=Annette, tree=Redwood}
Observations Map:{13456={lat=-25, long=-50}, 14567={lat=35, long=30}, 12345={lat=-85, long=-130}}

Insert the Maps into Aerospike

Insert one record in Aerospike with Key "koi", and Bin Names mapfishbin and mapobsbin.

By default, Aerospike data is unsorted, however Aerospike preserves order by index when inserting data. Java HashMaps are sorted by mapkey by default.

Create a Key Object

A Key uniquely identifies a specific record in your Aerospike server or cluster. Each key must have a Namespace and optionally a Set name.

  • In Aerospike, a Namespace is like a relational database's tablespace.
  • A Set is like a relational database table.
  • A Record is like a row in a relational database table.

The namespace test is configured on your Aerospike server or cluster.

For additional information on the Aerospike Data Model, go

here.
import com.aerospike.client.Key;

String mapSet = "mapset1";
String mapNamespace = "test";

String theKey = "koi";

Key key = new Key(mapNamespace, mapSet, theKey);
System.out.println("Key created." );

Output:

Key created.

Create a Bin Object for Each Map

A Bin is a data field in an Aerospike record.

import com.aerospike.client.Bin;

String mapFishBinName = "mapfishbin";
String mapObsBinName = "mapobsbin";

Bin bin1 = new Bin(mapFishBinName, mapFish);
Bin bin2 = new Bin(mapObsBinName, mapObs);

System.out.println( "Created " + bin1 + " and " + bin2 + ".");

Output:

Created mapfishbin:{color=Aquamarine, fruit=Pineapple, name=Annette, tree=Redwood} and mapobsbin:{13456={lat=-25, long=-50}, 14567={lat=35, long=30}, 12345={lat=-85, long=-130}}.

Create a Policy Object for Record Insertion

A Policy tells Aerospike the intent of a database operation.

For more information on policies, go

here.
import com.aerospike.client.policy.ClientPolicy;

ClientPolicy clientPolicy = new ClientPolicy();
System.out.println("Created a client policy.");

Output:

Created a client policy.

Put the Map Data into Aerospike

client.put(clientPolicy.writePolicyDefault, key, bin1, bin2);
System.out.println("Key: " + theKey + "\n" + mapFishBinName + ": " + mapFish + "\n" +
mapObsBinName + ": " + mapObs );

Output:

Key: koi
mapfishbin: {color=Aquamarine, fruit=Pineapple, name=Annette, tree=Redwood}
mapobsbin: {13456={lat=-25, long=-50}, 14567={lat=35, long=30}, 12345={lat=-85, long=-130}}

READING Maps and Map Elements From the Server

Now that the maps are in Aerospike, the client can return full or partial maps from bin contents. No data is modified by these ops.

Get the Record

A record can be retrieved using the key, namespace, and set name.

In the output:

  • gen is the generation number, the number of record writes.
  • exp is the expiration counter for the record.

For more information on both generation number and expiration, see the

Aerospike FAQ.
import com.aerospike.client.Record;

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
System.out.println(record);

Output:

(gen:1),(exp:359404034),(bins:(mapfishbin:{name=Annette, tree=Redwood, color=Aquamarine, fruit=Pineapple}),(mapobsbin:{13456={lat=-25, long=-50}, 12345={lat=-85, long=-130}, 14567={lat=35, long=30}}))

Get String Elements by Mapkey, Rank, and Value

Aerospike provides MapOperations to read string mapkeys and values from the database.

The mapFishBin is a map containing string mapkey/value pairs associated with the fish, "Koi".

For more information on map operations, go here.

Get String by Mapkey

Aerospike API can be used to look up a value by mapkey. The client returns the specified value as the contents of the bin.

For the list of return type options, go here.

import com.aerospike.client.Operation;
import com.aerospike.client.Value;
import com.aerospike.client.cdt.MapOperation;
import com.aerospike.client.cdt.MapReturnType;

String mapKeyToFind = "color";

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record colorString = client.operate(null, key,
MapOperation.getByKey(mapFishBinName, Value.get(mapKeyToFind), MapReturnType.VALUE)
);

System.out.println("The string map: " + record.getValue(mapFishBinName));
System.out.println("The " + mapKeyToFind + " in the string map is: " + colorString.getValue(mapFishBinName));

Output:

The string map: {name=Annette, tree=Redwood, color=Aquamarine, fruit=Pineapple}
The color in the string map is: Aquamarine

Get Highest Rank String

Aerospike's API contains operations to look up a map element by rank.

For information on list ranking, go here.

Integer highestRank = -1;

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record highestRankString = client.operate(null, key,
MapOperation.getByRank(mapFishBinName, highestRank, MapReturnType.VALUE)
);

System.out.println("The string map: " + record.getValue(mapFishBinName));
System.out.println("The highest rank string is: " + highestRankString.getValue(mapFishBinName));

Output:

The string map: {name=Annette, tree=Redwood, color=Aquamarine, fruit=Pineapple}
The highest rank string is: Redwood

Get Mapkey By String Value

Aerospike provides operations to look up an element by value and return the mapkey.

String valueToFind = "Pineapple";

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record foundMapKey = client.operate(null, key,
MapOperation.getByValue(mapFishBinName, Value.get(valueToFind), MapReturnType.KEY)
);

System.out.println("The string map: " + record.getValue(mapFishBinName));
System.out.println("The mapkey associated with " + valueToFind + " is: " + foundMapKey.getValue(mapFishBinName));

Output:

The string map: {name=Annette, tree=Redwood, color=Aquamarine, fruit=Pineapple}
The mapkey associated with Pineapple is: [fruit]

Get Map Size and Integer Elements by Index and Key Range

Aerospike operations can read integers associated with fish observations.

The mapobsbin is a list of Latitude/Longitude pairs stored by the time of fish observation in seconds from the start of the experiment. The number of seconds, latitude, and longitude are all integers.

Get the Number of Observations in the Map

Aerospike API's size operation returns a count of the mapkeys in a map.

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record sizeString = client.operate(null, key,
MapOperation.size(mapObsBinName)
);

System.out.println("The Observation Map: " + record.getValue(mapObsBinName));
System.out.println("The number of Observations in the Map: "
+ sizeString.getValue(mapObsBinName));

Output:

The Observation Map: {13456={lat=-25, long=-50}, 12345={lat=-85, long=-130}, 14567={lat=35, long=30}}
The number of Observations in the Map: 3

Get The First Observation from the Map

Aerospike API operations can look up a value by index. In Aerospike, the index operation can get one or more map elements by key order. Aerospike allows indexing forward from the beginning of the map using zero-based numbering. Negative numbers index backwards from the end of a map.

In this example, the first element by index represents the first time the fish was observed. Because the key 12345 is before 13456 and 14567, the first element by index is 12345.

For examples of indexes, go here.

Integer firstIdx = 0;

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record firstObservation = client.operate(null, key,
MapOperation.getByIndex(mapObsBinName, firstIdx, MapReturnType.KEY_VALUE)
);

System.out.println("The Observation Map: " + record.getValue(mapObsBinName));
System.out.println("The First Observation: " + firstObservation.getValue(mapObsBinName));

Output:

The Observation Map: {13456={lat=-25, long=-50}, 12345={lat=-85, long=-130}, 14567={lat=35, long=30}}
The First Observation: [12345={lat=-85, long=-130}]

Get All Locations Observed Between 13,000 and 15,000 seconds.

Aerospike delivers values by mapkey range. Get the latitude and longitude pairs for all observations between 13,000 and 15,000 seconds.

Integer lowerBound = 13000;
Integer upperBound = 15000;

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record rangeObservations = client.operate(null, key,
MapOperation.getByKeyRange(mapObsBinName, Value.get(lowerBound), Value.get(upperBound),
MapReturnType.KEY_VALUE)
);

System.out.println("The Observation Map: " + record.getValue(mapObsBinName));
System.out.println("The Observations between 13000 and 15000 seconds: "
+ rangeObservations.getValue(mapObsBinName));

Output:

The Observation Map: {13456={lat=-25, long=-50}, 12345={lat=-85, long=-130}, 14567={lat=35, long=30}}
The Observations between 13000 and 15000 seconds: [13456={lat=-25, long=-50}, 14567={lat=35, long=30}]

UPDATING Maps on the Aerospike Server

Aerospike's MapOperations can also modify data in the Aerospike Database.

Update the Fish Bin in Aerospike

The Fish Bin contains metadata about the fish.

Create a MapPolicy Java Object for the Fish Bin

When modifying maps, Aerospike requires a MapPolicy that governs write protection and order. The default MapPolicy works for Fish Bin.

For more information on mappolicy, go here.

import com.aerospike.client.cdt.MapPolicy;

MapPolicy mapFishBinPolicy = new MapPolicy();

System.out.println("Created default MapPolicy for " + mapFishBinName + ".")

Output:

Created default MapPolicy for mapfishbin.

Change the Tree to Larch

When new data is put into a map, Aerospike returns the size of the resulting map.

String treeMapkeyName = "tree";
String newTree = "Larch";

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record sizeOfMapWithNewTree = client.operate(null, key,
MapOperation.put(mapFishBinPolicy, mapFishBinName, Value.get(treeMapkeyName),
Value.get(newTree))
);
Record mapWithNewTree = client.get(null, key);

System.out.println("Before: " + record.getValue(mapFishBinName));
System.out.println("The size after the operation: "
+ sizeOfMapWithNewTree.getValue(mapFishBinName));
System.out.println(" After: " + mapWithNewTree.getValue(mapFishBinName));

Output:

Before: {name=Annette, tree=Redwood, color=Aquamarine, fruit=Pineapple}
The size after the operation: 4
After: {name=Annette, tree=Larch, color=Aquamarine, fruit=Pineapple}

Remove the Fruit

When removing a mapkey:value pair, Aerospike client returns the removed data.

String fruitMapkeyName = "fruit";

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record valOfRemovedFruit = client.operate(null, key,
MapOperation.removeByKey(mapFishBinName, Value.get(fruitMapkeyName),
MapReturnType.KEY_VALUE)
);
Record mapWithoutFruit = client.get(null, key);

System.out.println("Before: " + record.getValue(mapFishBinName));
System.out.println("The removed mapkey/value pair: "
+ valOfRemovedFruit.getValue(mapFishBinName));
System.out.println("After removing the " + fruitMapkeyName + ": "
+ mapWithoutFruit.getValue(mapFishBinName));

Output:

Before: {name=Annette, tree=Larch, color=Aquamarine, fruit=Pineapple}
The removed mapkey/value pair: [fruit=Pineapple]
After removing the fruit: {color=Aquamarine, name=Annette, tree=Larch}

Add Bait

To be sure that other scientists can catch the fish, add the fish's preferred bait to the record.

String mapkeyForBait = "bait";
String valueForBait = "Mosquito Larva";

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record sizeOfRecordWithBait = client.operate(null, key,
MapOperation.put(mapFishBinPolicy, mapFishBinName, Value.get(mapkeyForBait),
Value.get(valueForBait))
);
Record recordWithBait = client.get(null, key);

System.out.println("Before: " + record.getValue(mapFishBinName));
System.out.println("After adding Bait: " + recordWithBait.getValue(mapFishBinName));

Output:

Before: {color=Aquamarine, name=Annette, tree=Larch}
After adding Bait: {name=Annette, tree=Larch, color=Aquamarine, bait=Mosquito Larva}

Put an Observation Counter in the Map

The experiment continued past the original end date. The new work requires keeping track of the total number of observations.

String mapkeyObsCount = "Count";
Integer numObservations = 3;

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record sizeOfRecordWithObsCounter =
client.operate(null, key, MapOperation.put(mapFishBinPolicy, mapFishBinName,
Value.get(mapkeyObsCount),
Value.get(numObservations))
);
Record recordWithObsCount = client.get(null, key);

System.out.println("Before: " + record.getValue(mapFishBinName));
System.out.println("After Adding the Counter: " + recordWithObsCount.getValue(mapFishBinName));

Output:

Before: {name=Annette, tree=Larch, color=Aquamarine, bait=Mosquito Larva}
After Adding the Counter: {name=Annette, tree=Larch, Count=3, color=Aquamarine, bait=Mosquito Larva}

Update the Observation Map

Aerospike client can update map elements, such as integers and sub-maps.

The experiment continued past the original end date. The new work requires the regular addition of new observations and keeping track of the total number of observations.

Create a MapPolicy Object for the Observations Bin

In this example, the Observations Map should be maintained as mapkey-sorted in Aerospike, but are put unordered into the database by default. When storing any map on SSD hardware, Key Ordered Maps hold a significant performance advantage over Unordered Maps, at a cost of 4 bytes of storage for metadata.

The MapPolicy contains two types of configurations, MapOrder and MapWriteFlags. The maporder determines the sort order of the map. The mapwriteflags determine write behaviors, such as if the operation should fail when a mapkey/value already exists.

For more information on maporder, go here.

For more information on mapwriteflags, go here.

import com.aerospike.client.cdt.MapOrder;
import com.aerospike.client.cdt.MapWriteFlags;

Record recordObsUnordered = client.get(null, key);
MapPolicy mapObsBinPolicy = new MapPolicy(MapOrder.KEY_ORDERED, MapWriteFlags.DEFAULT);
Record changeOrder =
client.operate(null, key, MapOperation.setMapPolicy(mapObsBinPolicy, mapObsBinName));
Record recordObsOrdered = client.get(null, key);

System.out.println("Before Sorting: " + recordObsUnordered.getValue(mapObsBinName));
System.out.println("Applied mapkey-ordered MapPolicy for " + mapObsBinName + ".");
System.out.println("After Sorting: " + recordObsOrdered.getValue(mapObsBinName));

Output:

Before Sorting: {13456={lat=-25, long=-50}, 12345={lat=-85, long=-130}, 14567={lat=35, long=30}}
Applied mapkey-ordered MapPolicy for mapobsbin.
After Sorting: {12345={lat=-85, long=-130}, 13456={lat=-25, long=-50}, 14567={lat=35, long=30}}

Add a new Observation

int newObsTimestamp = 15678;
int newObsLat = 80;
int newObsLong = 110;

HashMap <Integer, HashMap> mapNewObs = new HashMap <Integer, HashMap>();
HashMap <String, Integer> mapNewCoords = new HashMap <String, Integer>();
mapNewCoords.put("lat", newObsLat);
mapNewCoords.put("long", newObsLong);

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record sizeOfNewObs = client.operate(null, key,
MapOperation.put(mapObsBinPolicy, mapObsBinName, Value.get(newObsTimestamp), Value.get(mapNewCoords))
);
Record recordWithNewObs = client.get(null, key);

System.out.println("Before: " + record.getValue(mapObsBinName));
System.out.println("The Size After Adding the Observation: " + sizeOfNewObs.getValue(mapObsBinName));
System.out.println("After Adding the Observation: " + recordWithNewObs.getValue(mapObsBinName));

Output:

Before: {12345={lat=-85, long=-130}, 13456={lat=-25, long=-50}, 14567={lat=35, long=30}}
The Size After Adding the Observation: 4
After Adding the Observation: {12345={lat=-85, long=-130}, 13456={lat=-25, long=-50}, 14567={lat=35, long=30}, 15678={lat=80, long=110}}

Remove the Oldest Observation by Index

This study only maintains the three most recent observations.

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record oldObs = client.operate(null, key,
MapOperation.removeByIndex(mapObsBinName, firstIdx, MapReturnType.KEY_VALUE)
);
Record updatedRecord = client.get(null, key);

System.out.println("Before: " + record.getValue(mapObsBinName));
System.out.println("The Removed Observation: " + oldObs.getValue(mapObsBinName));
System.out.println("After Observation Removal: " + updatedRecord.getValue(mapObsBinName));

Output:

Before: {12345={lat=-85, long=-130}, 13456={lat=-25, long=-50}, 14567={lat=35, long=30}, 15678={lat=80, long=110}}
The Removed Observation: [12345={lat=-85, long=-130}]
After Observation Removal: {13456={lat=-25, long=-50}, 14567={lat=35, long=30}, 15678={lat=80, long=110}}

Increment the Observation Counter

When incrementing a map value, Aerospike returns the new value.

int incNum = 1;

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record obsCount = client.operate(null, key,
MapOperation.increment(mapFishBinPolicy, mapFishBinName, Value.get(mapkeyObsCount), Value.get(incNum))
);
Record updatedRecord = client.get(null, key);

System.out.println("Before: " + record.getValue(mapFishBinName));
System.out.println("The New Count: " + obsCount.getValue(mapFishBinName));
System.out.println("After Increment: " + updatedRecord.getValue(mapFishBinName));

Output:

Before: {name=Annette, tree=Larch, Count=3, color=Aquamarine, bait=Mosquito Larva}
The New Count: 4
After Increment: {name=Annette, tree=Larch, Count=4, color=Aquamarine, bait=Mosquito Larva}

Notebook Cleanup

Truncate the Set

Truncate the set from the Aerospike Database.

import com.aerospike.client.policy.InfoPolicy;
InfoPolicy infoPolicy = new InfoPolicy();

client.truncate(infoPolicy, mapNamespace, mapSet, null);
System.out.println("Set Truncated.");

Output:

Set Truncated.

Close the Client connections to Aerospike

client.close();
System.out.println("Server connection(s) closed.");

Output:

Server connection(s) closed.

Code Summary

Overview

Here is a collection of all of the non-Jupyter code from this tutorial.

  1. Import Java Libraries.
  2. Import Aerospike Client Libraries.
  3. Start the Aerospike Client.
  4. Create Test Data.
  5. Put Record into Aerospike.
  6. Get Data from Aerospike.
    1. Get the Record.
    2. Get String by MapKey and Highest Rank.
    3. Get MapKey by String.
    4. Get the Number of Observations and 1st Observation By Index.
    5. Get Observations by MapKey Range.
  7. Update the Record in Aerospike
    1. Change the Tree to a Larch
    2. Remove the Fruit and add Bait.
    3. Sort the Observation Map.
    4. Add an Observation Counter.
    5. Add a New Observation.
    6. Remove the Oldest Operation.
    7. Increment the Observation Counter.
  8. Truncate the Set.
  9. Close the Client Connections.
// Import Java Libraries.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


// Import Aerospike Client Libraries.

import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Key;
import com.aerospike.client.Bin;
import com.aerospike.client.policy.ClientPolicy;
import com.aerospike.client.Record;
import com.aerospike.client.Operation;
import com.aerospike.client.Value;
import com.aerospike.client.cdt.MapOperation;
import com.aerospike.client.cdt.MapReturnType;
import com.aerospike.client.cdt.MapPolicy;
import com.aerospike.client.cdt.MapOrder;
import com.aerospike.client.cdt.MapWriteFlags;
import com.aerospike.client.policy.InfoPolicy;


// Start the Aerospike Client.

AerospikeClient client = new AerospikeClient("localhost", 3000);
System.out.println("Initialized the client and connected to the cluster.");


// Create Test Data.

HashMap <String, String> mapFish = new HashMap <String, String>();
mapFish.put("name", "Annette");
mapFish.put("fruit", "Pineapple");
mapFish.put("color", "Aquamarine");
mapFish.put("tree", "Redwood");
System.out.println("Created Fish Map: " + mapFish);

HashMap <Integer, HashMap> mapObs = new HashMap <Integer, HashMap>();
HashMap <String, Integer> mapCoords0 = new HashMap <String, Integer>();
mapCoords0.put("lat", -85);
mapCoords0.put("long", -130);
HashMap <String, Integer> mapCoords1 = new HashMap <String, Integer>();
mapCoords1.put("lat", -25);
mapCoords1.put("long", -50);
HashMap <String, Integer> mapCoords2 = new HashMap <String, Integer>();
mapCoords2.put("lat", 35);
mapCoords2.put("long", 30);

mapObs.put(13456, mapCoords1);
mapObs.put(14567, mapCoords2);
mapObs.put(12345, mapCoords0);
System.out.println("Created Observations Map: " + mapObs);


// Put Record into Aerospike.

String mapSet = "mapset1";
String mapNamespace = "test";
String theKey = "koi";
String mapFishBin = "mapfishbin";
String mapObsBin = "mapobsbin";
ClientPolicy clientPolicy = new ClientPolicy();
InfoPolicy infoPolicy = new InfoPolicy();

Key key = new Key(mapNamespace, mapSet, theKey);
Bin bin1 = new Bin(mapFishBin, mapFish);
Bin bin2 = new Bin(mapObsBin, mapObs);
client.put(clientPolicy.writePolicyDefault, key, bin1, bin2);

System.out.println("Inserted Key: " + theKey + "\n " + mapFishBin + ": " + mapFish + "\n " +
mapObsBin + ": " + mapObs );
System.out.println();


// Get Data from Aerospike.
// 1. Get the Record.
// 2. Get String by MapKey and Highest Rank.
// 3. Get MapKey by String.
// 3. Get the Number of Observations and 1st Observation By Index.
// 4. Get Observations by MapKey Range.

String mapKeyToFind = "color";
Integer highestRank = -1;
String valueToFind = "Pineapple";
Integer firstIdx = 0;
Integer lowerBound = 13000;
Integer upperBound = 15000;

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record results = client.operate(null, key,
MapOperation.getByKey(mapFishBin, Value.get(mapKeyToFind), MapReturnType.VALUE),
MapOperation.getByRank(mapFishBin, highestRank, MapReturnType.VALUE),
MapOperation.getByValue(mapFishBin, Value.get(valueToFind), MapReturnType.KEY),
MapOperation.size(mapObsBin),
MapOperation.getByIndex(mapObsBin, firstIdx, MapReturnType.KEY_VALUE),
MapOperation.getByKeyRange(mapObsBin, Value.get(lowerBound), Value.get(upperBound), MapReturnType.KEY_VALUE)
);

List<?> resultsFish = results.getList(mapFishBin);
List<?> resultsObs = results.getList(mapObsBin);

System.out.println("Read the Full Record From Aerospike:" + record);
System.out.println("The " + mapKeyToFind + " in the string map is: " + resultsFish.get(0));
System.out.println("The highest rank string is: " + resultsFish.get(1));
System.out.println("The mapkey associated with " + valueToFind + " is: " + resultsFish.get(2));
System.out.println("The number of Observations in the Map: " + resultsObs.get(0));
System.out.println("The First Observation: " + resultsObs.get(1));
System.out.println("The Observations between 13000 and 15000 seconds: " + resultsObs.get(2));
System.out.println();


// 7. Update the Record in Aerospike
// 1. Change the Tree to a Larch
// 2. Remove the Fruit and add Bait.
// 3. Add an Observation Counter.
// 4. Sort the Observation Map.
// 5. Add a New Observation.
// 6. Remove the Oldest Operation.
// 7. Increment the Observation Counter.

MapPolicy mapFishBinPolicy = new MapPolicy();
MapPolicy mapObsBinPolicy = new MapPolicy(MapOrder.KEY_ORDERED, MapWriteFlags.DEFAULT);

String treeMapkeyName = "tree";
String newTree = "Larch";
String fruitMapkeyName = "fruit";
String mapkeyForBait = "bait";
String valueForBait = "Mosquito Larva";
String mapkeyObsCount = "Count";
Integer numObservations = 3;
int newObsTimestamp = 15678;
int newObsLat = 80;
int newObsLong = 110;
int incNum = 1;

HashMap <Integer, HashMap> mapNewObs = new HashMap <Integer, HashMap>();
HashMap <String, Integer> mapNewCoords = new HashMap <String, Integer>();
mapNewCoords.put("lat", newObsLat);
mapNewCoords.put("long", newObsLong);

Key key = new Key(mapNamespace, mapSet, theKey);
Record record = client.get(null, key);
Record updatingRecord = client.operate(null, key,
MapOperation.put(mapFishBinPolicy, mapFishBin, Value.get(treeMapkeyName),
Value.get(newTree)),
MapOperation.removeByKey(mapFishBin, Value.get(fruitMapkeyName),
MapReturnType.KEY_VALUE),
MapOperation.put(mapFishBinPolicy, mapFishBin, Value.get(mapkeyForBait),
Value.get(valueForBait)),
MapOperation.put(mapFishBinPolicy, mapFishBin, Value.get(mapkeyObsCount),
Value.get(numObservations)),
MapOperation.setMapPolicy(mapObsBinPolicy, mapObsBin),
MapOperation.put(mapObsBinPolicy, mapObsBin, Value.get(newObsTimestamp),
Value.get(mapNewCoords)),
MapOperation.removeByIndex(mapObsBin, firstIdx, MapReturnType.KEY_VALUE),
MapOperation.increment(mapFishBinPolicy, mapFishBin, Value.get(mapkeyObsCount),
Value.get(incNum))
);
Record finalRecord = client.get(null, key);

List<?> updateFish = updatingRecord.getList(mapFishBin);
List<?> updateObs = updatingRecord.getList(mapObsBin);

System.out.println("Changed " + treeMapkeyName + " to " + newTree + "; there are now " + updateFish.get(0) + " map items in " + mapFishBin);
System.out.println("Removed item " + updateFish.get(1));
System.out.println("Added item [" + mapkeyForBait + "=" + valueForBait + "]; there are now " + updateFish.get(2) + " map items in " + mapFishBin);
System.out.println("Added Observation Counter; there are now " + updateFish.get(3) + " map items in " + mapFishBin);
System.out.println("Sorted " + mapObsBin);
System.out.println("Added New Observation {" + newObsTimestamp + "=" + mapNewCoords + "}, there are now " + updateObs.get(1) + " map items in " + mapObsBin);
System.out.println("Removed Oldest Observation: " + updateObs.get(2));
System.out.println("Incremented Observation Counter to reflect " + updateFish.get(4) + "th observation");
System.out.println();
System.out.println("After Record Edits: " + finalRecord);


// Truncate the Set.

client.truncate(infoPolicy, mapNamespace, mapSet, null);
System.out.println("Set Truncated.");


// Close the Client Connections.

client.close();

Output:

Initialized the client and connected to the cluster.
Created Fish Map: {color=Aquamarine, fruit=Pineapple, name=Annette, tree=Redwood}
Created Observations Map: {13456={lat=-25, long=-50}, 14567={lat=35, long=30}, 12345={lat=-85, long=-130}}
Inserted Key: koi
mapfishbin: {color=Aquamarine, fruit=Pineapple, name=Annette, tree=Redwood}
mapobsbin: {13456={lat=-25, long=-50}, 14567={lat=35, long=30}, 12345={lat=-85, long=-130}}

Read the Full Record From Aerospike:(gen:1),(exp:359404037),(bins:(mapfishbin:{name=Annette, tree=Redwood, color=Aquamarine, fruit=Pineapple}),(mapobsbin:{13456={lat=-25, long=-50}, 12345={lat=-85, long=-130}, 14567={lat=35, long=30}}))
The color in the string map is: Aquamarine
The highest rank string is: Redwood
The mapkey associated with Pineapple is: [fruit]
The number of Observations in the Map: 3
The First Observation: [12345={lat=-85, long=-130}]
The Observations between 13000 and 15000 seconds: [13456={lat=-25, long=-50}, 14567={lat=35, long=30}]

Changed tree to Larch; there are now 4 map items in mapfishbin
Removed item [fruit=Pineapple]
Added item [bait=Mosquito Larva]; there are now 4 map items in mapfishbin
Added Observation Counter; there are now 5 map items in mapfishbin
Sorted mapobsbin
Added New Observation {15678={lat=80, long=110}}, there are now 4 map items in mapobsbin
Removed Oldest Observation: [12345={lat=-85, long=-130}]
Incremented Observation Counter to reflect 4th observation

After Record Edits: (gen:2),(exp:359404038),(bins:(mapfishbin:{name=Annette, tree=Larch, Count=4, color=Aquamarine, bait=Mosquito Larva}),(mapobsbin:{13456={lat=-25, long=-50}, 14567={lat=35, long=30}, 15678={lat=80, long=110}}))
Set Truncated.

Takeaway – Aerospike Does Maps

Aerospike and its Java Client are up to the task of working with your map data. Its API provides rich operations to read and update list data using index, mapkey, value, and rank. Not modeled in this tutorial, Aerospike map operation also supports nested lists and maps, by assigning CTX or contexts to operations.

For more information on contexts, go here. For examples of contexts, go here.

What's Next?

Next Steps

Have questions? Don't hesitate to reach out if you have additional questions about working with lists at https://discuss.aerospike.com/.

Want to check out other Java notebooks?

  1. Hello, World
  2. Reading and Updating Lists
  3. Modeling Using Lists
  4. Aerospike Query and UDF

Are you running this from Binder? Download the Aerospike Notebook Repo and work with Aerospike Database and Jupyter locally using a Docker container.

Additional Resources