Source code for quantumengine.query_expressions

"""
Query expression system for QuantumEngine

This module provides a query expression system that allows building complex
queries programmatically and passing them to objects() and filter() methods.
"""

from typing import Any, Dict, List, Optional, Union
import json


[docs] class Q: """Query expression builder for complex queries. This class allows building complex query expressions that can be used with filter() and objects() methods. Example: # Complex AND/OR queries query = Q(age__gt=18) & Q(active=True) users = User.objects.filter(query) # Complex queries with objects() query = Q(department="engineering") | Q(department="sales") users = User.objects(query) """
[docs] def __init__(self, **kwargs): """Initialize a query expression. Args: **kwargs: Field filters to include in the query """ self.conditions = [] self.operator = 'AND' self.raw_query = None # Add conditions from kwargs for key, value in kwargs.items(): self.conditions.append((key, value))
[docs] def __and__(self, other: 'Q') -> 'Q': """Combine with another Q object using AND.""" result = Q() result.conditions = [self, other] result.operator = 'AND' return result
[docs] def __or__(self, other: 'Q') -> 'Q': """Combine with another Q object using OR.""" result = Q() result.conditions = [self, other] result.operator = 'OR' return result
[docs] def __invert__(self) -> 'Q': """Negate this query using NOT.""" result = Q() result.conditions = [self] result.operator = 'NOT' return result
[docs] @classmethod def raw(cls, query_string: str) -> 'Q': """Create a raw query expression. Args: query_string: Raw SurrealQL WHERE clause Returns: Q object with raw query """ result = cls() result.raw_query = query_string return result
[docs] def to_conditions(self) -> List[tuple]: """Convert this Q object to a list of conditions. Returns: List of (field, operator, value) tuples """ if self.raw_query: # Return raw query as a special condition return [('__raw__', '=', self.raw_query)] if not self.conditions: return [] # If conditions are simple (field, value) tuples, return them if all(isinstance(cond, tuple) and len(cond) == 2 for cond in self.conditions): result = [] for field, value in self.conditions: # Parse field__operator syntax if '__' in field: parts = field.split('__') field_name = parts[0] operator = parts[1] # Map Django-style operators to SurrealDB operators op_map = { 'gt': '>', 'lt': '<', 'gte': '>=', 'lte': '<=', 'ne': '!=', 'in': 'INSIDE', 'nin': 'NOT INSIDE', 'contains': 'CONTAINS', 'startswith': 'STARTSWITH', 'endswith': 'ENDSWITH', 'regex': 'REGEX' } surreal_op = op_map.get(operator, '=') result.append((field_name, surreal_op, value)) else: result.append((field, '=', value)) return result # For complex nested conditions, we need to handle recursively # This is a simplified implementation - for full support we'd need # more sophisticated query tree building all_conditions = [] for cond in self.conditions: if isinstance(cond, Q): all_conditions.extend(cond.to_conditions()) elif isinstance(cond, tuple) and len(cond) == 2: field, value = cond all_conditions.append((field, '=', value)) return all_conditions
[docs] def to_where_clause(self) -> str: """Convert this Q object to a WHERE clause string. Returns: WHERE clause string for SurrealQL """ if self.raw_query: return self.raw_query conditions = self.to_conditions() if not conditions: return "" # Build condition strings condition_strs = [] for field, op, value in conditions: if field == '__raw__': condition_strs.append(value) else: # Handle special operators if op in ('CONTAINS', 'STARTSWITH', 'ENDSWITH'): if op == 'CONTAINS': condition_strs.append(f"string::contains({field}, '{value}')") elif op == 'STARTSWITH': condition_strs.append(f"string::starts_with({field}, '{value}')") elif op == 'ENDSWITH': condition_strs.append(f"string::ends_with({field}, '{value}')") elif op == 'REGEX': condition_strs.append(f"string::matches({field}, r'{value}')") elif op in ('INSIDE', 'NOT INSIDE'): value_str = json.dumps(value) condition_strs.append(f"{field} {op} {value_str}") else: # Regular operators if isinstance(value, str) and not (isinstance(value, str) and ':' in value): # Quote string values condition_strs.append(f"{field} {op} '{value}'") else: # Don't quote numbers, booleans, or record IDs condition_strs.append(f"{field} {op} {json.dumps(value)}") # Join with operator if self.operator == 'AND': return ' AND '.join(condition_strs) elif self.operator == 'OR': return ' OR '.join(condition_strs) elif self.operator == 'NOT': return f"NOT ({' AND '.join(condition_strs)})" else: return ' AND '.join(condition_strs)
[docs] class QueryExpression: """Higher-level query expression that can include fetch, grouping, etc. This class provides a more comprehensive query building interface that includes not just WHERE conditions but also FETCH, GROUP BY, etc. """
[docs] def __init__(self, where: Optional[Q] = None): """Initialize a query expression. Args: where: Q object for WHERE clause conditions """ self.where = where self.fetch_fields = [] self.group_by_fields = [] self.order_by_field = None self.order_by_direction = 'ASC' self.limit_value = None self.start_value = None
[docs] def fetch(self, *fields: str) -> 'QueryExpression': """Add FETCH clause to resolve references. Args: *fields: Field names to fetch Returns: Self for method chaining """ self.fetch_fields.extend(fields) return self
[docs] def group_by(self, *fields: str) -> 'QueryExpression': """Add GROUP BY clause. Args: *fields: Field names to group by Returns: Self for method chaining """ self.group_by_fields.extend(fields) return self
[docs] def order_by(self, field: str, direction: str = 'ASC') -> 'QueryExpression': """Add ORDER BY clause. Args: field: Field name to order by direction: 'ASC' or 'DESC' Returns: Self for method chaining """ self.order_by_field = field self.order_by_direction = direction return self
[docs] def limit(self, value: int) -> 'QueryExpression': """Add LIMIT clause. Args: value: Maximum number of results Returns: Self for method chaining """ self.limit_value = value return self
[docs] def start(self, value: int) -> 'QueryExpression': """Add START clause for pagination. Args: value: Number of results to skip Returns: Self for method chaining """ self.start_value = value return self
[docs] def apply_to_queryset(self, queryset): """Apply this expression to a queryset. Args: queryset: BaseQuerySet to apply expression to Returns: Modified queryset """ # Apply WHERE conditions if self.where: conditions = self.where.to_conditions() for field, op, value in conditions: if field == '__raw__': # Add raw condition - this would need special handling in BaseQuerySet queryset.query_parts.append(('__raw__', '=', value)) else: queryset.query_parts.append((field, op, value)) # Apply FETCH if self.fetch_fields: queryset.fetch_fields.extend(self.fetch_fields) # Apply GROUP BY if self.group_by_fields: queryset.group_by_fields.extend(self.group_by_fields) # Apply ORDER BY if self.order_by_field: queryset.order_by_value = (self.order_by_field, self.order_by_direction) # Apply LIMIT if self.limit_value: queryset.limit_value = self.limit_value # Apply START if self.start_value: queryset.start_value = self.start_value return queryset