> ## Documentation Index
> Fetch the complete documentation index at: https://doc.lucidworks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Token and Phrase Spell Correction Job

export const schema = {
  "type": "object",
  "title": "Token and Phrase Spell Correction",
  "description": "Computes token and phrase-level spell corrections from signals data for use in synonym lists and query rewrite rules.",
  "required": ["id", "trainingCollection", "fieldToVectorize", "dataFormat", "type"],
  "properties": {
    "id": {
      "type": "string",
      "title": "Spark Job ID",
      "description": "The ID for this Spark job. Used in the API to reference this job. Allowed characters: a-z, A-Z, dash (-) and underscore (_).",
      "maxLength": 63,
      "pattern": "[a-zA-Z][_\\-a-zA-Z0-9]*[a-zA-Z0-9]?"
    },
    "sparkConfig": {
      "type": "array",
      "title": "Spark Settings",
      "description": "Spark configuration settings.",
      "hints": ["advanced"],
      "items": {
        "type": "object",
        "required": ["key"],
        "properties": {
          "key": {
            "type": "string",
            "title": "Parameter Name"
          },
          "value": {
            "type": "string",
            "title": "Parameter Value"
          }
        }
      }
    },
    "trainingCollection": {
      "type": "string",
      "title": "Input Collection",
      "description": "Specifies the collection containing search strings and event counts, ideally the signals collection.",
      "minLength": 1
    },
    "fieldToVectorize": {
      "type": "string",
      "title": "Input Field",
      "description": "Specifies the field containing search strings.",
      "default": "query",
      "minLength": 1
    },
    "dataFormat": {
      "type": "string",
      "title": "Data format",
      "description": "Specifies the Spark-compatible input data format, such as `solr`, `parquet`, or `orc`.",
      "default": "solr",
      "minLength": 1
    },
    "trainingDataFrameConfigOptions": {
      "type": "object",
      "title": "Dataframe Config Options",
      "description": "Sets additional key-value configuration options passed to the Spark DataFrame reader at load time.",
      "properties": {},
      "additionalProperties": {
        "type": "string"
      },
      "hints": ["advanced"]
    },
    "trainingDataFilterQuery": {
      "type": "string",
      "title": "Data filter query",
      "description": "Filters training data using a Solr query when reading from Solr, or a SQL expression for other data sources.",
      "default": "*:*",
      "hints": ["advanced"]
    },
    "sparkSQL": {
      "type": "string",
      "title": "Spark SQL filter query",
      "description": "Filters the loaded DataFrame using a Spark SQL query before processing. The input data is registered as a temporary table named `spark_input`.",
      "default": "SELECT * from spark_input",
      "hints": ["code/sql", "advanced"]
    },
    "trainingDataSamplingFraction": {
      "type": "number",
      "title": "Training data sampling fraction",
      "description": "Sets the fraction of training data to sample before processing. Use a value between `0` and `1`. `1.0` uses all available data.",
      "default": 1,
      "hints": ["advanced"],
      "maximum": 1,
      "exclusiveMaximum": false
    },
    "randomSeed": {
      "type": "integer",
      "title": "Random seed",
      "description": "Sets the random seed for deterministic operations including sampling and initialization. Fix this value to reproduce identical results across runs.",
      "default": 1234,
      "hints": ["advanced"]
    },
    "outputCollection": {
      "type": "string",
      "title": "Output Collection",
      "description": "Specifies the collection where misspelling and correction pairs are written. Defaults to the `query_rewrite_staging` collection.",
      "hints": ["dummy"]
    },
    "overwriteOutput": {
      "type": "boolean",
      "title": "Overwrite Output",
      "description": "When enabled, overwrites the output collection before writing new results.",
      "default": true,
      "hints": ["hidden", "advanced"]
    },
    "dataOutputFormat": {
      "type": "string",
      "title": "Data output format",
      "description": "Specifies the Spark-compatible output format, such as `solr`, `parquet`, or `orc`.",
      "default": "solr",
      "hints": ["advanced"],
      "minLength": 1
    },
    "sourceFields": {
      "type": "string",
      "title": "Fields to Load",
      "description": "Specifies the document fields to load from the input source.",
      "hints": ["hidden"]
    },
    "partitionCols": {
      "type": "string",
      "title": "Partition fields",
      "description": "Specifies a comma-delimited list of column names used to partition output when writing to non-Solr sinks.",
      "hints": ["advanced"]
    },
    "writeOptions": {
      "type": "array",
      "title": "Write Options",
      "description": "Sets additional key-value options passed to the Spark writer when writing output to Solr or other sinks.",
      "hints": ["advanced"],
      "items": {
        "type": "object",
        "required": ["key"],
        "properties": {
          "key": {
            "type": "string",
            "title": "Parameter Name"
          },
          "value": {
            "type": "string",
            "title": "Parameter Value"
          }
        }
      }
    },
    "readOptions": {
      "type": "array",
      "title": "Read Options",
      "description": "Sets additional key-value options passed to the Spark reader when loading input from Solr or other sources.",
      "hints": ["advanced"],
      "items": {
        "type": "object",
        "required": ["key"],
        "properties": {
          "key": {
            "type": "string",
            "title": "Parameter Name"
          },
          "value": {
            "type": "string",
            "title": "Parameter Value"
          }
        }
      }
    },
    "stopwordsBlobName": {
      "type": "string",
      "title": "Stopwords blob (Deprecated)",
      "description": "Specifies the name of the stopwords blob resource (a `.txt` or `.rtf` file uploaded to the blob store).",
      "hints": ["advanced"],
      "minLength": 1,
      "reference": "blob",
      "blobType": "file:spark"
    },
    "dictionaryCollection": {
      "type": "string",
      "title": "Dictionary Collection",
      "description": "Specifies the Solr collection containing correctly spelled terms, such as a product catalog."
    },
    "dictionaryField": {
      "type": "string",
      "title": "Dictionary Field",
      "description": "Specifies the document field in the dictionary collection containing correctly spelled terms."
    },
    "countField": {
      "type": "string",
      "title": "Count Field",
      "description": "Specifies the document field containing the event count.",
      "default": "count_i"
    },
    "mainType": {
      "type": "string",
      "title": "Main Event Type",
      "description": "Specifies the primary signal event type (such as `click`) that drives spell correction.",
      "default": "click"
    },
    "filterType": {
      "type": "string",
      "title": "Filtering Event Type",
      "description": "Specifies the secondary signal event type (such as `response`) used to filter out rare searches.",
      "default": "response"
    },
    "signalTypeField": {
      "type": "string",
      "title": "Field Name of Signal Type",
      "description": "Specifies the document field containing the signal event type.",
      "default": "type",
      "hints": ["advanced"]
    },
    "minCountMain": {
      "type": "integer",
      "title": "Minimum Main Event Count",
      "description": "Sets the minimum number of main events (such as clicks after aggregation) required for a query to be considered for spell correction.",
      "default": 1
    },
    "minCountFilter": {
      "type": "integer",
      "title": "Minimum Filtering Event Count",
      "description": "Sets the minimum number of filtering events (such as searches after aggregation) required for a query to pass the frequency filter.",
      "default": 10
    },
    "dictionaryDataFilterQuery": {
      "type": "string",
      "title": "Dictionary Data Filter Query",
      "description": "Specifies a Solr query used to filter dictionary data when loading.",
      "default": "*:*",
      "hints": ["advanced"]
    },
    "minPrefix": {
      "type": "integer",
      "title": "Minimum Prefix Match",
      "description": "Sets the minimum number of leading characters that must match between a misspelling and its correction.",
      "default": 1,
      "minimum": 0,
      "exclusiveMinimum": false
    },
    "minMispellingLen": {
      "type": "integer",
      "title": "Minimum Length of Misspelling",
      "description": "Sets the minimum length of a misspelling to consider for correction. Shorter strings may produce problematic corrections.",
      "default": 5,
      "minimum": 1,
      "exclusiveMinimum": false
    },
    "maxDistance": {
      "type": "integer",
      "title": "Maximum Edit Distance",
      "description": "Sets the maximum edit distance between a misspelling and its correction. Larger values increase recall but also noise.",
      "default": 2,
      "minimum": 1,
      "exclusiveMinimum": false
    },
    "lastCharMatchBoost": {
      "type": "number",
      "title": "Last Character Match Boost",
      "description": "Sets the boost applied when the last character of the misspelling matches the correction, used for ranking correction candidates.",
      "default": 1,
      "hints": ["advanced"]
    },
    "soundMatchBoost": {
      "type": "number",
      "title": "Sound Match Boost",
      "description": "Sets the boost applied when a correction sounds similar to the misspelling (phonetic match), used for ranking correction candidates.",
      "default": 3,
      "hints": ["advanced"]
    },
    "correctCntBoost": {
      "type": "number",
      "title": "Correction Count Boost",
      "description": "Sets the boost applied based on the correction's occurrence count, used for ranking correction candidates.",
      "default": 2,
      "hints": ["advanced"]
    },
    "editDistBoost": {
      "type": "number",
      "title": "Edit Distance Boost",
      "description": "Sets the boost weight for edit distance in the correction ranking formula.",
      "default": 2,
      "hints": ["advanced"]
    },
    "signalDataIndicator": {
      "type": "boolean",
      "title": "Input is Signal Data",
      "description": "When enabled, indicates that the input data is signal data. When disabled, indicates it is content document data.",
      "default": true
    },
    "analyzerConfigQuery": {
      "type": "string",
      "title": "Lucene Analyzer Schema for Processing Queries",
      "description": "Default is `'{ \"analyzers\": [ { \"name\": \"LetterTokLowerStem\",\"charFilters\": [ { \"type\": \"htmlstrip\" } ],\"tokenizer\": { \"type\": \"letter\" },\"filters\": [{ \"type\": \"lowercase\" },{ \"type\": \"KStem\" }] }],\"fields\": [{ \"regex\": \".+\", \"analyzer\": \"LetterTokLowerStem\" } ]}'`.",
      "default": "{ \"analyzers\": [ { \"name\": \"LetterTokLowerStem\",\"charFilters\": [ { \"type\": \"htmlstrip\" } ],\"tokenizer\": { \"type\": \"letter\" },\"filters\": [{ \"type\": \"lowercase\" },{ \"type\": \"KStem\" }] }],\"fields\": [{ \"regex\": \".+\", \"analyzer\": \"LetterTokLowerStem\" } ]}",
      "hints": ["lengthy", "code/json"],
      "minLength": 1
    },
    "analyzerConfigDictionary": {
      "type": "string",
      "title": "Lucene Analyzer Schema for Processing Dictionary",
      "description": "Default is `'{ \"analyzers\": [ { \"name\": \"LetterTokLowerStem\",\"charFilters\": [ { \"type\": \"htmlstrip\" } ],\"tokenizer\": { \"type\": \"letter\" },\"filters\": [{ \"type\": \"lowercase\" },{ \"type\": \"KStem\" }] }],\"fields\": [{ \"regex\": \".+\", \"analyzer\": \"LetterTokLowerStem\" } ]}'`.",
      "default": "{ \"analyzers\": [ { \"name\": \"LetterTokLowerStem\",\"charFilters\": [ { \"type\": \"htmlstrip\" } ],\"tokenizer\": { \"type\": \"letter\" },\"filters\": [{ \"type\": \"lowercase\" },{ \"type\": \"KStem\" }] }],\"fields\": [{ \"regex\": \".+\", \"analyzer\": \"LetterTokLowerStem\" } ]}",
      "hints": ["lengthy", "code/json"],
      "minLength": 1
    },
    "correctionThreshold": {
      "type": "number",
      "title": "Correct Spelling Threshold",
      "description": "Sets the occurrence count above which a token or phrase is classified as a correct spelling.",
      "default": 0.8,
      "hints": ["advanced"]
    },
    "misspellingThreshold": {
      "type": "number",
      "title": "Misspelling Threshold",
      "description": "Sets the occurrence count below which a token or phrase is classified as a potential misspelling.",
      "default": 0.8,
      "hints": ["advanced"]
    },
    "lenScale": {
      "type": "integer",
      "title": "Edit Dist vs String Length Scale",
      "description": "Sets a scaling factor to normalize query string length when comparing against edit distances.",
      "default": 5,
      "hints": ["advanced"]
    },
    "corMisRatio": {
      "type": "number",
      "title": "Correction and Misspelling Count Ratio",
      "description": "Sets the minimum ratio between correction occurrence count and misspelling occurrence count. Pairs below this ratio are excluded.",
      "default": 3,
      "hints": ["advanced"]
    },
    "stopwordsList": {
      "type": "array",
      "title": "List of stopwords",
      "description": "Specifies stop words defined in the Lucene analyzer configuration to exclude from processing.",
      "hints": ["readonly", "hidden"],
      "items": {
        "type": "string",
        "minLength": 1,
        "reference": "blob",
        "blobType": "file:spark"
      }
    },
    "enableAutoPublish": {
      "type": "boolean",
      "title": "Enable auto-publishing",
      "description": "When enabled, automatically publishes rewrites for rules without requiring manual review.",
      "default": false,
      "hints": ["advanced"]
    },
    "sparkPartitions": {
      "type": "integer",
      "title": "Set minimum Spark partitions for input",
      "description": "Sets the number of Spark partitions to repartition input into before processing. Increase for greater parallelism on large datasets.",
      "default": 200,
      "hints": ["advanced"]
    },
    "type": {
      "type": "string",
      "title": "Spark Job Type",
      "enum": ["tokenPhraseSpellCorrection"],
      "default": "tokenPhraseSpellCorrection",
      "hints": ["readonly"]
    }
  },
  "additionalProperties": true,
  "category": "Other",
  "categoryPriority": 1,
  "propertyGroups": [{
    "label": "Input/Output Parameters",
    "properties": ["trainingCollection", "outputCollection", "dataFormat", "trainingDataFilterQuery", "readOptions", "writeOptions", "trainingDataFrameConfigOptions", "trainingDataSamplingFraction", "randomSeed", "signalDataIndicator"]
  }, {
    "label": "Field Parameters",
    "properties": ["fieldToVectorize", "sourceFields", "signalTypeField", "mainType", "filterType", "countField"]
  }, {
    "label": "Boost Parameters",
    "properties": ["lastCharMatchBoost", "soundMatchBoost", "correctCntBoost", "editDistBoost"]
  }, {
    "label": "Model Tuning Parameters",
    "properties": ["minCountMain", "minCountFilter", "correctionThreshold", "misspellingThreshold", "lenScale", "corMisRatio", "maxDistance", "minMispellingLen", "minPrefix"]
  }, {
    "label": "Featurization Parameters",
    "properties": ["analyzerConfigQuery"]
  }, {
    "label": "Misc. Parameters",
    "properties": ["stopwordsBlobName", "dictionaryCollection", "dictionaryField", "dictionaryDataFilterQuery", "analyzerConfigDictionary"]
  }]
};

