# Understanding ContentType vs FileType

## User Intent

"What's the difference between contentType and fileType? When do I use each?"

## Operation

* **Concept**: Content classification hierarchy
* **GraphQL Fields**: `content.type` (ContentType), `content.fileType` (FileType)
* **Entity Type**: Content
* **Common Use Cases**: Filtering content, UI display logic, workflow routing, understanding content structure

## Key Concept: The Hierarchy

**ContentType is PRIMARY** - Semantic classification of what the content represents:

* `EMAIL` - Email messages
* `MESSAGE` - Chat messages (Slack, Teams, Discord)
* `PAGE` - Web pages
* `FILE` - Files (documents, images, audio, video, code)
* `POST` - Social posts (Reddit, RSS)
* `EVENT` - Calendar events
* `ISSUE` - Issue tracker items (Jira, Linear, GitHub)
* `TEXT` - Plain text, markdown, HTML
* `MEMORY` - Agent or user memory

**FileType is SECONDARY** - Physical format (ONLY when contentType = FILE):

* `DOCUMENT` - PDF, Word, Excel, PowerPoint
* `IMAGE` - JPEG, PNG, GIF, TIFF
* `AUDIO` - MP3, WAV, podcast files
* `VIDEO` - MP4, MOV, AVI
* `CODE` - Source code files
* `DATA` - JSON, XML, CSV
* `PACKAGE` - ZIP, TAR, archives
* `ANIMATION`, `DRAWING`, `GEOMETRY`, `POINT_CLOUD`, `SHAPE`, etc.

## TypeScript (Canonical)

```typescript
import { Graphlit } from 'graphlit-client';
import { ContentTypes, FileTypes } from 'graphlit-client/dist/generated/graphql-types';

const graphlit = new Graphlit();

// Query by ContentType
const emails = await graphlit.queryContents({
  types: [ContentTypes.Email],
});

emails.contents.results.forEach(content => {
  console.log(`Type: ${content.type}`);  // EMAIL
  console.log(`FileType: ${content.fileType}`);  // null (emails don't have fileType)
  
  // Access email-specific metadata
  if (content.type === ContentTypes.Email && content.email) {
    console.log(`From: ${content.email.from[0].email}`);
    console.log(`Subject: ${content.email.subject}`);
  }
});

// Query by FileType (for files)
const pdfs = await graphlit.queryContents({
  types: [ContentTypes.File],
  fileTypes: [FileTypes.Document],
});

pdfs.contents.results.forEach(content => {
  console.log(`Type: ${content.type}`);  // FILE
  console.log(`FileType: ${content.fileType}`);  // DOCUMENT
  
  // Access document-specific metadata
  if (content.document) {
    console.log(`Pages: ${content.document.pageCount}`);
    console.log(`Author: ${content.document.author}`);
  }
});

// Query all images (regardless of source)
const images = await graphlit.queryContents({
  fileTypes: [FileTypes.Image],
});

images.contents.results.forEach(content => {
  console.log(`Type: ${content.type}`);  // Could be FILE, MESSAGE (if image in message), etc.
  console.log(`FileType: ${content.fileType}`);  // IMAGE
  
  if (content.image) {
    console.log(`Dimensions: ${content.image.width}x${content.image.height}`);
  }
});
```

## ContentType → Metadata Field Mapping

**CRITICAL**: Each ContentType has a corresponding metadata field on the content object:

| ContentType       | Metadata Field                     | Type             | When Set               |
| ----------------- | ---------------------------------- | ---------------- | ---------------------- |
| `EMAIL`           | `content.email`                    | EmailMetadata    | Always for emails      |
| `MESSAGE`         | `content.message`                  | MessageMetadata  | Always for messages    |
| `EVENT`           | `content.event`                    | EventMetadata    | Always for events      |
| `ISSUE`           | `content.issue`                    | IssueMetadata    | Always for issues      |
| `POST`            | `content.post`                     | PostMetadata     | Always for posts       |
| `FILE` (Document) | `content.document`                 | DocumentMetadata | When fileType=DOCUMENT |
| `FILE` (Image)    | `content.image`                    | ImageMetadata    | When fileType=IMAGE    |
| `FILE` (Audio)    | `content.audio`                    | AudioMetadata    | When fileType=AUDIO    |
| `FILE` (Video)    | `content.video`                    | VideoMetadata    | When fileType=VIDEO    |
| `FILE` (Package)  | `content.package`                  | PackageMetadata  | When fileType=PACKAGE  |
| `PAGE`            | `content.html`, `content.markdown` | Strings          | Always for pages       |

**Access Pattern**:

