1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
new_join_by <- function(exprs = list(),
condition = character(),
filter = character(),
x = character(),
y = character()) {
out <- list(
exprs = exprs,
condition = condition,
filter = filter,
x = x,
y = y
)
structure(out, class = "dplyr_join_by")
}
# ------------------------------------------------------------------------------
# Internal generic
dbplyr_as_join_by <- function(x, error_call = caller_env()) {
UseMethod("dbplyr_as_join_by")
}
#' @export
dbplyr_as_join_by.default <- function(x, error_call = caller_env()) {
message <- glue(paste0(
"`by` must be a (named) character vector, list, `join_by()` result, ",
"or NULL, not {obj_type_friendly(x)}."
))
abort(message, call = error_call)
}
#' @export
dbplyr_as_join_by.dplyr_join_by <- function(x, error_call = caller_env()) {
x
}
#' @export
dbplyr_as_join_by.character <- function(x, error_call = caller_env()) {
x_names <- names(x) %||% x
y_names <- unname(x)
# If x partially named, assume unnamed are the same in both tables
x_names[x_names == ""] <- y_names[x_names == ""]
finalise_equi_join_by(x_names, y_names)
}
#' @export
dbplyr_as_join_by.list <- function(x, error_call = caller_env()) {
# TODO: check lengths
x_names <- x[["x"]]
y_names <- x[["y"]]
if (!is_character(x_names)) {
abort("`by$x` must evaluate to a character vector.")
}
if (!is_character(y_names)) {
abort("`by$y` must evaluate to a character vector.")
}
finalise_equi_join_by(x_names, y_names)
}
finalise_equi_join_by <- function(x_names, y_names) {
n <- length(x_names)
if (n == 0L) {
abort(
"Backwards compatible support for cross joins should have been caught earlier.",
.internal = TRUE
)
}
exprs <- purrr::map2(x_names, y_names, function(x, y) expr(!!x == !!y))
condition <- vctrs::vec_rep("==", times = n)
filter <- vctrs::vec_rep("none", times = n)
new_join_by(
exprs = exprs,
condition = condition,
filter = filter,
x = x_names,
y = y_names
)
}
# ------------------------------------------------------------------------------
join_by_common <- function(x_names,
y_names,
...,
error_call = caller_env()) {
check_dots_empty0(...)
by <- intersect(x_names, y_names)
if (length(by) == 0) {
message <- c(
"`by` must be supplied when `x` and `y` have no common variables.",
i = "Use `cross_join()` to perform a cross-join."
)
abort(message, call = error_call)
}
by_names <- by
by_names <- glue::glue_collapse(by_names, sep = ", ")
inform(glue("Joining with `by = join_by({by_names})`"))
finalise_equi_join_by(by, by)
}
|