jason810496 commented on code in PR #51480:
URL: https://github.com/apache/airflow/pull/51480#discussion_r2320738606


##########
airflow-core/src/airflow/api_fastapi/common/parameters.py:
##########
@@ -432,6 +442,76 @@ def depends(
         return cls().set_value(_TagFilterModel(tags=tags, 
tags_match_mode=tags_match_mode))
 
 
+class _TagsAdvancedFilter(BaseParam[str]):
+    """Advanced filter on tags supporting AND/OR logic via query string with 
full precedence."""
+
+    def __init__(self, value: str | None = None, skip_none: bool = True):
+        super().__init__(value, skip_none)
+
+    def parse_advanced_query(self, query: str):

Review Comment:
   Is it possible to add type annotation for the return type?



##########
airflow-core/src/airflow/api_fastapi/common/parameters.py:
##########
@@ -432,6 +442,76 @@ def depends(
         return cls().set_value(_TagFilterModel(tags=tags, 
tags_match_mode=tags_match_mode))
 
 
+class _TagsAdvancedFilter(BaseParam[str]):
+    """Advanced filter on tags supporting AND/OR logic via query string with 
full precedence."""
+
+    def __init__(self, value: str | None = None, skip_none: bool = True):
+        super().__init__(value, skip_none)
+
+    def parse_advanced_query(self, query: str):
+        """Parse the boolean tag query using pyparsing and return an 
expression tree."""
+        if not query:
+            return None
+        # Grammar: tag (AND|OR) tag, with parentheses to set precedence
+        ParserElement.enablePackrat()
+        AND = CaselessKeyword("AND")
+        OR = CaselessKeyword("OR")
+        tag = Word(alphanums + "-_:.@/")
+        expr = infixNotation(
+            tag,
+            [
+                (AND, 2, opAssoc.LEFT),
+                (OR, 2, opAssoc.LEFT),
+            ],
+        )
+        try:
+            parsed = expr.parseString(query, parseAll=True)[0]
+            return parsed
+        except ParseException as e:
+            raise HTTPException(400, f"Invalid tag query: {e}")
+
+    def _expr_to_sqlalchemy(self, expr):

Review Comment:
   Missing here as well.



##########
airflow-core/src/airflow/api_fastapi/common/parameters.py:
##########
@@ -432,6 +442,76 @@ def depends(
         return cls().set_value(_TagFilterModel(tags=tags, 
tags_match_mode=tags_match_mode))
 
 
+class _TagsAdvancedFilter(BaseParam[str]):
+    """Advanced filter on tags supporting AND/OR logic via query string with 
full precedence."""
+
+    def __init__(self, value: str | None = None, skip_none: bool = True):
+        super().__init__(value, skip_none)
+
+    def parse_advanced_query(self, query: str):
+        """Parse the boolean tag query using pyparsing and return an 
expression tree."""
+        if not query:
+            return None
+        # Grammar: tag (AND|OR) tag, with parentheses to set precedence
+        ParserElement.enablePackrat()
+        AND = CaselessKeyword("AND")
+        OR = CaselessKeyword("OR")
+        tag = Word(alphanums + "-_:.@/")
+        expr = infixNotation(
+            tag,
+            [
+                (AND, 2, opAssoc.LEFT),
+                (OR, 2, opAssoc.LEFT),
+            ],
+        )
+        try:
+            parsed = expr.parseString(query, parseAll=True)[0]
+            return parsed
+        except ParseException as e:
+            raise HTTPException(400, f"Invalid tag query: {e}")
+
+    def _expr_to_sqlalchemy(self, expr):
+        """Recursively convert the parsed pyparsing expression to SQLAlchemy 
conditions."""
+        if isinstance(expr, str):
+            return DagModel.tags.any(DagTag.name == expr)
+        if isinstance(expr, (list, tuple, ParseResults)):
+            if len(expr) == 1:
+                return self._expr_to_sqlalchemy(expr[0])
+            if len(expr) == 3 and isinstance(expr[1], str):
+                left, op, right = expr
+                left_cond = self._expr_to_sqlalchemy(left)
+                right_cond = self._expr_to_sqlalchemy(right)
+                if op.upper() == "AND":
+                    return and_(left_cond, right_cond)
+                if op.upper() == "OR":
+                    return or_(left_cond, right_cond)
+            # If the structure is not as expected, raise an error
+            raise HTTPException(400, "Invalid tag query structure")
+        raise HTTPException(400, "Invalid tag query structure")

Review Comment:
   Is it possible to make the error message more verbose? Maybe like having 
hint for missing which side, etc.
   Nice to have if it's possible, but not strong opnion for having it. 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to