Repository navigation
Concept rule definition and usage for event types #536
Description
Hi:
Just would like to raise a question here regarding the usage of concept rules and relevant rule/constraint definition for event types in SPG schema and associated concept rule file, thanks.
I have a SPG schema with the following concept types and associated event types defined:
EntityB(EntityB): EntityType
properties:
....
EntityA(EntityA): EntityType
properties:
....
relations:
hasB: EntityB
TaxOfEntityBEvent(TaxOfEntityBEvent): ConceptType
hypernymPredicate: isA
EntityBEvent(EntityBEvent): EventType
properties:
subject(subject): EntityB
eventName(eventName): Text
IND#belongTo(belongTo): TaxOfEntityBEvent
TaxOfEntityAEvent(TaxOfEntityAEvent): ConceptType
hypernymPredicate: isA
relations:
CAU#leadTo(leadTo): TaxOfEntityBEvent
EntityAEvent(EntityAEvent): EventType
properties:
subject(subject): EntityA
eventName(eventName): Text
IND#belongTo(belongTo): TaxOfEntityAEvent
CAU#leadTo(leadTo): EntityBEvent
And in the associated schema/concept.rule file, I have the following definitions:
TaxOfEntityAEvent/A_down:TaxOfEntityBEvent/B_down
rule: [[
Define (s:TaxOfEntityAEvent/A_down)-[p:leadTo]->(o:TaxOfEntityBEvent/B_down) {
Structure {
(s)-[:subject]->(n1:EntityA)-[:hasB]->(n2:EntityB)<-[:subject]-(o)
}
Constraint {}
Action {
downEvent = createNodeInstance(
type=EntityBEvent,
value={
subject=tp.id
}
)
createEdgeInstance(
src=s,
dst=downEvent,
type=leadTo,
value={}
)
}
}
]]
And in the builder directory I have the associated instance data for EntityA, EntityB, TaxOfEntityAEvent, TaxOfEntityBEvent, and EntityAEvent. I left instance data for EntityBEvent as empty, as I'm expecting the concept rule for 'leadTo' above would automatically create a graph node as 'EntityBEvent' and the edge linking it to graph node 'EntityAEvent', with the values set as configured in the Action field.
In reality, after importing the instance data into Graph DB using the indexer python script, the ConceptType graph nodes TaxOfEntityAEvent, TaxOfEntityBEvent, the EntityType nodes EntityA, EntityB, and the EventType EntityAEvent have been successfully created in the graph, but there is no graph node 'EntityBEvent' created, and no edge linked to graph node 'EntityAEvent' as expected. Querying the 'leadTo' relation with DSL also returns empty results.
I was wondering if there is something I did wrong during the definition, and was curious about how exactly the rules and constraints (not just in concept.rule, but also rules/constraints defined directly in the schema) work in general, are they taking effect on builder level or on query level? i.e., the rules / constraints would be validated during the process of indexing data to graph DB, or only on querying the graph data after that?
Any feedback would be appreciated, thanks.
Activity
suibianha commentedon Apr 11, 2025
Just found out it might be related to #240, where usage of leadTo needs to be explicitly specified.
However the solution for that issue and the relevant user manual update seems to be compatible with SPG version 0.5, and since version 0.6 the builder workflow seems to be updated.
Is it possible to provide a solution equivalent to the one for issue #240, which is compatible with the existing indexer python script on builder? Thanks.
JJUN99 commentedon Aug 11, 2026
We hit almost exactly the same symptoms with the supplychain example and spent a few days root-causing them, so sharing what we found.
To answer your last question first: concept rules execute at build (ingestion) time, not at query time. When a record whose type has associated concept rules passes through the builder's reason stage, the server classifies it (
belongTo) and fires causal (leadTo) rules, materializing the Action's nodes/edges into the graph right then. Nothing is ever created at query time — queries only compute over what ingestion materialized. So your expectation was correct:EntityBEventshould have been created during indexing.Why it silently doesn't happen — we traced three server-side defects in this exact code path:
createNodeInstancedon't carry anidproperty. Any follow-up rule run that starts from such a vertex dies with an NPE thatLocalReasonerRunnerswallows, returning an empty result. The generated event then loses itsbelongToclassification, and nothing downstream fires. Crucially, no error reaches the client ordocker logs— the stack trace only lands in/logs/openspgapp/common-error.loginside the server container. I'd check that file first; it likely explains your empty result.subject) until the sink stage after the whole pass, so any subsequent rule matching on those edges finds nothing and propagation stops after one hop.Two more practical checks that bit us: the concept rules must be registered (
knext schema reg_concept_rule --file ./schema/concept.rule) after schema commit, and stale builder checkpoint directories (*ckpt*) silently skip already-seen records on re-runs — delete them before re-indexing. Also note your Action referencestp.idwhile the Structure bindsn1/n2— if that's not just a transcription slip, the rule would fail to evaluate as well.With the fixes from #621/#622 applied (and the change proposed in #623), one ingestion propagates the full event chain in our tests.