![]() |
Photo by Jason Pearce |
<xf:instance id="my-book">
<book id="simple_book">
<chapter id="chapter_1">
<title>Chapter 1</title>
<para>Hello world!</para>
</chapter>
<chapter id="chapter_2">
...
</chapter>
</book>
</xf:instance>
Some key elements in this document have id
attributes which identify the element they are placed on. This means that if you have the value of an id, you can find the associated element. For example the following XPath expression finds the second chapter
element://*[@id = 'chapter_2']
The downside of using plain XPath here is that if you have a very large document, the expression might have to scan large portions of the document, possily the entire document. This can be costly. In theory, the XPath engine can back the XML document with indexes, but this is typically done only by XML databases such as eXist.However XPath specifies an
id()
function exactly for the purpose of improving on searching element with a given id. It is in fact very similar to the JavaScript getElementById()
function now implemented natively (and efficiently) by web browsers.The
id()
function is not strictly required to be faster than a plain document scan, but it can obviously be backed by an index (a hash map), and this is exactly how we just implemented it![1]The index is enabled only for a specific instance with:
<xf:instance id="my-book" xxf:index="id">
When support is enabled for an instance, all ids are indexed the first time the id()
function is called. Then, for each mutation of the document (insertion, deletion, replacement, change of value), the index is updated. [2] At this time, only elements with a local name of id
are indexed. This covers plain id
and xml:id
attributes.[3]You can use the
id()
function this way:<xf:insert
ref="id('chapter_2', instance('my-book'))"
origin="xf:element('chapter', xf:attribute('id', 'chapter_3'))"/>
This XForms action inserts a new empty chapter
element with id chapter_3
after the chapter
element with id chapter_2
.Form Builder benefits from this change a lot, because:
- The entire form being edited is stored in an XForms instance.
- All controls and other important elements are assigned ids.
- Finding elements by id is a very common operation.
-
Until this change, calling
id()
on regular (not readonly) instances in Orbeon Forms always returned an empty result. However, calling it on readonly instances already behaved as expected, as we are relying on the SaxonTinyTree
implementation for readonly instances. ↩
-
The implementation is just slightly non-trivial, because the spec says that when more than one element has a given id, the first in document order must be returned. So we must properly track multiple ids for that reason. ↩
-
In theory,
id()
must support any attribute or element with typexs:ID
but Orbeon Forms doesn’t implement this yet. In the future, we might also support indexing elements by other aspects, such as CSS classes or element names, as HTML does. ↩
No comments:
Post a Comment