Unicode Normalization for Search and Deduplication

Handle visually identical Unicode text with deliberate normalization rules, preserve original content and test search, uniqueness and imports without data loss.

In this article

Unicode Normalization for Search and Deduplication

Two strings can look identical on screen and still contain different Unicode sequences. That can cause unexpected search misses, duplicate records or inconsistent matching between services. Decide where normalization belongs in your data model rather than repeatedly cleaning text after users report problems.

This guide concerns text equivalence in storage and comparison. It is separate from identifying deceptive domain names or removing ordinary whitespace. A visually familiar character is not enough evidence that two raw strings are equal.

Reproduce the difference explicitly

Some accented characters can be represented as a composed character or as a base character followed by a combining mark. An illustrative JavaScript comparison is:

js
const composed = "\u00E9";
const decomposed = "e\u0301";
console.log(composed === decomposed);
console.log(composed.normalize("NFC") === decomposed.normalize("NFC"));

The first comparison checks the original sequences. The second compares normalized forms. The String.normalize reference documents canonical and compatibility normalization forms.

Use explicit escape sequences in a test fixture so reviewers can see the difference. A screenshot of two identical-looking words hides the evidence you are trying to explain.

Define the field's purpose

A display name, a search key and a cryptographic signature input have different requirements. Don't apply one global transformation to every string in the application.

For user content, preserve the original representation where it matters and derive a separate normalized comparison key. For an identifier with an established normalization policy, enforce that policy consistently at creation and lookup.

Write the rule beside the schema: which form is used, whether case is folded, how whitespace is treated and whether the original value is retained. Avoid changing the rule silently during a library update.

Choose canonical or compatibility behavior deliberately

Canonical normalization addresses canonically equivalent sequences. Compatibility normalization can also collapse distinctions that are meaningful in some contexts. That broader transformation may be appropriate for a particular search experience but unsuitable for preserving exact user input.

Test representative text from the languages your users actually enter. Include combining marks, full-width characters, symbols and strings that should remain different. Don't judge a multilingual policy using only ASCII examples.

Normalization is not transliteration and not a complete confusable-character defense. It also does not automatically provide locale-aware case handling or a security policy for usernames. Keep those decisions separate.

Apply the same rule on write and lookup

If you normalize a stored search key but compare it to an unnormalized query, the bug remains. Trace both paths: record creation, record update, query processing and import.

For a deduplication key, construct it through one shared implementation or a clearly specified cross-language contract. Test equivalent fixtures through every service that can write the field.

Use JSON Viewer to inspect synthetic fixtures with escaped representations and expected results. Use Text Diff for surrounding document changes, but don't rely on a visual diff to explain invisible code-point differences.

Check database uniqueness separately

A database collation may implement comparisons differently from your application. Determine whether uniqueness is enforced on the original value or on your normalized key. Don't assume the database and JavaScript agree automatically.

Create a test that inserts two canonically equivalent values through the actual application path. Confirm the intended outcome and the error reported to the user. Then test two values that must remain distinct.

If existing records collapse to one normalized key, plan conflict resolution before adding a unique constraint. Preserve the records and review collisions rather than deleting whichever row happens to be encountered second.

Preserve exact bytes for integrity workflows

Changing normalization changes the representation used by hashes or signatures. If a protocol requires exact bytes, apply its specified encoding and normalization rules at the correct stage. Don't normalize a signed message merely because it looks like harmless cleanup.

The SHA-256 Generator can demonstrate that controlled text representations produce different digests. That is a useful teaching experiment, not a reason to rewrite signed content before verification.

Keep original import files when a recovery or audit requirement justifies it. A normalized database value alone may not explain what a source system actually sent.

Plan a migration in stages

Generate normalized keys for existing records without replacing the original text. Count collisions and inspect a safe sample. Review whether collisions represent duplicates, distinct identities or content that should not have been included in the key.

Deploy the new lookup path with comparison logging that avoids recording private search text. Use counts and synthetic tests to establish whether the migration behaves as intended.

Once the policy is stable, enforce it for new writes and supported imports. Document the backward-compatibility behavior for clients that previously depended on raw sequence equality.

Test exports and round trips

Export synthetic records, import them into a fresh test environment and compare both original text and derived keys. Ensure that a conversion or spreadsheet path does not unexpectedly change character representation.

Include the normalization policy version in internal migration notes. If the policy later changes, you need to know which keys were generated under which assumptions. Keep display text readable while making comparison behavior reproducible.

Conclusion

Use Unicode normalization as an explicit field-level policy. Preserve original content when needed, derive consistent comparison keys and test database behavior and collisions. Visual equality is a useful clue, but reproducible code-point fixtures establish what your system actually compares.

Advertisement
Unicode Normalization for Search and Deduplication | Duck Cloud