Skip to content

Primary index queries

The digest of every record in the Aerospike database is indexed in the primary index (PI) of its namespace. This enables query access to all the records in a namespace, and with the assistance of a set index, fast query access to all the records in a set.

Foreground queries

Foreground queries are read-only. The examples below show bin projection, filter expressions, and a partitioned metadata-only count.

Insert data

The examples on this page use an ad-tech user profile set profiles in namespace test. Each record represents a user with an integer age, a string region, and a map segments keyed by segment ID. The map values are lists of [ttl_epoch, category, tier].

{
"uid": "user42",
"age": 28,
"region": "NA",
"segments": {
1001: [1704067200, "sports", "premium"],
1002: [1672531200, "travel", "standard"],
1003: [1711929600, "news", "basic"]
}
}

Enable the set index for the profiles set so queries get direct access to the records in the set, without needing to traverse the entire primary index:

Terminal window
Admin+> manage config namespace test set profiles param enable-index to true

Insert two sample records:

Key key1 = new Key("test", "profiles", "user42");
Map<Long, List<Object>> segs1 = new HashMap<>();
segs1.put(1001L, Arrays.asList(1704067200L, "sports", "premium"));
segs1.put(1002L, Arrays.asList(1672531200L, "travel", "standard"));
segs1.put(1003L, Arrays.asList(1711929600L, "news", "basic"));
client.put(null, key1,
new Bin("uid", "user42"),
new Bin("age", 28),
new Bin("region", "NA"),
new Bin("segments", segs1));
Key key2 = new Key("test", "profiles", "user99");
Map<Long, List<Object>> segs2 = new HashMap<>();
segs2.put(2001L, Arrays.asList(1711929600L, "finance", "premium"));
client.put(null, key2,
new Bin("uid", "user99"),
new Bin("age", 35),
new Bin("region", "EU"),
new Bin("segments", segs2));

Query all records in a set

Query the profiles set and project only the uid and region bins. Because the set index is enabled, the server does not need to traverse the primary index skipping records in other sets. The set index grants this query with direct access to the records of the profiles set.

Statement stmt = new Statement();
stmt.setNamespace("test");
stmt.setSetName("profiles");
stmt.setBinNames("uid", "region");
try (RecordSet rs = client.query(null, stmt)) {
while (rs.next()) {
Record rec = rs.getRecord();
System.out.printf("uid=%s region=%s%n",
rec.getString("uid"), rec.getString("region"));
}
}

Query with a filter expression

Filter records using a filter expression. The expression evaluates metadata predicates first for fast-path elimination, then bin predicates. This example selects profiles updated within the last five minutes (since_update), in the "NA" region, where age >= 21, and uses operation projection to return the number of segments as a computed bin.

long fiveMinMs = 5L * 60 * 1000;
Statement stmt = new Statement();
stmt.setNamespace("test");
stmt.setSetName("profiles");
stmt.setOperations(
ExpOperation.read("active_segments",
Exp.build(MapExp.size(Exp.mapBin("segments"))),
ExpReadFlags.DEFAULT));
QueryPolicy queryPolicy = new QueryPolicy();
queryPolicy.filterExp = Exp.build(
Exp.and(
Exp.lt(Exp.sinceUpdate(), Exp.val(fiveMinMs)),
Exp.eq(Exp.stringBin("region"), Exp.val("NA")),
Exp.ge(Exp.intBin("age"), Exp.val(21))));
try (RecordSet rs = client.query(queryPolicy, stmt)) {
while (rs.next()) {
Record rec = rs.getRecord();
System.out.printf("uid=%s active_segments=%d%n",
rec.getString("uid"), rec.getLong("active_segments"));
}
}

Query a single partition

Query partition 1000 for "NA" profiles with includeBinData = false to count records without transferring bin data. This pattern is a building block for parallel processing across multiple clients, where each client handles a range of partitions.

Statement stmt = new Statement();
stmt.setNamespace("test");
stmt.setSetName("profiles");
QueryPolicy qp = new QueryPolicy();
qp.includeBinData = false;
qp.filterExp = Exp.build(
Exp.eq(Exp.stringBin("region"), Exp.val("NA")));
int count = 0;
try (RecordSet rs = client.queryPartitions(qp, stmt,
PartitionFilter.id(1000))) {
while (rs.next()) {
count++;
}
}
System.out.printf("Partition 1000 NA count: %d%n", count);

Background queries

Background queries modify records in place on the server. The example below uses a background ops query with a map operation to remove expired ad-tech segments.

Remove expired segments

Remove ad-tech segments whose ttl_epoch has passed. The segments map values are lists [ttl_epoch, category, tier]. Because lists compare element-by-element, removeByValueRange(begin=null, end=[cutoff_ts]) removes all entries whose value list starts with an expired timestamp. This operation is idempotent — re-running it removes nothing if no expired segments remain.

long cutoffTs = 1704067200L; // 2024-01-01T00:00:00Z
Statement stmt = new Statement();
stmt.setNamespace("test");
stmt.setSetName("profiles");
QueryPolicy queryPolicy = new QueryPolicy();
queryPolicy.filterExp = Exp.build(
Exp.gt(MapExp.size(Exp.mapBin("segments")), Exp.val(0)));
ExecuteTask task = client.execute(null, stmt,
MapOperation.removeByValueRange("segments",
null, Value.get(Arrays.asList(cutoffTs)),
MapReturnType.NONE));
task.waitTillComplete();
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?