Faceted Navigation

Shaun McCance <shaunm at gnome.org>
Thu Dec 16 13:26:07 EST 2010

Hi folks,

I just blogged about an experiment I did to have faceted
navigation in Mallard:

http://blogs.gnome.org/shaunm/2010/12/16/more-faceted-navigation/

The blog post is just a demo, and is short on markup details
and other technical stuff. I want to lay out how I marked this
all up and how it works, and put out some questions.

This is all done with extension elements. There are two aspects
to the extension: tagging pages, and collecting tagged pages.
I think the tagging aspect can be reused for other extensions
or features.

Pages can include tags with the facet:tag element, inside the
info element. This takes two attributes: key and values. So in
the example on my blog, Message Board has this:

<facet:tag key="lang" values="c"/>
<facet:tag key="tech" values="webkit"/>

Think of each facet:tag as a categorized set of tags. The values
attribute can take a space-separated list. For example, if there
were a demo that mixed C and Python, it might have:

<facet:tag key="lang" values="c py"/>

Tags have no inherent meaning, and their values are intended to
be non-translatable identifiers, not human-readable strings. You
can tag away using whatever tag keys you want, and possibly build
other tools that do different things with some of your tags.

Oh, and just like with links, you can tag sections as well.

Then there's the facets pages. These are special pages that have
the type attribute set to "facets". I'll say "facets node" to
mean either a facets page or a section within a facets page. A
facets node can have facet:match and facet:choice elements in
its info element.

The facet:match element specifies which pages or sections should
be linked to. To match nodes that specify a tag, regardless of its
content, use it with just the key attribute:

<facet:match key="lang"/>

To match nodes with particular values for a tag, use both a key and
a values attribute:

<facet:match key="lang" values="c"/>

The values attribute is again a space-separated list. It matches a
node if it shares any values with the list in that node's tag. That
is, these two match:

<facet:match key="lang" values="c py"/>
<facet:tag key="lang" values="vala py"/>

Because they have an overlapping element ("py"). A facets node shows
a link to a target node iff:

 * All of the facets node's facet:match elements match the target
   node's tags.
 * And the target node is not linked to by a descendant node of the
   facets node.

Explanation on the second part: In my blog, the page matches anything
that has the "lang" tag set. Each section then matches particular values
of the "lang" tag. Skeleton markup:

<page type="facets" id="facets">
  <info>
    <facet:match key="lang"/>
  </info>
  <title>Facets!</title>
  <section id="c">
    <info>
      <facet:match key="lang" values="c"/>
    </info>
    <title>C</title>
  </section>
  <section id="py">
    <info>
      <facet:match key="lang" values="py"/>
    </info>
    <title>Python</title>
  </section>
</page>

The second bullet point allows you to chunk things into sections based
on the facet tags and not have them all also appear at the top.

Then we have the selection mechanism. A facets node can have any number
(including 0) of facet:choice elements. These have a leading facet:title
element followed by one or more facet:case elements. Example:

<facet:choice key="tech">
  <facet:title>Technology</facet:title>
  <facet:case values="gtk">GTK+</facet:case>
  <facet:case values="gstreamer">GStreamer</facet:case>
</facet:choice>

This says to create a control to allow you to filter based on the "tech"
facet tag. Human-readable text is given here. (Remember the tags are not
intended to be human-readable.) And again, values can be a list. A case
matches a target node if there's an overlap in values, just like match.

Facets nodes can have any number of facet:choice elements. Facet choices
affect all facet links in the facets node, including those in descendant
facets nodes. A choice is true for a link of any of the active cases in
the choice matches the target node. A link is displayed if every choice
in context (including those in ancestor nodes) is true for it.

Sheesh, sorry for the specification-like language. Open questions:

* There's no way to AND on cases or matches. For example, if I were to
change the Message Board example to have this:

<facet:tag key="tech" values="gtk webkit"/>

It would be displayed if you have either GTK+ or WebKit checked. Is it
useful to be able to display links only if all their tag values match?

* There's only one type of selector here: non-exclusive multiple choice.
I could imagine others. Exclusive multiple choice (i.e. radio buttons).
Numeric ranges. On/off toggles (you might think you can do that with a
single case in a choice, but it's different). What do we need? Probably
we can just start with this and grow from it as needs arise.

I'd like to hear comments, suggestions, and questions. What I've done
so far has worked really well, and I didn't hit any walls in the core
language that prevented me from doing the extension (which is good).
I think this is useful for "cookbook" documents. I wonder if others
have other uses for it.

Thanks,
Shaun McCance
Community Help Expert
http://syllogist.net/