diff --git a/DESCRIPTION b/DESCRIPTION index cda3c70f97..b9040db516 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -29,5 +29,5 @@ LinkingTo: Rcpp Suggests: tinytest, simplermarkdown, curl, bit64, Matrix, palmerpenguins, nycflights13, data.table, tibble VignetteBuilder: simplermarkdown Roxygen: list(markdown = TRUE) -RoxygenNote: 7.1.2 +RoxygenNote: 7.2.0 Encoding: UTF-8 diff --git a/R/QueryCondition.R b/R/QueryCondition.R index 366bb9aa6e..68faa21c8c 100644 --- a/R/QueryCondition.R +++ b/R/QueryCondition.R @@ -1,6 +1,6 @@ # MIT License # -# Copyright (c) 2021 TileDB Inc. +# Copyright (c) 2021-2022 TileDB Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -37,8 +37,8 @@ setClass("tiledb_query_condition", #' @return A 'tiledb_query_condition' object #' @export tiledb_query_condition <- function(ctx = tiledb_get_context()) { - stopifnot(`The argument must be a ctx object` = is(ctx, "tiledb_ctx"), - `This function needs TileDB 2.3.0 or newer` = tiledb_version(TRUE) >= "2.3.0") + stopifnot("The argument must be a ctx object" = is(ctx, "tiledb_ctx"), + "This function needs TileDB 2.3.0 or newer" = tiledb_version(TRUE) >= "2.3.0") ptr <- libtiledb_query_condition(ctx@ptr) query_condition <- new("tiledb_query_condition", ptr = ptr, init = FALSE) invisible(query_condition) @@ -61,11 +61,11 @@ tiledb_query_condition <- function(ctx = tiledb_get_context()) { #' @return The initialized 'tiledb_query_condition' object #' @export tiledb_query_condition_init <- function(attr, value, dtype, op, qc = tiledb_query_condition()) { - stopifnot(`Argument 'qc' with query condition object required` = is(qc, "tiledb_query_condition"), - `Argument 'attr' must be character` = is.character(attr), - `Argument 'value' must be of length one` = is.vector(value) && all.equal(length(value),1), - `Argument 'dtype' must be character` = is.character(dtype), - `Argument 'op' must be character` = is.character(op)) + stopifnot("Argument 'qc' with query condition object required" = is(qc, "tiledb_query_condition"), + "Argument 'attr' must be character" = is.character(attr), + "Argument 'value' must be of length one" = is.vector(value) && all.equal(length(value),1), + "Argument 'dtype' must be character" = is.character(dtype), + "Argument 'op' must be character" = is.character(op)) op <- match.arg(op, c("LT", "LE", "GT", "GE", "EQ", "NE")) ## maybe check dtype too libtiledb_query_condition_init(qc@ptr, attr, value, dtype, op) @@ -75,17 +75,18 @@ tiledb_query_condition_init <- function(attr, value, dtype, op, qc = tiledb_quer #' Combine two 'tiledb_query_condition' objects #' -#' Combines two query condition object using a relatiional operator. Note that at present -#' only 'AND' is supported. +#' Combines two query condition object using a relatiional operator. Support for operator +#' 'AND' is generally available, the 'OR' operator is available if TileDB 2.10 or newer is +#' used. #' @param lhs A 'tiledb_query_condition' object on the left-hand side of the relation #' @param rhs A 'tiledb_query_condition' object on the left-hand side of the relation #' @param op A character value with then relation, this must be one of 'AND', 'OR' or 'NOT'. #' @return The combined 'tiledb_query_condition' object #' @export tiledb_query_condition_combine <- function(lhs, rhs, op) { - stopifnot(`Argument 'lhs' must be a query condition object` = is(lhs, "tiledb_query_condition"), - `Argument 'rhs' must be a query condition object` = is(rhs, "tiledb_query_condition"), - `Argument 'op' must be a character` = is.character(op)) + stopifnot("Argument 'lhs' must be a query condition object" = is(lhs, "tiledb_query_condition"), + "Argument 'rhs' must be a query condition object" = is(rhs, "tiledb_query_condition"), + "Argument 'op' must be a character" = is.character(op)) op <- match.arg(op, c("AND", "OR", "NOT")) qc <- tiledb_query_condition() qc@ptr <- libtiledb_query_condition_combine(lhs@ptr, rhs@ptr, op) diff --git a/inst/tinytest/test_querycondition.R b/inst/tinytest/test_querycondition.R index 67b834dd11..d64a737d00 100644 --- a/inst/tinytest/test_querycondition.R +++ b/inst/tinytest/test_querycondition.R @@ -231,3 +231,56 @@ expect_equal(dim(fullarr), c(50,3)) subarr <- tiledb_array(uri, as.data.frame=TRUE, query_condition=parse_query_condition(region == "Northeast"))[] expect_equal(dim(subarr), c(9,3)) + + +## Testing OR condition + +## Pre-test: will return NA in case of error ie when TileDB Core does not yet have OR support +## wrapped in 'class()' to avoid a warning of 'is.na() applied to non-list or vector S4' +if (is.na(tryCatch(class(qc <- parse_query_condition(x3 == 1 || x4 == 2)), error = function(e) NA))) + exit_file("Skipping for lack of 'OR' support in TileDB") + +## Re-create penguins +uri <- tempfile() +fromDataFrame(penguins, uri, sparse=TRUE) + +## Basics +qc <- tiledb_query_condition_init("year", 2009, "INT32", "EQ") +arrwithqc <- tiledb_array(uri, as.data.frame=TRUE, query_condition=qc) +expect_equal(NROW(arrwithqc[]), 120L) + +lhs <- tiledb_query_condition_init("year", 2008, "INT32", "GE") +rhs <- tiledb_query_condition_init("year", 2008, "INT32", "LE") +qc <- tiledb_query_condition_combine(lhs, rhs, "AND") +arrwithqc <- tiledb_array(uri, as.data.frame=TRUE, query_condition=qc) +expect_equal(NROW(arrwithqc[]), 114L) # basically a different way of writing EQ via '<= && >=' + +lhs <- tiledb_query_condition_init("year", 2008, "INT32", "GE") +rhs <- tiledb_query_condition_init("year", 2008, "INT32", "LE") +qc <- tiledb_query_condition_combine(lhs, rhs, "OR") +arrwithqc <- tiledb_array(uri, as.data.frame=TRUE, query_condition=qc) +expect_equal(NROW(arrwithqc[]), 344L) # the OR makes it unconstrained via '<= || >=' + +## simple OR +qc <- parse_query_condition(species == "Adelie" || species == "Chinstrap") +arr <- tiledb_array(uri, as.data.frame=TRUE, query_condition=qc) +## Note that in R '||' is used for length-1 comparison, and '|' along a vector so '|' here +expect_equal(NROW(arr[]), sum(with(penguins, species == "Adelie" | species == "Chinstrap"))) + +## three elements works too +qc <- parse_query_condition(species == "Adelie" || species == "Chinstrap" || year >= 2009) +arr <- tiledb_array(uri, as.data.frame=TRUE, query_condition=qc) +expect_equal(NROW(arr[]), + sum(with(penguins, species == "Adelie" | species == "Chinstrap" | year >= 2009))) + +## three elements works too as does mixing AND and OR +qc <- parse_query_condition(species == "Adelie" || species == "Chinstrap" && year >= 2009) +arr <- tiledb_array(uri, as.data.frame=TRUE, query_condition=qc) +expect_equal(NROW(arr[]), + sum(with(penguins, species == "Adelie" | species == "Chinstrap" & year >= 2009))) + +## empty sets are fine +qc <- parse_query_condition(year < 2008 || year > 2010) +arr <- tiledb_array(uri, as.data.frame=TRUE, query_condition=qc) +expect_equal(NROW(arr[]), + sum(with(penguins, year < 2008 | year > 2010))) diff --git a/man/tiledb_query_condition_combine.Rd b/man/tiledb_query_condition_combine.Rd index 5604f31c1c..d5321cad95 100644 --- a/man/tiledb_query_condition_combine.Rd +++ b/man/tiledb_query_condition_combine.Rd @@ -17,6 +17,7 @@ tiledb_query_condition_combine(lhs, rhs, op) The combined 'tiledb_query_condition' object } \description{ -Combines two query condition object using a relatiional operator. Note that at present -only 'AND' is supported. +Combines two query condition object using a relatiional operator. Support for operator +'AND' is generally available, the 'OR' operator is available if TileDB 2.10 or newer is +used. }