Rebuild

By working out the dependencies between data items, it is possible to reduce the number of calculations to only those that are implied by some data change. However, if the structure of the data changes, then the whole set of dependencies needs to be recalculated. This process is called a rebuild and involves reevaluating all XPath expressions used in a particular model.

To see why this is necessary, imagine a slightly more complex model than the one we had above, where we have rows in an invoice, and each row has a 'total price' that is calculated by multiplying the number of items purchased by the individual item's selling price:

<xf:instance>
  <invoice xmlns="">
    <item sku="1223">
      <units>2</units>
      <price>6.99</price>
      <total />
    </item>
    <item sku="776">
      <units>5</units>
      <price>12.99</price>
      <total />
    </item>
  </invoice>
</xf:instance>

<xf:bind nodeset="item/total" calculate="../units * ../price" />

Since an XPath expression like "x/y" will select any y element that is a child of an x element, then this simple bind statement causes the XForms processor to create dependencies across all of the total elements; it's as if we had authored the following statements by hand:

<xf:bind nodeset="item[1]/total[1]" calculate="../units * ../price" />
<xf:bind nodeset="item[2]/total[1]" calculate="../units * ../price" />

But there is a big advantage with using the "item/total" technique, which is that if the data is updated in such a way that the number of rows changes, then the processor will automatically create a new set of dependencies. For example, if the user clicks some button to create a new item in the invoice, a new calculation will be automatically added for the new total node, and the effect would be the same as if we had explicitly written the following:

<xf:bind nodeset="item[1]/total[1]" calculate="../units * ../price" />
<xf:bind nodeset="item[2]/total[1]" calculate="../units * ../price" />
<xf:bind nodeset="item[3]/total[1]" calculate="../units * ../price" />

The process responsible for adding this calculation is rebuild.

When do rebuilds happen?

It is only necessary to reconstuct the dependencies when the structure of the instance data in a model changes. This would be when new nodes are added (using insert) or deleted (using delete), or when some instance data is entirely replaced (using submission with @replace="instance" or reset). In all of these situations the rebuild is automatic.

Improving performance

Although rebuild happens automatically, once you know what will trigger it, you can look at ways of reducing its impact on performance.

Since the process of rebuilding applies to an entire model, it is a good idea to avoid putting unrelated instances into the same model, unless there are no bind statements (and so nothing for rebuild to do). For example, if in our invoice we had an additional instance that is used to retrieve and update customer details, it would be a good idea to place this in a separate model. Otherwise, every time the user requests customer information from the server, the dependency engine would parse all of the XPath expressions for the invoice, updating the dependencies.

An example

Let's return to the simpler instance that we saw before:

<xf:instance>
  <instanceData xmlns="">
    <a>10</a>
    <b>10</b>
    <c />
    <d />
  </instanceData>
</xf:instance>

<xf:bind nodeset="c" calculate="../a * ../b" constraint=". &lt;= 100" />
<xf:bind nodeset="d" calculate="../a + ../b" constraint=". &lt;= 20" />

This gives us four calculations in our dependency graph:

Target Type Expression
c[1] calculate a[1] * b[1]
c[1] constraint c[1] <= 100
d[1] calculate a[1] + b[1]
d[1] constraint d[1] <= 20

as well as the following dependencies:

Node Dependents
a[1] c[1], d[1]
b[1] c[1], d[1]
c[1] c[1]'s constraint
d[1] d[1]'s constraint

We've used the '[]' syntax to convey the idea that if our instance data had more nodes--just as we saw in the invoice example, previously--then these XPath expressions would yield more dependencies.

The meaning of the table is that if any node in the left column changes, the nodes in the dependency list on the right will need recalculating. So if node a changes, then both c and d need to be recalculated; c would be set to a * b and d would be set to a + b. But note further down the table that when c changes, its constraint needs to be recalculated, and similarly, a change in d requires a recalculation of its constraint. A structure like this, where items are linked together, is often called a directed graph.

Master Dependency Directed Graph

We've said that you don't need to understand the dependency-engine in order to use it, so you certainly don't need to understand this next step; but if you are inclined towards the nuts and bolts, you'll no doubt find this feature of the XForms architecture interesting.

The nodes, dependents and calculations that we have in our two tables above, are actually treated as one item, which goes under the name master dependency directed graph. The dependency directed graph part of the name is due as we saw above, to the fact that each node contains a list of references to other nodes that depend on it, and this chain of dependencies takes the form of a directed graph.

The master part of the name is due to the fact that this graph contains all of the relevant information from a model--all of the possible calculations and all dependencies between them--as worked out during the rebuild phase.

Our MDDG for our simple form then, looks like this:

Target Type Expression Dependents
a[1] node c[1], d[1]
b[1] node c[1], d[1]
c[1] calculate a[1] * b[1] c[1]'s constraint
c[1] constraint c[1] <= 100
d[1] calculate a[1] + b[1] d[1]'s constraint
d[1] constraint d[1] <= 20

With this 'master' graph, given information about one or more nodes that have changed, the dependency engine can work out which calculations would need to be performed. This process of performing calculations based on the relationships between nodes is called recalculation, and we'll look at that next.