Back to Blog

Query Rewrite: Optimized Translation from Semantics to Storage

In traditional systems, developers need to know which table and which database holds the data to write queries. In coomia-dip, users query through Ontology semantics — they only care about "business objects" and "business relationships":

Coomia DIPPublished on December 24, 20258 min read
Share this articleTwitter / X

Query Rewrite: Optimized Translation from Semantics to Storage

“

Series: S10 Design Patterns · Article 9 | Level: Advanced | Reading Time: 18 min

#TL;DR

  • The Query Rewrite pattern translates users' high-level semantic queries (based on the Ontology model) into optimized storage engine queries (SQL/Iceberg/gRPC), while injecting cross-cutting concerns like permission filtering, tenant isolation, and data masking.
  • The coomia-dip query rewriter is a multi-stage pipeline: Parse, Semantic Validation, Permission Injection, Predicate Pushdown, Physical Query Generation, and Cost Optimization.
  • Combined with Nessie's Git-like branching and Iceberg's time-travel capabilities, the query rewriter supports cross-version queries and historical snapshot queries.

#Introduction: Users Should Not Know Where Data Lives

In traditional systems, developers need to know which table and which database holds the data to write queries. In coomia-dip, users query through Ontology semantics — they only care about "business objects" and "business relationships":

Python
# User's semantic query (agnostic to physical storage)
query = """
    Find all customers with credit scores below 600 who had
    large transactions in the last 30 days,
    along with their associated risk alerts
"""

# coomia-dip needs to translate this to:
# 1. Query Iceberg table customer_profiles for credit scores
# 2. Query Iceberg table transactions for recent 30-day transactions
# 3. Query gRPC service for risk alerts
# 4. Inject tenant isolation conditions
# 5. Inject data masking rules
# 6. JOIN results and sort

The query rewriter is this translation layer — it lets users query in "business language" while generating efficient physical execution plans.

#Part 1: Query Rewrite Pipeline

#1.1 Pipeline Architecture

Python
from dataclasses import dataclass, field
from typing import Any
from abc import ABC, abstractmethod

@dataclass
class QueryContext:
    tenant_id: str
    world_id: str
    actor_id: str
    actor_roles: list[str]
    branch: str = "main"
    timestamp: str | None = None
    explain: bool = False
    metadata: dict[str, Any] = field(default_factory=dict)

@dataclass
class SemanticQuery:
    object_types: list[str]
    filters: list[dict]
    projections: list[str]
    joins: list[dict]
    order_by: list[dict]
    limit: int | None = None
    offset: int | None = None
    aggregations: list[dict] = field(default_factory=list)

@dataclass
class PhysicalPlan:
    steps: list["PhysicalStep"]
    estimated_cost: float
    estimated_rows: int

@dataclass
class PhysicalStep:
    operation: str
    target: str
    details: dict[str, Any] = field(default_factory=dict)

class QueryRewritePipeline:
    def __init__(self, stages: list["RewriteStage"]):
        self._stages = stages

    async def rewrite(self, raw_query: str, context: QueryContext) -> PhysicalPlan:
        semantic = await self._stages[0].process(raw_query, context)
        for stage in self._stages[1:]:
            semantic = await stage.process(semantic, context)
        return semantic

class RewriteStage(ABC):
    @abstractmethod
    async def process(self, input_data: Any, context: QueryContext) -> Any: ...

#1.2 Parsing Stage

Python
class QueryParser(RewriteStage):
    async def process(self, raw_query: str, context: QueryContext) -> SemanticQuery:
        tokens = self._tokenize(raw_query)
        ast = self._build_ast(tokens)
        return SemanticQuery(
            object_types=self._extract_types(ast),
            filters=self._extract_filters(ast),
            projections=self._extract_projections(ast),
            joins=self._extract_joins(ast),
            order_by=self._extract_order_by(ast),
            limit=self._extract_limit(ast),
            offset=self._extract_offset(ast),
            aggregations=self._extract_aggregations(ast),
        )

#1.3 Semantic Validation Stage

Python
class SemanticValidator(RewriteStage):
    async def process(self, query: SemanticQuery, context: QueryContext) -> SemanticQuery:
        for obj_type in query.object_types:
            schema = await self._schema_registry.get(obj_type, context.world_id)
            if not schema:
                raise QueryValidationError(
                    f"ObjectType '{obj_type}' not found in world '{context.world_id}'"
                )

            for proj in query.projections:
                if proj.startswith(f"{obj_type}."):
                    field_name = proj.split(".", 1)[1]
                    if field_name not in schema["properties"]:
                        raise QueryValidationError(
                            f"Property '{field_name}' not found in '{obj_type}'"
                        )

        for join in query.joins:
            link = await self._schema_registry.get_link_type(
                join["from_type"], join["to_type"]
            )
            if not link:
                raise QueryValidationError(
                    f"No relationship between '{join['from_type']}' and '{join['to_type']}'"
                )
        return query

#1.4 Permission Injection Stage

