# Resource Matching Guide

**Last Updated:** 2026-01-09

Complete guide to how blog posts match to templates, downloads, and tools in the unified resource carousel system.

## Overview

The blog carousel uses intelligent matching to recommend relevant templates, downloads, and tools alongside blog posts. This guide explains the matching logic, scoring system, and how to add new resource types.

## Matching Architecture

### Data Flow

```
Blog Post
    ↓
Extract Cluster & Topics
    ↓
Cluster-to-Category Mapping
    ↓
Match Against Resources
    ├─→ Templates (category matching)
    ├─→ Downloads (tag/type matching)
    └─→ Tools (filter_category matching)
    ↓
Calculate Relevance Scores
    ↓
Sort & Rank
    ↓
Unified Resource List
```

## Cluster-to-Category Mapping

The `get_cluster_to_category_mapping()` function maps blog post clusters to resource categories:

### Dienstplan Cluster

**Maps to:**

- Templates: `shift_planning`
- Tools: `Schichtplanung`, `Dienstplanung`
- Downloads: Tags containing `schichtplan`, `dienstplan`, `planung`
- Keywords: `dienstplan`, `schichtplan`, `schichtplanung`, `dienstplanung`, `wochenplan`, `arbeitsplan`

**Example Matches:**

- Template: "Dienstplan Excel Vorlage" (category: `shift_planning`)
- Tool: "ROI-Rechner Schichtplanung" (filter_category: `Schichtplanung`)
- Download: "In 8 Schritten digital" (tags: `Ratgeber`, `PDF`)

### Zeiterfassung Cluster

**Maps to:**

- Templates: `time_tracking`
- Tools: `Zeiterfassung`, `Arbeitszeiterfassung`
- Downloads: Tags containing `zeiterfassung`, `arbeitszeiterfassung`, `stundenzettel`
- Keywords: `zeiterfassung`, `arbeitszeiterfassung`, `stundenzettel`, `stundennachweis`, `arbeitszeit`

**Example Matches:**

- Template: "Stundenzettel Excel Vorlage" (category: `time_tracking`)
- Tool: "Arbeitszeitrechner" (filter_category: `Zeiterfassung`)
- Download: "Zeiterfassung 2025: Pflichten & Lösungen" (tags: `Ratgeber`, `PDF`)

### Personalverwaltung Cluster

**Maps to:**

- Templates: `employee_management`, `recruitment`
- Tools: `Personalverwaltung`, `HR-Software`, `Mitarbeiterverwaltung`
- Downloads: Tags containing `personalverwaltung`, `mitarbeiterverwaltung`, `hr`, `onboarding`
- Keywords: `personalverwaltung`, `mitarbeiterverwaltung`, `personal`, `mitarbeiter`, `hr`, `personalmanagement`

**Example Matches:**

- Template: "Personalfragebogen Vorlage" (category: `employee_management`)
- Tool: "ShiftOps" (filter_category: `Personalverwaltung`)
- Download: "Onboarding-Checkliste für Schichtbetriebe" (tags: `Checkliste`, `PDF`)

### Compliance Cluster

**Maps to:**

- Templates: `compliance`
- Tools: `Compliance & Recht`, `Recht & Compliance`
- Downloads: Tags containing `compliance`, `recht`, `gesetz`, `vorschrift`, `checkliste`
- Keywords: `compliance`, `arbeitsrecht`, `gesetzliche`, `rechtlichen`, `pflichten`, `vorschriften`, `gesetze`

**Example Matches:**

- Template: "Compliance Checkliste" (category: `compliance`)
- Tool: "Arbeitszeitrechner" (filter_category: `Compliance & Recht`)
- Download: "Optimal vorbereitet: SV-Prüfung" (tags: `Checkliste`, `PDF`)

### Lohnabrechnung Cluster

**Maps to:**

- Templates: `payroll`
- Tools: `Lohnabrechnung`, `Payroll`, `Gehaltsrechner`
- Downloads: Tags containing `lohnabrechnung`, `payroll`, `gehalt`, `lohn`
- Keywords: `lohnabrechnung`, `payroll`, `gehalt`, `lohn`, `brutto`, `netto`

**Example Matches:**

