> For the complete documentation index, see [llms.txt](https://docs.graphlit.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.graphlit.dev/api-guides/use-cases/content/content-query-with-filters.md).

# Query with Filters

## User Intent

"I want to search and filter my ingested content by various criteria"

## Operation

* **SDK Method**: `graphlit.queryContents()`
* **GraphQL**: `queryContents` query
* **Entity Type**: Content
* **Common Use Cases**: Semantic search, filter by type/date/collection, faceted search, similarity search

## TypeScript (Canonical)

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

const graphlit = new Graphlit();

// Basic search - returns all content
const response = await graphlit.queryContents({});

console.log(`Found ${response.contents.results.length} content items`);

// Text search with semantic similarity
const searchResponse = await graphlit.queryContents({
  search: 'machine learning best practices',
  searchType: SearchTypes.Hybrid,  // Combines keyword + vector search
  limit: 10
});

console.log(`Search found ${searchResponse.contents.results.length} results`);

// Filter by content type
const pdfResponse = await graphlit.queryContents({
  types: [ContentTypes.File],
  fileTypes: [FileTypes.Document],
  fileExtensions: ['pdf'],
  limit: 20
});

console.log(`Found ${pdfResponse.contents.results.length} PDF files`);
```

## Parameters

### ContentFilter Options

**Search**:

* **`search`** (string): Search query text
  * Uses semantic similarity (embeddings)
  * Supports natural language queries
* **`searchType`** (SearchTypes): Search strategy
  * `KEYWORD` - Traditional keyword matching
  * `VECTOR` - Semantic/embedding search
  * `HYBRID` - Combined keyword + vector (recommended)

**Content Type Filters**:

* **`types`** (ContentTypes\[]): Filter by content type
  * `FILE`, `PAGE`, `TEXT`, `MEMORY`, `EMAIL`, `MESSAGE`, `POST`, `ISSUE`, `EVENT`
* **`fileTypes`** (FileTypes\[]): Filter by file type (when type = FILE)
  * `PDF`, `DOCX`, `IMAGE`, `AUDIO`, `VIDEO`, `MARKDOWN`, etc.
* **`textTypes`** (TextTypes\[]): Filter text content
  * `PLAIN`, `MARKDOWN`

**Temporal Filters**:

* **`creationDateRange`** (DateRangeFilter): Filter by creation date
  * `from` (Date): Start date
  * `to` (Date): End date
* **`modifiedDateRange`** (DateRangeFilter): Filter by modification date

**Organization Filters**:

* **`collections`** (EntityReferenceFilter\[]): Filter by collection membership
* **`feeds`** (EntityReferenceFilter\[]): Filter by source feed
* **`workflows`** (EntityReferenceFilter\[]): Filter by workflow used

**State Filters**:

* **`states`** (EntityState\[]): Filter by processing state
  * `Enabled`, `Disabled`, `Finished`, `Errored`

**Advanced Filters**:

* **`similarContents`** (EntityReferenceInput\[]): Find similar content
* **`observations`** (ObservationFilter): Filter by extracted entities

**Pagination & Sorting**:

* **`offset`** (number): Skip first N results (default: 0)
* **`limit`** (number): Max results to return (default: 10, max: 100)
* **`orderBy`** (OrderByTypes): Sort field
  * `RELEVANCE` - By search relevance score (when search is used)
  * `CREATION_DATE` - By creation date
  * `NAME` - Alphabetically by name
* **`direction`** (OrderDirectionTypes): Sort direction
  * `Ascending` - Ascending
  * `Descending` - Descending

## Response

```typescript
{
  contents: {
    results: Content[];  // Array of content items
    // Each content has:
    // - id, name, state, type, fileType, mimeType
    // - markdown (extracted text)
    // - uri (source URL)
    // - creationDate, modifiedDate
    // - collections, feed, workflow
    // - observations (extracted entities)
    // - relevance (search score, 0.0-1.0)
  }
}
```

## Developer Hints

### Search Type Selection

**When to use each search type**:

| Search Type | Best For                                     | How It Works              |
| ----------- | -------------------------------------------- | ------------------------- |
| `KEYWORD`   | Exact term matching, technical terms         | Traditional text matching |
| `VECTOR`    | Semantic/conceptual search, natural language | Embedding similarity      |
| `HYBRID`    | Most searches (recommended)                  | Combines both approaches  |

```typescript
// Use KEYWORD for exact terms
const keywordResults = await graphlit.queryContents({
  search: 'GPT-4',
  searchType: SearchTypes.Keyword
});

// Use VECTOR for conceptual search
const vectorResults = await graphlit.queryContents({
  search: 'articles about artificial intelligence',
  searchType: SearchTypes.Vector
});

// Use HYBRID for best results (default)
const hybridResults = await graphlit.queryContents({
  search: 'machine learning tutorials',
  searchType: SearchTypes.Hybrid
});
```

### OrderBy Behavior

```typescript
// When using search, ALWAYS use orderBy: RELEVANCE
const searchResults = await graphlit.queryContents({
  search: 'product documentation',
  orderBy: OrderByTypes.Relevance  // Sorts by search score
});

// When NOT searching, use CREATION_DATE or NAME
const recentContent = await graphlit.queryContents({
  orderBy: OrderByTypes.CreationDate,
  direction: OrderDirectionTypes.Descending,  // Newest first
  limit: 10
});
```

### Understanding Relevance Scores

```typescript
// Results include relevance scores when searching
const results = await graphlit.queryContents({
  search: 'project requirements',
  searchType: SearchTypes.Hybrid
});

results.contents.results.forEach(content => {
  console.log(`${content.name}: ${content.relevance?.toFixed(2)} relevance`);
  // Relevance is 0.0 to 1.0 (higher = more relevant)
});
```

### Pagination Best Practices

```typescript
// Fetch first page
let offset = 0;
const limit = 20;

const page1 = await graphlit.queryContents({
  search: 'meeting notes',
  offset: 0,
  limit: limit
});

// Fetch second page
const page2 = await graphlit.queryContents({
  search: 'meeting notes',
  offset: limit,  // offset = 20
  limit: limit
});

// Continue until results.length < limit
```

## Variations

### 1. Filter by Date Range

Find content created in a specific time period:

```typescript
const lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7);

