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:| Type | Description |
|---|---|
document | Root node of the document |
page | A page in the document |
section | A logical section |
paragraph | A paragraph of text |
line | A line of text |
word | An individual word |
table | A table structure |
row | A table row |
cell | A table cell |
image | An 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();
}
}
remove_tag is idempotent: if the node doesn’t carry the named tag, it’s a no-op and returns quietly rather than raising an error. When the tag is present, every instance of that tag name on the node is removed.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;
}
}
