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.

Copy
<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.

Copy
<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.

Copy
<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.

Copy
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.

Copy
<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>