Skip to main content
Content nodes form the hierarchical structure of a Kodexa document. Each node represents a piece of content (text, a section, a page, etc.) and can have children, features, and tags.

Node Basics

Creating Nodes

from kodexa_document import Document

with Document() as doc:
    # Create the root node
    root = doc.create_node("document", "My Document")
    doc.content_node = root

    # Create child nodes with content
    section = doc.create_node("section", "Introduction", parent=root)
    para1 = doc.create_node("paragraph", "First paragraph.", parent=section)
    para2 = doc.create_node("paragraph", "Second paragraph.", parent=section)

    # Create nodes without initial content
    table = doc.create_node("table", parent=root)
    row = doc.create_node("row", parent=table)
    cell = doc.create_node("cell", "Cell content", parent=row)
import { Kodexa } from '@kodexa-ai/document-wasm-ts';

async function createNodes() {
  await Kodexa.init();
  const doc = await Kodexa.createDocument();

  try {
    // Create the root node
    const root = await doc.createNode('document');
    await root.setContent('My Document');

    // Create child nodes
    const section = await doc.createNode('section');
    await section.setContent('Introduction');
    await root.addChild(section);

    const para1 = await doc.createNode('paragraph');
    await para1.setContent('First paragraph.');
    await section.addChild(para1);

    const para2 = await doc.createNode('paragraph');
    await para2.setContent('Second paragraph.');
    await section.addChild(para2);
  } finally {
    doc.dispose();
  }
}

Node Types

Common node types used in Kodexa documents:
TypeDescription
documentRoot node of the document
pageA page in the document
sectionA logical section
paragraphA paragraph of text
lineA line of text
wordAn individual word
tableA table structure
rowA table row
cellA table cell
imageAn image element

Node Content

Reading and Writing Content

with Document() as doc:
    root = doc.create_node("document")
    doc.content_node = root
    para = doc.create_node("paragraph", "Initial content", parent=root)

    # Read content
    print(f"Content: {para.content}")

    # Update content
    para.content = "Updated content"

    # Content can be None
    empty_node = doc.create_node("section", parent=root)
    print(f"Empty content: {empty_node.content}")  # None
async function workWithContent() {
  await Kodexa.init();
  const doc = await Kodexa.createDocument();

  try {
    const root = await doc.createNode('document');
    const para = await doc.createNode('paragraph');
    await para.setContent('Initial content');
    await root.addChild(para);

    // Read content
    console.log(`Content: ${para.content}`);

    // Update content
    await para.setContent('Updated content');

    // Get content (async)
    const content = await para.getContent();
    console.log(`Content: ${content}`);
  } finally {
    doc.dispose();
  }
}

Content Parts

Nodes can have multiple content parts for complex text:
with Document() as doc:
    root = doc.create_node("document")
    doc.content_node = root
    para = doc.create_node("paragraph", parent=root)

    # Set multiple content parts
    para.set_content_parts(["Hello ", "world", "!"])

    # Get content parts
    parts = para.get_content_parts()
    print(f"Parts: {parts}")  # ['Hello ', 'world', '!']

    # Full content joins parts
    print(f"Full: {para.content}")  # 'Hello world!'
async function contentParts() {
  await Kodexa.init();
  const doc = await Kodexa.createDocument();

  try {
    const root = await doc.createNode('document');
    const para = await doc.createNode('paragraph');
    await root.addChild(para);

    // Set content (parts can be set via features in TypeScript)
    await para.setContent('Hello world!');

    // Get content
    const content = await para.getContent();
    console.log(`Content: ${content}`);
  } finally {
    doc.dispose();
  }
}

Aggregated Content

Get all content from a node and its descendants:
with Document() as doc:
    root = doc.create_node("document")
    doc.content_node = root

    section = doc.create_node("section", parent=root)
    doc.create_node("paragraph", "First paragraph.", parent=section)
    doc.create_node("paragraph", "Second paragraph.", parent=section)
    doc.create_node("paragraph", "Third paragraph.", parent=section)

    # Get all content from section and children
    all_text = section.get_all_content(separator=" ")
    print(f"All content: {all_text}")
    # "First paragraph. Second paragraph. Third paragraph."
