Skip to content

Commit 2cb7a2c

Browse files
bugerclaude
andauthored
fix: exact search fails for camelCase symbols due to case mismatch in evaluate() (#526)
The optional-term evaluation path in Expr::evaluate() used `keywords` (original case) for term_indices lookup, but term_indices always stores lowercase keys. This caused exact search for mixed-case symbols like "cleanupScopeMappings" to silently fail — the regex found the symbol but evaluate() couldn't map it back to its term index. All other lookup paths in evaluate() already used lowercase_keywords. Closes #525 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 50d8d11 commit 2cb7a2c

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

src/search/elastic_query.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ impl Expr {
224224
} else {
225225
// No required terms: treat multiple keywords within a Term as alternatives
226226
// This avoids false negatives for stemming variants (e.g., repository/repositori).
227-
keywords.iter().any(|kw| {
227+
// Use lowercase_keywords for lookup since term_indices stores lowercase keys.
228+
lowercase_keywords.iter().any(|kw| {
228229
term_indices
229230
.get(kw)
230231
.map(|idx| matched_terms.contains(idx))

src/search/elastic_query_evaluate_tests.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,4 +493,63 @@ fn test_required_term_in_or_bug() {
493493

494494
println!("Test 4 - No terms present: matched_terms={matched_terms:?}, result={result}");
495495
assert!(!result, "Should return false when no terms are present");
496+
}
497+
498+
#[test]
499+
fn test_exact_camel_case_evaluate_uses_lowercase_keywords() {
500+
// Issue #525: exact search for camelCase symbols fails because evaluate()
501+
// uses `keywords` (original case) for term_indices lookup instead of
502+
// `lowercase_keywords`. term_indices stores lowercase keys, so lookup
503+
// with original case like "cleanupScopeMappings" returns None.
504+
//
505+
// This is the root cause of --exact missing real camelCase symbols.
506+
507+
// term_indices always stores lowercase keys (built from lowercase_keywords)
508+
let term_indices = create_term_indices(&["cleanupscopemappings"]);
509+
510+
// Exact term preserves original case in `keywords` but lowercase in `lowercase_keywords`
511+
let expr = create_exact_term("cleanupScopeMappings");
512+
513+
// The regex pattern finds the symbol in source → matched_terms = {0}
514+
let matched_terms = create_matched_terms(&[0]);
515+
516+
// This SHOULD return true: the term was found by regex search
517+
let result = expr.evaluate(&matched_terms, &term_indices, false);
518+
assert!(
519+
result,
520+
"Exact camelCase term should match when term index 0 is present in matched_terms. \
521+
Bug: evaluate() uses original-case keywords for term_indices lookup instead of lowercase_keywords."
522+
);
523+
524+
// Also test with ignore_negatives=true (used in early filtering)
525+
let result_ignore_neg = expr.evaluate(&matched_terms, &term_indices, true);
526+
assert!(
527+
result_ignore_neg,
528+
"Exact camelCase term should match with ignore_negatives=true"
529+
);
530+
}
531+
532+
#[test]
533+
fn test_exact_camel_case_all_present_uses_lowercase() {
534+
// Verify that the `all_present` check (used for required terms) also works
535+
// for exact camelCase terms
536+
let term_indices = create_term_indices(&["cleanupscopemappings"]);
537+
538+
let expr = Expr::Term {
539+
keywords: vec!["cleanupScopeMappings".to_string()],
540+
lowercase_keywords: vec!["cleanupscopemappings".to_string()],
541+
field: None,
542+
required: true,
543+
excluded: false,
544+
exact: true,
545+
};
546+
547+
let matched_terms = create_matched_terms(&[0]);
548+
549+
// Required exact camelCase term should match
550+
let result = expr.evaluate(&matched_terms, &term_indices, false);
551+
assert!(
552+
result,
553+
"Required exact camelCase term should match via lowercase_keywords lookup"
554+
);
496555
}

0 commit comments

Comments
 (0)