- Template: "Lohnabrechnung Vorlage" (category: `payroll`)
- Tool: "Brutto-Netto-Rechner" (filter_category: `Lohnberechnung`)
- Download: "Lohnabrechnungen nach Kalendermonaten" (tags: `Ratgeber`, `PDF`)

### Midijob-Minijob Cluster

**Maps to:**

- Templates: `payroll`
- Tools: `Lohnberechnung`, `Gehaltsrechner`, `Sozialversicherung`
- Downloads: Tags containing `lohnabrechnung`, `payroll`, `midijob`, `minijob`
- Keywords: `midijob`, `minijob`, `gleitzone`, `übergangsbereich`, `sozialversicherung`, `lohnabrechnung`

**Example Matches:**

- Template: "Lohnabrechnung Vorlage" (category: `payroll`)
- Tool: "Midijob-Rechner" (filter_category: `Lohnberechnung`, `Gehaltsrechner`, `Sozialversicherung`)
- Tool: "Minijob-Rechner" (filter_category: `Lohnberechnung`, `Gehaltsrechner`, `Sozialversicherung`)
- Download: "Lohnabrechnungen nach Kalendermonaten" (tags: `Ratgeber`, `PDF`)

## Relevance Scoring

The `calculate_resource_relevance()` function calculates a score from 0-100 based on multiple factors:

### Scoring Weights

1. **Cluster Match** (10 points)

   - Highest weight for direct cluster-to-category matches
   - Template: Category matches cluster mapping
   - Tool: Filter category matches cluster mapping
   - Download: Tags or type match cluster keywords

2. **Topic/Keyword Match** (5 points per match)

   - Medium weight for topic/keyword matches
   - Matches blog post topics against resource keywords
   - Checks resource name and description for topic keywords

3. **Tag Match** (3 points per match)

   - Medium weight for tag matches
   - Matches cluster keywords against resource tags
   - Checks resource description for cluster keywords

4. **Description Keyword Match** (1 point per match)
   - Low weight for general keyword matches
   - Extracts keywords from blog post title/description
   - Matches against resource text (name, description, tags)

### Score Calculation Example

**Blog Post:** "Employer Branding" (cluster: `tools`, topics: `personalverwaltung`)

**Template:** "Dienstplan Excel Vorlage" (category: `shift_planning`, tags: `dienstplan`, `arbeitsplanung`)

**Scoring:**

