Skip to content

Extractor (vaultkg.extractor)

vaultkg.extractor

extractor.py -- turn an Obsidian vault into NodeSpec / EdgeSpec records.

Nodes

note one Markdown file (note:<path>) heading one section of a note (heading:<path>#<slug>) tag one tag, nested tags chained by CONTAINS (tag:<tag>) attachment a non-Markdown file a note embeds or links (attachment:<path>) symbol a link target no file answers to (missing:<target>); the SDK keeps symbol nodes out of search results and out of meaningful_nodes, which is right for a page that does not exist

Edges

CONTAINS note -> top heading, heading -> subheading, tag -> nested tag LINKS_TO a plain [[wikilink]] or [text](note.md) EMBEDS ![[note]] / ![[image.png]] TAGGED note -> tag typed supports:: [[X]] or supports: ["[[X]]"] in frontmatter becomes SUPPORTS; any key works, normalised by :func:~vaultkg.parse.relation_name

Wikilinks resolve the way Obsidian resolves them: an exact vault path first, then a path relative to the linking note, then the file name anywhere in the vault (same folder preferred, then the shortest path), then a frontmatter alias. A Markdown link is a path (relative to the note, else to the vault root) and never resolves by name, so it is never ambiguous. A #Heading anchor that matches a heading in the target points the edge at that heading. Repeated links between the same two nodes with the same relation collapse into one edge whose weight is the count.

VaultExtractor(repo_path, config=None)

Bases: KGExtractor

Extractor for an Obsidian-style Markdown vault.

Parameters:

Name Type Description Default
repo_path Path

Vault root.

required
config dict[str, Any] | None

Optional {"exclude": [...]}: directory names or glob patterns (matched against vault-relative POSIX paths) to skip, in addition to :data:DEFAULT_EXCLUDE and every dot-directory.

None
Source code in src/vaultkg/extractor.py
125
126
127
128
def __init__(self, repo_path: Path, config: dict[str, Any] | None = None) -> None:
    super().__init__(repo_path, config)
    self.exclude: tuple[str, ...] = DEFAULT_EXCLUDE + tuple(self.config.get("exclude") or ())
    self._typed_rels: set[str] = set()

slug(text)

Anchor slug for a heading: lower-case words joined by -.

Parameters:

Name Type Description Default
text str

Heading text.

required

Returns:

Type Description
str

Slug; section when the text has no word characters.

Source code in src/vaultkg/extractor.py
 95
 96
 97
 98
 99
100
101
102
103
def slug(text: str) -> str:
    """Anchor slug for a heading: lower-case words joined by ``-``.

    :param text: Heading text.
    :return: Slug; ``section`` when the text has no word characters.
    """
    s = re.sub(r"[^\w\s-]", "", _fold(text))
    s = re.sub(r"[\s_-]+", "-", s).strip("-")
    return s or "section"