const response = await graphlit.queryContents({
  creationDateRange: {
    from: lastWeek,
    to: new Date()
  },
  orderBy: OrderByTypes.CreationDate,
  direction: OrderDirectionTypes.Descending
});

console.log(`Content created in last week: ${response.contents.results.length}`);
```

### 2. Filter by Collection

Search within specific collections:

```typescript
// Get collection ID first
const collections = await graphlit.queryCollections({ name: 'Product Docs' });
const collectionId = collections.collections.results[0]?.id;

if (collectionId) {
  const response = await graphlit.queryContents({
    collections: [{ id: collectionId }],
    search: 'API reference',
    searchType: SearchTypes.Hybrid
  });
  
  console.log(`Found ${response.contents.results.length} results in Product Docs`);
}
```

### 3. Filter by Multiple Content Types

Search across specific content types:

```typescript
const response = await graphlit.queryContents({
  types: [
    ContentTypes.Email,
    ContentTypes.Message,
    ContentTypes.Post
  ],
  search: 'project update',
  searchType: SearchTypes.Hybrid,
  limit: 50
});

console.log(`Found communications about project update: ${response.contents.results.length}`);
```

### 4. Similarity Search

Find content similar to existing content:

```typescript
// First, get a content ID
const originalContent = await graphlit.queryContents({
  search: 'machine learning tutorial',
  limit: 1
});

const contentId = originalContent.contents.results[0]?.id;

if (contentId) {
  // Find similar content
  const similarResults = await graphlit.queryContents({
    similarContents: [{ id: contentId }],
    searchType: SearchTypes.Vector,  // Must use Vector or Hybrid
    limit: 10
  });
  
  console.log(`Found ${similarResults.contents.results.length} similar documents`);
}
```

### 5. Filter by State

Find content in specific processing states:

```typescript
// Find content that failed processing
const errorContent = await graphlit.queryContents({
  states: [EntityState.Errored],
  orderBy: OrderByTypes.CreationDate,
  direction: OrderDirectionTypes.Descending
});

console.log(`Content with errors: ${errorContent.contents.results.length}`);

// Find content still processing
const processingContent = await graphlit.queryContents({
  states: [EntityState.Created]
});

console.log(`Content awaiting processing: ${processingContent.contents.results.length}`);
```

### 6. Filter by Feed Source

Find content from specific feeds:

```typescript
// Get feed ID
const feeds = await graphlit.queryFeeds();
const slackFeedId = feeds.feeds.results.find(f => f.name.includes('Slack'))?.id;

if (slackFeedId) {
  const response = await graphlit.queryContents({
    feeds: [{ id: slackFeedId }],
    creationDateRange: {
      from: new Date('2025-01-01'),
      to: new Date()
    }
  });
  
  console.log(`Slack messages in 2025: ${response.contents.results.length}`);
}
```

### 7. Complex Filter Combination

Combine multiple filters:

```typescript
const response = await graphlit.queryContents({
  // Text search
  search: 'quarterly results',
  searchType: SearchTypes.Hybrid,
  
  // Content type
  types: [ContentTypes.File],
  fileTypes: [FileTypes.Document],
  fileExtensions: ['pdf', 'docx'],
  
  // Date range
  creationDateRange: {
    from: new Date('2024-01-01'),
    to: new Date('2024-12-31')
  },
  
  // Pagination
  offset: 0,
  limit: 50,
  
  // Sorting
  orderBy: OrderByTypes.Relevance
});

console.log(`Found ${response.contents.results.length} relevant documents`);
```

### 8. Filter by Extracted Entities

Find content mentioning specific entities:

```typescript
// Find content mentioning a person
const response = await graphlit.queryContents({
  observations: {
    observable: {
      types: [ObservableTypes.Person],
      states: [EntityState.Enabled]
    },
    name: 'John Smith'
  },
  limit: 100
});

console.log(`Content mentioning John Smith: ${response.contents.results.length}`);
```

## Common Issues

**Issue**: Search returns no results even though content exists\
**Solution**: Check that content has finished processing (`state: Finished`). Embeddings must be generated for vector search.

**Issue**: Relevance scores seem low (< 0.5)\
**Solution**: This is normal. Relevance is relative. Use `HYBRID` search type for better results.

**Issue**: `orderBy: RELEVANCE` but results not sorted by relevance\
**Solution**: Relevance ordering only works when `search` parameter is provided. Without search, use `CREATION_DATE` or `NAME`.

**Issue**: Getting duplicate results across pages\
**Solution**: Ensure consistent `orderBy` and `direction` across pagination requests. Content may shift if new items are added during pagination.

**Issue**: Pagination returns fewer results than `limit`\
**Solution**: You've reached the end of results. This is expected behavior.

## Production Example

**Parallel query with count**:

```typescript
// Query results and total count in parallel
const [response, countResponse] = await Promise.all([
  graphlit.queryContents(filter),
  graphlit.countContents({
    ...filter,
    offset: 0,
    limit: 1000000  // Count all matching
  })
]);

const results = response.contents?.results || [];
const totalCount = countResponse.countContents?.count || undefined;
```