export const SchemaParamFields = ({schema}) => {
  const sanitize = str => {
    if (typeof str !== "string") return str;
    return str.replace(/^"(.*)"$/s, "$1").replace(/\\/g, "").replace(/"/g, "'");
  };
  const renderMd = str => {
    const s = sanitize(str);
    const text = (/[.!?]\)*$/).test(s) ? s : `${s}.`;
    return text.split(/(\*\*[^*]+\*\*|_[^_]+_|`[^`]+`)/g).map((part, i) => {
      if (part.startsWith("**")) return <strong key={i}>{part.slice(2, -2)}</strong>;
      if (part.startsWith("_")) return <em key={i}>{part.slice(1, -1)}</em>;
      if (part.startsWith("`")) return <code key={i}>{part.slice(1, -1)}</code>;
      return part;
    });
  };
  const {description, properties = {}, required: requiredProps = []} = schema;
  const visibleProps = useMemo(() => Object.entries(properties).filter(([, prop]) => !prop.hints?.includes("hidden")), [properties]);
  const renderProp = ([name, prop]) => {
    const isRequired = requiredProps.includes(name);
    const hasDefault = prop.default !== undefined;
    const rawDefault = prop.default;
    const hints = prop.hints || [];
    const isComplexDefault = hasDefault && (typeof rawDefault === "object" || typeof rawDefault === "string" && (rawDefault.length > 20 || rawDefault.includes('"')));
    const postBadges = [];
    if (prop.title) {
      postBadges.push(<><span className="text-stone-400 dark:text-stone-500">API property: </span>{name}</>);
    }
    const constraints = [];
    if (prop.minimum !== undefined && prop.maximum !== undefined) {
      constraints.push(`Range: ${prop.minimum} – ${prop.maximum}`);
    } else if (prop.minimum !== undefined) {
      constraints.push(`Min: ${prop.minimum}`);
    } else if (prop.maximum !== undefined) {
      constraints.push(`Max: ${prop.maximum}`);
    }
    if (prop.minLength !== undefined && prop.maxLength !== undefined) {
      constraints.push(`Length: ${prop.minLength} – ${prop.maxLength}`);
    } else if (prop.minLength !== undefined) {
      constraints.push(`Min length: ${prop.minLength}`);
    } else if (prop.maxLength !== undefined) {
      constraints.push(`Max length: ${prop.maxLength}`);
    }
    const fieldProps = {
      key: name,
      body: prop.title || name,
      type: prop.type,
      ...postBadges.length > 0 && ({
        post: postBadges
      }),
      ...isRequired && ({
        required: true
      }),
      ...!isComplexDefault && hasDefault ? {
        default: sanitize(String(rawDefault))
      } : {}
    };
    const isObject = prop.type === "object" && prop.properties;
    const isArrayOfObjects = prop.type === "array" && prop.items?.type === "object" && prop.items.properties;
    return <ParamField {...fieldProps}>
        {prop.description && <p>{renderMd(prop.description)}</p>}

        {prop.enum && <p>
            Allowed values: 
            {prop.enum.map((v, i) => <>{i > 0 && ", "}<code key={i}>{String(v)}</code></>)}
          </p>}

        {constraints.length > 0 && <p className="text-stone-500 dark:text-stone-400 text-sm">
            {constraints.join(" · ")}
          </p>}

        {isComplexDefault && <div className="flex">
            <p>
              <strong>Default:</strong>
            </p>
            <pre className="!my-0">
              <code>
                {JSON.stringify(rawDefault, null, 2)}
              </code>
            </pre>
          </div>}

        {isArrayOfObjects && <Expandable title="item properties">
            <SchemaParamFields schema={{
      properties: prop.items.properties,
      required: prop.items.required
    }} />
          </Expandable>}

        {isObject && <Expandable title="properties">
            <SchemaParamFields schema={{
      properties: prop.properties,
      required: prop.required
    }} />
          </Expandable>}
      </ParamField>;
  };
  return <div>
      {description && <p>{renderMd(description)}</p>}

      {visibleProps.map(renderProp)}
    </div>;
};

