1010use Utopia \Database \Exception \Duplicate as DuplicateException ;
1111use Utopia \Database \Exception \Limit as LimitException ;
1212use Utopia \Database \Exception \NotFound as NotFoundException ;
13+ use Utopia \Database \Exception \Operator as OperatorException ;
1314use Utopia \Database \Exception \Query as QueryException ;
1415use Utopia \Database \Exception \Timeout as TimeoutException ;
1516use Utopia \Database \Exception \Truncate as TruncateException ;
@@ -1923,7 +1924,7 @@ protected function getOperatorSQL(string $column, Operator $operator, int &$bind
19231924 $ values = $ operator ->getValues ();
19241925
19251926 switch ($ method ) {
1926- // Numeric operators with NULL handling and overflow prevention
1927+ // Numeric operators
19271928 case Operator::TYPE_INCREMENT :
19281929 $ bindKey = "op_ {$ bindIndex }" ;
19291930 $ bindIndex ++;
@@ -2000,62 +2001,89 @@ protected function getOperatorSQL(string $column, Operator $operator, int &$bind
20002001 }
20012002 return "{$ quotedColumn } = POWER(COALESCE( {$ quotedColumn }, 0), : $ bindKey) " ;
20022003
2003- // Boolean operator with NULL handling
2004+ // String operators
2005+ case Operator::TYPE_STRING_CONCAT :
2006+ $ bindKey = "op_ {$ bindIndex }" ;
2007+ $ bindIndex ++;
2008+ return "{$ quotedColumn } = CONCAT(COALESCE( {$ quotedColumn }, ''), : $ bindKey) " ;
2009+
2010+ case Operator::TYPE_STRING_REPLACE :
2011+ $ searchKey = "op_ {$ bindIndex }" ;
2012+ $ bindIndex ++;
2013+ $ replaceKey = "op_ {$ bindIndex }" ;
2014+ $ bindIndex ++;
2015+ return "{$ quotedColumn } = REPLACE( {$ quotedColumn }, : $ searchKey, : $ replaceKey) " ;
2016+
2017+ // Boolean operators
20042018 case Operator::TYPE_TOGGLE :
20052019 return "{$ quotedColumn } = NOT COALESCE( {$ quotedColumn }, FALSE) " ;
20062020
2007- case Operator::TYPE_CONCAT :
2021+ // Array operators
2022+ case Operator::TYPE_ARRAY_APPEND :
20082023 $ bindKey = "op_ {$ bindIndex }" ;
20092024 $ bindIndex ++;
2010- return "{$ quotedColumn } = CONCAT(COALESCE( {$ quotedColumn }, ''), : $ bindKey) " ;
2025+ return "{$ quotedColumn } = JSON_MERGE_PRESERVE(IFNULL( {$ quotedColumn }, JSON_ARRAY()), : $ bindKey) " ;
2026+
2027+ case Operator::TYPE_ARRAY_PREPEND :
2028+ $ bindKey = "op_ {$ bindIndex }" ;
2029+ $ bindIndex ++;
2030+ return "{$ quotedColumn } = JSON_MERGE_PRESERVE(: $ bindKey, IFNULL( {$ quotedColumn }, JSON_ARRAY())) " ;
20112031
20122032 case Operator::TYPE_ARRAY_INSERT :
2013- // Use JSON_ARRAY_INSERT for proper array insertion
20142033 $ indexKey = "op_ {$ bindIndex }" ;
20152034 $ bindIndex ++;
20162035 $ valueKey = "op_ {$ bindIndex }" ;
20172036 $ bindIndex ++;
2018- return "{$ quotedColumn } = JSON_ARRAY_INSERT( {$ quotedColumn }, CONCAT('$[', : $ indexKey, ']'), JSON_EXTRACT(: $ valueKey, '$')) " ;
2037+ return "{$ quotedColumn } = JSON_ARRAY_INSERT(
2038+ {$ quotedColumn },
2039+ CONCAT('$[', : $ indexKey, ']'),
2040+ JSON_EXTRACT(: $ valueKey, '$')
2041+ ) " ;
20192042
2020- case Operator::TYPE_ARRAY_INTERSECT :
2043+ case Operator::TYPE_ARRAY_REMOVE :
20212044 $ bindKey = "op_ {$ bindIndex }" ;
20222045 $ bindIndex ++;
20232046 return "{$ quotedColumn } = IFNULL((
2024- SELECT JSON_ARRAYAGG(jt1. value)
2025- FROM JSON_TABLE( {$ quotedColumn }, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt1
2026- WHERE jt1. value IN (SELECT value FROM JSON_TABLE( : $ bindKey, ' \$ [*]' COLUMNS(value TEXT PATH ' \$ ')) AS jt2)
2047+ SELECT JSON_ARRAYAGG(value)
2048+ FROM JSON_TABLE( {$ quotedColumn }, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt
2049+ WHERE value != : $ bindKey
20272050 ), JSON_ARRAY()) " ;
20282051
2029- case Operator::TYPE_ARRAY_DIFF :
2052+ case Operator::TYPE_ARRAY_UNIQUE :
2053+ return "{$ quotedColumn } = IFNULL((
2054+ SELECT JSON_ARRAYAGG(DISTINCT jt.value)
2055+ FROM JSON_TABLE( {$ quotedColumn }, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt
2056+ ), JSON_ARRAY()) " ;
2057+
2058+ case Operator::TYPE_ARRAY_INTERSECT :
20302059 $ bindKey = "op_ {$ bindIndex }" ;
20312060 $ bindIndex ++;
20322061 return "{$ quotedColumn } = IFNULL((
20332062 SELECT JSON_ARRAYAGG(jt1.value)
20342063 FROM JSON_TABLE( {$ quotedColumn }, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt1
2035- WHERE jt1.value NOT IN (SELECT value FROM JSON_TABLE(: $ bindKey, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt2)
2036- ), JSON_ARRAY()) " ;
2037-
2038- case Operator::TYPE_ARRAY_UNIQUE :
2039- return "{$ quotedColumn } = IFNULL((
2040- SELECT JSON_ARRAYAGG(DISTINCT jt.value)
2041- FROM JSON_TABLE( {$ quotedColumn }, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt
2064+ WHERE jt1.value IN (
2065+ SELECT value
2066+ FROM JSON_TABLE(: $ bindKey, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt2
2067+ )
20422068 ), JSON_ARRAY()) " ;
20432069
2044- case Operator::TYPE_ARRAY_REMOVE :
2070+ case Operator::TYPE_ARRAY_DIFF :
20452071 $ bindKey = "op_ {$ bindIndex }" ;
20462072 $ bindIndex ++;
20472073 return "{$ quotedColumn } = IFNULL((
2048- SELECT JSON_ARRAYAGG(value)
2049- FROM JSON_TABLE( {$ quotedColumn }, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt
2050- WHERE value != : $ bindKey
2074+ SELECT JSON_ARRAYAGG(jt1.value)
2075+ FROM JSON_TABLE( {$ quotedColumn }, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt1
2076+ WHERE jt1.value NOT IN (
2077+ SELECT value
2078+ FROM JSON_TABLE(: $ bindKey, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt2
2079+ )
20512080 ), JSON_ARRAY()) " ;
20522081
20532082 case Operator::TYPE_ARRAY_FILTER :
20542083 $ conditionKey = "op_ {$ bindIndex }" ;
20552084 $ bindIndex ++;
20562085 $ valueKey = "op_ {$ bindIndex }" ;
20572086 $ bindIndex ++;
2058- // Note: parent binds value as JSON-encoded, so we need to unquote it for TEXT comparison
20592087 return "{$ quotedColumn } = IFNULL((
20602088 SELECT JSON_ARRAYAGG(value)
20612089 FROM JSON_TABLE( {$ quotedColumn }, ' \$[*]' COLUMNS(value TEXT PATH ' \$')) AS jt
@@ -2072,9 +2100,22 @@ protected function getOperatorSQL(string $column, Operator $operator, int &$bind
20722100 END
20732101 ), JSON_ARRAY()) " ;
20742102
2103+ // Date operators
2104+ case Operator::TYPE_DATE_ADD_DAYS :
2105+ $ bindKey = "op_ {$ bindIndex }" ;
2106+ $ bindIndex ++;
2107+ return "{$ quotedColumn } = DATE_ADD( {$ quotedColumn }, INTERVAL : $ bindKey DAY) " ;
2108+
2109+ case Operator::TYPE_DATE_SUB_DAYS :
2110+ $ bindKey = "op_ {$ bindIndex }" ;
2111+ $ bindIndex ++;
2112+ return "{$ quotedColumn } = DATE_SUB( {$ quotedColumn }, INTERVAL : $ bindKey DAY) " ;
2113+
2114+ case Operator::TYPE_DATE_SET_NOW :
2115+ return "{$ quotedColumn } = NOW() " ;
2116+
20752117 default :
2076- // Fall back to parent implementation for other operators
2077- return parent ::getOperatorSQL ($ column , $ operator , $ bindIndex );
2118+ throw new OperatorException ("Invalid operator: {$ method }" );
20782119 }
20792120 }
20802121
0 commit comments