# section

Group the content under a heading into one chunk — and know where the heading itself ends up.

`section` groups the body under a heading into a single chunk, so a search hit
returns a self-contained passage instead of one stray paragraph.

  In every markdown-pipeline format — Markdown, HTML, TXT, PDF, EPUB, RTF,
  EML/MBOX, MSG, ODT, ODP, JSON, IPYNB — the heading is emitted as a **separate
  chunk** with `content_type: "heading"`, and the `section` chunk that follows
  holds only the body. It is not folded in.

  If you build "return the whole section" by taking chunks where
  `content_type == "section"`, you drop every heading, and your index fills up
  with orphaned heading chunks that have no body. Reassemble instead: a
  `heading` chunk and the `section` chunk that follows it share the same
  `heading_path`.

  DOCX, DOC, PPTX and PPT are the exception — their own chunkers put the heading
  line at the top of the section chunk's content.

## Real input → real output

Run against a small `notes.md` with three headings, at default parameters:

```python
from py_chunks import get_chunks

# One chunk per section body, with the heading trail in heading_path.
# The heading itself stays a SEPARATE chunk — it is not folded into the body.
for c in get_chunks("notes.md", mode="section"):
    print(c["content_type"], "|", c["metadata"]["heading_path"])

# heading | ['Chunking Notes']
# section | ['Chunking Notes']
# heading | ['Chunking Notes', 'Why structure matters']
# section | ['Chunking Notes', 'Why structure matters']
# heading | ['Chunking Notes', 'Modes']
# section | ['Chunking Notes', 'Modes']
```

```ts
import { getChunks } from "js-chunks";

for (const c of await getChunks("./notes.md", { mode: "section" })) {
  console.log(c.contentType, "|", c.metadata.heading_path);
}

// heading | [ 'Chunking Notes' ]
// section | [ 'Chunking Notes' ]
// heading | [ 'Chunking Notes', 'Why structure matters' ]
// section | [ 'Chunking Notes', 'Why structure matters' ]
// heading | [ 'Chunking Notes', 'Modes' ]
// section | [ 'Chunking Notes', 'Modes' ]
```

```rust
use chunks_rs::get_chunks;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    for c in &get_chunks("notes.md", "section", 3, 1, 3, 15)? {
        println!("{} | {}", c.content_type, c.metadata["heading_path"]);
    }
    Ok(())
}

// heading | ["Chunking Notes"]
// section | ["Chunking Notes"]
// heading | ["Chunking Notes","Why structure matters"]
// section | ["Chunking Notes","Why structure matters"]
// heading | ["Chunking Notes","Modes"]
// section | ["Chunking Notes","Modes"]
```

Six chunks from three sections: heading, body, heading, body, heading, body.
The `heading_path` is what pairs them back up.

## What it emits

| Format | `content_type` values emitted |
| --- | --- |
| Markdown-family — Markdown, HTML, TXT, PDF, EPUB, RTF, EML/MBOX, MSG, ODT, ODP, JSON, IPYNB | `heading` **and** `section` |
| DOCX, DOC, PPTX family, PPT | `section` only — heading text is the first line of the chunk |
| Spreadsheets, CSV/TSV | not supported (`mode must be one of [page_aware, row, semantic, sheet, sliding_window, table] for XLSX`) |

A document with no headings produces no `heading` chunks, obviously — and
JSON/JSONL render no headings of their own, so they usually have none.

Measured: `prose_heavy.md` → 10 `section` + 10 `heading`;
`arxiv_1301.3781_word2vec.pdf` → 31 `section` + 10 `heading`;
`all_round.docx` → 9 `section`, 0 `heading`.

## Units and formats

| Family | Unit grouped into a section |
| --- | --- |
| Markdown-family (md, html, txt, pdf, epub, rtf, eml/mbox, msg, odt, odp, json, ipynb) | markdown block — paragraphs, lists, tables and code blocks under one heading |
| DOCX, DOC | paragraph, from one heading to the next |
| PPTX | slides grouped by PPTX sections, or a title-divider heuristic |
| PPT | slide |

## Parameters

`section` reads none of the four mode parameters. Boundaries come entirely from
the document's heading structure.

  A section body longer than 2,000 characters is split at a paragraph boundary
  into several `section` chunks. The pieces carry `split_part` and `split_total`
  so you can rejoin them; a section that fits in one chunk leaves both `null`.
  This applies to the markdown-family formats — DOCX and PPTX have their own
  section builders.

## Metadata

`section` chunks carry `section_heading`, `heading_path`, `section_level`,
`block_types`, `char_count`, `paragraph_count` and `chunk_index` — the exact set
varies by format. Full listing in the
[Metadata Reference](/docs/metadata-reference).

## When to use it

- Document search indexes where a hit should return a whole passage.
- RAG where you want enough surrounding context to answer, scoped to one topic.
- Navigation / outline features keyed on headings — the standalone `heading`
  chunks are exactly the outline.
