---
title: "Searching document text | Nutrient .NET SDK"
canonical_url: "https://www.nutrient.io/guides/dotnet/csharp/extraction/search-document-text/"
md_url: "https://www.nutrient.io/guides/dotnet/csharp/extraction/search-document-text.md"
last_updated: "2026-10-08T00:00:00.000Z"
description: "Search and rank document text passages using Nutrient .NET SDK."
---

# Searching document text

A long PDF usually prompts a narrow question: *where does it mention the payment terms?* Reading the whole document back into your application — or running grep after grep — is slow and wasteful. Ranked text search answers the question directly, returning only the few passages that matter, each labeled with the original page it came from.

This sample shows how to search any document with Nutrient.NET SDK. The search runs fully offline, with no network calls.

[Download sample](https://www.nutrient.io/downloads/samples/csharp/search-document-text.zip)

## How Nutrient helps

Nutrient ranks a document with the proven BM-25 algorithm and returns the most relevant passages — each with the page it appears on — instead of the whole file. `Query.CreateIndex` converts an open `Document` to text and builds a searchable index, so you can ingest a PDF (or DOCX, PPTX, and other supported formats) and then query it with `TextSearch` as many times as you like.

The SDK handles:

- Converting the document and tokenizing and ranking its text

- Filtering weak matches so you receive a real answer or nothing — never a list padded with noise

- Optional word-variation matching (stemming), so a query for “organization” also matches “organizations”

- Reporting each result by its original page number

- Returning the result in your chosen format — context windows, plain text, or JSON

## Search a document

Configure the search on a `DocumentSettings`, open the document with it, and build a reusable search index from it with `Query.CreateIndex`. Then query that index — as many times as you like — by calling `TextSearch` on it. The [using statements](https://learn.microsoft.com/dotnet/csharp/language-reference/statements/using) release the settings and document handles after processing:

```csharp

using Nutrient;

try
{
    // Search configuration travels on the document's settings.
    using DocumentSettings settings = new DocumentSettings();
    settings.SearchSettings.TopK = 3;                   // return the three best passages
    settings.SearchSettings.StemmingLanguage = "auto";  // detect the language, match word variants

    using Document document = Document.Open("input.pdf", settings);
    Query index = Query.CreateIndex(document);         // convert + build the index once

    string result = index.TextSearch("How big is the Earth?");
    string text = string.IsNullOrEmpty(result)? "No relevant matches found." : result;

    File.WriteAllText("search-results.txt", text);
    Console.WriteLine(text);
}
catch (NutrientException e)
{
    Console.Error.WriteLine($"Error: {e.Message}");
    Environment.Exit(1);
}

```

`CreateIndex` converts the document and builds the index a single time; calling `TextSearch` on the returned index then queries it without re-doing either. The query is a natural-language string, matched case-insensitively; stack several relevant terms into one query, because BM-25 rewards rare, on-topic words.

## Understanding the output

Each result leads with the **original document page** and an approximate line, followed by the matching passage:

```text

Page 1, line 3
  We begin at home with Earth, a nearly spherical planet about 13,000 kilometers in diameter...

```

Results are keyed to the page because it’s a stable property of the source document; the line is an approximation — it points into the converted text and can shift between conversions, so use it as a within-page hint rather than an exact coordinate. When nothing clears the relevance floor, `TextSearch` returns an empty string — treat that as “no relevant match” rather than an error.

## Tune the search with settings

Every knob lives on the settings object that the `SearchSettings` property returns:

- `TopK` — the maximum number of passages to return (default `8`). Keep it small; you usually want the top one to three.

- `ContextLines` — how many lines of surrounding context to include with each hit (default `5`).

- `Mode` — how aggressively weak matches are filtered: `SearchMode.Strict`, `SearchMode.Balanced` (default), or `SearchMode.Lenient`.

- `Display` — the output format: `SearchDisplay.Windows` (default), `Text`, or `Json` for machine-readable results.

- `StemmingLanguage` — an ISO 639-1 code (`"en"`, `"fr"`, `"de"`, …) whose word-variation rules to apply, or `"auto"` to detect the document’s language. Empty means exact matching only.

For example, to receive JSON for downstream processing, set the display before building the index:

```text

settings.SearchSettings.Display = SearchDisplay.Json;

```

Each JSON result then carries its `page`, `score`, and `text`.

## Searching files, folders, and indexes

Besides a `Document`, you can build an index from a **path** with `Query.CreateIndex(path, settings)`, then call `TextSearch` on it as usual. The path is detected *by content* — not by extension, and with no flag — as one of three kinds:

```text

Query.CreateIndex("notes.md", settings).TextSearch(query);    // a text or Markdown file
Query.CreateIndex("./converted", settings).TextSearch(query); // a folder of.md /.txt files
Query.CreateIndex("report.idx", settings).TextSearch(query);  // a prebuilt index

```

- A **text or Markdown (`.md`/`.txt`) file** is searched line by line; results report **line ranges**.

- A **directory** searches every `.md`/`.txt` file in it as one collection, each result labeled by its source file.

- A **prebuilt index** is loaded as-is, skipping the tokenize-and-rank build step.

Anything else — a PDF, a Word file, a binary — is **rejected** with an error: open those as a `Document` and index them with `Query.CreateIndex(document)` instead.

### Persisting an index

The index a `Document` builds lives in memory. To reuse it across runs or processes, set the emit index path when building: the index is written to disk, and you reload it later by passing that file back — no document, no reconversion.

```text

settings.SearchSettings.EmitIndexPath = "report.idx";
using Document document = Document.Open("input.pdf", settings);
Query.CreateIndex(document);            // builds the index AND writes report.idx

// Later, in another process — no document needed:
Query.CreateIndex("report.idx", settings).TextSearch(query);

```

A file index built from a `Document` keeps its page information (reports `Page N`); one built from plain text reports line ranges.

## Error handling

Nutrient.NET SDK reports failures by throwing a `NutrientException`. Common failure scenarios include:

- The document can’t be opened or read due to a path or permission issue

- The search feature isn’t licensed

In production code, catch `NutrientException`, return a clear error message, and log the failure details for debugging.

## Conclusion

The workflow for ranked document search is:

1. Configure the search settings for the result count, format, and language you need.

2. Open the document with `Document.Open(path, settings)`.

3. Build a reusable index with `Query.CreateIndex(document)`.

4. Query it by calling `TextSearch(question)` on the index — as many times as you like.

5. Read the returned passages by page — or set the display to `SearchDisplay.Json` for downstream processing.

6. Handle `NutrientException` for robust error recovery.

For the conversion this builds on, refer to the [PDF to Markdown](https://www.nutrient.io/guides/dotnet/csharp/conversion/pdf-to-markdown.md) guide.

Download [this ready-to-use sample package](https://www.nutrient.io/downloads/samples/csharp/search-document-text.zip) to explore document search.
---

## Related pages

- [Nutrient .NET SDK extraction guides](/guides/dotnet/csharp/extraction.md)
- [Applying OCR to a PDF page](/guides/dotnet/csharp/extraction/apply-ocr-to-pdf-page.md)
- [Applying OCR to a PDF document](/guides/dotnet/csharp/extraction/apply-ocr-to-pdf.md)
- [Classifying documents](/guides/dotnet/csharp/extraction/classify-document.md)
- [Generating image descriptions using Claude](/guides/dotnet/csharp/extraction/describe-image-with-claude.md)
- [Generating image descriptions using local AI](/guides/dotnet/csharp/extraction/describe-image-with-local-ai.md)
- [Generating image descriptions using OpenAI](/guides/dotnet/csharp/extraction/describe-image-with-openai.md)
- [Detecting document language](/guides/dotnet/csharp/extraction/detect-document-language.md)
- [Extracting data from images using ICR](/guides/dotnet/csharp/extraction/extract-data-from-image-icr.md)
- [Extracting data from images using OCR](/guides/dotnet/csharp/extraction/extract-data-from-image-ocr.md)
- [Extracting data from images using vision language models](/guides/dotnet/csharp/extraction/extract-data-from-image-vlm.md)
- [Extracting data from specific pages](/guides/dotnet/csharp/extraction/extract-data-from-specific-pages.md)
- [Extracting form fields from images](/guides/dotnet/csharp/extraction/extract-form-fields-from-image.md)
- [Extracting structured data from documents](/guides/dotnet/csharp/extraction/extract-structured-data.md)
- [Generating extraction schemas](/guides/dotnet/csharp/extraction/generate-extraction-schema.md)
- [Extracting JSON data from a PDF document](/guides/dotnet/csharp/extraction/json-data-extraction.md)
- [Labeling form fields with a vision language model](/guides/dotnet/csharp/extraction/label-form-fields-with-vlm.md)
- [Parsing a document into structured content](/guides/dotnet/csharp/extraction/parse-document.md)
- [Extracting text from PDF documents](/guides/dotnet/csharp/extraction/pdf-to-text.md)
- [Reading barcodes with vision extraction](/guides/dotnet/csharp/extraction/read-barcodes-with-vision.md)
- [Extracting text from multilingual images](/guides/dotnet/csharp/extraction/read-text-from-image-multi-language.md)
- [Extracting text from images](/guides/dotnet/csharp/extraction/read-text-from-image.md)
- [Speeding up first ICR operation by predownloading models](/guides/dotnet/csharp/extraction/speed-up-first-icr-by-downloading-requirements.md)

