diff --git a/NAMESPACE b/NAMESPACE index fe64cd7e49..c82ec20db0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -17,6 +17,7 @@ export("cell_val_num<-") export("datetimes_as_int64<-") export("extended<-") export("filter_list<-") +export("query_condition<-") export("query_layout<-") export("return.data.frame<-") export("return.matrix<-") @@ -47,6 +48,7 @@ export(limitTileDBCores) export(max_chunk_size) export(name) export(nfilters) +export(query_condition) export(query_layout) export(r_to_tiledb_type) export(return.data.frame) @@ -127,6 +129,9 @@ export(tiledb_query_add_range_with_type) export(tiledb_query_alloc_buffer_ptr_char) export(tiledb_query_alloc_buffer_ptr_char_subarray) export(tiledb_query_buffer_alloc_ptr) +export(tiledb_query_condition) +export(tiledb_query_condition_combine) +export(tiledb_query_condition_init) export(tiledb_query_create_buffer_ptr) export(tiledb_query_create_buffer_ptr_char) export(tiledb_query_export_buffer) @@ -146,6 +151,7 @@ export(tiledb_query_result_buffer_elements) export(tiledb_query_set_buffer) export(tiledb_query_set_buffer_ptr) export(tiledb_query_set_buffer_ptr_char) +export(tiledb_query_set_condition) export(tiledb_query_set_layout) export(tiledb_query_set_subarray) export(tiledb_query_status) @@ -199,6 +205,7 @@ exportClasses(tiledb_domain) exportClasses(tiledb_filter) exportClasses(tiledb_filter_list) exportClasses(tiledb_query) +exportClasses(tiledb_query_condition) exportClasses(tiledb_sparse) exportClasses(tiledb_vfs) exportMethods("[") @@ -209,6 +216,7 @@ exportMethods("cell_val_num<-") exportMethods("datetimes_as_int64<-") exportMethods("extended<-") exportMethods("filter_list<-") +exportMethods("query_condition<-") exportMethods("query_layout<-") exportMethods("return.data.frame<-") exportMethods("return.matrix<-") @@ -231,6 +239,7 @@ exportMethods(is.sparse) exportMethods(max_chunk_size) exportMethods(name) exportMethods(nfilters) +exportMethods(query_condition) exportMethods(query_layout) exportMethods(return.data.frame) exportMethods(return.matrix) diff --git a/NEWS.md b/NEWS.md index 11fd9a30dd..351d89f5d2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,10 @@ * The builds now uses TileDB Embedded 2.3.0 (unless another version is found during build) (#258) +* Dense arrays with more than two dimensions can now be written (#260) + +* Query condition support is available for TileDB 2.3.0 or later, allowing (possibly multiple) numerical constraints on attributes (#261) + # tiledb 0.9.3 diff --git a/R/Query.R b/R/Query.R index ba6deccc84..09f6d2c94b 100644 --- a/R/Query.R +++ b/R/Query.R @@ -332,7 +332,7 @@ tiledb_query_add_range <- function(query, schema, attr, lowval, highval, stride= invisible(query) } -#' Set a range for a given query +#' Set a range for a given query, also supplying type #' #' @param query A TileDB Query object #' @param idx An integer index, zero based, of the dimensions @@ -465,3 +465,16 @@ tiledb_query_get_range <- function(query, dimidx, rngidx) { rngidx_argument=is.numeric(rngidx)) libtiledb_query_get_range_var(query@ptr, dimidx-1, rngidx-1) } + +#' Set a query combination object for a query +#' +#' @param query A TileDB Query object +#' @param qc A TileDB Query Combination object +#' @return The modified query object, invisibly +#' @export +tiledb_query_set_condition <- function(query, qc) { + stopifnot(`needs query object`=is(query, "tiledb_query"), + `needs query condition object`=is(qc, "tiledb_query_condition")) + query@ptr <- libtiledb_query_set_condition(query@ptr, qc@ptr) + invisible(query) +} diff --git a/R/QueryCondition.R b/R/QueryCondition.R new file mode 100644 index 0000000000..f5df0a850c --- /dev/null +++ b/R/QueryCondition.R @@ -0,0 +1,94 @@ +# MIT License +# +# Copyright (c) 2021 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 +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +#' An S4 class for a TileDB QueryCondition object +#' +#' @slot ptr An external pointer to the underlying implementation +#' @slot init A logical variable tracking if the query condition object has been +#' initialized +#' @exportClass tiledb_query_condition +setClass("tiledb_query_condition", + slots = list(ptr = "externalptr", + init = "logical")) + +#' Creates a 'tiledb_query_condition' object +#' +#' @param ctx (optional) A TileDB Ctx object; if not supplied the default +#' context object is retrieved +#' @return A 'tiledb_query_condition' object +#' @export +tiledb_query_condition <- function(ctx = tiledb_get_context()) { + stopifnot(`needs ctx object` = is(ctx, "tiledb_ctx"), + `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) +} + +#' Initialize a 'tiledb_query_condition' object +#' +#' Initializes (and possibly allocates) a query condition object using a triplet of +#' attribute name, comparison value, and operator. Six types of conditions are supported, +#' they all take a single scalar comparison argument and attribute to compare against. +#' At present only integer or numeric attribute comparisons are implemented. +#' @param attr A character value with the scheme attribute name +#' @param value A scalar value that the attribute is compared against +#' @param dtype A character value with the TileDB data type of the attribute column, for +#' example 'FLOAT64' or 'INT32' +#' @param op A character value with then comparison operation, this must be one of +#' 'LT', 'LE', 'GT', 'GE', 'EQ', 'NE'. +#' @param qc (optional) A 'tiledb_query_condition' object to be initialized by this call, +#' if none is given a new one is allocated. +#' @return The initialized 'tiledb_query_condition' object +#' @export +tiledb_query_condition_init <- function(attr, value, dtype, op, qc = tiledb_query_condition()) { + stopifnot(`needs query condition object`=is(qc, "tiledb_query_condition"), + `attr must be character`=is.character(attr), + `value must be of length one`=is.vector(value) && all.equal(length(value),1), + `dtype must be character`=is.character(dtype), + `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) + qc@init <- TRUE + invisible(qc) +} + +#' Combine two 'tiledb_query_condition' objects +#' +#' Combines two query condition object using a relatiional operator. Note that at present +#' only 'AND' is supported. +#' @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(`needs query condition object on lhs`=is(lhs, "tiledb_query_condition"), + `needs query condition object on rhs`=is(rhs, "tiledb_query_condition"), + `op must be 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) + qc@init <- TRUE + invisible(qc) +} diff --git a/R/RcppExports.R b/R/RcppExports.R index bccc02832c..086dedb2a0 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -641,6 +641,22 @@ libtiledb_query_get_range_var <- function(query, dim_idx, rng_idx) { .Call(`_tiledb_libtiledb_query_get_range_var`, query, dim_idx, rng_idx) } +libtiledb_query_set_condition <- function(query, query_cond) { + .Call(`_tiledb_libtiledb_query_set_condition`, query, query_cond) +} + +libtiledb_query_condition <- function(ctx) { + .Call(`_tiledb_libtiledb_query_condition`, ctx) +} + +libtiledb_query_condition_init <- function(query_cond, attr_name, condition_value, cond_val_type, cond_op_string) { + invisible(.Call(`_tiledb_libtiledb_query_condition_init`, query_cond, attr_name, condition_value, cond_val_type, cond_op_string)) +} + +libtiledb_query_condition_combine <- function(lhs, rhs, str) { + .Call(`_tiledb_libtiledb_query_condition_combine`, lhs, rhs, str) +} + libtiledb_zip_coords_numeric <- function(coords, coord_length) { .Call(`_tiledb_libtiledb_zip_coords_numeric`, coords, coord_length) } diff --git a/R/TileDBArray.R b/R/TileDBArray.R index 725cec68a3..34cfff2eb4 100644 --- a/R/TileDBArray.R +++ b/R/TileDBArray.R @@ -40,6 +40,7 @@ #' @slot encryption_key A character value #' @slot timestamp A POSIXct datetime variable #' @slot as.matrix A logical value +#' @slot query_condition A Query Condition object #' @slot ptr External pointer to the underlying implementation #' @exportClass tiledb_array setClass("tiledb_array", @@ -55,6 +56,7 @@ setClass("tiledb_array", encryption_key = "character", timestamp = "POSIXct", as.matrix = "logical", + query_condition = "tiledb_query_condition", ptr = "externalptr")) #' Constructs a tiledb_array object backed by a persisted tiledb array uri @@ -82,6 +84,8 @@ setClass("tiledb_array", #' to be openened. #' @param as.matrix optional logical switch, defaults to "FALSE"; currently limited to dense #' matrices; in the case of multiple attributes in query lists of matrices are returned +#' @param query_condition optional \code{tiledb_query_condition} object, by default uninitialized +#' without a condition; this functionality requires TileDB 2.3.0 or later #' @param ctx tiledb_ctx (optional) #' @return tiledb_array object #' @export @@ -97,6 +101,7 @@ tiledb_array <- function(uri, encryption_key = character(), timestamp = as.POSIXct(double(), origin="1970-01-01"), as.matrix = FALSE, + query_condition = new("tiledb_query_condition"), ctx = tiledb_get_context()) { query_type = match.arg(query_type) if (!is(ctx, "tiledb_ctx")) @@ -149,6 +154,7 @@ tiledb_array <- function(uri, encryption_key = encryption_key, timestamp = timestamp, as.matrix = as.matrix, + query_condition = query_condition, ptr = array_xptr) } @@ -210,6 +216,7 @@ setMethod("show", signature = "tiledb_array", ," encryption_key = ", if (length(object@encryption_key) == 0) "(none)" else "(set)", "\n" ," timestamp = ", if (length(object@timestamp) == 0) "(none)" else format(object@timestamp), "\n" ," as.matrix = ", if (object@as.matrix) "TRUE" else "FALSE", "\n" + ," query_condition = ", if (isTRUE(object@query_condition@init)) "(set)" else "(none)", "\n" ,sep="") }) @@ -295,6 +302,11 @@ setValidity("tiledb_array", function(object) { msg <- c(msg, "The 'as.data.frame' and 'as.matrix' slots cannot be both set to 'TRUE'.") } + if (!is(object@query_condition, "tiledb_query_condition")) { + valid <- FALSE + msg <- c(msg, "The 'query_condition' slot does not contain a query condition object.") + } + if (!is(object@ptr, "externalptr")) { valid <- FALSE msg <- c(msg, "The 'ptr' slot does not contain an external pointer.") @@ -311,7 +323,7 @@ setValidity("tiledb_array", function(object) { ## which, being an R date, has an internal representation of _days_ since the epoch, i.e. ## as.numeric(as.Date("2021-01-01")) yields 18628. ## -## We also convert to integer64 because that is +## We also convert the value to integer64 because that is the internal storage format .mapDatetime2integer64 <- function(val, dtype) { ## in case it is not a datetime type, or already an int64, return unchanged if (!grepl("^DATETIME_", dtype) || inherits(val, "integer64")) @@ -581,6 +593,11 @@ setMethod("[", "tiledb_array", MoreArgs=list(resrv=resrv, qryptr=qryptr, arrptr=arrptr), SIMPLIFY=FALSE) + ## if we have a query condition, apply it + if (isTRUE(x@query_condition@init)) { + qryptr <- libtiledb_query_set_condition(qryptr, x@query_condition@ptr) + } + ## fire off query and close array qryptr <- libtiledb_query_submit(qryptr) libtiledb_array_close(arrptr) @@ -1257,3 +1274,39 @@ setReplaceMethod("return.matrix", validObject(x) x }) + + +## -- query_condition accessors + +#' @rdname query_condition-tiledb_array-method +#' @export +setGeneric("query_condition", function(object) standardGeneric("query_condition")) + +#' @rdname query_condition-set-tiledb_array-method +#' @export +setGeneric("query_condition<-", function(x, value) standardGeneric("query_condition<-")) + +#' Retrieve query_condition value for the array +#' +#' A \code{tiledb_array} object can have a corresponding query condition object. +#' This methods returns it. +#' @param object A \code{tiledb_array} object +#' @return A \code{tiledb_query_condition} object +#' @export +setMethod("query_condition", signature = "tiledb_array", function(object) object@query_condition) + +#' Set query_condition object for the array +#' +#' A \code{tiledb_array} object can have an associated query condition object to set +#' conditions on the read queries. This methods sets the \sQuote{query_condition} object. +#' @param x A \code{tiledb_array} object +#' +#' @param value A \code{tiledb_query_conditon_object} +#' @return The modified \code{tiledb_array} array object +#' @export +setReplaceMethod("query_condition", signature = "tiledb_array", function(x, value) { + stopifnot(`need query_condition object` = is(value, "tiledb_query_condition")) + x@query_condition <- value + validObject(x) + x +}) diff --git a/inst/include/tiledb.h b/inst/include/tiledb.h index 28cca36d6c..c311d78110 100644 --- a/inst/include/tiledb.h +++ b/inst/include/tiledb.h @@ -69,4 +69,12 @@ struct vfs_fh { typedef struct vfs_fh vfs_fh_t; +#if TILEDB_VERSION_MAJOR == 2 && TILEDB_VERSION_MINOR < 3 +// we need a placeholder as tiledb::QueryCondition is in at least one function signature +namespace tiledb { + class QueryCondition { + }; +} +#endif + #endif // __tiledb_h__ diff --git a/inst/tinytest/test_querycondition.R b/inst/tinytest/test_querycondition.R new file mode 100644 index 0000000000..329a52b7f2 --- /dev/null +++ b/inst/tinytest/test_querycondition.R @@ -0,0 +1,161 @@ +library(tinytest) +library(tiledb) + +isOldWindows <- Sys.info()[["sysname"]] == "Windows" && grepl('Windows Server 2008', osVersion) +if (isOldWindows) exit_file("skip this file on old Windows releases") + +ctx <- tiledb_ctx(limitTileDBCores()) + +if (tiledb_version(TRUE) < "2.3.0") exit_file("TileDB Query Condition requires TileDB 2.3.* or greater") + +## simple data.frame to test against +D <- data.frame(a=1:20, + b=seq(101,120)+0.5) +uri <- tempfile() +fromDataFrame(D, uri, sparse=TRUE) +arr <- tiledb_array(uri) +qry <- tiledb_query(arr, "READ") +rows <- integer(20) +cola <- integer(20) +colb <- numeric(20) +tiledb_query_set_buffer(qry, "__tiledb_rows", rows) +tiledb_query_set_buffer(qry, "a", cola) +tiledb_query_set_buffer(qry, "b", colb) + +# check a >= 2 && a < 3 +lhs <- tiledb_query_condition_init("a", 2L, "INT32", "GE") +rhs <- tiledb_query_condition_init("a", 3L, "INT32", "LT") +qc <- tiledb_query_condition_combine(lhs, rhs, "AND") +qry <- tiledb_query_set_condition(qry, qc) +tiledb_query_submit(qry) +tiledb_query_finalize(qry) +n <- tiledb_query_result_buffer_elements(qry, "a") +ndf <- data.frame(rows=rows,a=cola,b=colb)[1:n,] +expect_equal(nrow(ndf), 1) +expect_equal(ndf[1,"a"], 2L) +tiledb_array_close(arr) +rm(qry) + +## check a >= 2 +qry <- tiledb_query(arr, "READ") +rows <- integer(20) +cola <- integer(20) +colb <- numeric(20) +tiledb_query_set_buffer(qry, "__tiledb_rows", rows) +tiledb_query_set_buffer(qry, "a", cola) +tiledb_query_set_buffer(qry, "b", colb) +lhs <- tiledb_query_condition_init("a", 2L, "INT32", "GE") +qry <- tiledb_query_set_condition(qry, lhs) +tiledb_query_submit(qry) +tiledb_query_finalize(qry) +n <- tiledb_query_result_buffer_elements(qry, "a") +ndf <- data.frame(rows=rows,a=cola,b=colb)[1:n,] +expect_equal(nrow(ndf), 19) +tiledb_array_close(arr) +rm(qry) + +## check a != 2 && a != 12 +qry <- tiledb_query(arr, "READ") +rows <- integer(20) +cola <- integer(20) +colb <- numeric(20) +tiledb_query_set_buffer(qry, "__tiledb_rows", rows) +tiledb_query_set_buffer(qry, "a", cola) +tiledb_query_set_buffer(qry, "b", colb) +lhs <- tiledb_query_condition_init("a", 2L, "INT32", "NE") +rhs <- tiledb_query_condition_init("a", 12L, "INT32", "NE") +qc <- tiledb_query_condition_combine(lhs, rhs, "AND") +qry <- tiledb_query_set_condition(qry, qc) +tiledb_query_submit(qry) +tiledb_query_finalize(qry) +n <- tiledb_query_result_buffer_elements(qry, "a") +ndf <- data.frame(rows=rows,a=cola,b=colb)[1:n,] +expect_equal(nrow(ndf), 18) +tiledb_array_close(arr) +rm(qry) + +## check a >=5 && b <= 115 +qry <- tiledb_query(arr, "READ") +rows <- integer(20) +cola <- integer(20) +colb <- numeric(20) +tiledb_query_set_buffer(qry, "__tiledb_rows", rows) +tiledb_query_set_buffer(qry, "a", cola) +tiledb_query_set_buffer(qry, "b", colb) +lhs <- tiledb_query_condition_init("a", 5L, "INT32", "GE") +rhs <- tiledb_query_condition_init("b", 115, "FLOAT64", "LE") +qc <- tiledb_query_condition_combine(lhs, rhs, "AND") +qry <- tiledb_query_set_condition(qry, qc) +tiledb_query_submit(qry) +tiledb_query_finalize(qry) +n <- tiledb_query_result_buffer_elements(qry, "a") +ndf <- data.frame(rows=rows,a=cola,b=colb)[1:n,] +expect_equal(nrow(ndf), 10) +tiledb_array_close(arr) +rm(qry) + +## check b == 115.5 (yes, yes, yes, we know EQ dicey on floats; can remove this if it croaks) +qry <- tiledb_query(arr, "READ") +rows <- integer(20) +cola <- integer(20) +colb <- numeric(20) +tiledb_query_set_buffer(qry, "__tiledb_rows", rows) +tiledb_query_set_buffer(qry, "a", cola) +tiledb_query_set_buffer(qry, "b", colb) +qc <- tiledb_query_condition_init("b", 115.5, "FLOAT64", "EQ") +qry <- tiledb_query_set_condition(qry, qc) +tiledb_query_submit(qry) +tiledb_query_finalize(qry) +n <- tiledb_query_result_buffer_elements(qry, "a") +ndf <- data.frame(rows=rows,a=cola,b=colb)[1:n,] +expect_equal(nrow(ndf), 1) +tiledb_array_close(arr) +rm(qry) + +## check b >= 115.4 && b <= 115.6 +qry <- tiledb_query(arr, "READ") +rows <- integer(20) +cola <- integer(20) +colb <- numeric(20) +tiledb_query_set_buffer(qry, "__tiledb_rows", rows) +tiledb_query_set_buffer(qry, "a", cola) +tiledb_query_set_buffer(qry, "b", colb) +lhs <- tiledb_query_condition_init("b", 115.4, "FLOAT64", "GE") +rhs <- tiledb_query_condition_init("b", 115.6, "FLOAT64", "LE") +qc <- tiledb_query_condition_combine(lhs, rhs, "AND") +qry <- tiledb_query_set_condition(qry, qc) +tiledb_query_submit(qry) +tiledb_query_finalize(qry) +n <- tiledb_query_result_buffer_elements(qry, "a") +ndf <- data.frame(rows=rows,a=cola,b=colb)[1:n,] +expect_equal(nrow(ndf), 1) +tiledb_array_close(arr) +rm(qry) + + +## tiledb_array support +if (!requireNamespace("palmerpenguins", quietly=TRUE)) exit_file("remainder needs 'palmerpenguins'") +library(palmerpenguins) +uri <- tempfile() +fromDataFrame(penguins, uri, sparse=TRUE) +unconstr <- tiledb_array(uri, as.data.frame=TRUE) +expect_equal(NROW(unconstr[]), 344L) # no condition -> 344 rows + +qc <- tiledb_query_condition_init("year", 2009, "INT32", "EQ") +arrwithqc <- tiledb_array(uri, as.data.frame=TRUE, query_condition=qc) +res <- arrwithqc[] +expect_equal(NROW(res), 120L) # year 2009 only -> 120 rows +expect_true(all(res$year == 2009)) + +arr2 <- tiledb_array(uri, as.data.frame=TRUE) +expect_equal(NROW(arr2[]), 344L) # no condition -> 344 rows +query_condition(arr2) <- qc +expect_equal(NROW(arr2[]), 120L) # year 2009 only -> 120 rows + +qc2 <- tiledb_query_condition_init("bill_length_mm", 40.0, "FLOAT64", "LT") +qc3 <- tiledb_query_condition_combine(qc, qc2, "AND") +query_condition(arr2) <- qc3 +res <- arr2[] +expect_equal(NROW(res), 34L) +expect_true(all(res$bill_length_mm < 40)) +expect_true(all(res$year == 2009)) diff --git a/man/query_condition-set-tiledb_array-method.Rd b/man/query_condition-set-tiledb_array-method.Rd new file mode 100644 index 0000000000..c19894eb6f --- /dev/null +++ b/man/query_condition-set-tiledb_array-method.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/TileDBArray.R +\name{query_condition<-} +\alias{query_condition<-} +\alias{query_condition<-,tiledb_array-method} +\title{Set query_condition object for the array} +\usage{ +query_condition(x) <- value + +\S4method{query_condition}{tiledb_array}(x) <- value +} +\arguments{ +\item{x}{A \code{tiledb_array} object} + +\item{value}{A \code{tiledb_query_conditon_object}} +} +\value{ +The modified \code{tiledb_array} array object +} +\description{ +A \code{tiledb_array} object can have an associated query condition object to set +conditions on the read queries. This methods sets the \sQuote{query_condition} object. +} diff --git a/man/query_condition-tiledb_array-method.Rd b/man/query_condition-tiledb_array-method.Rd new file mode 100644 index 0000000000..14cc296a28 --- /dev/null +++ b/man/query_condition-tiledb_array-method.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/TileDBArray.R +\name{query_condition} +\alias{query_condition} +\alias{query_condition,tiledb_array-method} +\title{Retrieve query_condition value for the array} +\usage{ +query_condition(object) + +\S4method{query_condition}{tiledb_array}(object) +} +\arguments{ +\item{object}{A \code{tiledb_array} object} +} +\value{ +A \code{tiledb_query_condition} object +} +\description{ +A \code{tiledb_array} object can have a corresponding query condition object. +This methods returns it. +} diff --git a/man/tiledb_array-class.Rd b/man/tiledb_array-class.Rd index 4d271ffcf4..2c6c1289e4 100644 --- a/man/tiledb_array-class.Rd +++ b/man/tiledb_array-class.Rd @@ -37,6 +37,8 @@ describes the (min,max) pair of ranges for dimension i} \item{\code{as.matrix}}{A logical value} +\item{\code{query_condition}}{A Query Condition object} + \item{\code{ptr}}{External pointer to the underlying implementation} }} diff --git a/man/tiledb_array.Rd b/man/tiledb_array.Rd index 8a708bb47a..b0aa9f52e8 100644 --- a/man/tiledb_array.Rd +++ b/man/tiledb_array.Rd @@ -17,6 +17,7 @@ tiledb_array( encryption_key = character(), timestamp = as.POSIXct(double(), origin = "1970-01-01"), as.matrix = FALSE, + query_condition = new("tiledb_query_condition"), ctx = tiledb_get_context() ) } @@ -54,6 +55,9 @@ to be openened.} \item{as.matrix}{optional logical switch, defaults to "FALSE"; currently limited to dense matrices; in the case of multiple attributes in query lists of matrices are returned} +\item{query_condition}{optional \code{tiledb_query_condition} object, by default uninitialized +without a condition; this functionality requires TileDB 2.3.0 or later} + \item{ctx}{tiledb_ctx (optional)} } \value{ diff --git a/man/tiledb_query_add_range_with_type.Rd b/man/tiledb_query_add_range_with_type.Rd index 34f54e6ad1..b1f9975b73 100644 --- a/man/tiledb_query_add_range_with_type.Rd +++ b/man/tiledb_query_add_range_with_type.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/Query.R \name{tiledb_query_add_range_with_type} \alias{tiledb_query_add_range_with_type} -\title{Set a range for a given query} +\title{Set a range for a given query, also supplying type} \usage{ tiledb_query_add_range_with_type( query, @@ -30,5 +30,5 @@ tiledb_query_add_range_with_type( The query object, invisibly } \description{ -Set a range for a given query +Set a range for a given query, also supplying type } diff --git a/man/tiledb_query_condition-class.Rd b/man/tiledb_query_condition-class.Rd new file mode 100644 index 0000000000..37dfad03af --- /dev/null +++ b/man/tiledb_query_condition-class.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/QueryCondition.R +\docType{class} +\name{tiledb_query_condition-class} +\alias{tiledb_query_condition-class} +\title{An S4 class for a TileDB QueryCondition object} +\description{ +An S4 class for a TileDB QueryCondition object +} +\section{Slots}{ + +\describe{ +\item{\code{ptr}}{An external pointer to the underlying implementation} + +\item{\code{init}}{A logical variable tracking if the query condition object has been +initialized} +}} + diff --git a/man/tiledb_query_condition.Rd b/man/tiledb_query_condition.Rd new file mode 100644 index 0000000000..de2f174ae4 --- /dev/null +++ b/man/tiledb_query_condition.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/QueryCondition.R +\name{tiledb_query_condition} +\alias{tiledb_query_condition} +\title{Creates a 'tiledb_query_condition' object} +\usage{ +tiledb_query_condition(ctx = tiledb_get_context()) +} +\arguments{ +\item{ctx}{(optional) A TileDB Ctx object; if not supplied the default +context object is retrieved} +} +\value{ +A 'tiledb_query_condition' object +} +\description{ +Creates a 'tiledb_query_condition' object +} diff --git a/man/tiledb_query_condition_combine.Rd b/man/tiledb_query_condition_combine.Rd new file mode 100644 index 0000000000..5604f31c1c --- /dev/null +++ b/man/tiledb_query_condition_combine.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/QueryCondition.R +\name{tiledb_query_condition_combine} +\alias{tiledb_query_condition_combine} +\title{Combine two 'tiledb_query_condition' objects} +\usage{ +tiledb_query_condition_combine(lhs, rhs, op) +} +\arguments{ +\item{lhs}{A 'tiledb_query_condition' object on the left-hand side of the relation} + +\item{rhs}{A 'tiledb_query_condition' object on the left-hand side of the relation} + +\item{op}{A character value with then relation, this must be one of 'AND', 'OR' or 'NOT'.} +} +\value{ +The combined 'tiledb_query_condition' object +} +\description{ +Combines two query condition object using a relatiional operator. Note that at present +only 'AND' is supported. +} diff --git a/man/tiledb_query_condition_init.Rd b/man/tiledb_query_condition_init.Rd new file mode 100644 index 0000000000..adf2318678 --- /dev/null +++ b/man/tiledb_query_condition_init.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/QueryCondition.R +\name{tiledb_query_condition_init} +\alias{tiledb_query_condition_init} +\title{Initialize a 'tiledb_query_condition' object} +\usage{ +tiledb_query_condition_init( + attr, + value, + dtype, + op, + qc = tiledb_query_condition() +) +} +\arguments{ +\item{attr}{A character value with the scheme attribute name} + +\item{value}{A scalar value that the attribute is compared against} + +\item{dtype}{A character value with the TileDB data type of the attribute column, for +example 'FLOAT64' or 'INT32'} + +\item{op}{A character value with then comparison operation, this must be one of +'LT', 'LE', 'GT', 'GE', 'EQ', 'NE'.} + +\item{qc}{(optional) A 'tiledb_query_condition' object to be initialized by this call, +if none is given a new one is allocated.} +} +\value{ +The initialized 'tiledb_query_condition' object +} +\description{ +Initializes (and possibly allocates) a query condition object using a triplet of +attribute name, comparison value, and operator. Six types of conditions are supported, +they all take a single scalar comparison argument and attribute to compare against. +At present only integer or numeric attribute comparisons are implemented. +} diff --git a/man/tiledb_query_set_condition.Rd b/man/tiledb_query_set_condition.Rd new file mode 100644 index 0000000000..3aef2ff3ed --- /dev/null +++ b/man/tiledb_query_set_condition.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Query.R +\name{tiledb_query_set_condition} +\alias{tiledb_query_set_condition} +\title{Set a query combination object for a query} +\usage{ +tiledb_query_set_condition(query, qc) +} +\arguments{ +\item{query}{A TileDB Query object} + +\item{qc}{A TileDB Query Combination object} +} +\value{ +The modified query object, invisibly +} +\description{ +Set a query combination object for a query +} diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 31834c40a4..1994c46590 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -1911,6 +1911,56 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } +// libtiledb_query_set_condition +XPtr libtiledb_query_set_condition(XPtr query, XPtr query_cond); +RcppExport SEXP _tiledb_libtiledb_query_set_condition(SEXP querySEXP, SEXP query_condSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< XPtr >::type query(querySEXP); + Rcpp::traits::input_parameter< XPtr >::type query_cond(query_condSEXP); + rcpp_result_gen = Rcpp::wrap(libtiledb_query_set_condition(query, query_cond)); + return rcpp_result_gen; +END_RCPP +} +// libtiledb_query_condition +XPtr libtiledb_query_condition(XPtr ctx); +RcppExport SEXP _tiledb_libtiledb_query_condition(SEXP ctxSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< XPtr >::type ctx(ctxSEXP); + rcpp_result_gen = Rcpp::wrap(libtiledb_query_condition(ctx)); + return rcpp_result_gen; +END_RCPP +} +// libtiledb_query_condition_init +void libtiledb_query_condition_init(XPtr query_cond, const std::string& attr_name, SEXP condition_value, const std::string& cond_val_type, const std::string& cond_op_string); +RcppExport SEXP _tiledb_libtiledb_query_condition_init(SEXP query_condSEXP, SEXP attr_nameSEXP, SEXP condition_valueSEXP, SEXP cond_val_typeSEXP, SEXP cond_op_stringSEXP) { +BEGIN_RCPP + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< XPtr >::type query_cond(query_condSEXP); + Rcpp::traits::input_parameter< const std::string& >::type attr_name(attr_nameSEXP); + Rcpp::traits::input_parameter< SEXP >::type condition_value(condition_valueSEXP); + Rcpp::traits::input_parameter< const std::string& >::type cond_val_type(cond_val_typeSEXP); + Rcpp::traits::input_parameter< const std::string& >::type cond_op_string(cond_op_stringSEXP); + libtiledb_query_condition_init(query_cond, attr_name, condition_value, cond_val_type, cond_op_string); + return R_NilValue; +END_RCPP +} +// libtiledb_query_condition_combine +XPtr libtiledb_query_condition_combine(XPtr lhs, XPtr rhs, const std::string& str); +RcppExport SEXP _tiledb_libtiledb_query_condition_combine(SEXP lhsSEXP, SEXP rhsSEXP, SEXP strSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< XPtr >::type lhs(lhsSEXP); + Rcpp::traits::input_parameter< XPtr >::type rhs(rhsSEXP); + Rcpp::traits::input_parameter< const std::string& >::type str(strSEXP); + rcpp_result_gen = Rcpp::wrap(libtiledb_query_condition_combine(lhs, rhs, str)); + return rcpp_result_gen; +END_RCPP +} // libtiledb_zip_coords_numeric NumericVector libtiledb_zip_coords_numeric(List coords, R_xlen_t coord_length); RcppExport SEXP _tiledb_libtiledb_zip_coords_numeric(SEXP coordsSEXP, SEXP coord_lengthSEXP) { @@ -2461,6 +2511,10 @@ static const R_CallMethodDef CallEntries[] = { {"_tiledb_libtiledb_query_get_range_num", (DL_FUNC) &_tiledb_libtiledb_query_get_range_num, 2}, {"_tiledb_libtiledb_query_get_range", (DL_FUNC) &_tiledb_libtiledb_query_get_range, 3}, {"_tiledb_libtiledb_query_get_range_var", (DL_FUNC) &_tiledb_libtiledb_query_get_range_var, 3}, + {"_tiledb_libtiledb_query_set_condition", (DL_FUNC) &_tiledb_libtiledb_query_set_condition, 2}, + {"_tiledb_libtiledb_query_condition", (DL_FUNC) &_tiledb_libtiledb_query_condition, 1}, + {"_tiledb_libtiledb_query_condition_init", (DL_FUNC) &_tiledb_libtiledb_query_condition_init, 5}, + {"_tiledb_libtiledb_query_condition_combine", (DL_FUNC) &_tiledb_libtiledb_query_condition_combine, 3}, {"_tiledb_libtiledb_zip_coords_numeric", (DL_FUNC) &_tiledb_libtiledb_zip_coords_numeric, 2}, {"_tiledb_libtiledb_zip_coords_integer", (DL_FUNC) &_tiledb_libtiledb_zip_coords_integer, 2}, {"_tiledb_libtiledb_group_create", (DL_FUNC) &_tiledb_libtiledb_group_create, 2}, diff --git a/src/finalizers.h b/src/finalizers.h index a42b10c469..8fa259d688 100644 --- a/src/finalizers.h +++ b/src/finalizers.h @@ -1,6 +1,6 @@ // MIT License // -// Copyright (c) 2020 TileDB Inc. +// Copyright (c) 2020-2021 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 @@ -162,6 +162,15 @@ extern "C" { } } + inline void libtiledb_query_condition_delete(SEXP sexp) { + XPtr qc(sexp); + tiledb::QueryCondition* ptr = qc.get(); + if (ptr != nullptr) { + delete ptr; + ptr = nullptr; + } + } + } #endif diff --git a/src/libtiledb.cpp b/src/libtiledb.cpp index 0a3bde7446..184bcd07bf 100644 --- a/src/libtiledb.cpp +++ b/src/libtiledb.cpp @@ -3149,6 +3149,134 @@ CharacterVector libtiledb_query_get_range_var(XPtr query, int dim return CharacterVector::create(rng[0], rng[1]); // start and end } +// [[Rcpp::export]] +XPtr libtiledb_query_set_condition(XPtr query, + XPtr query_cond) { +#if TILEDB_VERSION >= TileDB_Version(2,3,0) + query->set_condition(*query_cond.get()); +#endif + return query; +} + +/** + * Query Condition + */ +#if TILEDB_VERSION >= TileDB_Version(2,3,0) +const char* _tiledb_query_condition_op_to_string(tiledb_query_condition_op_t op) { + switch (op) { + case TILEDB_LT: + return "LT"; + case TILEDB_LE: + return "LE"; + case TILEDB_GT: + return "GT"; + case TILEDB_GE: + return "GE"; + case TILEDB_EQ: + return "EQ"; + case TILEDB_NE: + return "NE"; + default: + Rcpp::stop("Unknown condition op (%d)", op); + } +} + +tiledb_query_condition_op_t _tiledb_query_string_to_condition_op(const std::string& opstr) { + if (opstr == "LT") { + return TILEDB_LT; + } else if (opstr == "LE") { + return TILEDB_LE; + } else if (opstr == "GT") { + return TILEDB_GT; + } else if (opstr == "GE") { + return TILEDB_GE; + } else if (opstr == "EQ") { + return TILEDB_EQ; + } else if (opstr == "NE") { + return TILEDB_NE; + } else { + Rcpp::stop("Unknown TileDB op string '%s'", opstr.c_str()); + } +} + +const char* _tiledb_query_condition_combination_op_to_string(tiledb_query_condition_combination_op_t op) { + switch (op) { + case TILEDB_AND: + return "AND"; + case TILEDB_OR: + return "OR"; + case TILEDB_NOT: + return "NOT"; + default: + Rcpp::stop("Unknown condition combination op (%d)", op); + } +} + +tiledb_query_condition_combination_op_t _tiledb_query_string_to_condition_combination_op(const std::string& opstr) { + if (opstr == "AND") { + return TILEDB_AND; + } else if (opstr == "OR") { + return TILEDB_OR; + } else if (opstr == "NOT") { + return TILEDB_NOT; + } else { + Rcpp::stop("Unknown TileDB combination op string '%s'", opstr.c_str()); + } +} +#endif + +// [[Rcpp::export]] +XPtr libtiledb_query_condition(XPtr ctx) { +#if TILEDB_VERSION >= TileDB_Version(2,3,0) + XPtr query_cond(new tiledb::QueryCondition(*ctx.get())); +#else + XPtr query_cond(new tiledb::QueryCondition()); +#endif + //registerXptrFinalizer(query_cond, libtiledb_query_condition_delete); + return query_cond; +} + +// [[Rcpp::export]] +void libtiledb_query_condition_init(XPtr query_cond, + const std::string& attr_name, + SEXP condition_value, + const std::string& cond_val_type, + const std::string& cond_op_string) { + +#if TILEDB_VERSION >= TileDB_Version(2,3,0) + tiledb_query_condition_op_t op = _tiledb_query_string_to_condition_op(cond_op_string); + // this is a dual map first to the TILEDB_* type and then to the R type + std::string rtype = tiledb_datatype_R_type(cond_val_type); + if (rtype == "integer") { + int v = as(condition_value); + uint64_t cond_val_size = sizeof(int); + query_cond->init(attr_name, (void*) &v, cond_val_size, op); + } else if (rtype == "double") { + double v = as(condition_value); + uint64_t cond_val_size = sizeof(double); + query_cond->init(attr_name, (void*) &v, cond_val_size, op); + } else { + Rcpp::stop("Currently unsupport type: %s", cond_val_type); + } +#endif +} + +// [[Rcpp::export]] +XPtr libtiledb_query_condition_combine(XPtr lhs, + XPtr rhs, + const std::string& str) { +#if TILEDB_VERSION >= TileDB_Version(2,3,0) + tiledb_query_condition_combination_op_t op = _tiledb_query_string_to_condition_combination_op(str); + tiledb::QueryCondition res = lhs->combine(*rhs.get(), op); + auto query_cond = XPtr(new tiledb::QueryCondition(res)); +#else + XPtr query_cond(new tiledb::QueryCondition()); +#endif + //registerXptrFinalizer(query_cond, libtiledb_query_condition_delete); + return query_cond; +} + + /** * Array helper functions */