Day 33 of 40
Workflows, Search and Indexing
Automating content processes and making queries fast
By the end of today you should be able to
- Build a workflow model and explain the common step types
- Explain how workflows are launched and where instances live
- Write a query and choose the right index for it
- Diagnose and fix a repository traversal warning
Workflows
A workflow automates a content process: review and approval, scheduled
publication, asset processing, translation handoff. Models are built in the Workflow Model editor
(Tools → Workflow → Models) and stored under /conf.
Step types you will actually use:
- Participant step — assigns a task to a person or group and waits. This is the human approval gate; the task appears in their Inbox.
- Dynamic participant step — the assignee is chosen at runtime by a chooser class.
- Process step — runs Java. Implement
WorkflowProcessand register it as an OSGi service with aprocess.labelproperty so it appears in the editor. - OR split / AND split — branch conditionally, or run branches in parallel.
- Container step — invoke another workflow model as a sub-process.
- Goto step — jump, enabling loops.
@Component(service = WorkflowProcess.class,
property = {"process.label=WKND Notify Editor"})
public class NotifyEditorProcess implements WorkflowProcess {
@Override
public void execute(WorkItem item, WorkflowSession session,
MetaDataMap args) throws WorkflowException {
String path = item.getWorkflowData().getPayload().toString();
// ...
}
}
Workflows start manually, via a launcher (a rule watching a path and node
type for a change), or from code. Running instances live under /var/workflow/instances,
and their volume is a real operational concern — a launcher on a broad path can generate thousands of
instances and fill the repository. Purge configuration exists for exactly this reason.
A launcher on /content for any cq:PageContent modification fires on every save by every author. Scope launchers to the narrowest path and condition that achieves the goal, and check /var/workflow/instances after deploying one.
Querying
AEM offers several query languages over Oak. In practice:
- QueryBuilder — AEM's map-based API. Convenient, and what most AEM code uses.
- JCR-SQL2 — the underlying standard query language. More explicit, easier to reason about for performance.
- XPath — legacy, still seen in older code.
Map<String, String> params = new HashMap<>();
params.put("path", "/content/wknd");
params.put("type", "cq:Page");
params.put("property", "jcr:content/cq:template");
params.put("property.value", "/conf/wknd/settings/wcm/templates/article-page");
params.put("p.limit", "20");
Query query = queryBuilder.createQuery(
PredicateGroup.create(params), session);
SearchResult result = query.getResult();
Indexes
Oak does not index everything by default. An unindexed query traverses — walking nodes one by one — which is fine over a hundred nodes and catastrophic over a million.
Index types:
- Property index — for exact matches on a specific property. Cheap and fast.
- Lucene property index — the general workhorse, supporting full-text and complex conditions.
- Ordered index — for range queries and sorting.
Custom indexes are defined under /oak:index and ship in ui.apps. On Cloud
Service, index definitions must be named with a package suffix
(damAssetLucene-8-custom-1 style) and are deployed and reindexed by the pipeline.
Watch error.log for warnings about traversing a large number of nodes — Oak logs them explicitly. Cloud Service also surfaces traversal risks in the Developer Console. Every one of those warnings is a query that will get slower as content grows.
Practical rules that prevent most index problems:
- Always constrain by path. A query over all of
/contentis rarely what you want. - Always set a limit. Unbounded result sets are a memory risk.
- Prefer an existing index. Check what is already defined before adding one; indexes cost write performance and storage.
- Explain the query. The Query Performance tool (
/libs/granite/operations/content/diagnosistools/queryPerformance.html) shows the execution plan and whether an index was used.
Scope launchers narrowly and watch instance volume. Constrain every query by path and limit, and treat a traversal warning as a bug rather than noise.
Watch
Adobe's own videos for this topic. They load only when you press play.
Read on Experience League
The primary sources these notes are drawn from.
Your notes
Saved automatically to this browser.
Check yourself
10 questions on today's material. 80% to pass.