2025-06-24 19:56:56 +00:00
# Data Architecture
## Database Schema Overview
The system uses PostgreSQL 17.2 with a sophisticated schema designed for multi-tenant document processing, query orchestration, and comprehensive versioning. The schema is managed through database migrations and uses SQLC for type-safe Go integration.
2025-10-23 22:57:15 +00:00
## Entity Relationship Diagram
``` mermaid
erDiagram
clients ||--o{ documents : "owns"
clients ||--o{ batch_uploads : "owns"
clients ||--|| clientCanSync : "has sync status"
clients ||--o{ documentUploads : "uploads"
2025-11-26 19:23:42 +00:00
clients ||--o{ folders : "organizes"
2025-10-23 22:57:15 +00:00
clients ||--o{ collectorVersions : "has versions"
clients ||--|| collectorActiveVersions : "has active version"
clients ||--o{ collectorQueries : "configures"
clients ||--o{ collectorMinCleanVersions : "sets min clean version"
clients ||--o{ collectorMinTextVersions : "sets min text version"
batch_uploads ||--o{ documents : "contains"
batch_uploads ||--o{ documentUploads : "tracks uploads"
2025-11-26 19:23:42 +00:00
folders ||--o{ documents : "contains"
folders ||--o{ folders : "has children"
folders ||--o{ documentUploads : "targets"
2025-10-23 22:57:15 +00:00
documents ||--o{ documentEntries : "has entries"
documents ||--o{ documentCleans : "has clean versions"
2025-11-26 19:23:42 +00:00
documents ||--o{ documentLabels : "has labels"
documents ||--o{ documentFieldExtractions : "has field extractions"
2025-10-23 22:57:15 +00:00
documentEntries }o--|| documents : "references"
documentCleans }o--|| documents : "cleans"
documentCleans ||--o{ documentCleanEntries : "has entries"
documentCleans ||--o{ documentTextExtractions : "produces text"
documentTextExtractions }o--|| documentCleans : "from"
documentTextExtractions ||--o{ documentTextExtractionEntries : "has entries"
documentTextExtractions ||--o{ results : "generates"
2025-11-26 19:23:42 +00:00
labels ||--o{ documentLabels : "applied to documents"
documentFieldExtractions ||--o{ documentFieldExtractionVersions : "has versions"
documentFieldExtractions ||--o{ documentFieldExtractionArrayFields : "has array fields"
2025-10-23 22:57:15 +00:00
queries ||--o{ queryVersions : "has versions"
queries ||--|| queryActiveVersions : "has active version"
queries ||--o{ requiredQueries : "requires other queries"
queries ||--o{ queryConfigs : "has configs"
queries ||--o{ results : "produces"
queries ||--o{ collectorQueries : "used by collectors"
queryVersions ||--|| queryActiveVersions : "tracks active"
results }o--|| documentTextExtractions : "from text"
results }o--|| queries : "from query"
results ||--o{ resultDependencies : "depends on"
results ||--o{ resultDependencies : "required by"
collectorVersions ||--|| collectorActiveVersions : "tracks active"
collectorQueries }o--|| queries : "references"
clients {
varchar clientId PK
text name UK
}
clientCanSync {
uuid syncId PK
varchar clientId FK
boolean canSync
}
2025-11-26 19:23:42 +00:00
folders {
uuid id PK
text path
uuid parentId FK
varchar clientId FK
timestamp createdAt
varchar createdBy
}
2025-10-23 22:57:15 +00:00
documents {
uuid id PK
varchar clientId FK
text hash
text filename
uuid batch_id FK
2025-11-26 19:23:42 +00:00
uuid folderId FK
text originalPath
2025-10-23 22:57:15 +00:00
}
batch_uploads {
uuid id PK
varchar client_id FK
text original_filename
integer total_documents
integer processed_documents
integer failed_documents
integer invalid_type_documents
batch_status status
integer progress_percent
jsonb failed_filenames
timestamp created_at
timestamp completed_at
2025-11-26 19:23:42 +00:00
text archive_bucket
text archive_key
bigint file_size_bytes
2025-10-23 22:57:15 +00:00
}
documentUploads {
uuid id PK
text bucket
text key
varchar clientId FK
smallint part
timestamp createdAt
text filename
uuid batch_id FK
2025-11-26 19:23:42 +00:00
uuid folder_id FK
2025-10-23 22:57:15 +00:00
}
documentEntries {
uuid id PK
uuid documentId FK
text bucket
text key
}
documentCleans {
uuid id PK
uuid documentId FK
text bucket
text key
text hash
cleanMimeType mimetype
cleanFailType fail
}
documentCleanEntries {
uuid id PK
uuid cleanId FK
bigint version
}
documentTextExtractions {
uuid id PK
uuid cleanId FK
text bucket
text key
text hash
timestamp createdAt
smallint part
}
documentTextExtractionEntries {
uuid id PK
uuid textId FK
bigint version
}
2025-11-26 19:23:42 +00:00
labels {
text label PK
text description
}
documentLabels {
uuid id PK
uuid documentId FK
text label FK
timestamp appliedAt
varchar appliedBy
}
documentFieldExtractions {
uuid id PK
uuid documentId FK
text fileName
text contractTitle
int aareteDerivedAmendmentNum
text clientName
text payerName
date aareteDerivedEffectiveDt
date aareteDerivedTerminationDt
boolean autoRenewalInd
timestamp createdAt
varchar createdBy
}
documentFieldExtractionVersions {
uuid id PK
uuid fieldExtractionId FK
bigint version
varchar createdBy
timestamp createdAt
}
documentFieldExtractionArrayFields {
uuid id PK
uuid fieldExtractionId FK
smallint arrayIndex
text exhibitTitle
text reimbProvTin
date reimbEffectiveDt
text aareteDerivedClaimTypeCd
numeric reimbPctRate
numeric reimbFeeRate
}
2025-10-23 22:57:15 +00:00
queries {
uuid queryId PK
queryType queryType
}
queryVersions {
uuid queryId FK
int versionId "PK composite"
timestamp addedAt
}
queryActiveVersions {
uuid activeVersionEntryId PK
uuid queryId FK
int versionId FK
}
requiredQueries {
uuid requiredQueryEntryId PK
uuid queryId FK
uuid requiredQueryId FK
int addedVersion FK
int removedVersion FK
}
queryConfigs {
uuid configId PK
uuid queryId FK
jsonb config
int addedVersion FK
int removedVersion FK
}
results {
uuid id PK
uuid textEntryId FK
uuid queryId FK
text value
int queryVersion FK
}
resultDependencies {
uuid resultId FK
uuid requiredResultId FK
}
collectorVersions {
varchar clientId FK
int id "PK composite"
timestamp addedAt
}
collectorActiveVersions {
uuid id PK
varchar clientId FK
int versionId FK
}
collectorQueries {
uuid id PK
varchar clientId FK
varchar name
uuid queryId FK
int addedVersion FK
int removedVersion FK
}
collectorMinCleanVersions {
uuid id PK
varchar clientId FK
bigint versionId
int addedVersion FK
int removedVersion FK
}
collectorMinTextVersions {
uuid id PK
varchar clientId FK
bigint versionId
int addedVersion FK
int removedVersion FK
}
```
2025-06-24 19:56:56 +00:00
## Core Entities
### Clients (`clients`)
Central entity representing organizations or tenants in the system.
``` sql
CREATE TABLE clients (
2025-10-23 22:57:15 +00:00
clientId varchar ( 255 ) PRIMARY KEY ,
name TEXT NOT NULL ,
UNIQUE ( name )
2025-06-24 19:56:56 +00:00
) ;
```
**Key Characteristics ** :
- String-based client identifiers for human readability
- Multi-tenant isolation through client-scoped data
- Central reference point for all client-owned entities
2025-10-23 22:57:15 +00:00
- Unique constraint on name prevents duplicate client names
### Client Sync Status (`clientCanSync`)
Tracks whether a client is allowed to synchronize documents.
``` sql
CREATE TABLE clientCanSync (
syncId uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
clientId varchar ( 255 ) NOT NULL ,
canSync boolean NOT NULL ,
FOREIGN KEY ( clientId ) REFERENCES clients ( clientId )
) ;
```
**Key Features ** :
- Historical tracking of sync permission changes
- Multiple entries per client for audit trail
- Most recent entry determines current sync status
2025-06-24 19:56:56 +00:00
### Documents (`documents`)
Represents uploaded files requiring processing.
``` sql
CREATE TABLE documents (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
2025-10-23 22:57:15 +00:00
clientId varchar ( 255 ) NOT NULL ,
hash TEXT NOT NULL ,
filename TEXT ,
batch_id uuid ,
2025-11-26 19:23:42 +00:00
folderId uuid ,
originalPath TEXT ,
2025-10-23 22:57:15 +00:00
UNIQUE ( clientId , hash ) ,
FOREIGN KEY ( clientId ) REFERENCES clients ( clientId ) ,
2025-11-26 19:23:42 +00:00
FOREIGN KEY ( batch_id ) REFERENCES batch_uploads ( id ) ,
FOREIGN KEY ( folderId ) REFERENCES folders ( id )
2025-06-24 19:56:56 +00:00
) ;
2025-10-23 22:57:15 +00:00
-- Indexes
CREATE INDEX idx_documents_filename ON documents ( filename ) ;
CREATE INDEX idx_documents_batch_id ON documents ( batch_id ) ;
2025-11-26 19:23:42 +00:00
CREATE INDEX idx_documents_folderid ON documents ( folderId ) ;
2025-06-24 19:56:56 +00:00
```
**Key Features ** :
- UUID v7 primary keys for time-ordered insertion
- Client isolation with foreign key constraints
2025-10-23 22:57:15 +00:00
- Hash-based deduplication within client scope (per client)
- Filename preservation for folder path support (added in migration 105)
- Optional batch association for grouped uploads (added in migration 103)
2025-11-26 19:23:42 +00:00
- Virtual folder organization via `folderId` (added in migration 111)
- Immutable `originalPath` preserving exact upload path (added in migration 111)
- Indexed on filename, batch_id, and folderId for efficient queries
2025-06-24 19:56:56 +00:00
2025-09-09 19:34:03 +00:00
### Batch Uploads (`batch_uploads`)
Tracks batch document upload operations with ZIP archive processing.
``` sql
2025-10-23 22:57:15 +00:00
CREATE TYPE batch_status AS ENUM ( ' processing ' , ' completed ' , ' failed ' , ' cancelled ' ) ;
2025-09-09 19:34:03 +00:00
CREATE TABLE batch_uploads (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
2025-10-23 22:57:15 +00:00
client_id varchar ( 255 ) NOT NULL ,
original_filename TEXT NOT NULL ,
total_documents integer NOT NULL ,
2025-09-09 19:34:03 +00:00
processed_documents integer NOT NULL DEFAULT 0 ,
failed_documents integer NOT NULL DEFAULT 0 ,
2025-10-23 22:57:15 +00:00
invalid_type_documents integer NOT NULL DEFAULT 0 ,
status batch_status NOT NULL DEFAULT ' processing ' ,
progress_percent integer NOT NULL DEFAULT 0 ,
failed_filenames jsonb DEFAULT ' [] ' : : jsonb ,
2025-09-09 19:34:03 +00:00
created_at timestamp NOT NULL DEFAULT NOW ( ) ,
2025-10-23 22:57:15 +00:00
completed_at timestamp ,
2025-11-26 19:23:42 +00:00
archive_bucket text ,
archive_key text ,
file_size_bytes bigint ,
FOREIGN KEY ( client_id ) REFERENCES clients ( clientId ) ,
CONSTRAINT batch_storage_consistent CHECK (
( archive_bucket IS NULL AND archive_key IS NULL AND file_size_bytes IS NULL ) OR
( archive_bucket IS NOT NULL AND archive_key IS NOT NULL )
)
2025-09-09 19:34:03 +00:00
) ;
2025-10-23 22:57:15 +00:00
-- Indexes
CREATE INDEX idx_batch_uploads_client_id ON batch_uploads ( client_id ) ;
CREATE INDEX idx_batch_uploads_status ON batch_uploads ( status ) ;
2025-09-09 19:34:03 +00:00
```
**Batch Processing Features ** :
- UUID v7 for time-ordered batch tracking
2025-10-23 22:57:15 +00:00
- Comprehensive status tracking with ENUM types (`batch_status` )
- Progress counters for processed/failed/invalid documents
- Progress percentage for UI updates
- Failed filename tracking as JSONB array for flexible querying
- Original filename preservation for display
2025-09-09 19:34:03 +00:00
- Temporal tracking with creation and completion timestamps
2025-11-26 19:23:42 +00:00
- Storage metadata tracking (archive bucket/key and file size)
- Storage consistency constraint ensures bucket and key are both set or both null
2025-10-23 22:57:15 +00:00
- Indexed by client_id and status for efficient queries
- Note: Uses snake_case naming convention (unlike other tables)
2025-09-09 19:34:03 +00:00
2025-11-26 19:23:42 +00:00
### Folders (`folders`)
Virtual folder hierarchy for document organization. Folders are database-only entities with no direct relationship to S3 storage locations.
``` sql
CREATE TABLE folders (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
path text NOT NULL ,
parentId uuid ,
clientId varchar ( 255 ) NOT NULL ,
createdAt timestamp NOT NULL DEFAULT NOW ( ) ,
createdBy varchar ( 255 ) NOT NULL ,
FOREIGN KEY ( parentId ) REFERENCES folders ( id ) ,
FOREIGN KEY ( clientId ) REFERENCES clients ( clientId ) ,
UNIQUE ( clientId , path ) ,
CHECK ( id ! = parentId )
) ;
-- Indexes
CREATE INDEX idx_folders_parentid ON folders ( parentId ) ;
CREATE INDEX idx_folders_clientid ON folders ( clientId ) ;
CREATE INDEX idx_folders_path ON folders ( path ) ;
```
**Key Features ** :
- Self-referential hierarchy via `parentId` for nested folders
- Client isolation with unique constraint on (clientId, path)
- Self-reference prevention via check constraint (`id != parentId` )
- Path column stores virtual folder path (e.g., "/contracts/2025")
- Folders can be renamed without affecting S3 storage locations
- Audit tracking via `createdAt` and `createdBy`
- Note: Added in migration 109
### Labels System
#### Labels (`labels`)
Lookup table for document processing status labels.
``` sql
CREATE TABLE labels (
label text PRIMARY KEY ,
description text NOT NULL
) ;
-- Seeded initial values
INSERT INTO labels ( label , description ) VALUES
( ' Ingested ' , ' Document has been ingested into the system ' ) ,
( ' OCR_Processed ' , ' OCR text extraction completed ' ) ,
( ' GenAI_Processed ' , ' GenAI processing completed ' ) ,
( ' Doczy_AI_Completed ' , ' Doczy.AI processing completed ' ) ,
( ' Dashboard_Ready ' , ' Document ready for dashboard display ' ) ;
```
**Key Features ** :
- Predefined processing status labels
- Primary key on label text for direct referencing
- Description provides human-readable explanation
#### Document Labels (`documentLabels`)
Junction table tracking label application to documents.
``` sql
CREATE TABLE documentLabels (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
documentId uuid NOT NULL ,
label text NOT NULL ,
appliedAt timestamp NOT NULL DEFAULT NOW ( ) ,
appliedBy varchar ( 255 ) NOT NULL ,
FOREIGN KEY ( documentId ) REFERENCES documents ( id ) ON DELETE CASCADE ,
FOREIGN KEY ( label ) REFERENCES labels ( label )
) ;
-- Indexes
CREATE INDEX idx_documentlabels_documentid ON documentLabels ( documentId ) ;
CREATE INDEX idx_documentlabels_label ON documentLabels ( label ) ;
CREATE INDEX idx_documentlabels_appliedat ON documentLabels ( appliedAt ) ;
CREATE INDEX idx_documentlabels_document_label_time ON documentLabels ( documentId , label , appliedAt DESC ) ;
```
**Key Features ** :
- Tracks which labels are applied to which documents
- Audit tracking via `appliedAt` and `appliedBy`
- Cascade delete removes labels when document is deleted
- Composite index optimizes queries for most recent label application
- Multiple applications of same label are allowed (historical tracking)
- Note: Added in migration 110
2025-06-24 19:56:56 +00:00
### Queries (`queries`)
Defines data extraction logic with type-specific implementations.
``` sql
CREATE TABLE queries (
queryId uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
queryType querytype NOT NULL
) ;
CREATE TYPE querytype AS ENUM ( ' context_full ' , ' json_extractor ' ) ;
```
**Query Types ** :
- **`context_full` **: Returns complete document context
- **`json_extractor` **: Extracts specific JSON paths from structured data
### Results (`results`)
Stores extracted data from document processing.
``` sql
CREATE TABLE results (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
textEntryId uuid NOT NULL REFERENCES documentTextExtractions ( id ) ,
queryId uuid NOT NULL REFERENCES queries ( queryId ) ,
value text NOT NULL ,
queryVersion int4 NOT NULL
) ;
```
2025-11-26 19:23:42 +00:00
## Field Extractions System
The field extractions system stores structured data extracted from documents, supporting both single-value fields (1:1 relationship) and array fields (1:N relationship). This system was added in migrations 112-115.
### Field Extractions Architecture
``` mermaid
graph TD
Document[documents] --> FE[documentFieldExtractions]
FE --> FEV[documentFieldExtractionVersions]
FE --> FEAF[documentFieldExtractionArrayFields]
FEV --> CFE[currentFieldExtractions View]
CFE --> CFEWAC[currentFieldExtractionsWithArrayCount View]
FEAF --> CFEWAC
```
### Document Field Extractions (`documentFieldExtractions`)
Stores single-value extracted fields from documents (19 fields total, 1:1 relationship with document).
``` sql
CREATE TABLE documentFieldExtractions (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
documentId uuid NOT NULL ,
-- Document identification
fileName text ,
contractTitle text ,
aareteDerivedAmendmentNum int ,
-- Party information
clientName text ,
payerName text ,
payerState text ,
providerState text ,
-- Tax identification numbers (stored as text to preserve leading zeros)
filenameTin text ,
provGroupTin text ,
provGroupNpi text ,
provGroupNameFull text ,
provOtherTin text ,
provOtherNpi text ,
provOtherNameFull text ,
-- Contract dates
aareteDerivedEffectiveDt date ,
aareteDerivedTerminationDt date ,
-- Renewal information
autoRenewalInd boolean ,
autoRenewalTerm text ,
-- Metadata
createdAt timestamp NOT NULL DEFAULT NOW ( ) ,
createdBy varchar ( 255 ) NOT NULL ,
FOREIGN KEY ( documentId ) REFERENCES documents ( id )
) ;
-- Indexes
CREATE INDEX idx_documentfieldextractions_documentid
ON documentFieldExtractions ( documentId ) ;
CREATE INDEX idx_documentfieldextractions_createdby
ON documentFieldExtractions ( createdBy ) ;
CREATE INDEX idx_documentfieldextractions_filename
ON documentFieldExtractions ( fileName ) ;
```
**Single-Value Fields ** (19 fields):
- **Document identification**: fileName, contractTitle, aareteDerivedAmendmentNum
- **Party information**: clientName, payerName, payerState, providerState
- **Tax IDs (as text for leading zeros)**: filenameTin, provGroupTin, provGroupNpi, provGroupNameFull, provOtherTin, provOtherNpi, provOtherNameFull
- **Contract dates**: aareteDerivedEffectiveDt, aareteDerivedTerminationDt
- **Renewal info**: autoRenewalInd, autoRenewalTerm
### Document Field Extraction Versions (`documentFieldExtractionVersions`)
Version tracking for field extractions, enabling historical queries.
``` sql
CREATE TABLE documentFieldExtractionVersions (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
fieldExtractionId uuid NOT NULL ,
version bigint NOT NULL ,
createdBy varchar ( 255 ) NOT NULL ,
createdAt timestamp NOT NULL DEFAULT NOW ( ) ,
FOREIGN KEY ( fieldExtractionId ) REFERENCES documentFieldExtractions ( id )
) ;
-- Indexes
CREATE INDEX idx_documentfieldextractionversions_fieldextractionid
ON documentFieldExtractionVersions ( fieldExtractionId ) ;
CREATE INDEX idx_documentfieldextractionversions_version
ON documentFieldExtractionVersions ( version ) ;
```
**Key Features ** :
- Links field extractions to version numbers
- Audit tracking via createdBy and createdAt
- Enables temporal queries and rollback capability
### Document Field Extraction Array Fields (`documentFieldExtractionArrayFields`)
Stores array-value extracted fields (112 fields total, 1:N relationship with field extraction).
``` sql
CREATE TABLE documentFieldExtractionArrayFields (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
fieldExtractionId uuid NOT NULL ,
arrayIndex smallint NOT NULL ,
-- Array-value fields (112 fields, grouped by category)
-- Exhibit information
exhibitTitle text ,
exhibitPage text ,
-- Reimbursement provider information
reimbProvTin text ,
reimbProvNpi text ,
reimbProvName text ,
reimbEffectiveDt date ,
reimbTerminationDt date ,
-- Claim and product codes
aareteDerivedClaimTypeCd text ,
aareteDerivedProduct text ,
aareteDerivedLob text ,
aareteDerivedProgram text ,
aareteDerivedNetwork text ,
aareteDerivedProvType text ,
-- Provider taxonomy and specialty
provTaxonomyCd text ,
provTaxonomyCdDesc text ,
provSpecialtyCd text ,
provSpecialtyCdDesc text ,
-- Service location
placeOfServiceCd text ,
placeOfServiceCdDesc text ,
billTypeCd text ,
billTypeCdDesc text ,
-- Reimbursement rates
reimbPctRate numeric ( 10 , 4 ) ,
reimbFeeRate numeric ( 12 , 2 ) ,
reimbConversionFactor numeric ( 12 , 4 ) ,
-- Grouper information
grouperType text ,
grouperCd text ,
grouperCdDesc text ,
grouperPctRate numeric ( 10 , 4 ) ,
grouperBaseRate numeric ( 12 , 2 ) ,
-- ... (96 additional fields for outliers, facility adjustments, rate escalators, stop loss)
FOREIGN KEY ( fieldExtractionId ) REFERENCES documentFieldExtractions ( id ) ,
UNIQUE ( fieldExtractionId , arrayIndex ) ,
CHECK ( arrayIndex > = 0 )
) ;
-- Indexes
CREATE INDEX idx_documentfieldextractionarrayfields_fieldextractionid
ON documentFieldExtractionArrayFields ( fieldExtractionId ) ;
CREATE INDEX idx_documentfieldextractionarrayfields_id_index
ON documentFieldExtractionArrayFields ( fieldExtractionId , arrayIndex ) ;
```
**Array Field Categories ** (112 fields total):
- **Exhibit info**: exhibitTitle, exhibitPage
- **Reimbursement provider**: reimbProvTin, reimbProvNpi, reimbProvName, dates
- **Claim/product codes**: claimTypeCd, product, LOB, program, network, provType
- **Provider taxonomy**: taxonomyCd, specialtyCd with descriptions
- **Service location**: placeOfServiceCd, billTypeCd with descriptions
- **Patient demographics**: patientAgeMin, patientAgeMax
- **Reimbursement terms**: reimbTerm, carveout info, payment logic
- **Rates**: reimbPctRate, reimbFeeRate, conversionFactor, thresholds
- **Fee schedules**: feeSchedule, feeScheduleVersion
- **Service codes**: CPT4, revenue, diagnosis, NDC codes with descriptions
- **Grouper info**: grouperType, grouperCd, rates, severity, transfer flags
- **Outlier terms**: thresholds, rates, exclusions
- **Facility adjustments**: DSH, IME, NTAP, UC, GME indicators and rates
- **Rate escalator**: escalator settings, max rate increases
- **Stop loss**: thresholds, maximums, daily rates
**Key Constraints ** :
- Unique constraint on (fieldExtractionId, arrayIndex) prevents duplicate rows
- Check constraint ensures arrayIndex >= 0
### Field Extraction Views
#### Current Field Extractions (`currentFieldExtractions`)
Returns the newest field extraction per document.
``` sql
CREATE VIEW currentFieldExtractions AS
SELECT DISTINCT ON ( dfe . documentId )
dfe . id ,
dfe . documentId ,
dfe . fileName ,
dfe . contractTitle ,
-- ... all single-value fields ...
dfev . version ,
dfev . createdBy ,
dfev . createdAt
FROM documentFieldExtractions dfe
JOIN documentFieldExtractionVersions dfev
ON dfev . fieldExtractionId = dfe . id
ORDER BY dfe . documentId , dfev . id DESC ;
```
**Key Features ** :
- Uses `DISTINCT ON` for efficient latest-per-document queries
- Joins versions to include version metadata
- Orders by dfev.id DESC to get most recent version
#### Current Field Extractions With Array Count (`currentFieldExtractionsWithArrayCount`)
Extends currentFieldExtractions with count of array field rows.
``` sql
CREATE VIEW currentFieldExtractionsWithArrayCount AS
SELECT
cfe . * ,
COALESCE ( array_counts . arraySize , 0 ) as arraySize
FROM currentFieldExtractions cfe
LEFT JOIN (
SELECT fieldExtractionId , COUNT ( * ) as arraySize
FROM documentFieldExtractionArrayFields
GROUP BY fieldExtractionId
) array_counts ON array_counts . fieldExtractionId = cfe . id ;
```
**Key Features ** :
- Includes all fields from currentFieldExtractions
- Adds arraySize column showing count of related array field rows
- Uses COALESCE for documents with no array fields (returns 0)
2025-06-24 19:56:56 +00:00
## Versioning System
The schema implements a comprehensive versioning system enabling temporal queries and configuration evolution.
### Version Management Pattern
``` sql
-- Generic versioning pattern
CREATE TABLE entity_versions (
entityId uuid NOT NULL ,
versionId int4 NOT NULL ,
addedVersion int4 NOT NULL ,
removedVersion int4 ,
-- entity-specific fields
PRIMARY KEY ( entityId , versionId )
) ;
CREATE TABLE entity_active_versions (
entityId uuid PRIMARY KEY ,
versionId int4 NOT NULL
) ;
```
### Temporal Validity Functions
``` sql
-- Check if version is active at specific point
2025-10-23 22:57:15 +00:00
-- Note: Parameter order is _version, _addedVersion, _removedVersion
CREATE OR REPLACE FUNCTION isInVersion (
_version int ,
_addedVersion int ,
_removedVersion int
)
2025-06-24 19:56:56 +00:00
RETURNS boolean AS $ $
BEGIN
2025-10-23 22:57:15 +00:00
RETURN _version IS NULL OR (
_version > = _addedVersion AND
( _removedVersion IS NULL OR _version < _removedVersion )
) ;
2025-06-24 19:56:56 +00:00
END ;
2025-10-23 22:57:15 +00:00
$ $ LANGUAGE plpgsql ;
2025-06-24 19:56:56 +00:00
```
2025-10-23 22:57:15 +00:00
**Key Points ** :
- Returns true if `_version` is NULL (allowing queries without version constraints)
- Returns true if version is within the valid range [_addedVersion, _removedVersion)
- Used throughout views for temporal queries
2025-06-24 19:56:56 +00:00
### Version-Enabled Entities
- **Query Versions**: `queryVersions` , `queryActiveVersions`
- **Collector Versions**: `collectorVersions` , `collectorActiveVersions`
- **Processing Versions**: Track minimum required versions for document stages
## Document Processing Pipeline
### Document Lifecycle Tables
#### Document Uploads (`documentUploads`)
Initial upload tracking for audit and debugging.
2025-10-23 22:57:15 +00:00
``` sql
CREATE TABLE documentUploads (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
bucket TEXT NOT NULL ,
key TEXT NOT NULL ,
clientId varchar ( 255 ) NOT NULL ,
part unsignedsmallint NOT NULL ,
createdAt timestamp NOT NULL ,
filename TEXT ,
batch_id uuid ,
2025-11-26 19:23:42 +00:00
folder_id uuid ,
FOREIGN KEY ( clientId ) REFERENCES clients ( clientId ) ,
FOREIGN KEY ( folder_id ) REFERENCES folders ( id )
2025-10-23 22:57:15 +00:00
) ;
-- Indexes
CREATE INDEX idx_documentuploads_filename ON documentUploads ( filename ) ;
CREATE INDEX idx_documentuploads_batch_id ON documentUploads ( batch_id ) ;
CREATE INDEX idx_documentuploads_clientid ON documentUploads ( clientId ) ;
2025-11-26 19:23:42 +00:00
CREATE INDEX idx_documentuploads_folder_id ON documentUploads ( folder_id ) ;
2025-10-23 22:57:15 +00:00
```
**Key Features ** :
- Tracks initial S3 upload location (bucket/key)
- Part number for storage distribution
2025-11-26 19:23:42 +00:00
- Optional batch association (added in migration 107, note: no foreign key constraint on batch_id)
2025-10-23 22:57:15 +00:00
- Filename tracking (added in migration 106)
2025-11-26 19:23:42 +00:00
- Folder association for batch uploads (added in migration 116, has foreign key constraint)
- Indexed for efficient lookups by client, filename, batch, and folder
2025-10-23 22:57:15 +00:00
2025-06-24 19:56:56 +00:00
#### Document Entries (`documentEntries`)
Core document references after deduplication and validation.
2025-10-23 22:57:15 +00:00
``` sql
CREATE TABLE documentEntries (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
documentId uuid NOT NULL ,
bucket TEXT NOT NULL ,
key TEXT NOT NULL ,
FOREIGN KEY ( documentId ) REFERENCES documents ( id )
) ;
```
**Purpose ** :
- Links documents to their S3 storage locations after initial processing
- Multiple entries possible per document for version tracking
2025-06-24 19:56:56 +00:00
#### Document Cleans (`documentCleans`)
PDF validation and format processing records.
2025-10-23 22:57:15 +00:00
``` sql
CREATE TYPE cleanFailType AS ENUM (
' invalid_mimetype ' ,
' invalid_read ' ,
' invalid_read_pages ' ,
' zero_page_count ' ,
' large_file ' ,
' small_dimensions ' ,
' large_dimensions ' ,
' small_dpi ' ,
' large_dpi '
) ;
CREATE TYPE cleanMimeType AS ENUM ( ' application/pdf ' ) ;
CREATE TABLE documentCleans (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
documentId uuid NOT NULL ,
bucket TEXT ,
key TEXT ,
hash TEXT ,
mimetype cleanMimeType ,
fail cleanFailType ,
FOREIGN KEY ( documentId ) REFERENCES documents ( id ) ,
CONSTRAINT bucket_and_key_together CHECK (
( bucket IS NULL ) = ( key IS NULL ) AND
( bucket IS NULL ) = ( mimetype IS NULL ) AND
( bucket IS NULL ) = ( hash IS NULL )
) ,
CONSTRAINT location_xor_fail CHECK (
( bucket IS NOT NULL ) ! = ( fail IS NOT NULL )
)
) ;
```
**Validation Logic ** :
- Either succeeds (has bucket/key/mimetype/hash) OR fails (has fail type)
- Cannot have both success and failure indicators
- Tracks specific failure reasons for troubleshooting
#### Document Clean Entries (`documentCleanEntries`)
Version tracking for clean operations.
``` sql
CREATE TABLE documentCleanEntries (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
cleanId uuid NOT NULL ,
version bigint NOT NULL ,
FOREIGN KEY ( cleanId ) REFERENCES documentCleans ( id )
) ;
```
**Purpose ** :
- Enables version-based filtering of clean results
- Used by `currentCleanEntries` view to get latest valid cleans
2025-06-24 19:56:56 +00:00
#### Document Text Extractions (`documentTextExtractions`)
OCR and text extraction results from AWS Textract.
2025-10-23 22:57:15 +00:00
``` sql
CREATE TABLE documentTextExtractions (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
cleanId uuid NOT NULL ,
bucket TEXT NOT NULL ,
key TEXT NOT NULL ,
hash TEXT NOT NULL ,
createdAt timestamp NOT NULL ,
part unsignedsmallint NOT NULL ,
FOREIGN KEY ( cleanId ) REFERENCES documentCleans ( id )
) ;
```
**Key Features ** :
- Links to successful clean operation
- Stores S3 location of extracted text
- Hash for content verification
- Part number for storage distribution
#### Document Text Extraction Entries (`documentTextExtractionEntries`)
Version tracking for text extraction operations.
``` sql
CREATE TABLE documentTextExtractionEntries (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
textId uuid NOT NULL ,
version bigint NOT NULL ,
FOREIGN KEY ( textId ) REFERENCES documentTextExtractions ( id )
) ;
```
**Purpose ** :
- Enables version-based filtering of text extraction results
- Used by `currentTextEntries` view to get latest valid extractions
2025-06-24 19:56:56 +00:00
### Processing Dependencies
[link to rendered version here ](https://mermaid.live/edit#pako:eNo9jz0KwzAMRq9iNDcX6NClZOsU2inNYGy1Mfgn2AokhNy9tkytRTx9TyAdoIJGuAr4RrnM4jG8vcj1WmyQetRBrQ49VUyT6Lqb6D3FvUWFDKap7nHG1t2i9M1i-ksMLD1xo-YU6DeKUpEJTS5TdgdMq6U0xtonuAhwGJ00Ot9_AM3o-BONH5kNOM8fK3VMWw== )
``` mermaid
graph LR
Upload[documentUploads] --> Entry[documentEntries]
Entry --> Clean[documentCleans]
Clean --> Text[documentTextExtractions]
Text --> Results[results]
```
## Query Dependency System
### Dependency Management
``` sql
-- Query-to-query dependencies
CREATE TABLE requiredQueries (
queryId uuid NOT NULL REFERENCES queries ( queryId ) ,
requiredQueryId uuid NOT NULL REFERENCES queries ( queryId ) ,
versionId int4 NOT NULL ,
addedVersion int4 NOT NULL ,
removedVersion int4 ,
CHECK ( queryId ! = requiredQueryId )
) ;
-- Result-to-result dependencies
CREATE TABLE resultDependencies (
resultId uuid NOT NULL REFERENCES results ( id ) ,
requiredResultId uuid NOT NULL REFERENCES results ( id ) ,
CHECK ( resultId ! = requiredResultId )
) ;
```
### Dependency Resolution Views
``` sql
-- Recursive view for transitive dependencies
CREATE VIEW queryActiveDependencies AS
WITH RECURSIVE deps AS (
-- Direct dependencies
SELECT DISTINCT queryId , requiredQueryId , 1 as depth
FROM requiredQueries rq
JOIN queryActiveVersions qav ON rq . queryId = qav . queryId AND rq . versionId = qav . versionId
WHERE rq . removedVersion IS NULL
UNION
-- Transitive dependencies
SELECT d . queryId , rq . requiredQueryId , d . depth + 1
FROM deps d
JOIN requiredQueries rq ON d . requiredQueryId = rq . queryId
JOIN queryActiveVersions qav ON rq . queryId = qav . queryId AND rq . versionId = qav . versionId
WHERE rq . removedVersion IS NULL AND d . depth < 10 -- Cycle prevention
)
SELECT * FROM deps ;
```
## Collector Configuration System
### Client-Query Mapping
``` sql
-- Maps named queries to specific query implementations for clients
CREATE TABLE collectorQueries (
2025-10-23 22:57:15 +00:00
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
clientId varchar ( 255 ) NOT NULL ,
name varchar ( 255 ) NOT NULL ,
queryId uuid NOT NULL ,
addedVersion int NOT NULL ,
removedVersion int ,
FOREIGN KEY ( queryId ) REFERENCES queries ( queryId ) ,
FOREIGN KEY ( clientId ) REFERENCES clients ( clientId ) ,
FOREIGN KEY ( clientId , addedVersion ) REFERENCES collectorVersions ( clientId , id ) ,
FOREIGN KEY ( clientId , removedVersion ) REFERENCES collectorVersions ( clientId , id ) ,
UNIQUE ( clientId , name , removedVersion )
2025-06-24 19:56:56 +00:00
) ;
```
2025-10-23 22:57:15 +00:00
**Key Features ** :
- Maps human-readable query names to query implementations per client
- Version tracking with addedVersion/removedVersion
- Unique constraint on (clientId, name, removedVersion) allows name reuse across versions
2025-06-24 19:56:56 +00:00
### Version Thresholds
``` sql
2025-10-23 22:57:15 +00:00
-- Minimum clean processing version required
2025-06-24 19:56:56 +00:00
CREATE TABLE collectorMinCleanVersions (
2025-10-23 22:57:15 +00:00
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
clientId varchar ( 255 ) NOT NULL ,
versionId bigint NOT NULL ,
addedVersion int NOT NULL ,
removedVersion int ,
FOREIGN KEY ( clientId , addedVersion ) REFERENCES collectorVersions ( clientId , id ) ,
FOREIGN KEY ( clientId , removedVersion ) REFERENCES collectorVersions ( clientId , id ) ,
FOREIGN KEY ( clientId ) REFERENCES clients ( clientId )
2025-06-24 19:56:56 +00:00
) ;
2025-10-23 22:57:15 +00:00
-- Minimum text extraction version required
2025-06-24 19:56:56 +00:00
CREATE TABLE collectorMinTextVersions (
2025-10-23 22:57:15 +00:00
id uuid PRIMARY KEY DEFAULT uuid_generate_v7 ( ) ,
clientId varchar ( 255 ) NOT NULL ,
versionId bigint NOT NULL ,
addedVersion int NOT NULL ,
removedVersion int ,
FOREIGN KEY ( clientId , addedVersion ) REFERENCES collectorVersions ( clientId , id ) ,
FOREIGN KEY ( clientId , removedVersion ) REFERENCES collectorVersions ( clientId , id ) ,
FOREIGN KEY ( clientId ) REFERENCES clients ( clientId )
2025-06-24 19:56:56 +00:00
) ;
```
2025-10-23 22:57:15 +00:00
**Purpose ** :
- Controls which document processing versions are valid for a client
- Allows clients to ignore outdated processing results
- Version-tracked to support temporal queries
2025-06-24 19:56:56 +00:00
### Complex Dependency Views
``` sql
-- Builds complete query dependency tree per client
CREATE VIEW collectorQueryDependencyTree AS
SELECT DISTINCT
cq . clientId ,
cq . name as queryName ,
qad . queryId ,
qad . requiredQueryId ,
qad . depth
FROM collectorQueries cq
JOIN queryActiveDependencies qad ON cq . queryId = qad . queryId
WHERE cq . removedVersion IS NULL ;
```
2025-10-23 22:57:15 +00:00
## Database Views
The system uses extensive views to simplify complex queries and encapsulate business logic. Views are organized by domain.
### Query Management Views
#### Query Current Active Versions (`queryCurrentActiveVersions`)
Resolves the current active version for each query, handling cases where no active version is set.
``` sql
CREATE VIEW queryCurrentActiveVersions AS
SELECT DISTINCT
q . queryId ,
COALESCE (
( FIRST_VALUE ( av . versionId ) OVER ( PARTITION BY q . queryId ORDER BY av . activeVersionEntryId DESC ) ) ,
0
) : : int AS activeVersion
FROM queries AS q
LEFT JOIN queryActiveVersions AS av ON av . queryId = q . queryId ;
```
**Purpose ** : Provides latest active version per query, defaulting to 0 if none set.
#### Query Latest Versions (`queryLatestVersions`)
Tracks the highest version number for each query.
``` sql
CREATE VIEW queryLatestVersions AS
SELECT
q . queryId ,
COALESCE ( MAX ( v . versionId ) , 0 ) : : int AS latestVersion
FROM queries AS q
LEFT JOIN queryVersions AS v ON v . queryId = q . queryId
GROUP BY q . queryId ;
```
#### Query Current Configs (`queryCurrentConfigs`)
Returns the active configuration for each query based on current version.
``` sql
CREATE VIEW queryCurrentConfigs AS
SELECT av . queryId , c . config
FROM queryCurrentActiveVersions AS av
LEFT JOIN queryConfigs AS c ON av . queryId = c . queryId
AND isInVersion ( av . activeVersion , c . addedVersion , c . removedVersion ) ;
```
#### Query Current Required IDs (`queryCurrentRequiredIds`)
Lists direct dependencies for each query at their current active version.
``` sql
CREATE VIEW queryCurrentRequiredIds AS
SELECT DISTINCT av . queryId , r . requiredQueryId
FROM queryCurrentActiveVersions AS av
LEFT JOIN requiredQueries AS r ON av . queryId = r . queryId
AND isInVersion ( av . activeVersion , r . addedVersion , r . removedVersion ) ;
```
#### Query Current Required IDs Aggregated (`queryCurrentRequiredIdsAGG`)
Aggregates required query IDs into arrays for easier consumption.
``` sql
CREATE VIEW queryCurrentRequiredIdsAGG AS
SELECT queryId ,
COALESCE (
ARRAY_AGG ( DISTINCT requiredQueryId )
FILTER ( WHERE requiredQueryId ! = ' 00000000-0000-0000-0000-000000000000 ' ) : : uuid [ ] ,
ARRAY [ ] : : uuid [ ]
) : : uuid [ ] AS requiredIds
FROM queryCurrentRequiredIds
GROUP BY queryId ;
```
#### Full Active Queries (`fullActiveQueries`)
Complete query information with versions, configs, and dependencies.
``` sql
CREATE VIEW fullActiveQueries AS
SELECT DISTINCT
q . queryId ,
q . queryType ,
av . activeVersion ,
lv . latestVersion ,
c . config ,
r . requiredIds
FROM queries AS q
JOIN queryCurrentActiveVersions AS av ON q . queryId = av . queryId
JOIN queryLatestVersions AS lv ON lv . queryId = q . queryId
JOIN queryCurrentConfigs AS c ON q . queryId = c . queryId
JOIN queryCurrentRequiredIdsAGG AS r ON q . queryId = r . queryId ;
```
**Purpose ** : Single view providing all query metadata needed for execution.
#### Query Active Dependencies (`queryActiveDependencies`)
Recursive view computing transitive query dependencies with cycle detection.
``` sql
CREATE VIEW queryActiveDependencies AS
WITH RECURSIVE queryActiveDependencies ( queryId , requiredQueryId , path , cycle ) AS (
SELECT
queryId ,
requiredQueryId ,
ARRAY [ queryId , requiredQueryId ] : : uuid [ ] AS path ,
false AS cycle
FROM queryCurrentRequiredIds
UNION ALL
SELECT
q . queryId ,
qd . requiredQueryId ,
path | | qd . requiredQueryId ,
qd . requiredQueryId = ANY ( path ) AS cycle
FROM queryCurrentRequiredIds AS q
JOIN queryActiveDependencies AS qd ON q . queryId = qd . requiredQueryId
WHERE NOT qd . cycle
)
SELECT DISTINCT queryId AS id , requiredQueryId
FROM queryActiveDependencies
WHERE NOT cycle ;
```
**Key Features ** :
- Computes full dependency tree (not just direct dependencies)
- Detects and prevents infinite loops from circular dependencies
- Used for query execution ordering
### Collector Management Views
#### Collector Current Active Versions (`collectorCurrentActiveVersions`)
Resolves active collector version per client.
``` sql
CREATE VIEW collectorCurrentActiveVersions AS
SELECT
c . clientId ,
COALESCE ( v . versionId , 0 ) : : int AS activeVersion
FROM clients c
LEFT JOIN (
SELECT DISTINCT ON ( clientId )
clientId ,
versionId
FROM collectorActiveVersions
ORDER BY clientId , id DESC
) v ON c . clientId = v . clientId ;
```
#### Collector Latest Versions (`collectorLatestVersions`)
Tracks highest version number per collector.
``` sql
CREATE VIEW collectorLatestVersions AS
SELECT
j . clientId ,
COALESCE ( MAX ( v . id ) , 0 ) : : int AS latestVersion
FROM clients AS j
LEFT JOIN collectorVersions AS v ON v . clientId = j . clientId
GROUP BY j . clientId ;
```
#### Current Collector Min Text/Clean Versions
Views tracking minimum processing versions required per client.
``` sql
CREATE VIEW currentCollectorMinTextVersions AS
SELECT DISTINCT
av . clientId ,
COALESCE ( ctv . versionId , 0 ) AS minTextVersion
FROM collectorCurrentActiveVersions AS av
LEFT JOIN collectorMinTextVersions AS ctv ON av . clientId = ctv . clientId
AND isInVersion ( av . activeVersion , ctv . addedVersion , ctv . removedVersion ) ;
CREATE VIEW currentCollectorMinCleanVersions AS
SELECT DISTINCT
av . clientId ,
COALESCE ( ctv . versionId , 0 ) AS minCleanVersion
FROM collectorCurrentActiveVersions AS av
LEFT JOIN collectorMinCleanVersions AS ctv ON av . clientId = ctv . clientId
AND isInVersion ( av . activeVersion , ctv . addedVersion , ctv . removedVersion ) ;
```
#### Current Collector Queries (`currentCollectorQueries`)
Active query mappings per client.
``` sql
CREATE VIEW currentCollectorQueries AS
SELECT DISTINCT
av . clientId ,
q . name ,
q . queryId
FROM collectorCurrentActiveVersions AS av
LEFT JOIN collectorQueries AS q ON av . clientId = q . clientId
AND isInVersion ( av . activeVersion , q . addedVersion , q . removedVersion ) ;
```
#### Full Active Collectors (`fullActiveCollectors`)
Complete collector configuration with all metadata.
``` sql
CREATE VIEW fullActiveCollectors AS
SELECT DISTINCT
av . clientId ,
ccv . minCleanVersion ,
ctv . minTextVersion ,
av . activeVersion ,
lv . latestVersion ,
q . fields
FROM collectorCurrentActiveVersions AS av
JOIN collectorLatestVersions AS lv ON lv . clientId = av . clientId
JOIN currentCollectorMinCleanVersions AS ccv ON av . clientId = ccv . clientId
JOIN currentCollectorMinTextVersions AS ctv ON av . clientId = ctv . clientId
JOIN currentCollectorQueriesJSONAGG AS q ON av . clientId = q . clientId ;
```
#### Collector Query Dependency Tree (`collectorQueryDependencyTree`)
Recursive view computing all queries (direct and transitive) needed by a collector.
``` sql
CREATE VIEW collectorQueryDependencyTree AS
WITH RECURSIVE collectorQueryDependencyTree AS (
SELECT cq . clientId , cq . queryId , ri . requiredIds
FROM currentCollectorQueries AS cq
JOIN queryCurrentRequiredIdsAGG AS ri ON cq . queryId = ri . queryId
UNION ALL
SELECT acq . clientId , q . queryId , q . requiredIds
FROM queryCurrentRequiredIdsAGG AS q
JOIN collectorQueryDependencyTree AS acq ON q . queryId = ANY ( acq . requiredIds )
)
SELECT DISTINCT
ct . clientId ,
ct . queryId ,
q . queryType ,
av . activeVersion AS queryVersion ,
ct . requiredIds
FROM collectorQueryDependencyTree AS ct
JOIN queryCurrentActiveVersions AS av ON ct . queryId = av . queryId
JOIN queries AS q ON q . queryId = ct . queryId ;
```
**Purpose ** : Used to determine all queries that must be executed for a client's configuration.
### Document Processing Views
#### Current Clean Entries (`currentCleanEntries`)
Latest valid clean entry per document meeting minimum version requirements.
``` sql
CREATE VIEW currentCleanEntries AS
WITH RankedExtractions AS (
SELECT
dc . id , dc . documentId , dc . bucket , dc . key , dc . mimetype ,
dc . fail , dc . hash , dce . version , d . clientId ,
ROW_NUMBER ( ) OVER ( PARTITION BY dc . documentId ORDER BY dc . id DESC , dce . id DESC ) AS row_num
FROM documents d
JOIN currentCollectorMinCleanVersions ccv ON d . clientId = ccv . clientId
JOIN documentCleans dc ON dc . documentId = d . id
JOIN documentCleanEntries dce ON dc . id = dce . cleanId
AND dce . version > = ccv . minCleanVersion
)
SELECT id , documentId , bucket , key , version , hash , mimetype , fail , clientId
FROM RankedExtractions
WHERE row_num = 1 ;
```
**Key Features ** :
- Filters by collector's minimum clean version requirement
- Returns most recent clean entry per document
- Includes both successful and failed cleans
#### Current Text Entries (`currentTextEntries`)
Latest valid text extraction per document meeting minimum version requirements.
``` sql
CREATE VIEW currentTextEntries AS
WITH RankedExtractions AS (
SELECT
extract . id , cc . documentId , extract . bucket , extract . key ,
extract . hash , extract . cleanId , dtee . version AS extractionVersion ,
ROW_NUMBER ( ) OVER ( PARTITION BY cc . documentId ORDER BY dtee . id DESC ) AS row_num
FROM documents d
JOIN currentCleanEntries cc ON cc . documentId = d . id
JOIN currentCollectorMinTextVersions ctv ON ctv . clientId = d . clientId
JOIN documentTextExtractions extract ON extract . cleanId = cc . id
JOIN documentTextExtractionEntries dtee ON dtee . textId = extract . id
AND dtee . version > = ctv . minTextVersion
)
SELECT id , documentId , bucket , key , hash , cleanId , extractionVersion
FROM RankedExtractions
WHERE row_num = 1 ;
```
**Purpose ** : Used to determine which text extractions are valid for query processing.
#### Current Client Can Sync (`currentClientCanSync`)
Latest sync permission status per client.
``` sql
CREATE VIEW currentClientCanSync AS
WITH RankedExtractions AS (
SELECT
ccs . clientId ,
ccs . canSync ,
ROW_NUMBER ( ) OVER ( PARTITION BY ccs . clientId ORDER BY ccs . syncId DESC ) AS row_num
FROM clients c
JOIN clientCanSync ccs ON c . clientId = ccs . clientId
)
SELECT
c . clientId ,
COALESCE ( r . canSync , false ) AS canSync
FROM clients c
LEFT JOIN RankedExtractions r ON r . clientId = c . clientId AND r . row_num = 1 ;
```
#### Full Clients (`fullClients`)
Complete client information with sync status.
``` sql
CREATE VIEW fullClients AS
SELECT c . clientId , c . name , cs . canSync
FROM clients AS c
JOIN currentClientCanSync AS cs ON cs . clientId = c . clientId ;
```
2025-06-24 19:56:56 +00:00
## Advanced Database Features
### Business Logic Functions
#### Document Processing Functions
2025-10-23 22:57:15 +00:00
**listDocumentIDs ** : Paginated document listing with total count.
2025-06-24 19:56:56 +00:00
``` sql
2025-10-23 22:57:15 +00:00
CREATE OR REPLACE FUNCTION listDocumentIDs (
_clientId varchar ( 255 ) ,
_batchSize INTEGER ,
_offset INTEGER
)
RETURNS TABLE ( id uuid , totalCount BIGINT ) AS $ $
DECLARE
totalCount BIGINT : = 0 ;
2025-06-24 19:56:56 +00:00
BEGIN
2025-10-23 22:57:15 +00:00
SELECT COUNT ( * ) : : BIGINT INTO totalCount FROM documents WHERE clientId = _clientId ;
2025-06-24 19:56:56 +00:00
RETURN QUERY
2025-10-23 22:57:15 +00:00
SELECT d . id , totalCount
FROM documents AS d
WHERE d . clientId = _clientId
ORDER BY d . id
LIMIT _batchSize
OFFSET _offset ;
2025-06-24 19:56:56 +00:00
END ;
$ $ LANGUAGE plpgsql ;
```
2025-10-23 22:57:15 +00:00
**Key Features ** :
- Returns both document IDs and total count in single query
- Supports pagination via batch size and offset
- Orders by document ID for consistent paging
**collectorQueryDependencyTreeByClient ** : Optimized function for getting query dependencies per client.
2025-06-24 19:56:56 +00:00
``` sql
2025-10-23 22:57:15 +00:00
CREATE OR REPLACE FUNCTION collectorQueryDependencyTreeByClient (
_clientId varchar ( 255 )
2025-06-24 19:56:56 +00:00
)
2025-10-23 22:57:15 +00:00
RETURNS TABLE (
clientId varchar ( 255 ) ,
queryId uuid ,
queryType queryType ,
queryVersion int ,
requiredIds uuid [ ]
) AS $ $
BEGIN
RETURN QUERY
WITH clientQueries AS (
SELECT q . clientId , q . queryId
FROM currentCollectorQueries AS q
WHERE q . clientId = _clientId
) ,
dependencyTree AS (
WITH RECURSIVE collectorQueryDependencyTree AS (
SELECT cq . clientId , cq . queryId , ri . requiredIds
FROM clientQueries AS cq
JOIN queryCurrentRequiredIdsAGG AS ri ON cq . queryId = ri . queryId
UNION ALL
SELECT acq . clientId , q . queryId , q . requiredIds
FROM queryCurrentRequiredIdsAGG AS q
JOIN collectorQueryDependencyTree AS acq ON q . queryId = ANY ( acq . requiredIds )
)
SELECT DISTINCT
ct . clientId , ct . queryId , q . queryType ,
av . activeVersion AS queryVersion , ct . requiredIds
FROM collectorQueryDependencyTree AS ct
JOIN queryCurrentActiveVersions AS av ON ct . queryId = av . queryId
JOIN queries AS q ON q . queryId = ct . queryId
)
SELECT t . clientId , t . queryId , t . queryType , t . queryVersion , t . requiredIds
FROM dependencyTree AS t
WHERE t . clientID IS NOT NULL ;
END ;
$ $ LANGUAGE plpgsql ;
```
**Purpose ** : More efficient than view for single-client queries.
#### Result Validation Functions
**listValidDocumentResults ** : Complex dependency resolution for query results.
``` sql
CREATE OR REPLACE FUNCTION listValidDocumentResults ( doc_id uuid )
RETURNS TABLE ( documentId uuid , queryId uuid , resultId uuid , value text ) AS $ $
DECLARE
count_before INT ;
count_after INT ;
iterations INT : = 0 ;
BEGIN
-- Creates temporary tables and iteratively resolves dependencies
-- Full implementation in migration 00000000000102_document_views.up.sql:103-211
END ;
2025-06-24 19:56:56 +00:00
$ $ LANGUAGE plpgsql ;
```
2025-10-23 22:57:15 +00:00
**Key Features ** :
- Validates that all query dependencies have been satisfied
- Uses temporary tables for efficient processing
- Iteratively builds valid result set
- Checks both query dependencies and result dependencies
- Returns only results where all prerequisites are met
### Database Triggers
The schema uses several triggers to automate versioning and configuration management:
#### Query Version Auto-Increment
2025-06-24 19:56:56 +00:00
``` sql
2025-10-23 22:57:15 +00:00
CREATE OR REPLACE FUNCTION setQueryVersionNumber ( )
RETURNS TRIGGER AS $ $
BEGIN
SELECT COALESCE ( MAX ( versionId ) , 0 ) + 1
INTO NEW . versionId
FROM queryVersions
WHERE queryId = NEW . queryId ;
RETURN NEW ;
END ;
$ $ LANGUAGE plpgsql ;
CREATE TRIGGER setQueryVersionNumberTrigger
BEFORE INSERT ON queryVersions
FOR EACH ROW
EXECUTE FUNCTION setQueryVersionNumber ( ) ;
2025-06-24 19:56:56 +00:00
```
2025-10-23 22:57:15 +00:00
#### Collector Version Auto-Increment
``` sql
CREATE OR REPLACE FUNCTION setCollectorVersionNumber ( )
RETURNS TRIGGER AS $ $
BEGIN
SELECT COALESCE ( MAX ( id ) , 0 ) + 1
INTO NEW . id
FROM collectorVersions
WHERE clientId = NEW . clientId ;
RETURN NEW ;
END ;
$ $ LANGUAGE plpgsql ;
CREATE TRIGGER setCollectorVersionNumberTrigger
BEFORE INSERT ON collectorVersions
FOR EACH ROW
EXECUTE FUNCTION setCollectorVersionNumber ( ) ;
```
#### Query Config Version Management
``` sql
CREATE OR REPLACE FUNCTION removeQueryConfig ( )
RETURNS TRIGGER AS $ $
BEGIN
UPDATE queryConfigs
SET removedVersion = NEW . addedVersion
WHERE queryId = NEW . queryId AND removedVersion IS NULL ;
RETURN NEW ;
END ;
$ $ LANGUAGE plpgsql ;
CREATE TRIGGER removeQueryConfigTrigger
BEFORE INSERT ON queryConfigs
FOR EACH ROW
EXECUTE FUNCTION removeQueryConfig ( ) ;
```
**Purpose ** : Automatically closes out previous configs when new ones are added.
#### Collector Min Version Management
Similar triggers exist for `collectorMinCleanVersions` and `collectorMinTextVersions` to automatically set `removedVersion` when new minimum versions are added.
2025-06-24 19:56:56 +00:00
## Data Integrity and Constraints
### Referential Integrity
- All foreign key relationships enforced at database level
2025-10-23 22:57:15 +00:00
- No cascade deletes (explicit deletion required for safety)
2025-06-24 19:56:56 +00:00
- Unique constraints prevent duplicate business entities
2025-10-23 22:57:15 +00:00
- Composite foreign keys ensure version referential integrity
2025-06-24 19:56:56 +00:00
2025-10-23 22:57:15 +00:00
### Data Validation Constraints
#### Enum Types
- `queryType` : `'context_full'` , `'json_extractor'`
- `cleanMimeType` : `'application/pdf'`
- `cleanFailType` : Various failure reasons (invalid_mimetype, invalid_read, etc.)
- `batch_status` : `'processing'` , `'completed'` , `'failed'` , `'cancelled'`
#### Check Constraints
- `requiredQueries` : Self-reference prevention (`queryId != requiredQueryId` )
- `resultDependencies` : Self-reference prevention (`resultId != requiredResultId` )
- `documentCleans` : XOR constraint ensures either success OR failure, not both
- `bucket_and_key_together` : Bucket, key, mimetype, hash must all be present or all null
- `location_xor_fail` : Cannot have both location (success) and fail (failure) indicators
#### Custom Domain Types
- `unsignedsmallint` : SMALLINT constrained to non-negative values
- Used for part numbers in storage distribution
2025-06-24 19:56:56 +00:00
### Audit and Traceability
2025-10-23 22:57:15 +00:00
- UUID v7 generation provides time-ordered insertion
2025-06-24 19:56:56 +00:00
- Version tracking enables complete audit trails
2025-10-23 22:57:15 +00:00
- Temporal validity functions support point-in-time queries
- Triggers automatically manage version transitions
- All critical tables have UUID primary keys for global uniqueness
2025-06-24 19:56:56 +00:00
## Scalability Considerations
### Indexing Strategy
- Primary keys automatically indexed (UUID v7)
- Foreign keys automatically indexed
- Composite indexes on frequent query patterns
- Unique constraints create secondary indexes
### Partitioning Opportunities
- Time-based partitioning using UUID v7 timestamp component
- Client-based partitioning for multi-tenant isolation
- Archive strategies for old versions
### Performance Optimizations
- Window functions for latest version queries
- Recursive CTEs with depth limits for dependency resolution
- Pagination support in all list functions
- Efficient temporal queries using `isInVersion()` function
## Migration Strategy
### Database Evolution
2025-10-23 22:57:15 +00:00
- Sequential numbered migrations for version control (00000000000001, 00000000000002, etc.)
2025-06-24 19:56:56 +00:00
- Separate UP/DOWN migrations for rollback capability
- Extension management for UUID generation and other features
- View-based abstractions for query flexibility
2025-10-23 22:57:15 +00:00
- Migration files located in: `internal/database/migrations/`
### Migration Numbering Convention
- `00000000000001-00000000000099` : Core tables (extensions, queries, clients, collectors, documents, results)
- `00000000000100-00000000000199` : Views and functions
- `00000000000200+` : Schema modifications and new features
### Recent Schema Changes
- **Migration 103**: Added `batch_uploads` table and `documents.batch_id`
2025-11-26 19:23:42 +00:00
- **Migration 104**: Added S3 storage columns to `batch_uploads` (`archive_bucket` , `archive_key` , `file_size_bytes` ) with storage consistency constraint
2025-10-23 22:57:15 +00:00
- **Migration 105**: Added `documents.filename` column
- **Migration 106**: Added `documentUploads.filename` column
2025-11-26 19:23:42 +00:00
- **Migration 107**: Added `documentUploads.batch_id` column (without foreign key constraint)
2025-10-23 22:57:15 +00:00
- **Migration 108**: Added index on `documentUploads.clientId`
2025-11-26 19:23:42 +00:00
- **Migration 109**: Added `folders` table for virtual folder hierarchy with self-referential parentId
- **Migration 110**: Added `labels` and `documentLabels` tables for document processing status tracking
- **Migration 111**: Added `documents.folderId` and `documents.originalPath` columns; added comments for S3 immutability
- **Migration 112**: Added `documentFieldExtractions` table with 19 single-value fields
- **Migration 113**: Added `documentFieldExtractionVersions` table for version tracking
- **Migration 114**: Added `documentFieldExtractionArrayFields` table with 112 array fields
- **Migration 115**: Added `currentFieldExtractions` and `currentFieldExtractionsWithArrayCount` views
- **Migration 116**: Added `documentUploads.folder_id` column with foreign key constraint
2025-06-24 19:56:56 +00:00
### Version Compatibility
2025-10-23 22:57:15 +00:00
- Backward-compatible schema changes prioritized
- Graceful handling of version mismatches via views
- New columns added as nullable to avoid breaking existing code
- Indexes added separately from table creation for safety
## Key Schema Patterns
### Naming Conventions
- **Tables**: Generally use camelCase (e.g., `clientId` , `documentId` )
- **Exception**: `batch_uploads` uses snake_case (`client_id` , `original_filename` )
- **Views**: camelCase naming (e.g., `currentCleanEntries` , `fullActiveQueries` )
- **Functions**: camelCase naming (e.g., `listDocumentIDs` , `isInVersion` )
### Common Data Types
- **IDs**: `uuid` with `DEFAULT uuid_generate_v7()` for time-ordered insertion
- **Client references**: `varchar(255)` for clientId
- **Text fields**: `TEXT` type (not varchar) for unlimited length
- **Timestamps**: `timestamp` without timezone
- **Versions**: `int` or `bigint` depending on expected range
- **Part numbers**: `unsignedsmallint` (0-32767)
- **JSON data**: `jsonb` for queryable JSON storage
### Versioning Pattern
Most versioned entities follow this pattern:
1. Core table with basic info (e.g., `queries` )
2. Versions table with composite PK (e.g., `queryVersions` )
3. Active version tracking table (e.g., `queryActiveVersions` )
4. Related data with `addedVersion` /`removedVersion` (e.g., `queryConfigs` )
5. Views to compute current state (e.g., `queryCurrentActiveVersions` )
6. Triggers to auto-increment versions
### View Pattern
Views are layered for reusability:
1. **Base views ** : Compute current active versions
2. **Aggregation views ** : Roll up related data (e.g., `queryCurrentRequiredIdsAGG` )
3. **Full views ** : Join everything together (e.g., `fullActiveQueries` )
4. **Recursive views ** : Compute transitive relationships (e.g., `queryActiveDependencies` )
### Performance Considerations
- Views use `DISTINCT` liberally to handle version overlap
- Window functions (`ROW_NUMBER` , `FIRST_VALUE` ) for latest record selection
- Recursive CTEs include cycle detection
- Indexes created on foreign keys and frequently filtered columns
- Pagination functions return total count to avoid separate COUNT query
## Schema Statistics
### Table Count
- **Core entities**: 5 (clients, documents, batch_uploads, queries, results)
2025-11-26 19:23:42 +00:00
- **Organization tables**: 3 (folders, labels, documentLabels)
- **Field extraction tables**: 3 (documentFieldExtractions, documentFieldExtractionVersions, documentFieldExtractionArrayFields)
2025-10-23 22:57:15 +00:00
- **Supporting tables**: 16 (versions, entries, configs, dependencies, etc.)
2025-11-26 19:23:42 +00:00
- **Total tables**: 27
2025-10-23 22:57:15 +00:00
### View Count
- **Query management**: 6 views
- **Collector management**: 7 views
- **Document processing**: 3 views
2025-11-26 19:23:42 +00:00
- **Field extraction**: 2 views (currentFieldExtractions, currentFieldExtractionsWithArrayCount)
- **Total views**: 18+
2025-10-23 22:57:15 +00:00
### Function Count
- **Business logic**: 3 main functions
- **Trigger functions**: 5 functions
- **Utility functions**: 2 (uuid_generate_v7, isInVersion)
- **Total functions**: 10+
### Enum Types
- `queryType` : 2 values
- `cleanMimeType` : 1 value
- `cleanFailType` : 9 values
- `batch_status` : 4 values
- **Total enums**: 4 types, 16 values