A cursor is a special type of ResultSet processor. It wraps the ResultSet iterable and allows to setup cursor function handlers.
Use of forEach method is mandatory. If forEach() is not called, the cursor is not iterated and none of the grouping configuration methods defined will work.
Variables defined in one group can be seen in other group definition. Just take care about variable creation workflow as if you try to use a variable that is not still created at runtime, an error exception will be throw.
1 Simple cursor
A simple cursor may contain a before all and after all function. Finally you iterate the cursor by using forEach.
<script> let rs = Ax.db.executeQuery(` SELECT systables.tabid, colno, tabname, colname FROM systables, syscolumns WHERE systables.tabid = syscolumns.tabid AND systables.tabid < 4 and colno < 5 ORDER BY systables.tabid, colno `) rs.cursor() .beforeAll(row => { console.log("before all:" + row); }) .afterAll(row => { console.log("after all:" + row); }) // Iterate using JavaScript built in forEach .forEach(row => { console.log(" for each " + row); }); </script>
2 Simple group cursor
A common use of cursors is to perform grouping. The next example groups by column tabid.
<script> let rs = Ax.db.executeQuery(` SELECT systables.tabid, colno, tabname, colname FROM systables, syscolumns WHERE systables.tabid = syscolumns.tabid AND systables.tabid < 4 and colno < 5 ORDER BY systables.tabid, colno `) rs.cursor() .beforeAll(row => { console.log("before all:" + row); }) .afterAll(row => { console.log("after all:" + row); }) .group("tabid") .before(row => { console.log(" before group next: " + row); }) .after(row => { console.log(" after group prev:" + row); }) // Iterate using JavaScript built in forEach .forEach(row => { console.log(" for each " + row); }); </script>
before all:{tabid=1, tabname=systables, colname=tabname, colno=1}
beforeGroup next: {tabid=1, tabname=systables, colname=tabname, colno=1, __index__=1}
for each {tabid=1, tabname=systables, colname=tabname, colno=1, __index__=1}
for each {tabid=1, tabname=systables, colname=owner, colno=2, __index__=2}
for each {tabid=1, tabname=systables, colname=partnum, colno=3, __index__=3}
for each {tabid=1, tabname=systables, colname=tabid, colno=4, __index__=4}
after group prev:{tabid=1, tabname=systables, colname=tabid, colno=4, __index__=4}
beforeGroup next: {tabid=2, tabname=syscolumns, colname=colname, colno=1, __index__=1}
for each {tabid=2, tabname=syscolumns, colname=colname, colno=1, __index__=1}
for each {tabid=2, tabname=syscolumns, colname=tabid, colno=2, __index__=2}
for each {tabid=2, tabname=syscolumns, colname=colno, colno=3, __index__=3}
for each {tabid=2, tabname=syscolumns, colname=coltype, colno=4, __index__=4}
after group prev:{tabid=2, tabname=syscolumns, colname=coltype, colno=4, __index__=4}
beforeGroup next: {tabid=3, tabname=sysindices, colname=idxname, colno=1, __index__=1}
for each {tabid=3, tabname=sysindices, colname=idxname, colno=1, __index__=1}
for each {tabid=3, tabname=sysindices, colname=owner, colno=2, __index__=2}
for each {tabid=3, tabname=sysindices, colname=tabid, colno=3, __index__=3}
for each {tabid=3, tabname=sysindices, colname=idxtype, colno=4, __index__=4}
after group prev:{tabid=3, tabname=sysindices, colname=idxtype, colno=4, __index__=4}
after all:{tabid=3, tabname=sysindices, colname=idxtype, colno=4, __index__=4}
Notice that you should place forEach
at the end after all functions have been declared.
2.1 The row index
Each row contains a virtual column __index__
. This column contatins whe
row index in the group starting at 1 for first row in group. The row index can be
useful to break a cursor after some number of rows in group.
3 Complex group cursor
Group braks for multiple columns in resultset can be configured by introducing the group column list separated by commas. The next example show multicolumn groups.
<script> var rs = new Ax.rs.Reader().memory(options => { options.setColumnNames([ "group1", "group2", "number" ]); options.setColumnTypes([ Ax.sql.Types.INTEGER, Ax.sql.Types.INTEGER, Ax.sql.Types.INTEGER ]); }); for (var idx = 1; idx < 20; idx++) rs.rows().add([idx / 4, idx / 2, idx]); m_impacu = 0; rs.cursor() .group("group1") .before(row => { console.log("***** START GROUP " + row["group1"] + " *****"); console.log("*************************"); }) .group("group1,group2") .before(row => { m_impacu = 0; }) .after(row => { console.log("===== ACUMULATED NUMBER " + m_impacu + " ====="); }) .forEach(row => { //console.log(" for each " + row); console.log(row); m_impacu += row["number"]; }); return rs; </script>
4 Breaking a cursor
There's no built-in ability to break in forEach
. To interrupt execution you would have to throw an exception of some sort.
var BreakException = {}; try { [1, 2, 3].forEach(function(el) { console.log(el); if (el === 2) throw BreakException; }); } catch (e) { if (e !== BreakException) throw e; }
An optional and may be more elegant way is to avoid forEach
and use a common for loop that admits break.
<script> let rs = Ax.db.executeQuery(` SELECT systables.tabid, colno, tabname, colname FROM systables, syscolumns WHERE systables.tabid = syscolumns.tabid AND systables.tabid < 4 and colno < 5 ORDER BY systables.tabid, colno `) var cursor = rs.cursor() .beforeAll(row => { console.log("before all:" + row); }) .afterAll(row => { console.log("after all:" + row); }) .group("tabid") .before(row => { console.log(" beforeG group next: " + row); }) .after(row => { console.log(" after group prev:" + row); }); // Iterate using JavaScript built in for witich admits break for (let row of cursor) { if (row.tabid > 2) { break; console.log(" for each " + row); } } </script>