Skip to content

OPENNLP-1920: Add dependency-path relation extraction as a typed Document annotation layer - #1238

Draft
krickert wants to merge 33 commits into
OPENNLP-1919-dependency-annotationsfrom
OPENNLP-1920-relation-extraction
Draft

OPENNLP-1920: Add dependency-path relation extraction as a typed Document annotation layer#1238
krickert wants to merge 33 commits into
OPENNLP-1919-dependency-annotationsfrom
OPENNLP-1920-relation-extraction

Conversation

@krickert

Copy link
Copy Markdown
Contributor

Summary

  • add typed RelationMention and RelationPattern values
  • extract ordered entity relations over dependency paths with optional pivot triggers
  • emit stable, span-anchored relation annotations that reference the entity layer by index
  • validate malformed graphs and prerequisite layers, with tests and user documentation

Stack

This draft depends on OPENNLP-1919, OPENNLP-547, and OPENNLP-1888. It must remain draft until the dependency stack lands.

For each ordered entity pair the annotator walks the dependency path
between the entity heads, up to the lowest common ancestor and down,
and emits a typed relation for every registered path pattern that
matches, with an optional trigger constraint on the pivot token.
Relations reference entities by layer index, never object identity.

(cherry picked from commit a4c17a1)
Pattern parsing now matches the rest of the extraction family: no
regular expressions anywhere, including configuration parsing. The
public steps() accessor exposes the parsed path.

(cherry picked from commit f8054b2)
StringUtil.isWhitespace follows the project's Unicode whitespace
definition under the default mode, keeping pattern parsing consistent
with tokenization instead of the JDK's whitespace notion.

(cherry picked from commit 7397232)
… expectations with the StringUtil semantics of this base
…he project predicate

A trigger is compared against a single pivot token, so a trigger containing any
whitespace is a rule that can never match; construction now rejects it, closing
the half of the never-matching defect the case-mapping fix left open. Type,
path, and trigger blankness are judged under the project whitespace definition,
which covers the no-break spaces the JDK blank check misses, so a path spelled
from them can no longer construct a zero-step dead rule, and the steps of every
constructible pattern are guaranteed non-empty. New tests pin the
supplementary-plane cased trigger pair and the negative side of the annotator's
mapping seam: the JDK-lowercased spelling of the dotted capital I matches
nothing.
Replaces the private blank helper with the shared predicate; behavior is
identical since it already followed the toolkit whitespace definition.
Add docbkx/relation.xml, wire it into the manual, and cite RelationExtractionExampleTest.
The trigger matches the pivot token's lowercased surface form, not a
lemma; say "trigger word". Open the CDATA on its own line to match the
sibling chapters and avoid a leading blank line in the rendered listing.
… document syntax

- Walk each entity's chain to the root once per entity instead of once per ordered
  pair. RelationAnnotator.annotate now fills a chains list alongside entityHeads and
  passes the two chains straight into matchPair, so an entity that takes part in n
  pairs is walked once rather than n times. matchPair drops the heads and entityHeads
  parameters it no longer needs and its null-chain guard moves up into the pair loop,
  where an unreachable head and a cyclic arc set are now the same skip condition.
- Extract the path step markers as RelationPattern.UP_STEP and DOWN_STEP and use them
  in both places that knew the characters: the step validator in the compact
  constructor and the step assembly in RelationAnnotator.matchPair. The two sites can
  no longer drift apart from each other or from the manual.
- Judge a RelationMention type by StringUtil.isBlank rather than String.isBlank, so a
  type spelled entirely from no-break spaces is rejected in the mention exactly as it
  already was in the pattern. The record's javadoc now names the predicate instead of
  restating the toolkit whitespace definition, and it points at the container's
  reference-by-layer-and-index rule rather than claiming durability across
  serialization.
- Give the overridden annotate method the javadoc it was missing: the layers it
  requires, that an absent entity layer reads as empty, the stable pair and pattern
  ordering that fixes the output order, and the full set of conditions that raise
  IllegalArgumentException.
- Trim the javadoc that argued its own case. RelationPattern's trigger paragraph and
  the steps and splitSteps comments now state the rule once, and the surviving inline
  comment in the pair loop explains only why the chains are precomputed, which the
  code cannot say for itself.
- Pin every invalid RelationMention component in a test of its own. The new
  testRelationMentionValidation asserts the exact message for a null, blank, and
  no-break space only type, for a negative index, and for a subject equal to its
  object, replacing the single mention assertion that had been tucked into
  testPatternValidation.
- Turn the three loop-driven tests in RelationPatternTest into parameterized tests, so
  a failing character is reported as its own case instead of aborting the loop at the
  first one, and fold testLeadingAndTrailingNoBreakSpacesAreIgnored into
  testStepsSplitOnUnicodeSpaceSeparators, which already asserts a path fringed with
  no-break spaces.
- Replace the remaining bare Unicode escapes in the tests with the named constants
  that were already declared for them, and add JDK_LOWERCASED_ISTANBUL and
  NO_BREAK_SPACE in RelationAnnotatorTest so the dotted capital I and the no-break
  space read the same way in both test classes.
- Add a Pattern syntax section to the relation manual chapter covering the step
  grammar, the rule that up steps precede down steps, the pivot as the lowest common
  ancestor, how a multi-token entity's head is chosen, and that a trigger is matched
  verbatim and so must be lowercase and whitespace free.