```typescript
const content = await graphlit.getContent('content-id');

// Always check type first
switch (content.content.type) {
  case ContentTypes.Email:
    // Safe to access content.email
    console.log(`Subject: ${content.content.email.subject}`);
    break;
    
  case ContentTypes.Message:
    // Safe to access content.message
    console.log(`Channel: ${content.content.message.channelName}`);
    break;
    
  case ContentTypes.File:
    // Check fileType for specific metadata
    if (content.content.fileType === FileTypes.Document) {
      console.log(`Pages: ${content.content.document.pageCount}`);
    } else if (content.content.fileType === FileTypes.Image) {
      console.log(`Size: ${content.content.image.width}x${content.content.image.height}`);
    }
    break;
    
  case ContentTypes.Event:
    // Safe to access content.event
    console.log(`Start: ${content.content.event.startDateTime}`);
    break;
}
```

## Developer Hints

### FileType Only Exists for Files

```typescript
//  WRONG - Emails don't have fileType
const emails = await graphlit.queryContents({
  types: [ContentTypes.Email],
  fileTypes: [FileTypes.Document]  // This makes no sense!
});

//  CORRECT - FileType only for FILE content
const pdfs = await graphlit.queryContents({
  types: [ContentTypes.File],
  fileTypes: [FileTypes.Document],
});

//  ALSO CORRECT - FileType alone (implicit FILE contentType)
const images = await graphlit.queryContents({
  fileTypes: [FileTypes.Image],
});
```

### Automatic Classification

```typescript
// ContentType is automatically determined at ingestion:
// - Email from Gmail feed → ContentTypes.Email
// - Slack message → ContentTypes.Message
// - PDF file → ContentTypes.File with FileDocument
// - Web page → ContentTypes.Page
// - GitHub issue → ContentTypes.Issue

// You CANNOT override contentType
// It's determined by the source
```

### Query Flexibility

```typescript
// Query by ContentType only
const allEmails = await graphlit.queryContents({
  types: [ContentTypes.Email],
});

// Query by FileType only (searches all FILE content)
const allImages = await graphlit.queryContents({
  fileTypes: [FileTypes.Image],
});

// Query by both (most specific)
const pdfFiles = await graphlit.queryContents({
  types: [ContentTypes.File],
  fileTypes: [FileTypes.Document],
});

// Query multiple types (OR logic)
const communications = await graphlit.queryContents({
  types: [ContentTypes.Email, ContentTypes.Message],
});
```

## Variations

### 1. Query All Communication Content

```typescript
// Emails + Messages + Posts
const communications = await graphlit.queryContents({
  types: [
    ContentTypes.Email,
    ContentTypes.Message,
    ContentTypes.Post,
  ],
});
```

### 2. Query All Media Files

```typescript
// Images + Audio + Video
const media = await graphlit.queryContents({
  types: [ContentTypes.File],
  fileTypes: [
    FileTypes.Image,
    FileTypes.Audio,
    FileTypes.Video,
  ],
});
```

### 3. Query Documents Only (No Other Files)

```typescript
const documents = await graphlit.queryContents({
  types: [ContentTypes.File],
  fileTypes: [FileTypes.Document],
});

// This excludes images, audio, video, code, etc.
```

### 4. Query Calendar Events

```typescript
const events = await graphlit.queryContents({
  types: [ContentTypes.Event],
});

events.contents.results.forEach(content => {
  if (content.event) {
    console.log(`Event: ${content.event.subject}`);
    console.log(`When: ${content.event.startDateTime}`);
    console.log(`Attendees: ${content.event.attendees?.length || 0}`);
  }
});
```

### 5. Query Issues from Project Management Tools

```typescript
const issues = await graphlit.queryContents({
  types: [ContentTypes.Issue],
});

issues.contents.results.forEach(content => {
  if (content.issue) {
    console.log(`Issue: ${content.issue.title}`);
    console.log(`Status: ${content.issue.status}`);
    console.log(`Priority: ${content.issue.priority}`);
  }
});
```

### 6. UI Display Logic by Type

```typescript
const content = await graphlit.getContent('content-id');

// Render UI based on content type
function renderContent(content: Content) {
  switch (content.type) {
    case ContentTypes.Email:
      return <EmailViewer email={content.email} />;
    
    case ContentTypes.Message:
      return <MessageViewer message={content.message} />;
    
    case ContentTypes.File:
      if (content.fileType === FileTypes.Document) {
        return <DocumentViewer document={content.document} />;
      } else if (content.fileType === FileTypes.Image) {
        return <ImageViewer image={content.image} />;
      }
      break;
    
    case ContentTypes.Page:
      return <WebPageViewer html={content.html} />;
    
    case ContentTypes.Event:
      return <EventViewer event={content.event} />;
  }
}
```

### 7. Workflow Routing by Type

