UQA: The Database Repo That Treats SQL, Graphs, and Vector Search as the Same Algebra

A theory-heavy Python engine that uses posting lists, functors, and Bayesian calibration to turn multi-model database design into one unified execution model.

11 min read · cognica-io/uqa

A wide mechanical dispatch hub where several distinct query streams converge into one engraved chamber and then fan back out again. It explains how UQA treats many query paradigms as translations into a shared intermediate form rather than separate subsystems.
UQA's claim is simple and unusual. SQL, graph traversal, vector retrieval, text search, and geospatial predicates all pass through the same core abstraction.
Key Takeaways

Most databases promise unification and then hand you a pile of adapters. UQA, the Cognica / UQA repository, takes a harder route: it tries to make unification part of the algebra itself. That is why the repo reads less like a product demo and more like a thesis in executable form.

The center of gravity is the PostingList. In UQA, it is not just a search-engine artifact for document IDs. It is the universal intermediate representation that every query type has to pass through, which means boolean operations, ranking metadata, and cross-paradigm composition all live in one shared frame.

The universal primitive is a posting list

That choice changes the shape of the system. A SQL predicate, a graph traversal, a text query, and a vector search can all be normalized into the same structure before the engine decides how to combine, filter, or rerank them. The effect is subtle but important: the problem shifts from coordinating engines to translating expressions.

The engine's mental model is not five separate back ends. It is one normalization layer, one transformation layer, and one algebra of combinations.

The repo's most distinctive idea is that this is not a search system borrowing database language. It is a database model that treats search as one of several valid query shapes. That is why the documentation keeps returning to the same claim in different forms: the abstractions are supposed to be closed under composition.

The core insight is that posting lists — sorted sequences of (document_id, payload) pairs — can represent result sets from any paradigm.

UQA Documentation, Official Project Documentation · UQA GitHub Repository

Why category theory shows up in a database

UQA's Functor layer is the part that keeps the theory from collapsing into ad hoc adapters. Instead of saying, "here is a graph result and here is a relational result, please hand-wave the conversion," the repo uses explicit mappings between domains. That makes the translation itself part of the system design, not a side effect of implementation.

This is where the project gets intellectually unusual. In many multi-model systems, each data model gets its own execution island and a set of bridge routines. UQA is trying to preserve identity and composition across those bridges, which is why the category-theoretic framing matters. It gives the engine a principled way to move meaning, not just rows.

SQL, Cypher, and search all compile into one execution model

DimensionConventional multi-model systemsUQA
Core abstractionDifferent engines stitched togetherOne shared PostingList algebra
Query translationAdapters and special casesFunctor-based transformations
Text plus vectorOften fused heuristicallyHybrid search with calibrated relevance
Graph plus relationalSeparate planners or APIsCompiled into a common execution path
PhilosophyCoordination problemTranslation problem

The parsers and planners matter because they make the claim operational. PostgreSQL-compatible SQL, Cypher-like graph logic, and search-oriented operators all converge on shared execution primitives. That means a join, a traversal, and a retrieval step are not parallel universes. They are different front ends for the same machinery.

For readers used to systems that advertise themselves as "multi-model," this is the important distinction. UQA is not just storing many kinds of data. It is trying to compile many query paradigms into one execution story.

Hybrid search gets a math upgrade

A close-up of two hands converting different relevance measurements onto one calibrated scale. One side is a BM25 dial and the other is a vector similarity gauge, both feeding a balance that outputs a single relevance token. It explains why UQA uses calibration instead of heuristic score blending.
Hybrid search breaks when different rankers speak different score languages. UQA's answer is to calibrate them into comparable relevance signals before fusion.

This is one of the most practical parts of the repo. BM25 scores, embedding similarities, and other retrieval signals do not naturally live on the same scale. UQA's Bayesian calibration work tries to convert those raw signals into comparable relevance estimates, so fusion becomes a statistical problem instead of a guessing game.

Fusion approachWhat it doesTrade-off
Reciprocal rank fusionCombines ranked lists with a heuristic formulaEasy to apply, but scores stay semantically mismatched
Weighted score blendingAdds signals with hand-tuned weightsWorks until score ranges shift
Bayesian calibrationMaps raw scores into comparable relevanceHarder to implement, but mathematically cleaner
# Conceptual shape, not exact repo API
# Raw scores from different rankers
bm25 = 15.0
vector_sim = 0.82

# Calibration turns them into comparable relevance signals
bm25_rel = calibrate_bayesian(bm25)
vec_rel = calibrate_bayesian(vector_sim)

final_score = fuse(bm25_rel, vec_rel)

Why the Python implementation matters

The Python layer is not framed like a toy port. It is a reference implementation with strong discipline: strict typing, __slots__, NumPy, PyArrow, DuckDB offloading, and a lot of care around data movement. The message is clear. Python is the readable surface, not an excuse to relax about execution.

That matters because database projects often split into two traps. Either they stay pure and elegant but never get close to reality, or they optimize so aggressively that the architecture disappears. UQA seems to be trying for a third path: keep the algebra legible, then use targeted performance tactics where they actually matter.

Implementation choiceWhy it mattersWhat it signals
__slots__Cuts per-object overheadMemory awareness
NumPy and PyArrowEfficient columnar and binary data handlingRespect for data movement costs
DuckDB offloadingLets the engine delegate relational workPragmatism over purity
Strict typingKeeps the algebra honestReference implementation discipline

What UQA is really competing against

UQA is not competing head to head on one feature. It is competing against the assumption that data models need separate homes. That puts it in a different conversation from PostgreSQL, Elasticsearch or OpenSearch, Neo4j, vector databases like Milvus and Qdrant, and multi-model systems like ArangoDB.

SystemStrengthWhere UQA differs
PostgreSQLRelational maturityUQA tries to make graph, text, vector, and geospatial queries native to the same algebra
Elasticsearch / OpenSearchText retrieval scaleUQA treats search as one input among several, not the whole story
Neo4jGraph traversal depthUQA aims to unify graph work with relational and retrieval operations
Milvus / Qdrant / WeaviateVector searchUQA folds vectors into a broader query model
ArangoDBMulti-model convenienceUQA pushes harder on theoretical unification rather than model bundling

That is the real bet. If the abstraction holds, application teams stop stitching together specialized systems just to answer one mixed query. If it does not, the repo becomes a beautifully argued proof that database pluralism is still hard to escape.