Every annotator in the container rejects an absent required layer with a
message naming it and accepts present but empty layers as valid input.
The absent-entity-layer and empty-layers tests fail until the relation
annotator is aligned with that contract.
Validate required layers through DocumentAnnotators.requireLayers so an
absent layer is rejected with the shared message and empty layers
degrade to an empty relations layer, matching every other annotator.
Walk root chains over int arrays instead of boxed lists, fold the
repeated dependency layer fixture into one test helper, and cover
triggers with leading whitespace.
krickert added a commit to ai-pipestream/opennlp that referenced this pull request Aug 22, 2026
For each ordered entity pair the annotator walks the dependency path
between the entity heads, up to the lowest common ancestor and down,
and emits a typed relation for every registered path pattern that
matches, with an optional trigger constraint on the pivot token.
Relations reference entities by layer index, never object identity.

(cherry picked from commit a4c17a1)
Pattern parsing now matches the rest of the extraction family: no
regular expressions anywhere, including configuration parsing. The
public steps() accessor exposes the parsed path.

(cherry picked from commit f8054b2)
StringUtil.isWhitespace follows the project's Unicode whitespace
definition under the default mode, keeping pattern parsing consistent
with tokenization instead of the JDK's whitespace notion.

(cherry picked from commit 7397232)
… expectations with the StringUtil semantics of this base
…he project predicate

A trigger is compared against a single pivot token, so a trigger containing any
whitespace is a rule that can never match; construction now rejects it, closing
the half of the never-matching defect the case-mapping fix left open. Type,
path, and trigger blankness are judged under the project whitespace definition,
which covers the no-break spaces the JDK blank check misses, so a path spelled
from them can no longer construct a zero-step dead rule, and the steps of every
constructible pattern are guaranteed non-empty. New tests pin the
supplementary-plane cased trigger pair and the negative side of the annotator's
mapping seam: the JDK-lowercased spelling of the dotted capital I matches
nothing.
Replaces the private blank helper with the shared predicate; behavior is
identical since it already followed the toolkit whitespace definition.
Add docbkx/relation.xml, wire it into the manual, and cite RelationExtractionExampleTest.
The trigger matches the pivot token's lowercased surface form, not a
lemma; say "trigger word". Open the CDATA on its own line to match the
sibling chapters and avoid a leading blank line in the rendered listing.
… document syntax

- Walk each entity's chain to the root once per entity instead of once per ordered
  pair. RelationAnnotator.annotate now fills a chains list alongside entityHeads and
  passes the two chains straight into matchPair, so an entity that takes part in n
  pairs is walked once rather than n times. matchPair drops the heads and entityHeads
  parameters it no longer needs and its null-chain guard moves up into the pair loop,
  where an unreachable head and a cyclic arc set are now the same skip condition.
- Extract the path step markers as RelationPattern.UP_STEP and DOWN_STEP and use them
  in both places that knew the characters: the step validator in the compact
  constructor and the step assembly in RelationAnnotator.matchPair. The two sites can
  no longer drift apart from each other or from the manual.
- Judge a RelationMention type by StringUtil.isBlank rather than String.isBlank, so a
  type spelled entirely from no-break spaces is rejected in the mention exactly as it
  already was in the pattern. The record's javadoc now names the predicate instead of
  restating the toolkit whitespace definition, and it points at the container's
  reference-by-layer-and-index rule rather than claiming durability across
  serialization.
- Give the overridden annotate method the javadoc it was missing: the layers it
  requires, that an absent entity layer reads as empty, the stable pair and pattern
  ordering that fixes the output order, and the full set of conditions that raise
  IllegalArgumentException.
- Trim the javadoc that argued its own case. RelationPattern's trigger paragraph and
  the steps and splitSteps comments now state the rule once, and the surviving inline
  comment in the pair loop explains only why the chains are precomputed, which the
  code cannot say for itself.
- Pin every invalid RelationMention component in a test of its own. The new
  testRelationMentionValidation asserts the exact message for a null, blank, and
  no-break space only type, for a negative index, and for a subject equal to its
  object, replacing the single mention assertion that had been tucked into
  testPatternValidation.
- Turn the three loop-driven tests in RelationPatternTest into parameterized tests, so
  a failing character is reported as its own case instead of aborting the loop at the
  first one, and fold testLeadingAndTrailingNoBreakSpacesAreIgnored into
  testStepsSplitOnUnicodeSpaceSeparators, which already asserts a path fringed with
  no-break spaces.
- Replace the remaining bare Unicode escapes in the tests with the named constants
  that were already declared for them, and add JDK_LOWERCASED_ISTANBUL and
  NO_BREAK_SPACE in RelationAnnotatorTest so the dotted capital I and the no-break
  space read the same way in both test classes.
- Add a Pattern syntax section to the relation manual chapter covering the step
  grammar, the rule that up steps precede down steps, the pivot as the lowest common
  ancestor, how a multi-token entity's head is chosen, and that a trigger is matched
  verbatim and so must be lowercase and whitespace free.
Every annotator in the container rejects an absent required layer with a
message naming it and accepts present but empty layers as valid input.
The absent-entity-layer and empty-layers tests fail until the relation
annotator is aligned with that contract.
Validate required layers through DocumentAnnotators.requireLayers so an
absent layer is rejected with the shared message and empty layers
degrade to an empty relations layer, matching every other annotator.
Walk root chains over int arrays instead of boxed lists, fold the
repeated dependency layer fixture into one test helper, and cover
triggers with leading whitespace.
krickert added a commit that referenced this pull request Aug 26, 2026
krickert added a commit that referenced this pull request Aug 26, 2026
krickert added a commit that referenced this pull request Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant