When multiple kinds of data is fetched for a request, the application
must decide how to lay out the data to return to the user.
Page templates allows such page layouts to be defined as XML
configuration files - one file per layout, corresponding to one use case.
The layouts are structural - they do not specify widths
and heights, colors and similar, but define the various boxed
components that will make up the page, and their ordering and nesting.
It is also assumed that the complete application includes a frontend
which is capable of rendering finished pages from result laid out by a template.
Page layouts may contain choices which specify alternative
versions of the template. The choices in a template are taken by
a resolver component at run time. Given an optimizing
resolver the system can then learn to make the right choices given
each particular query and result. An optimizing resolver is not
bundled with the platform but must be added as a component.
Add these query parameters: page.id=[comma-separated list of page id's]
and presentation.format=page.
The results returned will be as defined by the page template selected for each query.
The source names used in page templates are the same as those defined
in the federation setup,
and/or of any internal search clusters defined in the application.
A presentation layer (frontend) which understands the results created
by the page templates in use must be set up or created to produce rendered pages.
That is beyond the scope of this document.
Introduction to Page Templates
A page template is an XML file which contains
a <page> tag at the top level. The page element
must have an id attribute, where the file name is the
same as the id, followed by .xml. If the id
is default, this template will be used whenever no template
is specified in the query. The templates may also be versioned,
see Component Versioning.
A page template consist of nested sections which correspond
to screen areas in the final layout. The top level section is defined
by the page itself, while further sections can be defined by explicit
<section> tags. Each section may set a layout which will be
used by the frontend renderer to lay out its content
- column and row must be supported by
all renderers, while some renderers may specify additional layouts.
Each section may also specify sources of data which should be placed
in the section. Renderers must be able to render multiple data items
from different sources in a section.
For example, this template creates a page consisting of four equally large regions containing one source each:
Data items from each possible source has a rendering implemented by the frontend.
These renderers are used when nothing is specified in the template.
If some alternative rendering is desired, this can be
specified by a renderer tag. The same is true for
rendering of the sections themselves. Here we specify a different
renderer for blog data items (hits), as well as for the entire
news/blog section.
Note that in order to add a renderer subelement, we now specify the
blog source by a tag rather than by an attribute. These two forms are
equivalent - the attribute variant is just a shorthand syntax.
Sources and renderers can be given arbitrary key-value parameters
- see the reference for details.
But what if we want to choose either news or blogs, but not both?
This can be achieved using a choice:
If we wanted to choose between two groups of multiple sections (or sources),
this can be done by adding an enclosing alternative tag around each group.
For the common special case of assigning a set of elements to a set of placeholders,
a choice can contain a map tag instead of a list of alternatives.
See the reference for details.
Using Choice Resolvers
If templates including choices are used, some component must resolve
those choices given each query and result. The system includes some
resolvers for demo and testing purposes, but a proper optimizing
resolver must be deployed as part of the application.
This section describes how to create, deploy and choose a resolver to use at runtime.
Writing a Resolver
Resolvers are subclasses of
com.yahoo.search.pagetemplates.engine.Resolver.
This API defines a method which accepts the page template in use
(which contains the choices), the Query/Result pair and returns a Resolution.
It is called at runtime once for every query which uses a page template.
There are also some helper methods which makes it simple to write
resolvers which make each choice independently. Here is an example
resolver which makes all choices by random using this helper methods:
packagecom.yahoo.search.pagetemplates.engine.resolvers;importcom.yahoo.search.Query;importcom.yahoo.search.Result;importcom.yahoo.search.pagetemplates.engine.*;importcom.yahoo.search.pagetemplates.model.*;importjava.util.*;/** A resolver which makes all choices by random. */publicclassRandomResolverextendsResolver{privateRandomrandom=newRandom(System.currentTimeMillis());// Use of this is multithread safe/** Chooses the last alternative of any choice */@Overridepublicvoidresolve(Choicechoice,Queryquery,Resultresult,Resolutionresolution){resolution.addChoiceResolution(choice,random.nextInt(choice.alternatives().size()));}/** Chooses a mapping which is always by the literal order given in the source template */@Overridepublicvoidresolve(MapChoicechoice,Queryquery,Resultresult,Resolutionresolution){Map<String,List<PageElement>>mapping=newHashMap<String,List<PageElement>>();// Draw a random element from the value list on each iteration and assign it to a placeholderList<String>placeholderIds=choice.placeholderIds();List<List<PageElement>>valueList=newArrayList<List<PageElement>>(choice.values());for(StringplaceholderId:placeholderIds)mapping.put(placeholderId,valueList.remove(random.nextInt(valueList.size())));resolution.addMapChoiceResolution(choice,mapping);}}
The packaged component is added to the components/
directory of the application package.
The page template searcher must be configured with a list of the
resolvers which should be available. This is done by expanding the
page template searcher configuration with a components configuration:
<searcherid="com.yahoo.search.pagetemplates.PageTemplateSearcher"bundle="the name in <artifactId> in your pom.xml"><configname="container.components"><componentindex="0"><id>default</id><classId>com.yahoo.my.Resolver1</classId><bundle>myBundleSymbolicName</bundle></component><componentindex="1"><id>mySecondResolver</id><classId>com.yahoo.my.Resolver2</classId><bundle>myBundleSymbolicName</bundle></component></config></searcher>
The resolver to use is determined by setting the query property
page.resolver to the id (and optionally version) of the resolver component -
either in the request, in a query profile or programmatically.
Two templates suitable for testing purposes are always available:
native.random, which makes each choice by random,
and native.deterministic which selects the last
alternative of each choice.
If the page.resolver parameter is not set, the resolver having the
id default is used. If no default resolver is deployed
the random resolver is used.
Examples
This section contains a few complete examples of page templates.
<pageid="richSerp"layout="mainAndRight"><sectionlayout="row"placement="main"><sectionlayout="column"description="left main pane"><sectionlayout="row"max="5"description="Bar of images, from one of two possible sources"><choice><sourcename="images"/><sourcename="flickr"/></choice></section><sectionmax="1"source="local map video ticker weather"description="A single relevant graphically rich element"/><sectionmax="10"source="web news"description="Various kinds of traditional search results"/></section><sectionlayout="column"order="[source]"source="answers blogs twitter"description="right main pane, ugc stuff, grouped by source"/></section><sectionlayout="column"source="ads"region="right"/></page>
A mapping of multiple source modules to places on the page:
<pageid="MapSourcesToSections"layout="column"description="4 sources are assigned to a section each"><sectionlayout="row"description="row 1"><sectionid="box1"><placeholderid="box1source"/></section><sectionid="box2"><placeholderid="box2source"/></section></section><sectionlayout="row"description="row 2"><sectionid="box3"><placeholderid="box3source"/></section><sectionid="box4"><placeholderid="box4source"/></section></section><choicemethod="myMethod"><mapto="box1source box2source box3source box4source"><sourcename="source1"/><sourcename="source2"/><sourcename="source3"/><sourcename="source4"/></map></choice></page>