export const LwTemplate = ({title = "Key questions to get you started", icon = "sparkles", cta = "Powered by Agent Studio", linkHref = "https://lucidworks.com/demo/?utm_source=docs&utm_medium=referral&utm_campaign=docs_cta_ai"}) => {
  const [isLoaded, setIsLoaded] = useState(false);
  useEffect(() => {
    const timer = setTimeout(() => {
      setIsLoaded(true);
    }, 500);
    return () => clearTimeout(timer);
  }, []);
  return <div className="lw-template-container">
      <Card title={title} icon={icon}>
        {isLoaded && <span dangerouslySetInnerHTML={{
    __html: `<lw-template id="a029c1a9-28be-427e-b0e1-5d918920246a"></lw-template
            >`
  }} />}
        <Link href={linkHref} className="agent-studio-link text-left text-gray-600 gap-2 dark:text-gray-400 text-sm font-medium flex flex-row items-center hover:text-primary dark:hover:text-primary-light group-hover:text-primary group-hover:dark:text-primary-light">Powered by Lucidworks Agent Studio</Link>
      </Card>
    </div>;
};

export const InlineImage = ({src, alt = '', height = '2em'}) => {
  return <img src={src} alt={alt} style={{
    display: 'inline',
    verticalAlign: 'start',
    height: height,
    margin: '0'
  }} />;
};

