Worcestershire sauce is a condiment by category and contains anchovy by composition, and a food database that stores only the first will pass it straight through a fish-free filter. NutraPlanner records those as two separate relationships — what a food is, and what it contains — and exclusion rules read both. In the catalog, "Sauce, worcestershire, ready-to-serve" carries a containment edge to the "Fish (meat)" grouping, which is what a Fish-Free profile matches against. The food entries come from the Canadian Nutrient File and the grouping hierarchy from EFSA's FoodEx2; the edges between them are ours, and neither source supplies allergen data. This describes how the filter decides, not a guarantee about a particular product: it works on generic food entries, so the label on what a client actually eats remains the source of truth.

Why does a fish-free filter need to know about Worcestershire sauce?

Because anchovy is an ingredient of Worcestershire sauce, not its category. Most food databases store one category per food, and that category answers "what is this?" rather than "what is in it?". Worcestershire sauce is a condiment; the fish is invisible to any filter that walks only the condiment relationship.

The problem is structural, not a data-entry gap. Adding "fish" as a second category fixes the exclusion case and immediately breaks the inclusion case, because the sauce would then be returned by a search for fish dishes. One relationship type cannot carry both meanings, so the fix is to give a food more than one kind of edge.

What is the difference between what a food is and what it contains?

NutraPlanner records more than one kind of relationship between a food and a food grouping: one for taxonomic identity — what the food is — and a separate one for composition, what it contains. A third state covers the cases where the answer is not yet settled; that is an internal data condition, not the regulated "may contain" statement printed on packaging. Which rules consult which relationships is the part that makes filtering correct.

The asymmetry is the design: identity implies containment, but containment does not imply identity. Butter is a dairy food and therefore contains dairy. A cream-based soup contains dairy but is not a dairy food. So exclusion rules evaluate every relationship type, while inclusion rules and recipe-similarity features are defined to read identity alone.

Browsing recipes by meal type — breakfast, lunch, dinner — does ship, but those assignments are curated per recipe rather than derived from the identity relationships described here.

Does excluding tree nuts also exclude almonds and walnuts?

Yes. The Tree-Nut-Free profile excludes the "Tree nuts" grouping and everything beneath it in the hierarchy, which catches almonds, cashews, pecans and walnuts — among the nine tree nuts Canada requires to be declared — without anyone enumerating them. Peanut-Free is a separate profile against a separate grouping, because, as the CFIA puts it, peanuts are legumes that grow underground and are not tree nuts — a client allergic to both needs both profiles.

Each rule also controls how far it reaches through the grouping hierarchy — a single grouping on its own, or a grouping together with everything beneath it. Covering everything beneath is the default, because that is what people mean by a category.

Are allergen filters strict, or just preferences?

Strict. Every rule the matcher evaluates must pass or the recipe is rejected — there is no partial credit, and nothing is nudged up or down a ranking instead of being removed. A rule saved as a soft preference rather than a requirement has no effect today: it neither removes a recipe nor changes where one ranks. If you want a constraint enforced, it has to be saved as a requirement.

That matters most for safety profiles, so the system does not leave it to convention: rules in an allergen, intolerance or aversion profile are enforced strictly as a property of the profile, not of how carefully they were authored. An allergen rule cannot be saved as a suggestion by accident.

What happens when the food data is incomplete?

It is never newly admitted to a filter. When the matcher runs on a recipe it cannot evaluate — an ingredient with no resolved food, or no usable quantity — it clears that recipe's computed rows and stops without writing new ones. Separately, a recipe carrying an unconfirmed skipped ingredient is held out of every exclusion profile and every upper-bound nutrient constraint until the skip is reviewed.

That is a statement about recipes the matcher rejects as a whole, not about individual ingredients, and three limits sit underneath it. The most consequential comes first, and it has two forms. Each ingredient line is resolved to a generic food by name, automatically and mostly without individual confirmation. Most often the line resolves to nothing at all, and a line that resolves to nothing carries no allergen edges — so a recipe naming an ingredient in its text can pass a free-from filter for that very allergen. Less often the line resolves to the wrong food and the recipe inherits that food's edges instead. Either way the result is the same: a recipe reported as free of an allergen it contains, with nothing on screen to signal it.

The second limit: a match set by direct curation is a deliberate manual override, and is not cleared by the incomplete-data path. The third: matching is only queued for recipes that are currently evaluatable, so a recipe matched while complete and later edited into an incomplete state keeps the rows it already had. Taken together, the guarantee is about admission — what gets newly written — not about continuous re-verification, and not about the correctness of the name matching underneath it.