```typescript
// Create different workflows for different content types
const documentWorkflow = await graphlit.createWorkflow({
  name: "Document Extraction",
  preparation: {
    jobs: [{
      connector: {
        type: FilePreparationServiceTypes.Document
      }
    }]
  },
  extraction: { /* ... */ }
});

const audioWorkflow = await graphlit.createWorkflow({
  name: "Audio Transcription",
  preparation: {
    jobs: [{
      connector: {
        type: FilePreparationServiceTypes.Deepgram,
        fileTypes: [FileTypes.Audio, FileTypes.Video],
        deepgram: {
          model: DeepgramModels.Nova2
        }
      }
    }]
  }
});

// Route content to appropriate workflow
const content = await graphlit.getContent('content-id');

if (content.content.fileType === FileTypes.Document) {
  // Re-process with document workflow
} else if (content.content.fileType === FileTypes.Audio) {
  // Re-process with audio workflow
}
```

## Common Issues & Solutions

**Issue**: Querying for emails by fileType returns no results

```typescript
//  WRONG
const emails = await graphlit.queryContents({
  types: [ContentTypes.Email],
  fileTypes: [FileTypes.Document]  // Emails don't have fileType!
});
```

**Solution**: Only use fileType with FILE content

```typescript
//  CORRECT
const emails = await graphlit.queryContents({
  types: [ContentTypes.Email],
});
```

**Issue**: Metadata field is null even though content exists

```typescript
const content = await graphlit.getContent('content-id');
console.log(content.content.email);  // undefined
```

**Solution**: Check content type first

```typescript
if (content.content.type === ContentTypes.Email) {
  // Now safe to access email metadata
  console.log(content.content.email.subject);
} else {
  console.log(`Content is ${content.content.type}, not EMAIL`);
}
```

**Issue**: Want to query "all files" but fileType filter is too specific

```typescript
//  TOO SPECIFIC - Only gets documents
const files = await graphlit.queryContents({
  fileTypes: [FileTypes.Document],
});
```

**Solution**: Use contentType=FILE without fileType filter

```typescript
//  CORRECT - Gets all files
const allFiles = await graphlit.queryContents({
  types: [ContentTypes.File],
});
```

**Issue**: TypeScript type errors when accessing metadata

```typescript
// TypeScript error: Property 'email' does not exist
console.log(content.content.email.subject);
```

**Solution**: Use type guards

```typescript
if (content.content.type === ContentTypes.Email && content.content.email) {
  // TypeScript knows email exists
  console.log(content.content.email.subject);
}
```

## Production Example

**Real-world pattern: Content type routing in UI**:

```typescript
async function displayContent(contentId: string) {
  const content = await graphlit.getContent(contentId);
  
  console.log(`=== CONTENT DISPLAY ===`);
  console.log(`Type: ${content.content.type}`);
  console.log(`Name: ${content.content.name}`);
  
  switch (content.content.type) {
    case ContentTypes.Email:
      console.log(`\n EMAIL`);
      console.log(`From: ${content.content.email.from[0].email}`);
      console.log(`Subject: ${content.content.email.subject}`);
      console.log(`Date: ${content.content.creationDate}`);
      console.log(`Attachments: ${content.content.email.attachmentCount || 0}`);
      break;
      
    case ContentTypes.Message:
      console.log(`\n💬 MESSAGE`);
      console.log(`Channel: ${content.content.message.channelName}`);
      console.log(`Author: ${content.content.message.author?.name}`);
      console.log(`Mentions: ${content.content.message.mentions?.length || 0}`);
      break;
      
    case ContentTypes.File:
      console.log(`\n FILE`);
      console.log(`FileType: ${content.content.fileType}`);
      console.log(`Size: ${content.content.fileSize} bytes`);
      
      if (content.content.fileType === FileTypes.Document) {
        console.log(`Pages: ${content.content.document?.pageCount}`);
        console.log(`Author: ${content.content.document?.author}`);
      } else if (content.content.fileType === FileTypes.Image) {
        console.log(`Dimensions: ${content.content.image?.width}x${content.content.image?.height}`);
      }
      break;
      
    case ContentTypes.Page:
      console.log(`\n🌐 WEB PAGE`);
      console.log(`URL: ${content.content.uri}`);
      break;
      
    case ContentTypes.Event:
      console.log(`\n📅 EVENT`);
      console.log(`Subject: ${content.content.event?.subject}`);
      console.log(`Start: ${content.content.event?.startDateTime}`);
      console.log(`Attendees: ${content.content.event?.attendees?.length || 0}`);
      break;
      
    case ContentTypes.Issue:
      console.log(`\n🎫 ISSUE`);
      console.log(`Title: ${content.content.issue?.title}`);
      console.log(`Status: ${content.content.issue?.status}`);
      console.log(`Priority: ${content.content.issue?.priority}`);
      break;
  }
  
  // Show extracted entities regardless of type
  if (content.content.observations?.length > 0) {
    console.log(`\n🏷 ENTITIES (${content.content.observations.length}):`);
    content.content.observations.forEach(obs => {
      console.log(`  ${obs.type}: ${obs.observable.name}`);
    });
  }
}

// Usage
await displayContent('email-content-id');
await displayContent('slack-message-id');
await displayContent('pdf-document-id');
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.graphlit.dev/api-guides/use-cases/content/content-type-vs-file-type-explained.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
