How do I stop a property-update rule from firing when the node is being created?
Question
How do I stop a property-update rule from firing when the node is being created?
Answer
Creating a node also sets its initial property values, and each of those is a
property event. So a rule written as "when jcr:title changes" fires during
creation as well as on a later edit.
There is no isNew() or "is creation" flag on any Jahia rule fact. The way to
express it is a negative condition: this property changed, and no node was added
at the same path in this batch.
Jahia ships a DSL sentence for exactly that:
The {node} has not been added = not AddedNodeFact ( path == ({node}.getPath()) )
The three rules
rule "nodeA created"
when
A new node is created
- the node has the type mynt:nodeA
then
> logger.info("created {}", node.getPath());
end
rule "nodeA jcr:title updated, but not at creation"
when
A property jcr:title has been set on a node
- the node has the type mynt:nodeA
The node has not been added
then
> logger.info("title updated on existing {}", node.getPath());
end
rule "nodeA deleted"
when
A node is deleted
- the node has the type mynt:nodeA
then
> logger.info("deleted {}", node.getPath());
end
The node has not been added is a standalone pattern and must be on its own
line - it is not a - constraint fragment appended to the sentence above it.
In raw DRL, without the DSL:
rule "nodeA jcr:title updated, but not at creation"
when
> property : ChangedPropertyFact ( name == "jcr:title", node : node, node.types contains "mynt:nodeA" )
> not AddedNodeFact ( path == ( node.getPath() ) )
then
> logger.info("title updated on existing {}", node.getPath());
end
Why it works
RulesListener collects every JCR event from one session.save() into a single
list - the AddedNodeFact for the new node and the ChangedPropertyFact for each
initial property - and only then executes the rules, through a stateless
session that inserts all facts before firing any rule. So when the update rule is
evaluated during a creation, the AddedNodeFact is already in working memory and
the not condition is false.
The caveat that will catch you out
The guard works per session.save(). If your code does:
node.addNode("a", "mynt:nodeA");
session.save(); // batch 1: AddedNodeFact
node.setProperty("jcr:title", "x");
session.save(); // batch 2: no AddedNodeFact
then the second batch has no AddedNodeFact, and the update rule will fire.
Content Editor and jContent create a node and its properties in a single save, so
the guard holds for normal authoring - but not necessarily for your own code.
Two more worth knowing:
- Adding a new language to an existing node fires the update rule. The
property lives on a
jnt:translationchild, andRulesListenerdeliberately does not emit anAddedNodeFactfor translation nodes. This is usually what you want, but it is not "creation" in the sense most people mean. - Some properties never produce a rule event at all, including
jcr:primaryType,jcr:uuid,jcr:created,jcr:createdBy,jcr:lastModifiedandjcr:lastModifiedBy. If your rule never fires, check it is not watching one of those.
Do not use getOperationType() for this
getOperationType() exists on the facts, but it does not distinguish creation
from update. It returns only "import", "session", "clone" or null.
A related trap: a condition such as - not in operation copy is always true,
because "copy" is never produced. It looks like a guard and does nothing. This
mistake is present in at least one real Jahia module, so do not copy it from
existing code.
Measured
Reproduced on Jahia 8.2.3.2 by loading the three rules plus a deliberately unguarded copy of the update rule, so the guarded and unguarded versions could be compared in the same run, and watching which fired.
| Scenario | create rule | update rule, unguarded | update rule, guarded |
|---|---|---|---|
Node created with jcr:title in one save | fired | fired | did not fire |
jcr:title edited later | - | fired | fired |
| Node created, then titled in a second save | fired | fired | fired |
Row one is the problem and the fix together: creating the node fires the update rule, and the guard suppresses exactly that firing. Row two confirms the guard does not suppress anything else - a real edit still fires it.
Row three is the caveat, measured rather than predicted. Split the work across two
session.save() calls and the guard stops helping, because the second batch
contains no AddedNodeFact. If your code creates a node and sets its properties
in separate saves, this will not do what you want.
One thing to know before you rely on it
The DSL sentence is supplied by the platform and compiles - the rules above were loaded by Jahia without a parser error. But no rule shipped with Jahia or with any public Jahia module uses it, so it is not a pattern the platform exercises on its own behalf. It behaved correctly in the three cases above; test it against your own content model rather than assuming it generalises.
This article was drafted with AI assistance, then reviewed and curated by Jahia Customer Support engineers before publication.