- Cluster match: 0 points (cluster `tools` doesn't map to `shift_planning`)
- Topic match: 0 points (no topic matches)
- Tag match: 0 points (no tag matches)
- Description match: 1 point (keyword "planung" found)

**Total Score:** 1 point (below minimum threshold of 5)

**Template:** "Personalfragebogen Vorlage" (category: `employee_management`, tags: `personal`, `mitarbeiter`)

**Scoring:**

- Cluster match: 0 points (cluster `tools` doesn't map to `employee_management`)
- Topic match: 5 points (`personalverwaltung` matches tag `personal`)
- Tag match: 3 points (`personalverwaltung` keyword matches tag `personal`)
- Description match: 1 point (keyword "personal" found)

**Total Score:** 9 points (above minimum threshold)

## Resource Loading Functions

### Load Templates

```php
$templates = load_templates_for_blog_post($post_cluster, $post_topics, $limit = 5);
```

**Process:**

1. Load templates from `templates_index_data.php`
2. Filter by `status === 'available'`
3. Calculate relevance score for each template
4. Filter by `score > 0`
5. Sort by score (descending)
6. Return top N templates

**Returns:** Array of template resources with unified structure

### Load Downloads

```php
$downloads = load_downloads_for_blog_post($post_cluster, $post_topics, $limit = 5);
```

**Process:**

1. Load downloads from `downloads_index_data.php`
2. Filter by `status === 'available'`
3. Calculate relevance score for each download
4. Filter by `score > 0`
5. Sort by score (descending)
6. Return top N downloads

**Returns:** Array of download resources with unified structure

### Load Tools

```php
$tools = load_tools_for_blog_post($post_cluster, $post_topics, $limit = 5);
```

**Process:**

1. Load tools from `tools_index_data.php`
2. Filter by `status === 'available'`
3. Calculate relevance score for each tool
4. Filter by `score > 0`
5. Sort by score (descending)
6. Return top N tools

**Returns:** Array of tool resources with unified structure

## Unified Resource Structure

All resources return a unified structure:

```php
[
    'type' => 'blog'|'template'|'download'|'tool',
    'title' => string,
    'url' => string,
    'description' => string,
    'image' => array|null,
    'badge' => string,
    'relevance_score' => float,
    // Type-specific fields preserved
]
```

### Type-Specific Fields

**Blog Posts:**

- `category` - Category slug
- `publication_date` - ISO 8601 date
- `slug` - Post slug
- `post` - Full post data array

**Templates:**

- `category` - Template category
- `tags` - Template tags
- `slug` - Template slug

**Downloads:**

- `tags` - Download tags
- `download_type` - Download type (ratgeber, checkliste, etc.)
- `slug` - Download slug

**Tools:**

- `tags` - Tool tags
- `filter_categories` - Filter categories
- `icon` - SVG icon path
- `slug` - Tool slug

## Adding New Resource Types

To add a new resource type:

1. **Define Resource Type Constant**

   ```php
   define('RESOURCE_TYPE_NEWTYPE', 'newtype');
   ```

2. **Add to Cluster Mapping**

   ```php
   'cluster_name' => [
       'newtypes' => ['category1', 'category2'],
       'keywords' => ['keyword1', 'keyword2']
   ]
   ```

3. **Create Loading Function**

   ```php
   function load_newtypes_for_blog_post($post_cluster, $post_topics, $limit = 5) {
       // Load data
       // Calculate scores
       // Return unified structure
   }
   ```

4. **Update Relevance Scoring**

   ```php
   elseif ($resource_type === RESOURCE_TYPE_NEWTYPE) {
       // Add scoring logic
   }
   ```

5. **Update Unified Loader**

   ```php
   $newtypes = load_newtypes_for_blog_post($primary_cluster, $post_topics, $other_limit);
   foreach ($newtypes as $newtype) {
       if ($newtype['relevance_score'] >= $min_relevance) {
           $unified_resources[] = $newtype;
       }
   }
   ```

6. **Update Badge Mapping**

   ```php
   elseif ($resource_type === RESOURCE_TYPE_NEWTYPE) {
       return [
           'text' => 'NewType',
           'color_class' => 'bg-color-100 text-color-800'
       ];
   }
   ```

7. **Update ResourceCard Component**
   - Add rendering logic for new type
   - Add badge display
   - Add image/icon handling

## Best Practices

### Relevance Thresholds

- **Minimum Score:** 5 points (configurable via `min_relevance` option)
- **High Relevance:** 20+ points (strong cluster + topic matches)
- **Medium Relevance:** 10-19 points (cluster or multiple topic matches)
- **Low Relevance:** 5-9 points (keyword matches only)

### Performance

- All loading functions use static caching
- Cache keys include cluster, topics, and limit
- Cache persists for request lifetime
- Consider implementing persistent cache for production

### Testing

Use `scripts/blog/test-unified-resources.php` to test:

- Resource type identification
- Relevance scoring accuracy
- Sorting correctness
- Edge cases (no matches, invalid slugs)

## Troubleshooting

### No Resources Found

**Possible Causes:**

- Blog post has no cluster assigned
- No resources match cluster/topics
- Relevance scores below minimum threshold
- Resource data files not loaded correctly

**Solutions:**

- Check cluster mapping in `blog-cluster-mapping.json`
- Verify resource data files exist and are valid
- Lower `min_relevance` threshold
- Check cluster-to-category mapping completeness

### Incorrect Matches

**Possible Causes:**

- Cluster mapping incomplete
- Relevance scoring weights incorrect
- Resource metadata incomplete

**Solutions:**

- Review cluster-to-category mapping
- Adjust scoring weights in `calculate_resource_relevance()`
- Verify resource tags/categories are accurate

### Performance Issues

**Possible Causes:**

- No caching implemented
- Loading all resources before filtering
- N+1 query pattern

**Solutions:**

- Verify static caching is working
- Filter resources before scoring
- Batch load resource data

## Related Documentation

- [Related Posts Logic](RELATED_POSTS_LOGIC.md) - Complete algorithm documentation
- [Component API](COMPONENT_API.md) - ResourceCard component documentation
- [Blog Cluster Mapping](../data/blog-cluster-mapping.json) - Cluster data
- [Blog Topics](../data/blog-topics-extracted.json) - Topic data
