fix(search): soft-fail on per-row excluded type and malformed regex name
Cross-task review found two edge cases that violated the basket-mode soft-fail contract: - Per-row excluded type (e.g. type = c(NA, "special_district")) hit .coerce_type()'s abort inside the per-row resolver, killing the whole basket call. Now treated as no_match in the sidecar. - Malformed regex in the substring fallback (e.g. name = "San(Diego") propagated DuckDB engine errors. .escape_regex() now backslash- escapes meta characters before the regexp_matches call. Utility- mode regex behavior is unchanged. Plus a new public-surface test for the all-no-match case.
This commit is contained in:
+22
-1
@@ -132,6 +132,14 @@ cog_gov_search <- function(name = NULL, state = NULL, type = NULL) {
|
||||
)
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
.escape_regex <- function(x) {
|
||||
# Backslash-escape POSIX regex metacharacters so `name` is treated as a
|
||||
# literal substring in the DuckDB regexp_matches call (substring fallback
|
||||
# only; utility-mode intentionally preserves regex behavior).
|
||||
gsub("([\\^$.|?*+(){}\\[\\]])", "\\\\\\1", x, perl = TRUE)
|
||||
}
|
||||
|
||||
#' @noRd
|
||||
.is_excluded_type <- function(type) {
|
||||
excluded <- c("4", "5", "special_district", "school_district")
|
||||
@@ -245,6 +253,19 @@ cog_gov_search <- function(name = NULL, state = NULL, type = NULL) {
|
||||
))
|
||||
}
|
||||
|
||||
# Short-circuit: excluded type (4/5 / special_district / school_district)
|
||||
# -> no_match without SQL, preserving soft-fail contract.
|
||||
if (!is.na(type) && .is_excluded_type(type)) {
|
||||
empty <- .empty_xwalk_tibble()
|
||||
return(list(
|
||||
status = "no_match",
|
||||
match_method = NA_character_,
|
||||
n_candidates = 0L,
|
||||
row = empty,
|
||||
candidates = empty
|
||||
))
|
||||
}
|
||||
|
||||
preds <- character(0)
|
||||
if (!is.na(state)) {
|
||||
st_fips <- .coerce_state_to_fips(state)
|
||||
@@ -282,7 +303,7 @@ cog_gov_search <- function(name = NULL, state = NULL, type = NULL) {
|
||||
"SELECT * FROM canonical_fips_xwalk",
|
||||
base_where,
|
||||
conj,
|
||||
sprintf("regexp_matches(gov_name, %s, 'i')", .sql_lit_chr(name))
|
||||
sprintf("regexp_matches(gov_name, %s, 'i')", .sql_lit_chr(.escape_regex(name)))
|
||||
)
|
||||
sub <- tibble::as_tibble(DBI::dbGetQuery(con, sub_sql))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user