How to count repeats in a inner field

Hi!

I'm working on some tests for JavaRosa and I'm trying to build a form that will verify relations between inner and outer groups in an abstract way. The forms have no real use in the field.

Let's say I have a form with a repeat group and a field inside the group that keeps the count of the number of repeats there are as in:

<?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:jr="http://openrosa.org/javarosa" xmlns:h="http://www.w3.org/1999/xhtml">
  <h:head>
    <h:title>Some form</h:title>
    <model>
      <instance>
        <data id="some-form">
          <group jr:template="">
            <count/>
          </group>
        </data>
      </instance>
      <bind nodeset="/data/group/count" calculate="count(/data/group)" type="int"/>
    </model>
  </h:head>
  <h:body>
    <group ref="/data/group">
      <repeat nodeset="/data/group"/>
    </group>
  </h:body>
</h:html>

Let's say that I create three repeat groups. Then, I would expect this in my instance:

<group><count>3</count></group>
<group><count>3</count></group>
<group><count>3</count></group>

(I know it's silly)

Instead, I'm getting all 1 as values of the count field.

What am I doing wrong?

@ggalmazor and I did some pairing on this and it turned out that he was working off some code with a regression in it. This test works great in released versions of JavaRosa/Collect!

1 Like

OK, switched to master and now considering the following form:

<?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:jr="http://openrosa.org/javarosa" xmlns:h="http://www.w3.org/1999/xhtml">
  <h:head>
    <h:title>Some form</h:title>
    <model>
      <instance>
        <data id="some-form">
          <outer jr:template="">
            <inner jr:template="">
              <count/>
            </inner>
            <count/>
          </outer>
          <count/>
        </data>
      </instance>
      <bind nodeset="/data/outer/inner/count" calculate="count(..)" type="int"/>
      <bind nodeset="/data/outer/count" calculate="count(../inner)" type="int"/>
      <bind nodeset="/data/count" calculate="count(/data/outer/inner)" type="int"/>
    </model>
  </h:head>
  <h:body>
    <group ref="/data/outer">
      <repeat nodeset="/data/outer">
        <group ref="/data/outer/inner">
          <repeat nodeset="/data/outer/inner"/>
        </group>
      </repeat>
    </group>
  </h:body>
</h:html>

It's basically the same form from my original post but now there are a couple of nested repeat groups. If I create 3 inner groups in the first outer group, and 2 inner groups in the second outer group, I'd expect the following instance:

<outer>
  <inner><count>3</count></inner>
  <inner><count>3</count></inner>
  <inner><count>3</count></inner>
  <count>3</count>
</outer>
<outer>
  <inner><count>2</count></inner>
  <inner><count>2</count></inner>
  <count>2</count>
</outer>
<count>5</count>

Instead, all the count values in the inner groups are getting a 1. The other counts are OK.

Maybe there's wrong on their calculate expression? If not, I think we may have a bug (I verified that I get the same results with the v2.17.0 git tag)

I don't believe so. Do you get the same issue if you use an absolute path as opposed to a relative one?

An absolute ref as in calculate="count(/data/outer/inner)" works, but it gives me the count of all inner groups regardless to what outer group they belong to (5 in all counts).

It looks like the parent ref in the calculate="count(..)" of /data/outer/inner/count isn't resolving the ref we need aka /data/outer[x]/inner (xth outer instance, unbound inner instance).

Instead, it's resolving the specific yth inner instance where the field's at, which is kinda reasonable too, since the parent ref shouldn't change anything about the parent of the field itself, and the field is actually at position outer[x]/inner[y].

I'm also trying with calculate="count(../../inner)", which reads as "go up to the yth inner instance, go up to the xth outer instance, then down to an unbounded inner ref", but it's still giving me 1 as count results.

Following the triggerable evaluation code, it all goes down to how we're contextualizing the triggerable's target refs with the evaluation context's ref. These assertions explain the core of the problem:

TreeReference context = getRef("/data/outer[0]/inner[1]/count[0]");
assertThat(
    getRef("..").contextualize(context),
    is(getRef("/data/outer[0]/inner[1]"))
);
assertThat(
    getRef("../../inner").contextualize(context),
    is(getRef("/data/outer[0]/inner[1]"))
);

I would expect the second result to be /data/outer[0]/inner.

1 Like