---
title: "“Did you mean” search suggestions in C#"
url: https://recursiveai.org/oneindex/cookbook/suggest-query-corrections/
description: "Suggest corrected search queries in C# with OneIndex. Build an unstemmed field dictionary and return “did you mean” suggestions with scores and edit counts."
version: "0.4.0-preview.2"
---

# “Did you mean” search suggestions in C#

**OneIndex** is a free, open-source search library for .NET from Recursive AI. MIT or Apache-2.0, preview, runs in-process.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneIndex) · [GitHub](https://github.com/recursive-ai-oss/oneindex-csharp) · [Docs](https://recursiveai.org/oneindex/docs/) · [Download example (.zip)](https://recursiveai.org/oneindex/cookbook/suggest-query-corrections/suggest-query-corrections.zip)

```shell
dotnet add package RecursiveAI.OneIndex --prerelease
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

The snippet reads the sample dictionary from the .zip; the full program below builds the dictionary itself.

```csharp
using OneIndex.Analysis;
using OneIndex.Codecs;
using OneIndex.Storage;
using OneIndex.Suggest;
var storage =
    new FileSystemStorage("dictionary");
using var reader = TextSegmentReader.Open(
    storage, "sample.terms",
    "sample.postings", "sample.lengths");
var field = reader.GetTextField("title")!;
var corrector = new DidYouMean(
    new WhitespaceLowercaseAnalyzer());
foreach (var suggestion in
    corrector.Suggest(field, "quikc brown"))
    Console.WriteLine(suggestion.Text);
```

The `dictionary/` files in the download contain `quick brown fox`, `quick brown dog`, and `quiet brown dog`.

```text
quick brown
```

Unzip [the example](https://recursiveai.org/oneindex/cookbook/suggest-query-corrections/suggest-query-corrections.zip), then `dotnet run`.

## The full program

This example uses preview 0.4.0-preview.2; APIs and file formats may change between previews.

The program builds a field dictionary from three strings: `quick brown fox`, `quick brown dog`, and `quiet brown dog`. It uses `WhitespaceLowercaseAnalyzer`, which lowercases words without stemming them (removing word endings).

The download includes the three generated dictionary files in `dictionary/`. Their only input is the text above. If they are absent, the program creates them. `TextSegmentReader` opens those same files for the snippet at the top.

```csharp
using OneIndex.Analysis;
using OneIndex.Codecs;
using OneIndex.Search;
using OneIndex.Storage;
using OneIndex.Suggest;
using System.Globalization;

var analyzer = new WhitespaceLowercaseAnalyzer();
string[] documents = ["quick brown fox", "quick brown dog", "quiet brown dog"];
var storage = new FileSystemStorage("dictionary");
if (!File.Exists("dictionary/sample.terms"))
{
    var buffer = new InvertedBuffer();
    buffer.DeclareField("title", PostingsFeatures.Frequencies);
    for (int id = 0; id < documents.Length; id++)
        buffer.AddField(id, "title", analyzer.GetTokens(documents[id].AsMemory()));
    using (var writer = new TextSegmentWriter(storage,
        "sample.terms", "sample.postings", "sample.lengths", documents.Length))
    {
        buffer.WriteTo(writer);
        writer.Finish();
    }
}
using var reader = TextSegmentReader.Open(storage,
    "sample.terms", "sample.postings", "sample.lengths");
var field = reader.GetTextField("title")!;
var corrector = new DidYouMean(analyzer);
foreach (var suggestion in corrector.Suggest(field, "quikc brown"))
    Console.WriteLine(FormattableString.Invariant(
        $"{suggestion.Text} | score {suggestion.Score:F3} | edits {suggestion.Edits}"));
```

`dotnet run` prints:

```text
quick brown | score -0.416 | edits 1
```

An empty suggestion list means no correction was proposed; it is not a list of matching documents.

## Variations

Replace the `corrector` declaration and print loop in the full program to allow an error in the first character and return at most one suggestion:

```csharp
var corrector = new DidYouMean(analyzer,
    new SuggestOptions { PrefixLength = 0, MaxSuggestions = 1 });
foreach (var suggestion in corrector.Suggest(field, "xuick brown"))
    Console.WriteLine(suggestion.Text);
```

```text
quick brown
```

## Related recipes

[Faceted search in C#](https://recursiveai.org/oneindex/cookbook/faceted-search/) · [Typo-tolerant search in C#](https://recursiveai.org/oneindex/cookbook/typo-tolerant-search/) · [Full-text search](https://recursiveai.org/oneindex/cookbook/full-text-search-csharp/)

---

## The short answer

Use `OneIndex.Suggest.DidYouMean` with a field’s dictionary readers and the same analyzer used to build that field. `Suggest` returns corrected query text, scores, edit counts, and individual corrections. It does not execute the corrected query or return documents. The full example builds an unstemmed dictionary, then proposes `quick brown` for `quikc brown`.

This is a separate feature from forgiving search. A UI can offer the proposed text as a clickable alternative, then execute a search only when the reader chooses it. The documented contract is [Did you mean](https://recursiveai.org/oneindex/docs/did-you-mean/).

## How do I add “did you mean” functionality in C#?

Build the suggestion vocabulary from the content that is actually searchable. The full program uses a text segment with one field called `title`; it adds analyzed tokens, finishes the segment writer, and opens an `ITextFieldReader` for that field. The reader supplies dictionary terms and document frequencies to the corrector. No external spelling dictionary is required.

Use an unstemmed field when the suggested text must be readable. A stemmed dictionary contains shortened terms, so the correction can contain those stems rather than normal words. The sample’s whitespace-lowercasing analyzer does not stem. It must be used consistently for indexing and suggesting; switching analyzers only at suggestion time cannot restore word endings missing from the dictionary.

The top snippet opens the sample files that come with the download. The full program also shows how to create them from the three visible strings, so you can replace the input with your own content. For a snapshot containing several text segments, use the overload accepting a list of field readers rather than considering just one segment. The corrector merges candidate frequencies across them.

Treat the result as advice, not an automatic replacement. Keep the typed text available and use the proposed text as the label for an alternate search. If there are no suggestions, there is no replacement to show. An empty list does not establish that the original query has no matching documents; matching is a separate operation.

## How does the Did-You-Mean feature work?

By default, only words absent from the field dictionary are corrected. Known words remain untouched, and numbers are never corrected. The default edit allowance begins at four code points for one edit and six for two. Unlike forgiving search’s first-letter penalty, the suggestion API requires the first code point to be correct by default. Set `PrefixLength = 0` to allow a first-letter correction, as the variation demonstrates.

Candidate terms are chosen by fewest edits, then document frequency, then term-byte order. Multi-word suggestions combine candidates and reward words that occur together in documents. The score includes frequency and co-occurrence rewards and an edit penalty. It is not a probability or a percentage confidence; the sample’s negative score is a valid ranking value, not a failure.

The default maximum is three suggestions. `MaxSuggestions` limits the returned alternatives; it does not guarantee that many. `SuggestMode.Popular` can also replace a known word when a close term is sufficiently more common, whereas the default `Missing` mode cannot. Punctuation and untouched spans are preserved, but replacement terms use their indexed, case-folded form. See the [suggestion settings](https://recursiveai.org/oneindex/docs/did-you-mean/) for the bounded dictionary walk and combination rules before tuning results on a larger vocabulary.