[localhost link]: http://localhost:3000/docs/5/fusion/reference/config-ref/jobs/token-and-phrase-spell-correction

[mintlify link]: https://doc.lucidworks.com/docs/5/fusion/reference/config-ref/jobs/token-and-phrase-spell-correction

[old doc.lw link]: https://doc.lucidworks.com/fusion/5.9/566

Detect misspellings in queries or documents using the numbers of occurrences of words and phrases.

<Note>
  This job is deprecated in Fusion 5.9.15 and will be removed in a future release.
  Lucidworks recommends migrating to [Neural Hybrid Search](/docs/5/fusion/hybrid-search/overview), which achieves superior relevance compared to legacy machine learning methods.
</Note>

<Card title="Resolving Underperforming Queries" class="note-image" href="https://academy.lucidworks.com/resolving-underperforming-queries-with-query-rewriting-jobs" cta="Take this course on the LucidAcademy." icon="graduation-cap" iconType="duotone">
  The course for **Resolving Underperforming Queries** focuses on tips for tuning, running, and cleaning up Fusion’s query rewrite jobs.
</Card>

|                      |                                                                              |
| -------------------- | ---------------------------------------------------------------------------- |
| **Default job name** | `COLLECTION_NAME_spell_correction`                                           |
| **Input**            | Raw signals (the `COLLECTION_NAME_signals` collection by default)            |
| **Output**           | Synonyms (the `COLLECTION_NAME_query_rewrite_staging` collection by default) |

|                          | query | count\_i | type | timestamp\_tdt | user\_id | doc\_id | session\_id | fusion\_query\_id |
| ------------------------ | ----- | -------- | ---- | -------------- | -------- | ------- | ----------- | ----------------- |
| Required signals fields: | ✅     | ✅        | ✅    |                |          |         |             |                   |

This job extracts tail tokens (one word) and phrases (two words) and finds similarly-spelled head tokens and phrases. For example, if two queries are spelled similarly, but one leads to a lot of traffic (head) and the other leads to a little or zero traffic (tail), then it is likely that the tail query is misspelled and the head query is its correction.

If several matching head tokens are found for each tail token, the job can pick the best correction using multiple configurable criteria.

You can review, edit, deploy, or delete output from this job using the [Query Rewriting UI](/docs/5/fusion/getting-data-out/query-enhancement/query-rewriting).

<Tip>
  Misspelled terms are completely replaced by their corrected terms. If you want to expand the query to include all alternative terms, set the synonyms to bi-directional. See [Synonym Detection](/docs/5/fusion/reference/config-ref/jobs/synonym-detection) for more information.
