Repository: single-cell-data/TileDB-SOMA (language: python)

Issue:
[r] Port the query-condition logic from TileDB-R to TileDB-SOMA-R
**Issue and/or context:** #3051 [[sc-55672]](https://app.shortcut.com/tiledb-inc/story/55672/r-port-the-query-condition-logic-from-tiledb-r-to-tiledb-soma-r)

**Changes:**

Here I am only introducing a non-exported function `parse_query_condition_new`, and unit-testing it thoroughly. Replacing the exiting `parse_query_condition` with this will be a follow-on PR.

**Notes for Reviewer:**

Timestamp handling will be deferred to a follow-up PR: #3163.
[r] Move `parse_query_condition_new` -> `parse_query_condition`
**Issue and/or context:** Issue #3051 / [[sc-55672]](https://app.shortcut.com/tiledb-inc/story/55672/r-duplicate-query-condition-logic). Stack: #3162, #3163, this.

**Changes:**

Replaces the existing `parse_query_condition`, which uses TileDB-R, with the one currently named `parse_query_condition_new` which uses `libtiledbsoma`.

**Notes for Reviewer:**

This or #3163 can be landed in either order.


Discussion / hints:
@nguyenv re https://github.com/single-cell-data/TileDB-SOMA/pull/3162#discussion_r1801606040 I'll do these in a follow-up PR


Fix patch:
diff --git a/apis/r/R/QueryCondition.R b/apis/r/R/QueryCondition.R
index 982e090410..4269a43cec 100644
--- a/apis/r/R/QueryCondition.R
+++ b/apis/r/R/QueryCondition.R
@@ -111,6 +111,11 @@ parse_query_condition <- function(
         if (is.symbol(node)) {
             stop("Unexpected symbol in expression: ", format(node))
 
+        } else if (node[[1]] == '(') {
+            spdl::debug("[parseqc] paren [{}]",
+                as.character(node[2]));
+            return(.parse_tree_to_qc(node[[2]]))
+
         } else if (.is_boolean_operator(node[1])) {
             spdl::debug("[parseqc] boolop [{}] [{}] [{}]",
                 as.character(node[2]),


Test patch:
diff --git a/apis/r/tests/testthat/test-query-condition.R b/apis/r/tests/testthat/test-query-condition.R
index 6c65c1800b..2ae9703735 100644
--- a/apis/r/tests/testthat/test-query-condition.R
+++ b/apis/r/tests/testthat/test-query-condition.R
@@ -77,6 +77,11 @@ test_that("DataFrame Factory", {
           expect_equal(df$int32, -310)
           expect_equal(as.character(df$enum), c("green"))
       },
+      'soma_joinid == 10.0' = function(df) {
+          expect_equal(df$soma_joinid, 10)
+          expect_equal(df$int32, -310)
+          expect_equal(as.character(df$enum), c("green"))
+      },
       'soma_joinid > 4 && soma_joinid < 8' = function(df) {
           expect_equal(df$soma_joinid, 5:7)
           expect_equal(df$string, c("egg", "fig", "goose"))
@@ -85,6 +90,9 @@ test_that("DataFrame Factory", {
       'soma_joinid < 4 || soma_joinid > 8' = function(df) {
           expect_equal(df$soma_joinid, c(1:3, 9:10))
       },
+      '(soma_joinid < 4) || (soma_joinid > 8)' = function(df) {
+          expect_equal(df$soma_joinid, c(1:3, 9:10))
+      },
 
       'int8 == 8' = function(df) {
           expect_equal(length(df$soma_joinid), 0)
@@ -92,6 +100,17 @@ test_that("DataFrame Factory", {
       'int8 == -12' = function(df) {
           expect_equal(df$soma_joinid, c(2))
       },
+      'uint8 == 12' = function(df) {
+          expect_equal(df$soma_joinid, c(2))
+      },
+      'uint8 == 268' = function(df) {
+          # 12+256
+          expect_equal(df$soma_joinid, c(2))
+      },
+      'uint8 == -244' = function(df) {
+          # 12-256
+          expect_equal(df$soma_joinid, c(2))
+      },
       'int16 > -203' = function(df) {
           expect_equal(df$soma_joinid, c(1, 2))
       },
@@ -124,6 +143,9 @@ test_that("DataFrame Factory", {
       'string == "cat" || string == "dog"' = function(df) {
           expect_equal(df$soma_joinid, c(3, 4))
       },
+      '(string == "cat") || (string == "dog")' = function(df) {
+          expect_equal(df$soma_joinid, c(3, 4))
+      },
       "string == 'cat' || string == 'dog'" = function(df) {
           expect_equal(df$soma_joinid, c(3, 4))
       },
@@ -206,6 +228,26 @@ test_that("DataFrame Factory", {
           expect_equal(df$soma_joinid, 1:10)
       },
 
+      'uint8 <= 14 && uint16 == 202 || uint32 == 308' = function(df) {
+          expect_equal(df$soma_joinid, c(2, 8))
+      },
+      '(uint8 <= 14 && uint16 == 202) || uint32 == 308' = function(df) {
+          expect_equal(df$soma_joinid, c(2, 8))
+      },
+      'uint8 <= 14 && (uint16 == 202 || uint32 == 308)' = function(df) {
+          expect_equal(df$soma_joinid, c(2))
+      },
+
+      'uint32 == 308 || uint8 <= 14 && uint16 == 202' = function(df) {
+          expect_equal(df$soma_joinid, c(2, 8))
+      },
+      'uint32 == 308 || (uint8 <= 14 && uint16 == 202)' = function(df) {
+          expect_equal(df$soma_joinid, c(2, 8))
+      },
+      '(uint32 == 308 || uint8 <= 14) && uint16 == 202' = function(df) {
+          expect_equal(df$soma_joinid, c(2))
+      },
+
       # TODO: for a follow-up PR
       'timestamp_s < "1970-01-01 01:00:05 UTC"' = function(df) {
           expect_equal(df$soma_joinid, 1:4)
@@ -249,8 +291,13 @@ test_that("DataFrame Factory", {
       ' ',
       'nonesuch < 10',
       'soma_joinid << 10',
+      '(soma_joinid < 10',
       'soma_joinid',
-      'soma_joinid < 4 or soma_joinid > 8'
+      'soma_joinid ==',
+      'soma_joinid && int8',
+      'soma_joinid ==1 &&',
+      'soma_joinid < 4 or soma_joinid > 8',
+      'soma_joinid == "ten"'
     )
 
     for (query_string in names(bad_cases)) {