feat(search): add per-row resolver — exact match branch
.resolve_basket_row() handles the exact-match case. Substring fallback and disambiguation branches follow in subsequent commits.
This commit is contained in:
+50
@@ -158,3 +158,53 @@ cog_gov_search <- function(name = NULL, state = NULL, type = NULL) {
|
||||
|
||||
list(name = name, state = state_norm, type = type_norm)
|
||||
}
|
||||
|
||||
# Resolve a single basket-mode input row. Returns a list with components:
|
||||
# status : "resolved" | "largest_pop" | "ambiguous" | "no_match"
|
||||
# match_method : "exact" | "substring" | NA_character_
|
||||
# n_candidates : int
|
||||
# row : tibble (single resolved row, or 0-row tibble for unresolved)
|
||||
# candidates : tibble (all rows that matched, for sidecar)
|
||||
# Internal use only; takes an active DuckDB connection to reuse the session.
|
||||
#' @noRd
|
||||
.resolve_basket_row <- function(name, state, type, con) {
|
||||
preds <- character(0)
|
||||
if (!is.na(state)) {
|
||||
st_fips <- .coerce_state_to_fips(state)
|
||||
preds <- c(preds, sprintf("fips_state = %s", .sql_lit_chr(st_fips)))
|
||||
}
|
||||
if (!is.na(type)) {
|
||||
int_type <- .coerce_type(type)
|
||||
preds <- c(preds, sprintf("govs_type = %d", int_type))
|
||||
}
|
||||
|
||||
base_where <- if (length(preds) == 0L) "" else paste("WHERE", paste(preds, collapse = " AND "))
|
||||
|
||||
exact_sql <- paste(
|
||||
"SELECT * FROM canonical_fips_xwalk",
|
||||
base_where,
|
||||
if (nzchar(base_where)) "AND" else "WHERE",
|
||||
sprintf("LOWER(gov_name) = LOWER(%s)", .sql_lit_chr(name))
|
||||
)
|
||||
exact <- tibble::as_tibble(DBI::dbGetQuery(con, exact_sql))
|
||||
|
||||
if (nrow(exact) == 1L) {
|
||||
return(list(
|
||||
status = "resolved",
|
||||
match_method = "exact",
|
||||
n_candidates = 1L,
|
||||
row = exact,
|
||||
candidates = exact
|
||||
))
|
||||
}
|
||||
|
||||
# Substring + disambiguation branches added in subsequent tasks.
|
||||
empty <- exact[0, , drop = FALSE]
|
||||
list(
|
||||
status = "no_match",
|
||||
match_method = NA_character_,
|
||||
n_candidates = 0L,
|
||||
row = empty,
|
||||
candidates = empty
|
||||
)
|
||||
}
|
||||
|
||||
@@ -136,3 +136,35 @@ test_that(".validate_basket_args allows NULL state and type", {
|
||||
expect_equal(out$state, c(NA_character_, NA_character_))
|
||||
expect_equal(out$type, c(NA_character_, NA_character_))
|
||||
})
|
||||
|
||||
test_that(".resolve_basket_row exact match returns one row", {
|
||||
con <- uscogdata:::.ensure_session()
|
||||
out <- uscogdata:::.resolve_basket_row(
|
||||
name = "BROWARD COUNTY", state = "FL", type = NA_character_, con = con
|
||||
)
|
||||
expect_equal(out$status, "resolved")
|
||||
expect_equal(out$match_method, "exact")
|
||||
expect_equal(out$n_candidates, 1L)
|
||||
expect_equal(nrow(out$row), 1L)
|
||||
expect_equal(out$row$canonical_govid, "101006006")
|
||||
expect_equal(out$row$gov_name, "BROWARD COUNTY")
|
||||
})
|
||||
|
||||
test_that(".resolve_basket_row exact match is case-insensitive", {
|
||||
con <- uscogdata:::.ensure_session()
|
||||
out <- uscogdata:::.resolve_basket_row(
|
||||
name = "broward county", state = "FL", type = NA_character_, con = con
|
||||
)
|
||||
expect_equal(out$status, "resolved")
|
||||
expect_equal(out$match_method, "exact")
|
||||
expect_equal(out$row$canonical_govid, "101006006")
|
||||
})
|
||||
|
||||
test_that(".resolve_basket_row exact match honors per-row type", {
|
||||
con <- uscogdata:::.ensure_session()
|
||||
out <- uscogdata:::.resolve_basket_row(
|
||||
name = "SAN DIEGO CITY", state = "CA", type = "city", con = con
|
||||
)
|
||||
expect_equal(out$status, "resolved")
|
||||
expect_equal(out$row$canonical_govid, "052037010")
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user