Skip to main content

Apply a UDF


Single record

Use the Aerospike C client API aerospike_key_apply() to apply a Record UDF against a record in the database.

Before using aerospike_key_apply(), the UDF module that contains the function must register with the Aerospike server. See Register UDF.

This example is an excerpt from examples/basic_examples/get in the Aerospike C client package.

To continue, the client must have an established the cluster connection.

Defining the UDF

To define bin_transform in the basice_udf module:

function bin_transform(record, bin_name, x, y)
record[bin_name] = (record[bin_name] * x) + y
aerospike:update(record)
return record[bin_name]
end

This simple arithmetic UDF has three arguments: bin_name, x, and y. It updates the value in the specified bin after performing the arithmetic operation, and returns the resulting bin value.

Initializing a Key

The application identifies records in the database using a key. In this example, the key value is the string test-key, stored in namespace test within set test-set. key can be other data types such as integer or blob.

as_key key;
as_key_init_str(&key, "test", "test-set", "test-key");

Passing Parameters to the UDF

The bin_transform UDF requires a string parameter and two integer parameters, so first, populate an argument list:

as_arraylist args;
as_arraylist_inita(&args, 3);
as_arraylist_append_str(&args, "test-bin-2");
as_arraylist_append_int64(&args, 4);
as_arraylist_append_int64(&args, 400);

Applying a UDF on a Record

To call the UDF on the record in the database using key:

as_val * result = NULL;
if (aerospike_key_apply(&as, &err, NULL, &key, "mymodule", "add",
(as_list *) &args, &result) != AEROSPIKE_OK) {
fprintf(stderr, "err(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);
}

The value from the bin_transform() UDF returns in the result parameter:

  • If key does not exist, AEROSPIKE_ERR_RECORD_NOT_FOUND returns.
  • If the UDF is found or if there is a run-time error executing the UDF, details on the error returns in as_error.

Cleaning Up Resources

Use as_val_destroy() to release the UDF and associated resources:

as_val_destroy(&result);

Use as_arraylist_destroy() to clean up the arguments list:

as_arraylist_destroy(&args);
note

The key does not require an explicit destroy call since it and its members are created on the stack.

Transform

Use the Aerospike C client to scan the records in the database and transform each record using UDFs.

In SQL, a transformation on all rows in a table is performed as an UPDATE statement without predicates:

UPDATE test.demo
SET a = 1, b = 2, c = 3

The Aerospike C client provides similar capabilities, but goes beyond what SQL provides by allowing you to transform records by applying a Record UDF on each record. These Record UDFs are applied to individual records, can have arguments, and can read and write the bins of a record and perform calculations.

Scanning and applying a Record UDF on each record can only be performed in the background; that is, the client sends the scan request to the database and does not wait for results. The scan is queued and runs on the database, and no results return to the client. The client has a scan ID, so can check status.

Defining the Scan

For scans that transform each record, define a UDF to execute on each record and execute the scan using the background scan operation, aerospike_scan_background().

You build the scan just like in Scan Records, but use as_scan_apply_each():

as_scan scan;
as_scan_init(&scan, "test", "demo");

as_scan_apply_each(&scan, "mymodule", "mytransform", NULL);

Operation sequence:

  1. Create an as_scan on the stack (line 1).
  2. Initialize the scan to scan the Namespace test within set demo (line 2).
  3. Add a Record UDF to apply to each record scanned.
note

The UDF mytransform() is defined in the mymodule UDF module. The UDF in this example is provided arguments.

Defining the Record UDF

The example uses the mytransform Record UDF defined in mymodule and mimics the SQL statement above.

function mytransform(rec)
rec['a'] = 1
rec['b'] = 2
rec['c'] = 3
aerospike:update(rec)
end

This more elaborate example increments a, calculates b based on a, and calculates c based on the values of a and b:

function mytransform(rec)
rec['a'] = rec['a'] + 1
rec['b'] = rec['a'] * 2
rec['c'] = rec['a'] + rec['b']
aerospike:update(rec)
end

Executing the Scan

To execute the scan using aerospike_scan_background():

uint64_t scan_id = 0;
if (aerospike_scan_background(&as, &err, NULL, &scan, &scan_id) != AEROSPIKE_OK) {
fprintf(stderr, "error(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);
}

scan_id queries the scan status. If scan_id = 0 (zero), the scan operation generates and sets the value of scan_id.

Checking Scan Status

To query scan status using aerospike_scan_info():

as_scan_info scan_info;
if (aerospike_scan_info(&as, &err, NULL, scan_id, &scan_info) != AEROSPIKE_OK) {
fprintf(stderr, "error(%d) %s at [%s:%d]\n", err.code, err.message, err.file, err.line);
}

This populates as_scan_info with the scan status.