async function aggregatedContent() {
  await Kodexa.init();
  const doc = await Kodexa.createDocument();

  try {
    const root = await doc.createNode('document');
    const section = await doc.createNode('section');
    await root.addChild(section);

    for (const text of ['First paragraph.', 'Second paragraph.', 'Third paragraph.']) {
      const para = await doc.createNode('paragraph');
      await para.setContent(text);
      await section.addChild(para);
    }

    // Get all content via children
    const children = await section.getChildren();
    const allText = children.map(c => c.content).join(' ');
    console.log(`All content: ${allText}`);
  } finally {
    doc.dispose();
  }
}

Tree Navigation

Parent and Children

with Document.from_kddb("document.kddb") as doc:
    root = doc.content_node

    # Get all children
    children = root.get_children()
    print(f"Root has {len(children)} children")

    for child in children:
        # Get parent (back to root)
        parent = child.get_parent()
        print(f"Node type: {child.type}, parent type: {parent.type}")

        # Get this node's children
        grandchildren = child.get_children()
        print(f"  Has {len(grandchildren)} children")
async function navigateTree() {
  await Kodexa.init();
  const doc = await Kodexa.fromBlob(kddbBlob);

  try {
    const root = await doc.getRoot();
    if (!root) return;

    // Get all children
    const children = await root.getChildren();
    console.log(`Root has ${children.length} children`);

    for (const child of children) {
      // Get parent (back to root)
      const parent = await child.getParent();
      console.log(`Node type: ${child.type}, parent type: ${parent?.type}`);

      // Get this node's children
      const grandchildren = await child.getChildren();
      console.log(`  Has ${grandchildren.length} children`);
    }
  } finally {
    doc.dispose();
  }
}

Siblings

with Document.from_kddb("document.kddb") as doc:
    paragraphs = doc.select("//paragraph")

    for para in paragraphs:
        # Get all siblings (nodes with same parent)
        siblings = para.get_siblings()
        print(f"Has {len(siblings)} siblings")

        # Navigate to next/previous sibling
        next_node = para.next_node()
        prev_node = para.previous_node()

        if next_node:
            print(f"Next: {next_node.type}")
        if prev_node:
            print(f"Previous: {prev_node.type}")
async function navigateSiblings() {
  await Kodexa.init();
  const doc = await Kodexa.fromBlob(kddbBlob);

  try {
    const paragraphs = await doc.select('//paragraph');

    for (const para of paragraphs) {
      // Navigate to next/previous sibling
      const nextNode = await para.nextNode();
      const prevNode = await para.previousNode();

      // Check position
      const isFirst = await para.isFirstChild();
      const isLast = await para.isLastChild();

      console.log(`First: ${isFirst}, Last: ${isLast}`);
    }
  } finally {
    doc.dispose();
  }
}

Depth and Position

with Document.from_kddb("document.kddb") as doc:
    nodes = doc.select("//*")

    for node in nodes:
        # Depth in tree (root = 0)
        depth = node.get_depth()

        # Index among siblings
        index = node.index

        print(f"Type: {node.type}, Depth: {depth}, Index: {index}")
async function nodePositions() {
  await Kodexa.init();
  const doc = await Kodexa.fromBlob(kddbBlob);

  try {
    const nodes = await doc.select('//*');

    for (const node of nodes) {
      // Depth in tree
      const depth = await node.getDepth();

      // Index among siblings
      const index = node.index;

      console.log(`Type: ${node.type}, Depth: ${depth}, Index: ${index}`);
    }
  } finally {
    doc.dispose();
  }
}

Node Features