</Tip>

This job’s output, and output from the [Phrase Extraction job](/docs/5/fusion/reference/config-ref/jobs/phrase-extraction), can be used as input for the [Synonym Detection job](/docs/5/fusion/reference/config-ref/jobs/synonym-detection).

Solr treats spelling corrections as synonyms. See the blog post [Multi-Word Synonyms: Solr Adds Query-Time Support](https://lucidworks.com/post/multi-word-synonyms-solr-adds-query-time-support//) for more details.

<LwTemplate />

## 1. Create a job

Create a Token and Phrase Spell Correction job in the Jobs Manager.

**How to create a new job**

1. In the Fusion workspace, navigate to <InlineImage src="/assets/images/5.0/icons/workspace-menu-collections.png" /> > **Jobs**.
2. Click **Add** and select the job type **Token and phrase spell correction**.\
   The New Job Configuration panel appears.

## 2. Configure the job

Use the information in this section to configure the Token and Phrase Spell Correction job.

### Required configuration

The configuration must specify:

* **Spark Job ID.** Used in the API to reference the job. Maximum 63 alphabetic characters, hyphen (-), and underscore (\_).
* **INPUT COLLECTION.** The `trainingCollection` parameter that can contain signal data or non-signal data. For signal data, select **Input is Signal Data** (`signalDataIndicator`). Signals can be raw (from the `_signals` collection) aggregated (from the `_signals_aggr` collection).
* **INPUT FIELD.** The `fieldToVectorize` parameter.
* **COUNT FIELD**
  For example, if signal data follows the default Fusion setup, then `count_i` is the field that records the count of raw signals and `aggr_count_i` is the field that records the count after aggregation.

<Note>
  See [Configuration properties](#configuration-properties) for more information.
</Note>

### Event types

The spell correction job lets you analyze query performance based on two different events:

* The main event (the **Main Event Type**/`mainType` parameter)
* The filtering/secondary event (the **Filtering Event Type**/`filterType` parameter)\
  If you only have one event type, leave this parameter empty.

For example, if you specify the main event type to be `click` with a minimum count of 0 and the filtering event type to be `query` with a minimum count of 20, then the job:

* Filters on the queries that get searched at least 20 times.
* Checks among those popular queries to determine which ones didn’t get clicked at all, or were only clicked a few times.

### Spell check documents

If you unselect the **Input is Signal Data** checkbox to indicate finding misspellings from content documents rather than signals, then you do not need to specify the following parameters:

* Count Field
* Main Event Field
* Filtering Event Type
* Field Name of Signal Type
* Minimum Main Event Count
* Minimum Filtering Event Count

### Use a custom dictionary

You can upload a custom dictionary of terms that are specific to your data, and specify it using the **Dictionary Collection** (`dictionaryCollection`) and **Dictionary Field** (`dictionaryField`) parameters. For example, in an e-commerce use case, you can use the catalog terms as the custom dictionary by specifying the product catalog collection as the dictionary collection and the product description field as the dictionary field.

### Example configuration

This is an example configuration:

<img src="https://mintcdn.com/lucidworks/NR6PWuMFSzL-y-FO/assets/images/4.1/spellcheck-config.png?fit=max&auto=format&n=NR6PWuMFSzL-y-FO&q=85&s=97285b7285ce094a8be3a51f264e90f8" alt="Spell correction job configuration" width="1193" height="3746" data-path="assets/images/4.1/spellcheck-config.png" />

When you have configured the job, click **Save** to save the configuration.

## 3. Run the job

<Tip>
  If you are finding spelling corrections in aggregated data, you need to run an aggregation job before running the Token and Phrase Spelling Correction job. You *do not* need to run a Head/Tail Analysis job. The Token and Phrase Spell Correction job does the head/tail processing it requires.
</Tip>

**How to run the job**

1. In the Fusion workspace, navigate to <InlineImage src="/assets/images/5.0/icons/workspace-menu-collections.png" /> > **Jobs**.
2. Select the job from the job list.
3. Click **Run**.
4. Click **Start**.

## 4. Analyze job output

After the job finishes, misspellings and corrections are output into the
`query_rewrite_staging`
collection by default; you can change this by setting the `outputCollection`.

An example record is as follows:

```txt theme={"dark"}
correction_s                  laptop battery
mis_string_len_i              14
misspelling_s                 laptop baytery
aggr_job_id_s                 162fcf94b20T3704c333
score                         1
collation_check_s             token correction included
corCount_misCount_ratio_d     2095
sound_match_b                 true
id                            bf79c43b-fc6d-43a7-931e-185fdac5b624
aggr_type_s                   tokenPhraseSpellCorrection
aggr_id_s                     ecom_spell_check
correction_types_s            phrase => phrase
cor_count_i                   68648960
suggested_correction_s        baytery=>battery
cor_string_len_i              14
token_wise_correction_s       baytery=>battery
cor_token_size_i              2
edit_dist_i                   1
timestamp_tdt                 2018-04-25T13:23:40.728Z
mis_count_i                   32768
lastChar_match_b              true
mis_token_size_i              2
token_corr_for_phrase_cnt_i   1
```

For easy evaluation, you can export the result output to a CSV file.

<img src="https://mintcdn.com/lucidworks/NR6PWuMFSzL-y-FO/assets/images/4.1/spellcheck-output.png?fit=max&auto=format&n=NR6PWuMFSzL-y-FO&q=85&s=2b6d657d803c35d7fa3f95057c1cb12e" alt="Spellcheck output" width="2232" height="1276" data-path="assets/images/4.1/spellcheck-output.png" />

## 5. Use spell correction results

You can use the resulting corrections in various ways. For example:

* Put misspellings into the synonym list to perform auto-correction.
* Help evaluate and guide the Solr spellcheck configuration.
* Put misspellings into typeahead or autosuggest lists.
* Perform document cleansing (for example, clean a product catalog or medical records) by mapping misspellings to corrections.

### Useful output fields

In the job output, you generally only need to analyze the `suggested_corrections` field, which provides suggestions about using token correction or whole-phrase correction. If the confidence of the correction is not high, then the job labels the pair as "review" in this field. Pay special attention to the output records with the "review" labels.

With the output in a CSV file, you can sort by `mis_string_len` (descending) and `edit_dist` (ascending) to position more probable corrections at the top. You can also sort by the ratio of correction traffic over misspelling traffic (the `corCount_misCount_ratio` field) to only keep high-traffic boosting corrections.

For phrase misspellings, the misspelled tokens are separated out and put in the `token_wise_correction` field. If the associated token correction is already included in the one-word correction list, then the `collation_check` field is labeled as "token correction include." You can choose to drop those phrase misspellings to reduce duplications.

Fusion counts how many phrase corrections can be solved by the same token correction and puts the number into the `token_corr_for_phrase_cnt` field. For example, if both "outdoor surveillance" and "surveillance camera" can be solved by correcting "surveillance" to "surveillance", then this number is 2, which provides some confidence for dropping such phrase corrections and further confirms that correcting "surveillance" to "surveillance" is legitimate.

You might also see cases where the token-wise correction is not included in the list. For example, "xbow" to "xbox" is not included in the list because it can be dangerous to allow an edit distance of 1 in a word of length 4. But if multiple phrase corrections can be made by changing this token, then you can add this token correction to the list.

<Tip>
  Phrase corrections with a value of 1 for `token_corr_for_phrase_cnt` and with `collation_check` labeled as "token correction not included" could be potentially-problematic corrections.
</Tip>

Fusion labels misspellings due to misplaced whitespaces with "combine/break words" in the `correction_types` field. If there is a user-provided dictionary to check against, and both spellings are in the dictionary with and without whitespace in the middle, we can treat these pairs as bi-directional synonyms ("combine/break words (bi-direction)" in the `correction_types` field).

The `sound_match` and `lastChar_match` fields also provide useful information.

## Job tuning

The job’s default configuration is a conservative, designed for higher accuracy and lower output. To produce a higher volume of output, you can consider giving more permissive values to the parameters below. Likewise, give them more restrictive values if you are getting too many results with low accuracy.

<Note>
  When tuning these values, always test the new configuration in a non-production environment before deploying it in production.
</Note>

|                                                                                                                                                                 |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `trainingDataFilterQuery`/**Data filter query**                                                                                                                 |
| See [Event types](#event-types) above, then adjust this value to reflect the secondary event for your search application. To query all data, set this to `*:*`. |
| `minCountFilter`/**Minimum Filtering Event Count**                                                                                                              |
| Lower this value to include less-frequent misspellings based on the data filter query.                                                                          |
| `maxDistance`/**Maximum Edit Distance**                                                                                                                         |
| Raise this value to increase the number of potentially-related tokens and phrases detected.                                                                     |
| `minMispellingLen`/**Minimum Length of Misspelling**                                                                                                            |
| Lower this value to include shorter misspellings (which are harder to correct accurately).                                                                      |

## Query rewrite jobs post-processing cleanup

To perform more extensive cleanup of query rewrites, complete the procedures in **Query Rewrite Jobs Post-processing Cleanup**.

<Accordion title="Query Rewrite Jobs Post-processing Cleanup">
  <Danger>The Synonym Detection job uses the output of the Misspelling Detection job and Phrase Extraction job.  Therefore, post processing *must* occur in the order specified in this topic for the [Synonym detection job cleanup](#synonym-detection-job-cleanup), [Phrase extraction job cleanup](#phrase-extraction-job-cleanup), and [Misspelling detection job cleanup](#misspelling-detection-job-cleanup) procedures.  The [Head-Tail Analysis job cleanup](#head-tail-analysis-job-cleanup) can occur in any order.</Danger>

  ## Synonym detection job cleanup

  Use this job to remove low confidence synonyms.

  ### Prerequisites

  Complete this:

  * **AFTER** the Misspelling Detection and Phrase Extraction jobs have successfully completed.
  * **BEFORE** removing low confidence synonym suggestions generated in the *post processing* phrase extraction cleanup and misspelling detection cleanup procedures detailed later in this topic.

  ### Remove low confidence synonym suggestions

  Use either a [Synonym cleanup method 1 - API call](#synonym-cleanup-method-1-api-call) or the [Synonym cleanup method 2 - Fusion Admin UI](#synonym-cleanup-method-2-fusion-admin-ui) to remove low confidence synonym suggestions.

  #### Synonym cleanup method 1 - API call

  1. Open the `delete_lowConf_synonyms.json` file.
     ```json theme={"dark"}
     {
     "type" : "rest-call",
     "id" : "DC_Large_QR_DELETE_LOW_CONFIDENCE_SYNONYMS",
     "callParams" : {
         "uri" : "solr://DC_Large_query_rewrite_staging/update",
         "method" : "post",
         "queryParams" : {
         "wt" : "json"
         },
         "headers" : { },
         "entity" : "<root><delete><query>type:synonym AND confidence:[0 TO 0.0005]</query></delete><commit/></root>"
     },
     "type" : "rest-call",
     "type" : "rest-call"
     }
     ```
       <Note>
         **REQUEST ENTITY** specifies the threshold for low confidence synonyms. Edit the upper range from 0.0005 to increase or decrease the threshold based on your data.
       </Note>
  2. Enter `<your query_rewrite_staging collection name/update>` in the **uri** field. An example URI value for an app called `DC_Large` would be `DC_Large_query_rewrite_staging/update`.
  3. Change the `id` field if applicable.
  4. Specify the **upper confidence level** in the **entity** field.

     <Note>   The **entity** field specifies the threshold for low confidence synonyms. Edit the upper range to increase or decrease the threshold based on your data.</Note>

  #### Synonym cleanup method 2 - Fusion Admin UI

  1. Log in to Fusion and select **Collections > Jobs**.
  2. Select **Add+ > Custom and Other Jobs > REST Call**.
  3. Enter **delete-low-confidence-synonyms** in the **ID** field.
  4. Enter `<your query_rewrite_staging collection name/update>` in the **ENDPOINT URI** field. An example URI value for an app called `DC_Large` would be `DC_Large_query_rewrite_staging/update`.
  5. Enter **POST** in the **CALL METHOD** field.
  6. In the **QUERY PARAMETERS** section, select **+** to add a property.
  7. Enter **wt** in the **Property Name** field.
  8. Enter **json** in the **Property Value** field.
  9. In the **REQUEST PROTOCOL HEADERS** section, select **+** to add a property.
  10. Enter the following as a **REQUEST ENTITY (AS STRING)**

      `<root><delete><query>type:synonym AND confidence: [0 TO 0.0005]</query></delete><commit/></root>`

      <Note>   **REQUEST ENTITY** specifies the threshold for low confidence synonyms. Edit the upper range from 0.0005 to increase or decrease the threshold based on your data.</Note>

  ### Delete all synonym suggestions

  To delete *all* of the synonym suggestions, enter the following in the **REQUEST ENTITY** section:

  `<root><delete><query>type:synonym</query></delete><commit/></root>`

  <Note>This entry may be helpful when tuning the synonym detection job and testing different configuration parameters.</Note>

  ## Phrase extraction job cleanup

  Use this job to remove low confidence phrase suggestions.

  ### Prerequisites

  Complete this:

  * **AFTER** you complete [Synonym detection job cleanup](#synonym-detection-job-cleanup)

  ### Remove low confidence phrase suggestions

  Use either a [Phrase cleanup method 1 - API call](#phrase-cleanup-method-1-api-call) or the [Phrase cleanup method 2 - Fusion Admin UI](#phrase-cleanup-method-2-fusion-admin-ui) to remove low confidence phrase suggestions.

  #### Phrase cleanup method 1 - API call

  1. Open the `delete_lowConf_phrases.json` file.
     ```json theme={"dark"}
     {
     "type" : "rest-call",
     "id" : "DC_Large_QR_DELETE_LOW_CONFIDENCE_PHRASES",
     "callParams" : {
         "uri" : "solr://DC_Large_query_rewrite_staging/update",
         "method" : "post",
         "queryParams" : {
         "wt" : "json"
         },
         "headers" : { },
         "entity" : " <root><delete><query>type:phrase AND confidence:[0 TO <INSERT VALUE HERE>]</query></delete><commit/></root>"
     },
     "type" : "rest-call",
     "type" : "rest-call"
     }
     ```
  2. Enter `<your query_rewrite_staging collection name/update>` in the **uri** field. An example URI value for an app called `DC_Large` would be `DC_Large_query_rewrite_staging/update`.
  3. Change the **id** field if applicable.
  4. Specify the **upper confidence level** in the **entity** field.

     <Note>   The **entity** field specifies the threshold for low confidence phrases. Edit the upper range to increase or decrease the threshold based on your data.</Note>

  #### Phrase cleanup method 2 - Fusion Admin UI

  1. Log in to Fusion and select **Collections > Jobs**.
  2. Select **Add+ > Custom and Other Jobs > REST Call**.
  3. Enter **remove-low-confidence-phrases** in the **ID** field.
  4. Enter `<your query_rewrite_staging collection name/update>` in the **ENDPOINT URI** field. An example URI value for an app called `DC_Large` would be `DC_Large_query_rewrite_staging/update`.
  5. Enter **POST** in the **CALL METHOD** field.
  6. In the **QUERY PARAMETERS** section, select **+** to add a property.
  7. Enter **wt** in the **Property Name** field.
  8. Enter **json** in the **Property Value** field.
  9. In the **REQUEST PROTOCOL HEADERS** section, select **+** to add a property.
  10. Enter the following as a **REQUEST ENTITY (AS STRING)**

      `<root><delete><query>type:phrase AND confidence: [0 TO <insert value>]</query></delete><commit/></root>`

      <Note>   **REQUEST ENTITY** specifies the threshold for low confidence phrases. Edit the upper range to increase or decrease the threshold based on your data.</Note>

  ### Delete all phrase suggestions

  To delete *all* of the phrase suggestions, enter the following in the **REQUEST ENTITY** section:

  `<root><delete><query>type:phrase</query></delete><commit/></root>`

  <Note>This entry may be helpful when tuning the phrase extraction job and testing different configuration parameters.</Note>

  ## Misspelling detection job cleanup

  Use this job to remove low confidence spellings (also referred to as misspellings).

  ### Prerequisites

  Complete this:

  * **AFTER** you complete [Synonym detection job cleanup](#synonym-detection-job-cleanup) and [Phrase extraction job cleanup](#phrase-extraction-job-cleanup)

  ### Remove misspelling suggestions

  Use either a [Misspelling cleanup method 1 - API call](#misspelling-cleanup-method-1-api-call) or the [Misspelling cleanup method 2 - Fusion Admin UI](#misspelling-cleanup-method-2-fusion-admin-ui) to remove misspelling suggestions.

  #### Misspelling cleanup method 1 - API call

  1. Open the `delete_lowConf_misspellings.json` file.
     ```json theme={"dark"}
     {
     "type" : "rest-call",
     "id" : "DC_Large_QR_DELETE_LOW_CONFIDENCE_MISSPELLINGS",
     "callParams" : {
         "uri" : "solr://DC_Large_query_rewrite_staging",
         "method" : "post",
         "queryParams" : {
         "wt" : "json"
         },
         "headers" : { },
         "entity" : "<root><delete><query>type:spell AND confidence:[0 TO 0.5]</query></delete><commit/></root>"
     },
     "type" : "rest-call",
     "type" : "rest-call"
     }
     ```
  2. Enter `<your query_rewrite_staging collection name/update>` in the **uri** field. An example URI value for an app called `DC_Large` would be `DC_Large_query_rewrite_staging/update`.
  3. Change the **id** field if applicable.
  4. Specify the **upper confidence level** in the **entity** field.

     <Note>   The **entity** field specifies the threshold for low confidence spellings. Edit the upper range to increase or decrease the threshold based on your data.</Note>

  #### Misspelling cleanup method 2 - Fusion Admin UI

  1. Log in to Fusion and select **Collections > Jobs**.
  2. Select **Add+ > Custom and Other Jobs > REST Call**.
  3. Enter **remove-low-confidence-spellings** in the **ID** field.
  4. Enter `<your query_rewrite_staging collection name/update>` in the **ENDPOINT URI** field. An example URI value for an app called `DC_Large` would be `DC_Large_query_rewrite_staging/update`.
  5. Enter **POST** in the **CALL METHOD** field.
  6. In the **QUERY PARAMETERS** section, select **+** to add a property.
  7. Enter **wt** in the **Property Name** field.
  8. Enter **json** in the **Property Value** field.
  9. In the **REQUEST PROTOCOL HEADERS** section, select **+** to add a property.
  10. Enter the following as a **REQUEST ENTITY (AS STRING)**

      `<root><delete><query>type:spell AND confidence: [0 TO 0.5]</query></delete><commit/></root>`

      <Note>   **REQUEST ENTITY** specifies the threshold for low confidence spellings. Edit the upper range from 0.5 to increase or decrease the threshold based on your data.</Note>

  ### Delete all misspelling suggestions

  To delete *all* of the misspelling suggestions, enter the following in the **REQUEST ENTITY** section:

  `<root><delete><query>type:spell</query></delete><commit/></root>`

  <Note>This entry may be helpful when tuning the misspelling detection job and testing different configuration parameters.</Note>

  ## Head-tail analysis job cleanup

  The head-tail analysis job puts tail queries into one of multiple reason categories. For example, a tail query that includes a number might be assigned to the 'numbers' reason category. If the output in a particular category is not useful, you can remove it from the results. The examples in this section remove the numbers category.

  ### Prerequisites

  The head-tail analysis job cleanup does *not* have to occur in a specific order.

  ### Remove head-tail analysis query suggestions

  Use either a [Head-tail analysis cleanup method 1 - API call](#head-tail-analysis-cleanup-method-1-api-call) or the [Head-tail analysis cleanup method 2 - Fusion Admin UI](#head-tail-analysis-cleanup-method-2-fusion-admin-ui) to remove query category suggestions.

  #### Head-tail analysis cleanup method 1 - API call

  1. Open the `delete_lowConf_headTail.json` file.
     ```json theme={"dark"}
     {
     "type" : "rest-call",
     "id" : "DC_Large_QR_HEAD_TAIL_CLEANUP",
     "callParams" : {
         "uri" : "solr://DC_Large_query_rewrite_staging/update",
         "method" : "post",
         "queryParams" : {
         "wt" : "json"
         },
         "headers" : { },
         "entity" : "<root><delete><query>reason_code_s:(\"number\" \"number spelling\" \"number rare-term\" \"question number other-specific\" \"number others\" \"number other-specific\" \"number other-extra\" \"product number other-specific\" \"product number other-extra\" \"product number spelling\" \"product number others\" \"product number rare-term\" \"product question number\" \"product number re-wording\" \"question number other-extra\" \"number re-wording\")</query></delete><commit/></root>"
     },
     "type" : "rest-call",
     "type" : "rest-call"
     }
     ```
  2. Enter `<your query_rewrite_staging collection name/update>` in the **uri** field. An example URI value for an app called `DC_Large` would be `DC_Large_query_rewrite_staging/update`.
  3. Change the **id** field if applicable.

  #### Head-tail analysis cleanup method 2 - Fusion Admin UI

  1. Log in to Fusion and select **Collections > Jobs**.
  2. Select **Add+ > Custom and Other Jobs > REST Call**.
  3. Enter **remove-low-confidence-head-tail** in the **ID** field.
  4. Enter `<your query_rewrite_staging collection name/update>` in the **ENDPOINT URI** field. An example URI value for an app called `DC_Large` would be `DC_Large_query_rewrite_staging/update`.
  5. Enter **POST** in the **CALL METHOD** field.
  6. In the **QUERY PARAMETERS** section, select **+** to add a property.
  7. Enter **wt** in the **Property Name** field.
  8. Enter **json** in the **Property Value** field.
  9. In the **REQUEST PROTOCOL HEADERS** section, select **+** to add a property.
  10. Enter the following as a **REQUEST ENTITY (AS STRING)**
      ```
      <root><delete><query>reason_code_s:("number" "number spelling" "number rare-term" "question number other-specific" "number others" "number other-specific" "number other-extra" "product number other-specific" "product number other-extra" "product number spelling" "product number others" "product number rare-term" "product question number" "product number re-wording" "question number other-extra" "number re-wording")</query></delete><commit/></root>
      ```

  ### Delete all head-tail suggestions

  To delete *all* of the head-tail suggestions, enter the following in the **REQUEST ENTITY** section:

  `<root><delete><query>type:tail</query></delete><commit/></root>`

  <Note>This entry may be helpful when tuning the head-tail job and testing different configuration parameters.</Note>
</Accordion>

## Configuration properties

<SchemaParamFields schema={schema} />