Python
class PermissionInjector(RewriteStage):
    async def process(self, query: SemanticQuery, context: QueryContext) -> SemanticQuery:
        # Tenant isolation
        query.filters.append({
            "field": "_tenant_id", "operator": "eq",
            "value": context.tenant_id, "injected": True,
        })

        # World isolation
        query.filters.append({
            "field": "_world_id", "operator": "eq",
            "value": context.world_id, "injected": True,
        })

        # RBAC row-level filters
        for obj_type in query.object_types:
            row_filter = await self._rbac_service.get_row_filter(
                context.actor_id, context.actor_roles, obj_type
            )
            if row_filter:
                query.filters.append({
                    **row_filter, "injected": True, "source": "rbac",
                })

        # Remove inaccessible fields
        accessible = await self._rbac_service.get_accessible_fields(
            context.actor_id, context.actor_roles, query.object_types
        )
        query.projections = [
            p for p in query.projections if p in accessible or p == "*"
        ]
        return query

#1.5 Physical Query Generation

Python
class PhysicalQueryGenerator(RewriteStage):
    async def process(self, query: SemanticQuery, context: QueryContext) -> PhysicalPlan:
        steps = []

        for obj_type in query.object_types:
            storage = await self._storage_registry.get_storage(obj_type, context.world_id)

            if storage.type == "iceberg":
                sql = self._generate_iceberg_sql(query, obj_type, storage, context)
                steps.append(PhysicalStep(
                    operation="scan", target=storage.table_name,
                    details={"sql": sql, "branch": context.branch,
                             "timestamp": context.timestamp, "engine": "trino"},
                ))
            elif storage.type == "grpc_service":
                steps.append(PhysicalStep(
                    operation="rpc_call", target=storage.endpoint,
                    details={
                        "service": storage.service_name, "method": "Query",
                        "filters": [f for f in query.filters if self._applies_to(f, obj_type)],
                        "projections": [p for p in query.projections if p.startswith(f"{obj_type}.")],
                    },
                ))

        for join in query.joins:
            steps.append(PhysicalStep(
                operation="join", target="in_memory",
                details={"type": join.get("type", "inner"),
                         "left": join["from_type"], "right": join["to_type"],
                         "on": join["on"]},
            ))

        if query.aggregations:
            steps.append(PhysicalStep(
                operation="aggregate", target="in_memory",
                details={"aggregations": query.aggregations},
            ))

        if query.order_by:
            steps.append(PhysicalStep(
                operation="sort", target="in_memory",
                details={"order_by": query.order_by},
            ))

        return PhysicalPlan(
            steps=steps,
            estimated_cost=self._estimate_cost(steps),
            estimated_rows=self._estimate_rows(steps),
        )

    def _generate_iceberg_sql(
        self, query: SemanticQuery, obj_type: str,
        storage: "StorageInfo", context: QueryContext
    ) -> str:
        projections = [
            p.split(".", 1)[1] for p in query.projections
            if p.startswith(f"{obj_type}.")
        ]
        proj_clause = ", ".join(projections) if projections else "*"

        where_parts = []
        for f in query.filters:
            if self._applies_to(f, obj_type):
                field = f["field"].replace(f"{obj_type}.", "")
                where_parts.append(
                    f"{field} {f['operator']} {self._format_value(f['value'])}"
                )
        where_clause = " AND ".join(where_parts) if where_parts else "1=1"

        table_ref = f"{storage.catalog}.{storage.schema_name}.{storage.table_name}"
        if context.branch != "main":
            table_ref += f"@{context.branch}"
        if context.timestamp:
            table_ref += f" FOR SYSTEM_TIME AS OF TIMESTAMP '{context.timestamp}'"

        sql = f"SELECT {proj_clause} FROM {table_ref} WHERE {where_clause}"

        if query.limit:
            sql += f" LIMIT {query.limit}"
        if query.offset:
            sql += f" OFFSET {query.offset}"
        return sql

#Part 2: Optimization Strategies

#2.1 Predicate Pushdown

Python
class PredicatePushdown(RewriteStage):
    async def process(self, plan: PhysicalPlan, context: QueryContext) -> PhysicalPlan:
        join_steps = [s for s in plan.steps if s.operation == "join"]
        scan_steps = [s for s in plan.steps if s.operation in ("scan", "rpc_call")]

        for join_step in join_steps:
            join_filters = join_step.details.get("filters", [])
            for f in list(join_filters):
                target_type = self._get_filter_type(f)
                for scan in scan_steps:
                    if scan.target == target_type:
                        scan.details.setdefault("filters", []).append(f)
                        join_filters.remove(f)
                        break
        return plan

#2.2 Projection Pruning