Features are typed key-value metadata attached to individual nodes:
with Document() as doc:
    root = doc.create_node("document")
    doc.content_node = root
    para = doc.create_node("paragraph", "Styled text", parent=root)

    # Add features (type, name, value)
    para.add_feature("style", "font-family", "Arial")
    para.add_feature("style", "font-size", "12pt")
    para.add_feature("style", "font-weight", "bold")
    para.add_feature("spatial", "bbox", {"x": 100, "y": 200, "w": 300, "h": 50})
    para.add_feature("ocr", "confidence", 0.95)

    # Retrieve a specific feature
    font = para.get_feature("style", "font-family")
    if font:
        print(f"Font: {font.get_value()}")

    # Get all features of a type
    style_features = para.get_features_of_type("style")
    for f in style_features:
        print(f"  {f.name}: {f.get_value()}")

    # Get all features
    all_features = para.get_features()
    print(f"Total features: {len(all_features)}")

    # Check if feature exists
    has_bbox = para.has_feature("spatial", "bbox")
async function nodeFeatures() {
  await Kodexa.init();
  const doc = await Kodexa.createDocument();

  try {
    const root = await doc.createNode('document');
    const para = await doc.createNode('paragraph');
    await para.setContent('Styled text');
    await root.addChild(para);

    // Add features (type, name, value)
    await para.setFeature('style', 'font-family', 'Arial');
    await para.setFeature('style', 'font-size', '12pt');
    await para.setFeature('style', 'font-weight', 'bold');
    await para.setFeature('spatial', 'bbox', { x: 100, y: 200, w: 300, h: 50 });
    await para.setFeature('ocr', 'confidence', 0.95);

    // Retrieve a specific feature value
    const font = await para.getFeatureValue('style', 'font-family');
    console.log(`Font: ${font}`);

    // Get all features of a type
    const styleFeatures = await para.getFeaturesOfType('style');
    for (const f of styleFeatures) {
      console.log(`  ${f.name}: ${f.value}`);
    }

    // Get all features
    const allFeatures = await para.getFeatures();
    console.log(`Total features: ${allFeatures.length}`);

    // Check if feature exists
    const hasBbox = await para.hasFeature('spatial', 'bbox');
  } finally {
    doc.dispose();
  }
}

Node Tags

Tags annotate nodes with labels, optional confidence scores, and values:
with Document() as doc:
    root = doc.create_node("document")
    doc.content_node = root
    para = doc.create_node("paragraph", "Invoice total: $1,234.56", parent=root)

    # Simple tag
    para.tag("important")

    # Tag with confidence and value
    para.tag("invoice-total", confidence=0.95, value="$1,234.56")

    # Tag with additional data
    para.tag("extracted-field",
        confidence=0.92,
        value="1234.56",
        tag_uuid="field-uuid-123"
    )

    # Check for tag
    if para.has_tag("important"):
        print("Node is marked important")

    # Get tag details
    tag = para.get_tag("invoice-total")
    if tag:
        print(f"Value: {tag.get('Value')}")
        print(f"Confidence: {tag.get('Confidence')}")

    # Get all tags
    all_tags = para.get_tags()
    print(f"Tags: {all_tags}")

    # Remove a tag
    para.remove_tag("important")
async function nodeTags() {
  await Kodexa.init();
  const doc = await Kodexa.createDocument();

  try {
    const root = await doc.createNode('document');
    const para = await doc.createNode('paragraph');
    await para.setContent('Invoice total: $1,234.56');
    await root.addChild(para);

    // Simple tag
    await para.tag('important');

    // Tag with options
    await para.tagWithOptions('invoice-total', {
      confidence: 0.95,
      value: '$1,234.56'
    });

    // Check for tag
    const hasTag = await para.hasTag('important');
    if (hasTag) {
      console.log('Node is marked important');
    }

    // Get all tags
    const allTags = await para.getTags();
    console.log(`Tags: ${allTags}`);

    // Remove a tag
    await para.removeTag('important');
  } finally {
    doc.dispose();
  }
}

Spatial Data (Bounding Boxes)

Nodes can have spatial information for document layout:
with Document() as doc:
    root = doc.create_node("document")
    doc.content_node = root
    para = doc.create_node("paragraph", "Text on page", parent=root)

    # Set bounding box via feature
    para.add_feature("spatial", "bbox", {
        "x": 100,      # Left position
        "y": 200,      # Top position
        "width": 300,  # Width
        "height": 50   # Height
    })

    # Set page reference
    para.add_feature("spatial", "page", 0)

    # Get bounding box
    bbox = para.get_feature("spatial", "bbox")
    if bbox:
        box = bbox.get_value()
        print(f"Position: ({box['x']}, {box['y']})")
        print(f"Size: {box['width']} x {box['height']}")
