Here's the complete rayray architecture in a copyable format:
MARKDOWN
# rayray Architecture
## System Overview
┌─────────────────────────────────────────────────────────────────┐
│ rayray System │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ │
│ │ File Input │ │
│ │ Directory │ │
│ │ or URL │ │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Metadata │ │
│ │ Extractor │ │
│ │ (Per File) │ │
│ └─────────┬──────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Metadata │ │
│ │ Normalizer │ │
│ │ (Schema) │ │
│ └─────────┬──────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Metadata │ │
│ │ Writer │ │
│ │ (Batch) │ │
│ └─────────┬──────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Database │ │
│ │ Layer │ │
│ │ (Persistent) │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
TEXT
### Component Breakdown
#### 1. Metadata Extractor (Per-File Processing)
**Responsibilities:**
- Read file headers without loading full content into memory
- Parse format-specific metadata structures
- Handle binary metadata streams (EXIF, XMP, ICC profiles)
- Extract video codec headers (moov boxes in MP4, etc.)
**Key Design Decisions:**
- Process files **individually** — metadata extraction is independent
- Use **streaming** where possible — don't load entire files
- **Binary metadata** can be embedded anywhere (not just headers)
#### 2. Metadata Normalizer (Schema Layer)
**Schema Design:**
┌─────────────────────────────────────┐
│ ┌──────────────────┐ │
│ │ file_id │ │
│ │ file_path │ │
│ │ file_size │ │
│ │ file_type │ │
│ │ file_hash │ │
│ └──────────────────┘ │
│ │
│ ┌───────────┐ │
│ │ ┌─────┐ │ │
│ │ │IMG │ │ │
│ │ │VID │ │ │
│ │ └─────┘ │ │
│ └───────────┘ │
│ │
└─────────────────────────────────────┘
TEXT
**Fields to Extract:**
| Category | Fields |
|-----------|---------|
| **Identity** | filename, filepath, hash, size |
| **Dimensions** | width, height, duration (video) |
| **Camera** | make, model, exposure, aperture, ISO, focal_length |
| **GPS** | latitude, longitude, altitude, timestamp |
| **Author** | creator, copyright, description, tags |
| **Technical** | codec, bitrate, fps, color_space |
#### 3. Metadata Writer (Batch to DB)
**Batching Strategy:**
- Extract metadata from files → accumulate in memory
- Write to database in **batches** (e.g., every 100 records or 100ms)
- Prevents database from being overwhelmed by individual inserts
**Why Batch?**
- Reduces database connection overhead
- Enables transaction-level grouping
- Allows for retry logic at batch level
---
## Data Flow
┌──────────────┐
│ Input File │ ───> Read headers (streaming)
└──────────────┘ │
▼
┌───────────────────────┐
│ Extract Metadata │ (one file at a time)
│ ├─ Camera info │
│ ├─ GPS coordinates │
│ ├─ Dimensions │
│ └─ Timestamp │
└───────────────────────┘
│
▼
┌───────────────────────┐
│ Normalize to Schema │
│ ├─ Convert types │
│ ├─ Validate fields │
│ └─ Apply defaults │
└───────────────────────┘
│
▼
┌───────────────────────┐
│ Accumulate in Batch │
│ (e.g., 100 records) │
└───────────────────────┘
│
▼
┌───────────────────────┐
│ Write to Database │
│ (transaction) │
└───────────────────────┘
TEXT
---
## Technology Stack Recommendations
### Option A: Lightweight (Python)
├─ Extractor: python - PIL, exifread, ffprobe (subprocess)
├─ Schema: pydantic (validation + serialization)
├─ DB: SQLite or PostgreSQL
└─ CLI: click or typer
TEXT
### Option B: High-Performance (Go/Rust)
├─ Extractor: go-exif, go-ffprobe, or native bindings
├─ Schema: struct + json validation
├─ DB: PostgreSQL with connection pooling
├─ Concurrent processing of file queues
└─ HTTP API for web access
TEXT
---
## Key Architectural Decisions
| Decision | Recommendation |
|----------|----------------|
| **File Reading** | Streaming (seek to offsets) — don't load full file |
| **Concurrency** | Process files concurrently, metadata extraction is I/O bound |
| **Batch Size** | 100-500 records per batch, or 100ms timeout |
| **DB Connection** | Single connection pool, batch writes |
| **Error Handling** | Per-file errors don't crash batch; log and continue |
| **Schema Flexibility** | Use JSON field for extended/unstructured metadata |
---
## Next Steps
1. **Define schema** — exact fields to extract
2. **Choose database** — SQL vs NoSQL, local vs distributed
3. **Build extractor** — per-file metadata parsing
4. **Add batching** — accumulate and write in groups
5. **Add CLI/API** — interface to trigger processing
6. **Add monitoring** — logs, metrics, health checks
Option 2: Export from Chat Interface
Most chat interfaces allow you to:
Select and Copy — Highlight the entire response and copy (Ctrl/Cmd + C)
Save as File — Paste into a text editor and save as .md extension
File name suggestion: rayray-architecture.md
Would you like me to format any specific section differently or provide additional details?
<<<reasoning_content_start>>>
Let me think about this carefully.
The user wants to implement rayray with MongoDB as the database. I need to provide a proper MongoDB schema for storing metadata from images and videos.
Key considerations for MongoDB schema design:
1. **Document Structure**: MongoDB uses JSON-like documents, not relational tables
2. **Indexes**: Need to plan for query patterns (search by tags, camera model, location, etc.)
3. **Embedded vs Referenced**: Should metadata be embedded or separate?
4. **Geospatial**: GPS data should use geospatial indexes
5. **Flexibility**: MongoDB's strength is flexible schemas, but we need structure for queries
Let me design a comprehensive schema:
**Collection: `media_files`**
Each document should contain:
- File identification (path, hash, size)
- Basic file info (type, dimensions, duration)
- Camera metadata (EXIF data)
- GPS coordinates (for geospatial queries)
- User/author info
- Tags/keywords
**Indexes to create:**
- File hash (unique)
- File path
- Camera make/model
- GPS location (2dsphere)
- Tags
- File type + dimensions (compound)
- Upload/creation date
Let me provide a complete schema with examples, indexes, and sample queries.
<<<reasoning_content_end>>># MongoDB Schema for rayray
## Document Schema
### Primary Collection: `media_files`
```json
{
"_id": ObjectId("..."),
"file": {
"path": "/path/to/file.jpg",
"hash": "sha256:abc123...",
"size": 2456789,
"mimeType": "image/jpeg"
},
"dimensions": {
"width": 4096,
"height": 3072,
"aspectRatio": "16:9"
},
"camera": {
"make": "Canon",
"model": "EOS R5",
"lens": "RF 24-70mm",
"exposure": 1.6,
"aperture": 2.8,
"iso": 800,
"focalLength": "50mm"
},
"location": {
"coordinates": [
-122.4194,
37.7749
],
"address": "San Francisco, CA",
"altitude": 12
},
"timestamps": {
"created": "2024-01-15T10:30:00Z",
"modified": "2024-01-15T10:30:00Z",
"ingested": "2024-01-15T10:30:00Z"
},
"author": {
"name": "Jane Doe",
"copyright": "© Jane Doe 2024"
},
"tags": ["sunset", "urban", "golden-hour"],
"description": "Golden hour over city skyline",
"status": "indexed",
"metadataRaw": {
"exif": "...",
"icc": "..."
}
}
```
---
## Schema Visualization
```
┌──────────────────────────────────────────────────────────────┐
│ rayray.mongodb │
│ └── media_files │
│ └── (Document) │
│ └── { _id, file, │
│ │ dimensions, │
│ │ camera, │
│ │ location, │
│ │ author, │
│ │ tags } │
└──────────────────────────────────────────────────────────────┘
```
---
## Collection Schema Diagram
```
┌─────────────────────────────────────────────────────────────┐
│ COLLECTION: media_files │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ _id (ObjectId) │ │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ │ file │ │ │
│ │ │ │ ┌─────────┐ │ │ │
│ │ │ │ │ path │ │ │ │
│ │ │ │ │ hash │ (unique index) │ │ │
│ │ │ │ │ size │ │ │ │
│ │ │ │ │ mimeType │ │ │ │
│ │ │ │ └─────────┘ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ │ dimensions │ │ │
│ │ │ │ ┌─────────┐ │ │ │
│ │ │ │ │ width │ │ │ │
│ │ │ │ │ height │ │ │ │
│ │ │ │ │ bitrate│ (video) │ │ │
│ │ │ │ │ fps │ (video) │ │ │
│ │ │ │ │duration │ (video) │ │ │
│ │ │ │ └─────────┘ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ │ camera │ │ │
│ │ │ │ ┌─────────┐ │ │ │
│ │ │ │ │ make │ │ │ │
│ │ │ │ │ model │ │ │ │
│ │ │ │ │ exposure│ │ │ │
│ │ │ │ │ aperture│ │ │ │
│ │ │ │ │ iso │ │ │ │
│ │ │ │ │ focalLen│ │ │ │
│ │ │ │ └─────────┘ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ │ location │ │ │
│ │ │ │ ┌─────────┐ │ │ │
│ │ │ │ │ latitude│ (2dsphere index) │ │ │
│ │ │ │ │longitude│ (2dsphere index) │ │ │
│ │ │ │ │ altitude│ │ │ │
│ │ │ │ │ address│ │ │ │
│ │ │ │ └─────────┘ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ │ tags │ │ │
│ │ │ │ ┌─────────┐ │ │ │
│ │ │ │ │ sunset │ │ │ │
│ │ │ │ │ urban │ (text index) │ │ │
│ │ │ │ │golden-h │ │ │ │
│ │ │ │ │ etc │ │ │ │
│ │ │ │ └─────────┘ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ │ timestamps │ │ │
│ │ │ │ ┌─────────┐ │ │ │
│ │ │ │ │ created │ (date index) │ │ │
│ │ │ │ │ modified│ (date index) │ │ │
│ │ │ │ │ ingested│ │ │ │
│ │ │ │ └─────────┘ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ │ author │ │ │
│ │ │ │ ┌─────────┐ │ │ │
│ │ │ │ │ name │ │ │ │
│ │ │ │ │ copyright│ │ │ │
│ │ │ │ │ contact │ │ │ │
│ │ │ │ └─────────┘ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ │ status │ │ │
│ │ │ │ ┌─────────┐ │ │ │
│ │ │ │ │ indexed │ (status index) │ │ │
│ │ │ │ │ valid │ │ │ │
│ │ │ │ │ invalid │ │ │ │
│ │ │ │ └─────────┘ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ │ metadataRaw │ │ │
│ │ │ │ ┌─────────┐ │ │ │
│ │ │ │ │ exif │ (raw binary/JSON) │ │ │
│ │ │ │ │ xmp │ │ │ │
│ │ │ │ │ ict │ │ │ │
│ │ │ │ │ 3rdp │ (extended fields) │ │ │
│ │ │ │ └─────────┘ │ │ │
│ │ │ └──────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
```
---
## Index Strategy
### Indexes to Create
```javascript
// 1. File hash (unique identifier)
db.media_files.createIndex({ file_hash: 1 }, { unique: true, sparse: true })
// 2. File path (for lookup by path)
db.media_files.createIndex({ file_path: 1 })
// 3. Camera make (for filtering)
db.media_files.createIndex({ camera_make: 1 })
// 4. GPS location (geospatial queries)
db.media_files.createIndex({ location_coordinates: "2dsphere" })
// 5. Tags (text search)
db.media_files.createIndex({ tags: "text" })
// 6. File type + dimensions (compound)
db.media_files.createIndex({ file_type: 1, dimensions_width: -1 })
// 7. Creation date (for sorting/filtering)
db.media_files.createIndex({ timestamps_created: -1 })
// 8. Status (for filtering valid files)
db.media_files.createIndex({ status: 1 })
```
---
## Sample Queries
### Query 1: Get files by camera model
```javascript
db.media_files.find({
camera_model: /Canon/i
}).limit(10)
```
### Query 2: Find files near a location
```javascript
db.media_files.find({
location_coordinates: {
$nearSphere: {
type: "Point",
coordinates: [-122.4194, 37.7749]
},
maxDistance: 50000 // meters
}
})
```
### Query 3: Search by tags
```javascript
db.media_files.find({
$text: { $search: "sunset urban photography" }
})
```
### Query 4: Filter by dimensions (for responsive web)
```javascript
db.media_files.find({
dimensions_width: { $gte: 1920, $lte: 2560 },
dimensions_height: { $gte: 1080, $lte: 1440 }
})
```
### Query 5: Find all files with GPS data
```javascript
db.media_files.find({
location_coordinates: { $exists: true }
})
```
---
## Aggregation Pipeline Examples
### Pipeline 1: Camera model statistics
```javascript
db.media_files.aggregate([
{ $group: { _id: "$camera_make", count: { $sum: 1 } } },
{ $sort: { count: -1 } },
{ $limit: 10 }
])
```
### Pipeline 2: Files per author
```javascript
db.media_files.aggregate([
{ $group: { _id: "$author_name", count: { $sum: 1 }, files: { $push: "$file_path" } } },
{ $sort: { count: -1 } }
])
```
---
## Schema Summary Table
| Field | Type | Index | Notes |
|-------|------|-------|-------|
| `_id` | ObjectId | auto | MongoDB auto-generated |
| `file_hash` | String | unique | SHA-256 hash |
| `file_path` | String | yes | Original file path |
| `file_size` | Number | - | File size in bytes |
| `mimeType` | String | - | e.g., `image/jpeg`, `video/mp4` |
| `dimensions_width` | Number | - | Image/video width |
| `dimensions_height` | Number | - | Image/video height |
| `camera_make` | String | yes | Camera manufacturer |
| `camera_model` | String | yes | Camera model |
| `location_coordinates` | Array | 2dsphere | [longitude, latitude] |
| `tags` | Array | text | Searchable tags |
| `status` | String | yes | `indexed`, `valid`, `invalid` |
---
## Collection Structure Diagram
```
┌──────────────────────────────────────────────────────────────────┐
│ COLLECTION: media_files │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ ┌───────┐ │ │
│ │ │ _id │ (ObjectId) │ │
│ │ └───────┘ │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ file │ │ dimensions │ │ │
│ │ │ └──────────────┘ │ └──────────────┘ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ hash (unique) │ │ │ │
│ │ │ └──────────────────┘ │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ path │ │ │ │
│ │ │ │ size │ │ │ │
│ │ │ │ mimeType │ │ │ │
│ │ │ └──────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ camera_make │ │ │ │
│ │ │ │ camera_model │ │ │ │
│ │ │ │ camera_lens │ │ │ │
│ │ │ │ camera_exposure │ │ │ │
│ │ │ │ camera_aperture │ │ │ │
│ │ │ │ camera_iso │ │ │ │
│ │ │ │ camera_focalLength │ │ │ │
│ │ │ └──────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ location │ │ │ │
│ │ │ │ coordinates │ (2d │ │ │
│ │ │ │ altitude │ sphere) │ │
│ │ │ │ address │ │ │
│ │ │ └──────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ tags │ (text │ │ │
│ │ │ │ └──────────────┘ search) │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ timestamps │ (date │ │ │
│ │ │ │ └──────────────┘ index) │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ author │ │ │ │
│ │ │ │ └──────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ description │ │ │ │
│ │ │ │ status │ (status) │ │
│ │ │ └──────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ metadataRaw │ (JSON │ │ │
│ │ │ │ └──────────────┘ extended) │ │
│ │ └──────────────────────────────────────────────────────┘ │
│ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ COLLECTION: users │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ ┌───────┐ │ │
│ │ │ _id │ (ObjectId) │ │
│ │ └───────┘ │ │
│ │ ┌──────────────────────────────────────┐ │
│ │ │ username │ │
│ │ │ email │ │
│ │ │ role (admin, curator) │ │
│ │ │ created │ │
│ │ │ updated │ │
│ │ └──────────────────────────────────────┘ │
│ │ └──────────────────────────────────────┘ │
│ │ │
│ COLLECTION: batches │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ ┌───────┐ │ │
│ │ │ _id │ (ObjectId) │ │
│ │ └───────┘ │ │
│ │ ┌──────────────────────────────────────┐ │
│ │ │ batch_id │ │
│ │ │ file_count │ │
│ │ │ status │ │
│ │ │ created │ │
│ │ │ completed │ │
│ │ └──────────────────────────────────────┘ │
│ │ └──────────────────────────────────────┘ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ COLLECTION: logs │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ ┌───────┐ │ │
│ │ │ _id │ (ObjectId) │ │
│ │ └───────┘ │ │
│ │ ┌──────────────────────────────────────┐ │
│ │ │ level (info, warn, error) │ │
│ │ │ message │ │
│ │ │ timestamp │ │
│ │ │ file_id │ │
│ │ │ error_type │ │
│ │ └──────────────────────────────────────┘ │
│ │ └──────────────────────────────────────┘ │
│ └──────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
```
---
## Would You Like...
1. **Sample data insertion** — Example documents to test with?
2. **Connection pool setup** — How to structure the DB connection?
3. **Batching strategy** — How to batch writes efficiently?
4. **Error handling** — Retry, logging, and recovery?
Which would you like to explore next?