Skip to content

Operation expressions code example

For the complete documentation index see: llms.txt

All documentation pages available in markdown.

This page is for developers using Aerospike client libraries. Complete Setup first. After working through this example, you can compute grades and a report server-side with operation expressions.

In the following example, these new capabilities that were not possible earlier are illustrated:

  • expressions involving zero or more bins to write a bin
  • named “computed bins” that return the value of a specified expression involving zero or more bins
  • conditional evaluation of expression
  • use of variables in an expression

The code has the following steps:

  1. The test data is populated with three randomly generated test scores ranging from 50 to 99 for student ids 1-20.
  2. The data is updated by writing two additional bins: “class” which represents the teacher’s input (0-9) based on class participation, and “grade” which is computed by adding “classwork” to average of test scores, and using this formula to compute the grade: scores below 70 receive C; scores from 70 through 85 receive B; scores above 85 receive A.
  3. A report is then generated for the id, grade, total score, and min/max/average of test scores.

The following snippets assume the client setup from Setup.

import com.aerospike.client.exp.Expression;
import com.aerospike.client.exp.ExpOperation;
import com.aerospike.client.exp.ExpReadFlags;
import com.aerospike.client.exp.ExpWriteFlags;
// start with a clean state
truncateTestData();
// 1. The test data is populated with three randomly generated test scores ranging from 50 to 100
// for student ids 1-20.
Random rand = new Random(1);
final int SCORE_RANGE = 50;
WritePolicy wpolicy = copyWritePolicyDefault();
wpolicy.sendKey = true;
for (int i = 1; i <= 20; i++) {
Key key = new Key(NAMESPACE, SET_NAME, "id-"+i);
Bin id = new Bin("id", i);
List<Integer> testScores = List.of(
50 + rand.nextInt(SCORE_RANGE),
50 + rand.nextInt(SCORE_RANGE),
50 + rand.nextInt(SCORE_RANGE));
Bin tests = new Bin("tests", testScores);
client.put(wpolicy, key, id, tests);
}
System.out.println("Test data populated.");
// 2. The data is updated by writing two additional bins: "class" which represents the teacher's input (0-10)
// based on class participation, and "grade" which is computed by adding "classwork" to average of test scores,
// and using this formula: 50-70 -> C, 65==70-85 -> B, 85+ -> A.
// define the expressions for average test score, total score, grade, min test score, and max test score
Exp avgScoreExp = Exp.let(
Exp.def("testsBin", Exp.listBin("tests")),
Exp.div(
Exp.add(
ListExp.getByIndex(ListReturnType.VALUE, Exp.Type.INT, Exp.val(0), Exp.var("testsBin")),
ListExp.getByIndex(ListReturnType.VALUE, Exp.Type.INT, Exp.val(1), Exp.var("testsBin")),
ListExp.getByIndex(ListReturnType.VALUE, Exp.Type.INT, Exp.val(2), Exp.var("testsBin"))),
Exp.val(3)));
Expression avgScoreExpression = Exp.build(avgScoreExp);
Exp totalScoreExp = Exp.add(avgScoreExp, Exp.intBin("class"));
Expression totalScoreExpression = Exp.build(totalScoreExp);
Exp gradeExp = Exp.let(
Exp.def("total", totalScoreExp),
Exp.cond(
Exp.lt(Exp.var("total"), Exp.val(70)), Exp.val("C"),
Exp.le(Exp.var("total"), Exp.val(85)), Exp.val("B"),
Exp.gt(Exp.var("total"), Exp.val(85)), Exp.val("A"),
Exp.unknown()));
Expression gradeExpression = Exp.build(gradeExp);
Exp minExp = ListExp.getByRank(ListReturnType.VALUE, Exp.Type.INT, Exp.val(0), Exp.listBin("tests")); // min
Expression minExpression = Exp.build(minExp);
Exp maxExp = ListExp.getByRank(ListReturnType.VALUE, Exp.Type.INT, Exp.val(-1), Exp.listBin("tests")); // max
Expression maxExpression = Exp.build(maxExp);
// update class bin with a random 0-9, and the grade bin using gradeExpOp
for (int i = 1; i <= 20; i++) {
Key key = new Key(NAMESPACE, SET_NAME, "id-"+i);
int classwork = rand.nextInt(SCORE_RANGE)/5;
Bin classBin = new Bin(new String("class"), classwork);
// write gradeExp to the bin "grade"
Operation gradeExpOp = ExpOperation.write("grade",
gradeExpression,
ExpWriteFlags.DEFAULT);
client.operate(wpolicy, key, Operation.put(classBin), gradeExpOp);
}
System.out.println("Updated class participation and computed grades.");;
// run a report for all students with id, grade, total, min, max, avg
// using total, min, max, and avg expressions defined above
Operation ops[] = { Operation.get("id"),
Operation.get("grade"),
ExpOperation.read("total", totalScoreExpression, ExpReadFlags.DEFAULT),
ExpOperation.read("testMin", minExpression, ExpReadFlags.DEFAULT),
ExpOperation.read("testMax", maxExpression, ExpReadFlags.DEFAULT),
ExpOperation.read("testAvg", avgScoreExpression, ExpReadFlags.DEFAULT)
};
for (int i = 1; i <= 20; i++) {
Key key = new Key(NAMESPACE, SET_NAME, "id-"+i);
Record record = client.operate(null, key, ops);
System.out.format("key=%s bins=%s\n", key.userKey, record.bins);
}

