Skip to content

Wikilink regex drops same-page anchors ([[#Heading]]) and discards the #Heading fragment on cross-page anchored links #3333

Description

@tourko

Environment: graphifyy 0.9.53 (confirmed present in 0.9.52 too — regex is byte-for-byte identical between the two)

Summary

extractors/markdown.py's wikilink regex fails to extract two common Obsidian link forms, so any entity referenced only through them gets no graph edge:

_MD_WIKILINK_RE = re.compile(r'(?<!\!)\[\[([^\]|#]+)(?:[#|][^\]]*)?\]\]')
  1. A same-page anchor with no page name, [[#Heading]], doesn't match at all — the capture group [^\]|#]+ requires at least one non-# character before hitting #, |, or ], and here # is the very first character.
  2. A cross-page anchored link, [[Page#Heading|alias]], does match, but the non-capturing group (?:[#|][^\]]*)? throws away everything from the # or | onward — so the link resolves only to Page, never to the specific heading/section it points at.

Repro

import re
_MD_WIKILINK_RE = re.compile(r'(?<!\!)\[\[([^\]|#]+)(?:[#|][^\]]*)?\]\]')

print(_MD_WIKILINK_RE.search('[[#SomeHeading]]'))
# -> None  (expected: a match resolving to the current file + "SomeHeading")

print(_MD_WIKILINK_RE.search('[[Other Page#SomeHeading|alias]]').group(1))
# -> 'Other Page'  (expected: page="Other Page", heading="SomeHeading" — both available for resolution)

Impact

In a corpus where entities are extracted at sub-page granularity (one node per heading/struct/section rather than one node per file — common on reference-style docs), any entity referenced only via [[#Heading]] or [[Page#Heading]] links can never receive a syntactic (EXTRACTED) edge, because the regex can't preserve enough information to resolve to that specific node. On a ~1,100-node knowledge-graph build, this was one contributor to 133 nodes ending up with zero edges. It also silently breaks the [[#Heading]] same-page-anchor convention entirely, since those links aren't just under-resolved — they never enter the link list at all.

Suggested fix

Capture the heading fragment instead of discarding it, and allow an empty page-name group so pure anchor links match:

_MD_WIKILINK_RE = re.compile(r'(?<!\!)\[\[([^\]|#]*)(?:#([^\]|]*))?(?:\|[^\]]*)?\]\]')
  • group(1): page name (empty string for a same-page anchor — caller should default to the current file)
  • group(2): heading fragment, if any, for resolving to a sub-page node when the extractor supports that granularity

Happy to open a PR if that's welcome — wanted to confirm the intended resolution behaviour first (does the project have a concept of heading-level nodes it would want this to resolve to, or should #Heading just get ignored on purpose?).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions