Secondary index queries
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
Jump to the Code block for a combined complete example.
Basic SI queries can employ the following index filters:
- Equality comparison against string or numeric indexes.
- Range comparison against numeric indexes. Range result sets are inclusive of lower and upper limits.
- Point-In-Region or Region-Contain-Point comparisons against geo indexes.
You can use record filtering with expressions with secondary index queries.
Policies
See Basic Queries for query policy information.
Setup
The following examples will use the setup and record structure below to illustrate secondary index queries in an Aerospike database.
using Aerospike.Client;using System;using System.Collections;
// Define host configurationHost config = new Host("127.0.0.1", 3000);// Establishes a connection to the serverAerospikeClient client = new AerospikeClient(null, config);The record structure:
Occurred: IntegerReported: IntegerPosted: IntegerReport: Map{ shape: List, summary: String, city: String, state: String, duration: String}Location: GeoJSONCreate an index
You should plan and schedule index creation/removal on production systems as described in Secondary Index Capacity Planning. An index consumes RAM for every index entry. Background index creation/removal can take a substantial amount of resources. It is important to carefully plan and schedule index creation/removal on production systems.
The following command uses the Aerospike Admin tool (asadm) to create an integer index on the sandbox namespace, ufodata set, and occurred bin. This is the recommended way to create a secondary index.
asadm -e 'enable; manage sindex create numeric occurred_idx ns sandbox set ufodata bin occurred'The Aerospike Client API can be used to create a secondary index as well. The following example creates the same index as the example above.
// Create index taskIndexTask task = client.CreateIndex(null, //policy "sandbox", // namespace "ufodata", // set name "occurred_idx", // index name "occurred", // bin name IndexType.NUMERIC //index type);
// Wait for the task to completetask.Wait();In this example, the IndexType is NUMERIC. Aerospike supports index types NUMERIC,STRING, GEO2DSPHERE, and BLOB as of server 7.0.
Index build timeouts and reruns
Index creation is asynchronous. CreateIndex() returns as soon as the server accepts the request, and IndexTask.Wait() polls until the build finishes. The policy passed to CreateIndex() supplies the socket timeout used for the create command, for each status poll, and as the overall deadline for Wait(). Passing null, as in the example above, falls back to the client’s default write policy, whose 30-second socket timeout is fine for a small set but often too short for a large one.
Two outcomes deserve their own handling:
-
Wait()timed out.This means the client stopped polling, not that the build failed. The server keeps building the index, so reporting it as “create index failed” is misleading, and retrying the create will only produce the error below.
-
The index already exists.
Rerunning a create script throws
AerospikeExceptionwithResultCode.INDEX_ALREADY_EXISTS(server code 200). Catching it is what makes the script idempotent, and it also covers another client creating the same index concurrently.
Policy indexPolicy = new(){ // Bounds the create command, each status poll, and the Wait() deadline. socketTimeout = 60000};
try{ IndexTask task = client.CreateIndex( indexPolicy, ns, SetName, IndexName, BinName, IndexType.NUMERIC);
task.Wait(); Console.WriteLine($"Created index {IndexName}");}catch (AerospikeException.Timeout ae){ // Wait() stopped polling. The server keeps building the index. Console.Error.WriteLine($"Still waiting on index {IndexName}: {ae.Message}"); throw;}catch (AerospikeException ae) when (ae.Result == ResultCode.INDEX_ALREADY_EXISTS){ Console.WriteLine($"Index {IndexName} already exists");}Remove an index
The following command uses the Aerospike Admin (asadm) to remove the index created above.
asadm -e 'enable; manage sindex delete occurred_idx ns sandbox set ufodata'The Aerospike Client API can be used to remove a secondary index as well. The following example removes the same index as the example above.
// Create index taskIndexTask task = client.DropIndex(null, //policy "sandbox", // namespace "ufodata", // set name "occurred_idx", // index name);
// Wait for the task to completetask.Wait();Dropping an index that is not there throws AerospikeException with ResultCode.INDEX_NOTFOUND, so teardown code benefits from the same treatment as creation.
try{ client.DropIndex(indexPolicy, ns, SetName, IndexName).Wait(); Console.WriteLine($"Dropped index {IndexName}");}catch (AerospikeException ae) when (ae.Result == ResultCode.INDEX_NOTFOUND){ Console.WriteLine($"Index {IndexName} does not exist");}Query an index
The following example queries the sandbox namespace and ufodata set name, with an inclusive range filter on the occurred
bin, returning records with a bin value between 20210101 and 20211231.
// Create statementStatement stmt = new Statement();
// Set namespace and set namestmt.SetNamespace("sandbox");stmt.SetSetName("ufodata");
// Create index filterstmt.SetFilter(Filter.Range("occurred", 20210101, 20211231));
// Execute the queryRecordSet recordSet = client.Query(null, stmt);
// Get the resultstry{ while(recordSet.Next()) { Key key = recordSet.Key; Record record = recordSet.Record; // Do something Console.WriteLine("Key: {0} | Record: {1}\\n", key.userKey, record.ToString().Split("bins:")[1]); }}finally{ recordSet.Close();}
// Close the connection to the serverclient.Close();Query an index with a Filter Expression
The following example will use the geo string defined in the expandable section below.
View the language specific data creation
// Create geo regionstring region = "{ " +" \\"type\\": \\"Polygon\\", " +" \\"coordinates\\": [ " +" [ " +" [-109.061279296875, 36.97622678464096], " +" [-102.01904296874999, 36.97622678464096], " +" [-102.01904296874999, 41.0130657870063], " +" [-109.061279296875, 41.0130657870063], " +" [-109.061279296875, 36.97622678464096] " +" ] " +" ] " +"}";This example queries the same namespace and set name, while using the same index filter as the example above, but adds a Filter Expression to the
query policy to only return records with a location bin value within the geo region specified above.
// Create new query policyQueryPolicy queryPolicy = new QueryPolicy();queryPolicy.filterExp = Exp.Build( Exp.GeoCompare(Exp.GeoBin("location"), Exp.Geo(region)));
// Create statementStatement stmt = new Statement();
// Set namespace and set namestmt.SetNamespace("sandbox");stmt.SetSetName("ufodata");
// Create index filterstmt.SetFilter(Filter.Range("occurred", 20210101, 20211231));
// Execute the queryRecordSet recordSet = client.Query(queryPolicy, stmt);
// Get the resultstry{ while(recordSet.Next()) { Key key = recordSet.Key; Record record = recordSet.Record; // Do something Console.WriteLine("Key: {0} | Record: {1}\\n", key.userKey, record.ToString().Split("bins:")[1]); }}finally{ recordSet.Close();}
// Close the connection to the serverclient.Close();Query an index then create a batch operation on the returned keys
Currently, Transaction and CDT operations are not available for basic queries. The following example shows how a basic query and a batch operation can be combined to use transaction operations on the results.
This example queries the same namespace and set name, while using the same index filter, geo region, and Filter Expression as above, but
only returns record metadata, which is then used to create a batch operation to return only the city and state from the report map.
// Create new query policyQueryPolicy queryPolicy = new QueryPolicy();queryPolicy.filterExp = Exp.Build( // region defined in expandable section above Exp.GeoCompare(Exp.GeoBin("location"), Exp.Geo(region)));// do not include record binsqueryPolicy.includeBinData = false;
// Create statementStatement stmt = new Statement();
// Set namespace and set namestmt.SetNamespace("sandbox");stmt.SetSetName("ufodata");
// Create index filterstmt.SetFilter(Filter.Range("occurred", 20210101, 20211231));
// Create keys listList<Key> keysList = new List<Key>();
// Execute the queryRecordSet recordSet = client.Query(queryPolicy, stmt);
// Get the resultstry{ while(recordSet.Next()) { keysList.Add(recordSet.Key); }}finally{ recordSet.Close();}
// Convert list to arrayKey[] keys = keysList.ToArray();
// Create map key liststring[] mapKeys = {"city", "state"};
// Get 'city' and 'state' from report map for each recordBatchResults batchResult = client.Operate(null, null, keys, MapOperation.GetByKeyList("report", mapKeys, MapReturnType.VALUE));
// Access the recordsforeach (BatchRecord batchRecord in batchResult.records){ Key key = batchRecord.key; Record record = batchRecord.record; if(record != null) { // Do something Console.WriteLine("Key: {0} | Record: {1}\\n", key.userKey, record.ToString().Split("bins:")[1]); }}
// Close the connection to the serverclient.Close();Pagination
See Pagination for more information.
Code block
Expand this section for a single code block to execute a basic SI query
using Aerospike.Client;using System;using System.Collections;
// Define host configurationHost config = new Host("127.0.0.1", 3000);// Establishes a connection to the serverAerospikeClient client = new AerospikeClient(null, config);
string region = "{ " +" \\"type\\": \\"Polygon\\", " +" \\"coordinates\\": [ " +" [ " +" [-109.061279296875, 36.97622678464096], " +" [-102.01904296874999, 36.97622678464096], " +" [-102.01904296874999, 41.0130657870063], " +" [-109.061279296875, 41.0130657870063], " +" [-109.061279296875, 36.97622678464096] " +" ] " +" ] " +"}";
QueryPolicy queryPolicy = new QueryPolicy();queryPolicy.filterExp = Exp.Build( Exp.GeoCompare(Exp.GeoBin("location"), Exp.Geo(region)));
// Create statementStatement stmt = new Statement();
// Set namespace and set namestmt.SetNamespace("sandbox");stmt.SetSetName("ufodata");
// Create index filterstmt.SetFilter(Filter.Range("occurred", 20210101, 20211231));
// Execute the queryRecordSet recordSet = client.Query(queryPolicy, stmt);
// Get the resultstry{ while(recordSet.Next()) { Key key = recordSet.Key; Record record = recordSet.Record; // Do something Console.WriteLine("Key: {0} | Record: {1}\\n", key.userKey, record.ToString().Split("bins:")[1]); }}finally{ recordSet.Close();}
// Close the connection to the serverclient.Close();