我正在开发一个使用dbplyr
查询数据库的Shiny应用程序,但是我很难使用正确的语法实现我常用的dplyr
方法来进行条件过滤。看起来潜在的问题是dbplyr
不允许您计算外部提供的向量,因此当用户提供一个潜在选项的向量时is.null
会失败。
<SQL>
SELECT *
FROM `mtcars`
WHERE (CASE WHEN (((4.0, 6.0) IS NULL)) THEN 1 WHEN (`cyl` IN (4.0, 6.0)) THEN 1 END)
最后,将有许多参数需要计算,所以我不想简单地将整个查询放在if/else语句as has been suggested in similar SO questions中。对于如何在构建于dbplyr
上的Shiny应用程序中最好地实现条件过滤器,有什么建议吗?
# load libraries
library(dplyr)
library(dbplyr)
# create database and dataframe
mtcars_db <- tbl_memdb(mtcars)
mtcars_df <- collect(mtcars_db)
# parameterized filter function
filter_function <- function(data, user_selection) {
{{data}} |>
filter(
case_when(
is.null({{user_selection}}) ~ TRUE,
cyl %in% {{user_selection}} ~ TRUE
)
)
}
# save vector of user selections
cylinders <- c(4, 6)
# works with dataframes
filter_function(mtcars_df, NULL) # works
filter_function(mtcars_df, cylinders) # works
filter_function(mtcars_db, NULL) # works
filter_function(mtcars_db, cylinders) # fails
# show query of failing version
filter_function(mtcars_db, cylinders) |>
show_query()
2条答案
按热度按时间bbuxkriu1#
如果在
is.null(user_selection)
时返回data
,则filter函数适用于所有四种用例:bf1o4zei2#
我通常推荐@langtang的方法,但在多种情况下,这显然是不切实际的。
问题的原因是SQL没有办法测试整个列表(例如
(4.0, 6.0)
)是否为空。因此,一个解决方案是只测试列表的第一个值:我用OR(
|
)代替了case_when
,并删除了{{ }}
,因为它没有。但这只是一个风格选择。这个解决方案的关键部分是
local
,它强制在翻译之前对其内容进行评估。如果我们不包含local
,那么dbplyr将尝试翻译类似(4.0, 6.0)[1]
的内容(它无法做到这一点)。但是当我们使用local
时,dbplyr只需翻译4
(这是微不足道的)。这利用了
NULL[1]
也是NULL
的R行为。我们必须假设
user_selection
从来没有NULL
作为第一个值,通常情况应该是这样。