Collection Hierarchy
The Firestore database follows a strict hierarchical structure:All user data is nested under
/users/{userId}. This path-based organization is critical for the security model, as it allows ownership verification without database reads.Visual Data Structure
Collection Schemas
Users Collection: /users/{userId}
The root document for each user stores their profile information.
Schema
| Field | Type | Required | Description |
|---|---|---|---|
id | string | ✅ | User’s Firebase Auth UID (must match document ID) |
email | string | ✅ | User’s email address |
displayName | string | ❌ | User’s display name (from auth provider) |
photoURL | string | ❌ | User’s profile photo URL |
createdAt | Timestamp | ✅ | Account creation timestamp |
lastLoginAt | Timestamp | ❌ | Last login timestamp |
Example Document
TypeScript Interface
Security Rules
Argument Maps: /users/{userId}/argumentMaps/{mapId}
Stores argument analysis results generated by the AI system.
Schema
| Field | Type | Required | Description |
|---|---|---|---|
id | string | ✅ | Unique map identifier (auto-generated) |
userId | string | ✅ | Owner’s UID (must match path {userId}) |
name | string | ✅ | Argument topic or URL (first 150 chars for text) |
creationDate | Timestamp | ✅ | When the map was created |
jsonData | string | ✅ | Serialized AnalysisResult object |
lastModified | Timestamp | ❌ | Last update timestamp |
tags | string[] | ❌ | User-defined tags for organization |
Example Document
TypeScript Interface
Security Rules
The
userId field is required and must match the {userId} in the path. This is enforced by the isCreatingOwnSubcollectionDoc() security rule.Sources: /users/{userId}/sources/{sourceId}
Stores source documents (URLs, PDFs, text) that users analyze. This collection is defined in the security rules but not yet fully implemented in the application.
Schema (Proposed)
| Field | Type | Required | Description |
|---|---|---|---|
id | string | ✅ | Unique source identifier |
userId | string | ✅ | Owner’s UID |
type | string | ✅ | Source type: "url", "text", "pdf" |
url | string | ❌ | URL if type is "url" |
content | string | ❌ | Raw text content |
title | string | ✅ | Source title or description |
createdAt | Timestamp | ✅ | Creation timestamp |
scrapedAt | Timestamp | ❌ | When URL was last scraped |
Example Document
Security Rules
Nested Data Structures
ThejsonData field in ArgumentMapDocument contains a serialized AnalysisResult object:
AnalysisResult Schema
ArgumentNode Schema
ArgumentTree Schema
TheArgumentNode can be transformed into a tree structure:
Tweet Schema
Data Flow Diagram
Data Operations
Creating an Argument Map
Fromsrc/lib/actions.ts:
Why Use Server Actions Instead of Client SDK?
Why Use Server Actions Instead of Client SDK?
Security: Server Actions allow:
- Token verification before database writes
- Protection against client-side manipulation
- Centralized error handling
- Rate limiting and abuse prevention
Querying User’s Argument Maps
Client-side query example:Updating an Argument Map
Deleting an Argument Map
Indexing Strategy
Firestore automatically creates indexes for simple queries. For the Argument Analysis Tool, the following indexes are recommended:Single-Field Indexes (Auto-created)
users/{userId}/argumentMaps:creationDate(descending)users/{userId}/argumentMaps:name(ascending)users/{userId}/argumentMaps:tags(array-contains)
Composite Indexes (Create if needed)
By Creation Date + Tags
By Creation Date + Tags
Firestore will automatically prompt you to create composite indexes when you run a query that requires one. Follow the provided link to auto-generate the index.
Data Migration Considerations
Adding New Fields
When adding new fields to existing collections:- Make fields optional in TypeScript interfaces
- Provide default values when reading documents
- Update security rules if the field affects authorization
Renaming Collections
Firestore doesn’t support renaming collections. To migrate:- Create new collection structure
- Copy data programmatically
- Update application code to use new paths
- Update security rules
- Delete old collection after verification
Best Practices
Always Include userId
Every subcollection document must have a
userId field matching the path.Use Auto-Generated IDs
Let Firestore generate document IDs with
.doc() for uniqueness.Denormalize When Needed
Store frequently accessed data redundantly to avoid multiple reads.
Limit Document Size
Keep documents under 1MB. Use subcollections for large datasets.
Document Size Management
Why Serialize AnalysisResult?
Why Serialize AnalysisResult?
The
jsonData field stores the entire AnalysisResult as a serialized string:Pros:- Single read operation to get full analysis
- Atomic writes (all or nothing)
- Easy to version (entire structure changes together)
- Document size grows with argument complexity
- Cannot query individual arguments within the map
arguments subcollection:Performance Optimization
Read Efficiency
- Use
.get()instead of.list()when you know the document ID - Limit query results with
.limit(n)to prevent large reads - Use pagination with
startAfter()for large collections
Write Efficiency
- Use batch writes for multiple operations
- Use transactions for read-modify-write operations
- Avoid hot spots by not updating the same document too frequently
Next Steps
Security Rules
Learn how data access is protected
Architecture Overview
Understand the overall system design