Python
class ProjectionPruning(RewriteStage):
    async def process(self, plan: PhysicalPlan, context: QueryContext) -> PhysicalPlan:
        needed_fields: set[str] = set()
        for step in plan.steps:
            if step.operation == "join":
                needed_fields.update(step.details.get("on", {}).values())
            if step.operation == "sort":
                for o in step.details.get("order_by", []):
                    needed_fields.add(o["field"])
            if step.operation == "aggregate":
                for a in step.details.get("aggregations", []):
                    needed_fields.add(a["field"])

        for step in plan.steps:
            if step.operation == "scan":
                current = step.details.get("projections", [])
                if current and current != ["*"]:
                    step.details["projections"] = [
                        p for p in current if p in needed_fields or p in current
                    ]
        return plan

#2.3 Data Masking Injection

Python
class DataMaskingInjector(RewriteStage):
    async def process(self, plan: PhysicalPlan, context: QueryContext) -> PhysicalPlan:
        masking_rules = await self._masking_service.get_rules(
            context.actor_id, context.actor_roles
        )
        if masking_rules:
            plan.steps.append(PhysicalStep(
                operation="transform", target="in_memory",
                details={"type": "masking", "rules": masking_rules},
            ))
        return plan

#Part 3: Time-Travel Queries

#3.1 Historical Queries via Nessie Branches

Python
class TimeTravelQueryRewriter:
    async def rewrite_for_time_travel(
        self, query: SemanticQuery, timestamp: datetime, context: QueryContext
    ) -> SemanticQuery:
        context.timestamp = timestamp.isoformat()
        commit = await self._nessie.get_commit_at(context.branch, timestamp)
        context.metadata["nessie_commit"] = commit.hash
        return query

    async def rewrite_for_branch(
        self, query: SemanticQuery, branch: str, context: QueryContext
    ) -> SemanticQuery:
        context.branch = branch
        return query

    async def diff_query(
        self, query: SemanticQuery, from_ref: str, to_ref: str, context: QueryContext
    ) -> dict:
        results_from = await self._executor.execute(
            query, QueryContext(**{**vars(context), "branch": from_ref})
        )
        results_to = await self._executor.execute(
            query, QueryContext(**{**vars(context), "branch": to_ref})
        )
        return {
            "added": [r for r in results_to if r not in results_from],
            "removed": [r for r in results_from if r not in results_to],
            "modified": self._find_modified(results_from, results_to),
        }

#Part 4: Query Cache

#4.1 Semantic-Level Caching

Python
class SemanticQueryCache:
    async def get_or_execute(
        self, query: SemanticQuery, context: QueryContext,
        executor: callable, ttl_seconds: int = 300
    ) -> list[dict]:
        cache_key = self._compute_cache_key(query, context)
        cached = await self._cache.get(cache_key)
        if cached is not None:
            return cached
        results = await executor(query, context)
        await self._cache.set(cache_key, results, ttl=ttl_seconds)
        return results

    def _compute_cache_key(self, query: SemanticQuery, context: QueryContext) -> str:
        import hashlib, json
        key_data = {
            "types": sorted(query.object_types),
            "filters": sorted(str(f) for f in query.filters),
            "projections": sorted(query.projections),
            "tenant": context.tenant_id,
            "world": context.world_id,
            "branch": context.branch,
            "timestamp": context.timestamp,
        }
        return hashlib.sha256(
            json.dumps(key_data, sort_keys=True).encode()
        ).hexdigest()

#Part 5: EXPLAIN Query Analysis

Python
class QueryExplainer:
    async def explain(self, raw_query: str, context: QueryContext) -> dict:
        context.explain = True
        plan = await self._pipeline.rewrite(raw_query, context)
        return {
            "query": raw_query,
            "plan": {
                "steps": [
                    {"operation": s.operation, "target": s.target, "details": s.details}
                    for s in plan.steps
                ],
                "estimated_cost": plan.estimated_cost,
                "estimated_rows": plan.estimated_rows,
            },
            "optimizations_applied": [
                "predicate_pushdown", "projection_pruning", "data_masking_injection"
            ],
            "injected_filters": [
                f for step in plan.steps
                for f in step.details.get("filters", [])
                if f.get("injected")
            ],
        }

#Key Takeaways

  1. Semantics to Physical: The query rewriter translates Ontology semantic queries into optimized physical queries for underlying storage engines
  2. Multi-Stage Pipeline: Parse, Validate, Permission Injection, Predicate Pushdown, Physical Generation, Cost Optimization
  3. Transparent Security: Tenant isolation, RBAC row-level filtering, and data masking are transparently injected during query rewriting
  4. Time Travel: Nessie and Iceberg integration supports historical snapshot queries and cross-version comparisons
  5. Performance Optimization: Predicate pushdown and projection pruning reduce data transfer; semantic caching reduces redundant computation
  6. Observable: EXPLAIN capability lets developers understand query execution plans and optimization strategies

#Next Article

In the next article, we will explore the Delivery Guarantee pattern — how coomia-dip ensures cross-Layer messages are neither lost nor duplicated.

S10-10: Delivery Guarantee: No Message Loss, No Duplication

#Tags

#DesignPatterns #QueryRewrite #PredicatePushdown #PermissionInjection #TimeTravel #Nessie #Iceberg #QueryOptimization