Output:

Test data populated.
Updated class participation and computed grades.
key=id-1 bins={id=1, grade=A, total=90, testMin=85, testMax=97, testAvg=90}
key=id-2 bins={id=2, grade=C, total=57, testMin=54, testMax=63, testAvg=57}
key=id-3 bins={id=3, grade=B, total=79, testMin=56, testMax=84, testAvg=72}
key=id-4 bins={id=4, grade=A, total=86, testMin=69, testMax=98, testAvg=80}
key=id-5 bins={id=5, grade=B, total=73, testMin=62, testMax=67, testAvg=64}
key=id-6 bins={id=6, grade=A, total=88, testMin=62, testMax=92, testAvg=79}
key=id-7 bins={id=7, grade=A, total=88, testMin=76, testMax=96, testAvg=87}
key=id-8 bins={id=8, grade=B, total=82, testMin=60, testMax=99, testAvg=80}
key=id-9 bins={id=9, grade=B, total=80, testMin=59, testMax=98, testAvg=77}
key=id-10 bins={id=10, grade=C, total=69, testMin=52, testMax=87, testAvg=64}
key=id-11 bins={id=11, grade=C, total=54, testMin=50, testMax=55, testAvg=53}
key=id-12 bins={id=12, grade=C, total=61, testMin=55, testMax=63, testAvg=58}
key=id-13 bins={id=13, grade=B, total=81, testMin=70, testMax=89, testAvg=78}
key=id-14 bins={id=14, grade=B, total=70, testMin=60, testMax=84, testAvg=69}
key=id-15 bins={id=15, grade=B, total=84, testMin=77, testMax=87, testAvg=80}
key=id-16 bins={id=16, grade=A, total=90, testMin=70, testMax=92, testAvg=83}
key=id-17 bins={id=17, grade=C, total=61, testMin=50, testMax=57, testAvg=53}
key=id-18 bins={id=18, grade=B, total=77, testMin=55, testMax=83, testAvg=71}
key=id-19 bins={id=19, grade=B, total=79, testMin=58, testMax=95, testAvg=79}
key=id-20 bins={id=20, grade=B, total=81, testMin=54, testMax=84, testAvg=72}

You may view the state of the database and ensure correctness of the output by running the following command in the terminal tab:

aql -c "select * from test.expressions"

AEL text equivalent

The Developer SDK expresses the same computation as AEL text, using let ... then to bind the three test scores, and when in place of cond for the grade lookup. The average score (testAvg) is:

let (t0 = $.tests.[0], t1 = $.tests.[1], t2 = $.tests.[2])
then ((${t0} + ${t1} + ${t2}) / 3)

The total score (testAvg plus class) extends that same binding:

let (t0 = $.tests.[0], t1 = $.tests.[1], t2 = $.tests.[2],
avg = ((${t0} + ${t1} + ${t2}) / 3), classBin = $.class)
then (${avg} + ${classBin})

The “grade” write expression reuses total inside a when:

let (t0 = $.tests.[0], t1 = $.tests.[1], t2 = $.tests.[2],
avg = ((${t0} + ${t1} + ${t2}) / 3), classBin = $.class,
total = ${avg} + ${classBin})
then (
when (
${total} < 70 => 'C',
${total} <= 85 => 'B',
${total} > 85 => 'A',
default => unknown))

The min and max test scores use the same rank selectors as ListExp.getByRank above: $.tests.[#0]:INT (smallest) and $.tests.[#-1]:INT (largest). The :INT pin is required on the tail of a selectFrom target because a read has to resolve its own result type, and an unpinned rank selector gives the compiler nothing to infer from. Writing the grade and reading the report with the Developer SDK:

DataSet students = DataSet.of(NAMESPACE, SET_NAME);
final String TESTS = "t0 = $.tests.[0], t1 = $.tests.[1], t2 = $.tests.[2]";
final String SUM = "(${t0} + ${t1} + ${t2})";
final String AVG = "(" + SUM + " / 3)";
// Individual expressions (each is a complete AEL string)
final String avg = "let (" + TESTS + ") then " + AVG;
final String total =
"let (" + TESTS + ", avg = " + AVG + ", classBin = $.class) "
+ "then (${avg} + ${classBin})";
final String grade =
"let (" + TESTS + ", avg = " + AVG + ", classBin = $.class, total = ${avg} + ${classBin}) "
+ "then (when (${total} < 70 => 'C', ${total} <= 85 => 'B', ${total} > 85 => 'A', default => unknown))";
session.upsert(students.id(String.valueOf(i)))
.bin("grade").upsertFrom(grade)
.execute();
Record record = session.query(students.id(String.valueOf(i)))
.bin("total").selectFrom(total)
.bin("testMin").selectFrom("$.tests.[#0]:INT")
.bin("testMax").selectFrom("$.tests.[#-1]:INT")
.bin("testAvg").selectFrom(avg)
.execute()
.getFirstRecord();

Next

Continue to Usage notes and next steps.