c45e1dd427
all schema and rest apis for text extraction support * in progress * stage 8 complete * phase 9 completed * phase 9 complete * ongoing - s3 path fix * working * optimize ci build * e2e tests * missing test
1752 lines
55 KiB
Markdown
1752 lines
55 KiB
Markdown
# 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.
|
|
|
|
## Entity Relationship Diagram
|
|
|
|
```mermaid
|
|
erDiagram
|
|
clients ||--o{ documents : "owns"
|
|
clients ||--o{ batch_uploads : "owns"
|
|
clients ||--|| clientCanSync : "has sync status"
|
|
clients ||--o{ documentUploads : "uploads"
|
|
clients ||--o{ folders : "organizes"
|
|
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"
|
|
|
|
folders ||--o{ documents : "contains"
|
|
folders ||--o{ folders : "has children"
|
|
folders ||--o{ documentUploads : "targets"
|
|
|
|
documents ||--o{ documentEntries : "has entries"
|
|
documents ||--o{ documentCleans : "has clean versions"
|
|
documents ||--o{ documentLabels : "has labels"
|
|
documents ||--o{ documentFieldExtractions : "has field extractions"
|
|
|
|
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"
|
|
|
|
labels ||--o{ documentLabels : "applied to documents"
|
|
|
|
documentFieldExtractions ||--o{ documentFieldExtractionVersions : "has versions"
|
|
documentFieldExtractions ||--o{ documentFieldExtractionArrayFields : "has array fields"
|
|
|
|
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
|
|
}
|
|
|
|
folders {
|
|
uuid id PK
|
|
text path
|
|
uuid parentId FK
|
|
varchar clientId FK
|
|
timestamp createdAt
|
|
varchar createdBy
|
|
}
|
|
|
|
documents {
|
|
uuid id PK
|
|
varchar clientId FK
|
|
text hash
|
|
text filename
|
|
uuid batch_id FK
|
|
uuid folderId FK
|
|
text originalPath
|
|
}
|
|
|
|
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
|
|
text archive_bucket
|
|
text archive_key
|
|
bigint file_size_bytes
|
|
}
|
|
|
|
documentUploads {
|
|
uuid id PK
|
|
text bucket
|
|
text key
|
|
varchar clientId FK
|
|
smallint part
|
|
timestamp createdAt
|
|
text filename
|
|
uuid batch_id FK
|
|
uuid folder_id FK
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
```
|
|
|
|
## Core Entities
|
|
|
|
### Clients (`clients`)
|
|
Central entity representing organizations or tenants in the system.
|
|
|
|
```sql
|
|
CREATE TABLE clients (
|
|
clientId varchar(255) PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
UNIQUE (name)
|
|
);
|
|
```
|
|
|
|
**Key Characteristics**:
|
|
- String-based client identifiers for human readability
|
|
- Multi-tenant isolation through client-scoped data
|
|
- Central reference point for all client-owned entities
|
|
- 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
|
|
|
|
### Documents (`documents`)
|
|
Represents uploaded files requiring processing.
|
|
|
|
```sql
|
|
CREATE TABLE documents (
|
|
id uuid PRIMARY KEY DEFAULT uuid_generate_v7(),
|
|
clientId varchar(255) NOT NULL,
|
|
hash TEXT NOT NULL,
|
|
filename TEXT,
|
|
batch_id uuid,
|
|
folderId uuid,
|
|
originalPath TEXT,
|
|
UNIQUE(clientId, hash),
|
|
FOREIGN KEY (clientId) REFERENCES clients(clientId),
|
|
FOREIGN KEY (batch_id) REFERENCES batch_uploads(id),
|
|
FOREIGN KEY (folderId) REFERENCES folders(id)
|
|
);
|
|
|
|
-- Indexes
|
|
CREATE INDEX idx_documents_filename ON documents(filename);
|
|
CREATE INDEX idx_documents_batch_id ON documents(batch_id);
|
|
CREATE INDEX idx_documents_folderid ON documents(folderId);
|
|
```
|
|
|
|
**Key Features**:
|
|
- UUID v7 primary keys for time-ordered insertion
|
|
- Client isolation with foreign key constraints
|
|
- 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)
|
|
- 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
|
|
|
|
### Batch Uploads (`batch_uploads`)
|
|
Tracks batch document upload operations with ZIP archive processing.
|
|
|
|
```sql
|
|
CREATE TYPE batch_status AS ENUM ('processing', 'completed', 'failed', 'cancelled');
|
|
|
|
CREATE TABLE batch_uploads (
|
|
id uuid PRIMARY KEY DEFAULT uuid_generate_v7(),
|
|
client_id varchar(255) NOT NULL,
|
|
original_filename TEXT NOT NULL,
|
|
total_documents integer NOT NULL,
|
|
processed_documents integer NOT NULL DEFAULT 0,
|
|
failed_documents integer NOT NULL DEFAULT 0,
|
|
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,
|
|
created_at timestamp NOT NULL DEFAULT NOW(),
|
|
completed_at timestamp,
|
|
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)
|
|
)
|
|
);
|
|
|
|
-- Indexes
|
|
CREATE INDEX idx_batch_uploads_client_id ON batch_uploads(client_id);
|
|
CREATE INDEX idx_batch_uploads_status ON batch_uploads(status);
|
|
```
|
|
|
|
**Batch Processing Features**:
|
|
- UUID v7 for time-ordered batch tracking
|
|
- 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
|
|
- Temporal tracking with creation and completion timestamps
|
|
- Storage metadata tracking (archive bucket/key and file size)
|
|
- Storage consistency constraint ensures bucket and key are both set or both null
|
|
- Indexed by client_id and status for efficient queries
|
|
- Note: Uses snake_case naming convention (unlike other tables)
|
|
|
|
### 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
|
|
|
|
### 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
|
|
);
|
|
```
|
|
|
|
## 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)
|
|
|
|
## 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
|
|
-- Note: Parameter order is _version, _addedVersion, _removedVersion
|
|
CREATE OR REPLACE FUNCTION isInVersion(
|
|
_version int,
|
|
_addedVersion int,
|
|
_removedVersion int
|
|
)
|
|
RETURNS boolean AS $$
|
|
BEGIN
|
|
RETURN _version IS NULL OR (
|
|
_version >= _addedVersion AND
|
|
(_removedVersion IS NULL OR _version < _removedVersion)
|
|
);
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
**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
|
|
|
|
### 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.
|
|
|
|
```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,
|
|
folder_id uuid,
|
|
FOREIGN KEY (clientId) REFERENCES clients(clientId),
|
|
FOREIGN KEY (folder_id) REFERENCES folders(id)
|
|
);
|
|
|
|
-- 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);
|
|
CREATE INDEX idx_documentuploads_folder_id ON documentUploads(folder_id);
|
|
```
|
|
|
|
**Key Features**:
|
|
- Tracks initial S3 upload location (bucket/key)
|
|
- Part number for storage distribution
|
|
- Optional batch association (added in migration 107, note: no foreign key constraint on batch_id)
|
|
- Filename tracking (added in migration 106)
|
|
- Folder association for batch uploads (added in migration 116, has foreign key constraint)
|
|
- Indexed for efficient lookups by client, filename, batch, and folder
|
|
|
|
#### Document Entries (`documentEntries`)
|
|
Core document references after deduplication and validation.
|
|
|
|
```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
|
|
|
|
#### Document Cleans (`documentCleans`)
|
|
PDF validation and format processing records.
|
|
|
|
```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
|
|
|
|
#### Document Text Extractions (`documentTextExtractions`)
|
|
OCR and text extraction results from AWS Textract.
|
|
|
|
```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
|
|
|
|
### 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 (
|
|
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)
|
|
);
|
|
```
|
|
|
|
**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
|
|
|
|
### Version Thresholds
|
|
```sql
|
|
-- Minimum clean processing version required
|
|
CREATE TABLE collectorMinCleanVersions (
|
|
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)
|
|
);
|
|
|
|
-- Minimum text extraction version required
|
|
CREATE TABLE collectorMinTextVersions (
|
|
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)
|
|
);
|
|
```
|
|
|
|
**Purpose**:
|
|
- Controls which document processing versions are valid for a client
|
|
- Allows clients to ignore outdated processing results
|
|
- Version-tracked to support temporal queries
|
|
|
|
### 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;
|
|
```
|
|
|
|
## 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;
|
|
```
|
|
|
|
## Advanced Database Features
|
|
|
|
### Business Logic Functions
|
|
|
|
#### Document Processing Functions
|
|
|
|
**listDocumentIDs**: Paginated document listing with total count.
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION listDocumentIDs(
|
|
_clientId varchar(255),
|
|
_batchSize INTEGER,
|
|
_offset INTEGER
|
|
)
|
|
RETURNS TABLE (id uuid, totalCount BIGINT) AS $$
|
|
DECLARE
|
|
totalCount BIGINT := 0;
|
|
BEGIN
|
|
SELECT COUNT(*)::BIGINT INTO totalCount FROM documents WHERE clientId = _clientId;
|
|
|
|
RETURN QUERY
|
|
SELECT d.id, totalCount
|
|
FROM documents AS d
|
|
WHERE d.clientId = _clientId
|
|
ORDER BY d.id
|
|
LIMIT _batchSize
|
|
OFFSET _offset;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
**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.
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION collectorQueryDependencyTreeByClient(
|
|
_clientId varchar(255)
|
|
)
|
|
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;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
**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
|
|
```sql
|
|
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();
|
|
```
|
|
|
|
#### 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.
|
|
|
|
## Data Integrity and Constraints
|
|
|
|
### Referential Integrity
|
|
- All foreign key relationships enforced at database level
|
|
- No cascade deletes (explicit deletion required for safety)
|
|
- Unique constraints prevent duplicate business entities
|
|
- Composite foreign keys ensure version referential integrity
|
|
|
|
### 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
|
|
|
|
### Audit and Traceability
|
|
- UUID v7 generation provides time-ordered insertion
|
|
- Version tracking enables complete audit trails
|
|
- Temporal validity functions support point-in-time queries
|
|
- Triggers automatically manage version transitions
|
|
- All critical tables have UUID primary keys for global uniqueness
|
|
|
|
## 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
|
|
- Sequential numbered migrations for version control (00000000000001, 00000000000002, etc.)
|
|
- Separate UP/DOWN migrations for rollback capability
|
|
- Extension management for UUID generation and other features
|
|
- View-based abstractions for query flexibility
|
|
- 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`
|
|
- **Migration 104**: Added S3 storage columns to `batch_uploads` (`archive_bucket`, `archive_key`, `file_size_bytes`) with storage consistency constraint
|
|
- **Migration 105**: Added `documents.filename` column
|
|
- **Migration 106**: Added `documentUploads.filename` column
|
|
- **Migration 107**: Added `documentUploads.batch_id` column (without foreign key constraint)
|
|
- **Migration 108**: Added index on `documentUploads.clientId`
|
|
- **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
|
|
|
|
### Version Compatibility
|
|
- 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)
|
|
- **Organization tables**: 3 (folders, labels, documentLabels)
|
|
- **Field extraction tables**: 3 (documentFieldExtractions, documentFieldExtractionVersions, documentFieldExtractionArrayFields)
|
|
- **Supporting tables**: 16 (versions, entries, configs, dependencies, etc.)
|
|
- **Total tables**: 27
|
|
|
|
### View Count
|
|
- **Query management**: 6 views
|
|
- **Collector management**: 7 views
|
|
- **Document processing**: 3 views
|
|
- **Field extraction**: 2 views (currentFieldExtractions, currentFieldExtractionsWithArrayCount)
|
|
- **Total views**: 18+
|
|
|
|
### 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 |