Ambiguity gets the same treatment one level down. Where the answer depends on the formulation, the unsettled state fails closed: the exclusion filter rejects the food while the question is open. A false rejection costs a recipe suggestion; a false acceptance puts an allergen in front of a client who then has to catch it at the label. The system fails closed rather than relying on that catch. Ambiguity is resolved at the level of the generic food entry. For genuinely product-dependent allergens the intended resting state is ambiguous rather than absent — that coverage is still being built out, which is why the caveat below matters.

How do you know the food-to-allergen links are correct?

There are two links in the chain, and they are not equally strong. The second — food to food grouping — carries a provenance record naming its origin (a seed import, an automated judging pass, or a human curator), with a confidence level recorded when a judging pass runs and a validation timestamp only where a judge agreed or a curator confirmed. An association with no timestamp has not been confirmed, and that gap is what our validation reporting works from.

The first link — ingredient text to food — is the weaker one, and it is worth being blunt about. Resolving a written ingredient line to a generic food entry is automated, and the overwhelming majority of those resolutions have never been individually confirmed by a person. That is the least certain step in the whole chain, and it sits upstream of everything else described here.

This matters because the dangerous failure mode is under-association, not over-association. A missing containment edge is silent: nothing errors, the filter simply returns a recipe it should have withheld. Compound foods are flagged as such precisely because they need comprehensive component coverage rather than a single identity edge, and they are targeted separately, with full-catalog search scope.

Put it into practice

Frequently asked questions

Why would a fish-free filter reject Worcestershire sauce?

Because anchovy is an ingredient of Worcestershire sauce. NutraPlanner records a containment relationship from the food to the "Fish (meat)" grouping, kept separate from the groupings that describe what the sauce is. Exclusion rules read containment edges as well as identity edges, so a Fish-Free profile matches the hidden ingredient. Always confirm against the label of the specific product a client will use.

Why do exclusion and inclusion rules read different edges?

Because containment does not imply identity. A cream-based soup contains dairy, so a dairy-free exclusion rule must catch it — but the soup is not a dairy food, so an inclusion rule for dairy dishes should not return it. Exclusion rules therefore read identity and containment together, while inclusion rules are defined to read identity alone. No inclusion rule ships today — every food-grouping rule in the product is an exclusion rule. Category browsing exists but is curated per recipe rather than derived from identity edges.

Does a tree-nut-free filter also exclude peanuts?

No. Tree-Nut-Free and Peanut-Free are separate profiles matching separate food groupings, because a peanut is a legume rather than a tree nut. A client allergic to both needs both profiles applied. The tree-nut rule does cover almonds, cashews, pecans and walnuts automatically, because the rule covers the tree-nut grouping and everything beneath it. Filtering narrows the recipe set; for a diagnosed allergy, verify the label on the product the client will actually eat.

Are allergen filters strict, or can they be soft preferences?

Strict. Every rule the matcher evaluates must pass or the recipe is rejected; nothing is scored or ranked instead of being removed. A rule saved as a soft preference has no effect today — it neither filters nor ranks — so a constraint you want enforced has to be saved as a requirement. Rules in an allergen, intolerance or aversion profile are enforced strictly as a property of the profile, so a safety rule cannot be left as a soft preference by mistake.

What happens to a recipe whose ingredient data is incomplete?

It is never newly admitted to a filter. When the matcher runs on a recipe it cannot evaluate — an unresolved ingredient or a missing quantity — it clears that recipe's computed rows and writes no new ones. A recipe with an unconfirmed skipped ingredient is likewise held out of every hard-exclusion profile until the skip is reviewed. The larger limit sits upstream of that gate: each ingredient line is resolved to a generic food by name, automatically and mostly without individual confirmation. A line that resolves to nothing carries no allergen edges, and a line resolved to the wrong food carries the wrong ones — either way the recipe can be reported as free of an allergen it contains. Two smaller limits: a match set by direct curation is a manual override and is not cleared this way, and matching is only queued for recipes that are currently evaluatable. Treat a passing filter as a shortlist and verify the label for any client with a diagnosed allergy.

Can I rely on allergen filtering instead of checking labels?

No. The filter works on generic food entries from the Canadian Nutrient File, with no brand or product-level data, and it models neither cross-contamination nor the precautionary statements manufacturers put on packaging. Manufacturers also reformulate without notice. Filtering narrows the recipe set; the ingredient label on the specific product a client will eat remains the source of truth.

References

  1. Health Canada — Canadian Nutrient File (the generic food entries)
  2. EFSA — FoodEx2 classification and description system (the grouping hierarchy)
  3. CFIA — Industry resource: food allergen labelling
  4. Food Allergy Canada — Reading food labels

Published by NutraPlanner. · Last updated 2026-08-04

Share

Similar articles

All articles