fuzzy_match(): require an aff/cf/x qualifier token on both sides - #295
Merged
Merged
Conversation
… sides
check_match()'s word-position confirmation check only ever compares as
many word positions as the shorter of query/candidate has. That let a
qualifier word in the query -- "aff"/"cf" (uncertain identification) or
"x" (hybrid marker) -- get silently absorbed into an unrelated word's
position purely by first-letter coincidence:
"acacia aff aneura" (3 words) confirmed against the unrelated accepted
species "acacia aptaneura" (2 words), because word 2 of the query
("aff") and word 2 of the candidate ("aptaneura") both start with "a"
-- the real epithet "aneura" (word 3) was never compared to anything.
match_05 (species-level fuzzy match) runs before match_06 (aff./cf.
detection) and match_08 (hybrid detection), so this let affinis- and
hybrid-qualified names resolve all the way to an unrelated species
instead of being correctly capped at genus rank. Reordering those match
steps earlier is not a safe fix on its own: APC_synonyms has 1156 real
names containing "aff."/"cf." that legitimately need match_05b to run
first (see #291), and the same reasoning holds for match_08 and real
named hybrids.
Fix instead in fuzzy_match()'s check_match(): if the query contains
"aff", "cf", or "x" as a standalone word, require the candidate to
contain that same token too, or reject outright before the coarser
first-letter word-position check runs. This is existence-based rather
than position-based, so it tolerates ordinary word-count drift elsewhere
in phrase names (locality/collector text) -- only activates when a
qualifier token is actually present in the query, and a typo'd version
of a real aff./cf./hybrid-named entry still matches, since the token is
present on both sides (verified against "Isoetes sp. aff. muelleri" and
"Persoonia x lucida", both real APC entries).
Verified against the full match-step benchmark + match_branches
snapshot: 184 passed, 0 failed, no regressions.
Fixes #291, #294.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dfalster
approved these changes
Aug 24, 2026
dfalster
left a comment
Member
There was a problem hiding this comment.
Looks reasonable, but I couldn't tell if it actually works without testing against data which you've done
This was referenced Aug 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #291 and #294:
"Acacia aff. aneura"("resembles Acacia aneura, notconfidently identified") was resolving all the way to the unrelated accepted
species
"Acacia aptaneura"instead of being capped at genus rank.Root cause
fuzzy_match()'scheck_match()confirmation step only compares as manyword positions as the shorter of query/candidate has. For
"acacia aff aneura"(3 words) against"acacia aptaneura"(2 words), itonly checks word 1 (
acacia==acacia) and word 2 — and word 2 of the queryis the qualifier
"aff", word 2 of the candidate is the whole epithet"aptaneura", both coincidentally starting with"a". The real epithet"aneura"(word 3) is never compared to anything.Combined with
match_05(species-level fuzzy match) running beforematch_06(aff./cf.detection) andmatch_08(hybrid detection), thislet affinis- and hybrid-qualified names slip past the dedicated handling
entirely.
Why not just reorder the match steps (the initially-proposed fix on
#291)?
resources$APC_synonymshas 1,156 real names containing literalaff./cf.(e.g."Isoetes sp. aff. muelleri") that legitimately needmatch_05bto run first so a typo'd version still resolves to its realname, rather than being caught by the qualifier heuristic and capped at
genus rank. The same reasoning applies to
match_08and real named hybrids(e.g.
"Persoonia x lucida","Eucalyptus x alpina"— both real acceptedspecies). Full trace is in the issue threads.
Fix
In
check_match(): if the query contains"aff","cf", or"x"as astandalone word, require the candidate to contain that same token too, or
reject the candidate outright before the coarser first-letter word-position
check runs. This is existence-based, not position-based, so ordinary
word-count drift elsewhere in phrase names (locality/collector text) is
untouched — it only activates when a qualifier token is actually present in
the query.
Tests
New
tests/testthat/test-functions-fuzzy_match.R:fuzzy_match()unit tests for both the fix (aff/xno longer confirmagainst an unrelated candidate) and the preserved legitimate cases above.
align_taxa()end-to-end test confirming"Acacia aff. aneura"nowresolves via
match_06aat genus rank, not a wrong species match.Verification
NOT_CRAN=true devtools::test()— 188 passed, 0 failed (the only otherfailures are 3 pre-existing, network-only tests in
test-connection.Rthatrequire live internet access unavailable in this sandbox; unrelated to this
change). Includes the full
match_branchessnapshot (the safety net formatch_taxa()) — no regressions.Fixes #291. Fixes #294.
🤖 Generated with Claude Code