async function spatialData() {
  await Kodexa.init();
  const doc = await Kodexa.createDocument();

  try {
    const root = await doc.createNode('document');
    const para = await doc.createNode('paragraph');
    await para.setContent('Text on page');
    await root.addChild(para);

    // Set bounding box
    await para.setBBox(100, 200, 300, 50);

    // Get bounding box
    const bbox = await para.getBBox();
    if (bbox) {
      console.log(`Position: (${bbox.x}, ${bbox.y})`);
      console.log(`Size: ${bbox.width} x ${bbox.height}`);
    }

    // Set rotation
    await para.setRotate(90);
  } finally {
    doc.dispose();
  }
}

Building Document Structures

Invoice Example

from kodexa_document import Document

def build_invoice_structure():
    with Document() as doc:
        # Document root
        root = doc.create_node("document", "Invoice #12345")
        doc.content_node = root

        # Header section
        header = doc.create_node("section", "Header", parent=root)

        vendor = doc.create_node("paragraph", "Vendor: Acme Corp", parent=header)
        vendor.tag("vendor-name", value="Acme Corp", confidence=0.98)

        date = doc.create_node("paragraph", "Date: 2024-01-15", parent=header)
        date.tag("invoice-date", value="2024-01-15", confidence=0.95)

        # Line items table
        table = doc.create_node("table", parent=root)

        items = [
            ("Widget A", "100.00"),
            ("Widget B", "250.00"),
            ("Service Fee", "50.00")
        ]

        for desc, amount in items:
            row = doc.create_node("row", parent=table)

            desc_cell = doc.create_node("cell", desc, parent=row)
            desc_cell.tag("line-item-description")

            amt_cell = doc.create_node("cell", f"${amount}", parent=row)
            amt_cell.tag("line-item-amount", value=amount, confidence=0.92)

        # Total
        total = doc.create_node("paragraph", "Total: $400.00", parent=root)
        total.tag("invoice-total", value="400.00", confidence=0.99)

        return doc
async function buildInvoiceStructure() {
  await Kodexa.init();
  const doc = await Kodexa.createDocument();

  try {
    // Document root
    const root = await doc.createNode('document');
    await root.setContent('Invoice #12345');

    // Header section
    const header = await doc.createNode('section');
    await header.setContent('Header');
    await root.addChild(header);

    const vendor = await doc.createNode('paragraph');
    await vendor.setContent('Vendor: Acme Corp');
    await header.addChild(vendor);
    await vendor.tagWithOptions('vendor-name', { value: 'Acme Corp', confidence: 0.98 });

    const date = await doc.createNode('paragraph');
    await date.setContent('Date: 2024-01-15');
    await header.addChild(date);
    await date.tagWithOptions('invoice-date', { value: '2024-01-15', confidence: 0.95 });

    // Line items table
    const table = await doc.createNode('table');
    await root.addChild(table);

    const items = [
      { desc: 'Widget A', amount: '100.00' },
      { desc: 'Widget B', amount: '250.00' },
      { desc: 'Service Fee', amount: '50.00' }
    ];

    for (const item of items) {
      const row = await doc.createNode('row');
      await table.addChild(row);

      const descCell = await doc.createNode('cell');
      await descCell.setContent(item.desc);
      await row.addChild(descCell);
      await descCell.tag('line-item-description');

      const amtCell = await doc.createNode('cell');
      await amtCell.setContent(`$${item.amount}`);
      await row.addChild(amtCell);
      await amtCell.tagWithOptions('line-item-amount', { value: item.amount, confidence: 0.92 });
    }

    // Total
    const total = await doc.createNode('paragraph');
    await total.setContent('Total: $400.00');
    await root.addChild(total);
    await total.tagWithOptions('invoice-total', { value: '400.00', confidence: 0.99 });

    return doc;
  } catch (e) {
    doc.dispose();
    throw e;
  }
}