Skip to content
Back to skills

003 Name Skill 541f68b0

DSecurity

Expert guidance on Apache Solr search integration for TYPO3: installation, Index Queue, faceting, suggest, PSR-14 events, custom indexers, LLM/vector search (Solr native), DDEV/Docker/production setup, deep debugging & troubleshooting, and file indexing via solrfal. Use when working with solr, search, indexing, facets, suggest, autocomplete, vector search, solrfal, tika.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
devopspythonrustgojavaphpbashsqldockerterraformtesting

Works with

  • terminal
  • cli
  • api

Security analysis

D59/100
  • mediumUses curl or wget to download content
  • criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study
  • criticalDownloads and executes remote scripts — classic supply chain attack

Pro shows the line behind each finding and how to fix it

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 003-name-skill_541f68b0 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 003 Name Skill 541f68b0?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 003 Name Skill 541f68b0
[![Security: D — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-003-name-skill-541f68b0/badge)](https://www.skillsdirectory.com/skills/tools-only-003-name-skill-541f68b0)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
name: typo3-solr
description: >-
  Expert guidance on Apache Solr search integration for TYPO3: installation,
  Index Queue, faceting, suggest, PSR-14 events, custom indexers, LLM/vector
  search (Solr native), DDEV/Docker/production setup, deep debugging &
  troubleshooting, and file indexing via solrfal. Use when working with solr,
  search, indexing, facets, suggest, autocomplete, vector search, solrfal, tika.
compatibility: TYPO3 13.4 - 14.x
metadata:
  version: "1.0.0"
  related_skills:
    - typo3-content-blocks
    - typo3-datahandler
    - typo3-seo
    - ai-search-optimization
---

# Apache Solr for TYPO3

> **Compatibility:** TYPO3 v13.4 LTS (current) and v14.x (when EXT:solr 14.0 releases).
> EXT:solr 13.1 targets TYPO3 13.4. A `task/14LTS_compatibility` dev branch is actively being developed.
> All custom PHP code in this skill uses TYPO3 v14 conventions (PHP 8.2+, `#[AsEventListener]`, constructor promotion).

> **TYPO3 API First:** Always use TYPO3's built-in APIs and EXT:solr's TypoScript/PSR-14 events before creating custom implementations. Do not reinvent what EXT:solr already provides.

## Sources

This skill is based on the following authoritative sources:

1. [EXT:solr Documentation](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/)
2. [EXT:tika Documentation](https://docs.typo3.org/p/apache-solr-for-typo3/tika/main/en-us/)
3. [Version Matrix](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Appendix/VersionMatrix.html)
4. [GitHub: TYPO3-Solr/ext-solr](https://github.com/TYPO3-Solr/ext-solr)
5. [GitHub: v14 dev branch](https://github.com/TYPO3-Solr/ext-solr/tree/task/14LTS_compatibility)
6. [Apache Solr Reference Guide](https://solr.apache.org/guide/solr/latest/)
7. [Solr Dense Vector Search](https://solr.apache.org/guide/solr/latest/query-guide/dense-vector-search.html)
8. [Solr Text to Vector (LLM)](https://solr.apache.org/guide/solr/latest/query-guide/text-to-vector.html)
9. [typo3-solr.com](https://www.typo3-solr.com/)
10. [hosted-solr.com](https://hosted-solr.com/en/)
11. [ddev-typo3-solr](https://github.com/ddev/ddev-typo3-solr)
12. [Mittwald Solr Docs](https://developer.mittwald.de/docs/v2/platform/databases/solr/)
13. [helhum/dotenv-connector](https://github.com/helhum/dotenv-connector)

## 1. Architecture Overview

EXT:solr connects TYPO3 CMS to an Apache Solr search server, providing full-text search, faceted navigation, autocomplete, and (since Solr 9.8) vector/semantic search.

```mermaid
graph LR
    subgraph typo3 [TYPO3 CMS]
        Editor[Editor creates/edits content]
        Monitor[Monitoring detects changes]
        Queue[Index Queue]
        Scheduler[Scheduler Worker]
        Plugin[Search Plugin]
    end
    subgraph solr [Apache Solr Server]
        Core[Solr Core]
        Schema[Schema / Configset]
    end
    Editor --> Monitor
    Monitor --> Queue
    Queue --> Scheduler
    Scheduler -->|HTTP POST documents| Core
    Plugin -->|HTTP GET query| Core
    Core --> Plugin
```

### Document Lifecycle

```mermaid
sequenceDiagram
    participant E as Editor
    participant T as TYPO3
    participant M as Monitor
    participant Q as Index Queue
    participant S as Scheduler
    participant Solr as Solr Server

    E->>T: Create/edit record
    T->>M: DataHandler triggers PSR-14 event
    M->>Q: Add/update queue item
    Note over Q: tx_solr_indexqueue_item
    S->>Q: Poll for pending items
    Q->>S: Return items to index
    S->>Solr: POST document (JSON)
    Solr-->>S: 200 OK
    S->>Q: Mark item as indexed
```

### Component Overview

| Component | Package | Purpose | Required? |
|-----------|---------|---------|-----------|
| **EXT:solr** | `apache-solr-for-typo3/solr` | Core search integration | Yes |
| **EXT:tika** | `apache-solr-for-typo3/tika` | Text/metadata extraction from files | Only for file indexing |
| **EXT:solrfal** | Funding extension | FAL file indexing into Solr | Only for file indexing |
| **EXT:solrconsole** | Funding extension | Backend management console | Optional |
| **EXT:solrdebugtools** | Funding extension | Query debugging, score analysis | Optional (recommended for dev) |

<!-- SCREENSHOT: backend-module-overview.png - EXT:solr backend module main view -->

## 2. Version Compatibility Matrix

| EXT:solr | TYPO3 | Apache Solr | Configset | PHP | EXT:tika | EXT:solrfal |
|----------|-------|-------------|-----------|-----|----------|-------------|
| **13.1** | **13.4** | **9.10.1** | `ext_solr_13_1_0` | 8.2 - 8.4 | 13.1 | 13.0 |
| 12.1 | 12.4 | 9.10.1 | `ext_solr_12_1_0` | 8.1 - 8.3 | 12.1 | 12.0 |

### TYPO3 v14 Readiness

EXT:solr does **not** yet have a stable v14 release. Active development happens on the [`task/14LTS_compatibility`](https://github.com/TYPO3-Solr/ext-solr/tree/task/14LTS_compatibility) branch (last updated Feb 17, 2026).

**Key v14 changes in the dev branch:**
- Fluid v5 ViewHelper compatibility
- TSFE removal: `FrontendEnvironment/Tsfe` refactored to `FrontendSimulation/FrontendAwareEnvironment`
- `ext_emconf.php` removed (Composer-only)
- TCA `searchFields` deprecation handled
- FlexForm registration via `registerPlugin()` instead of `addPiFlexFormValue()`
- New configset: `ext_solr_14_0_0`

**Testing with the dev branch:**

```bash
composer require apache-solr-for-typo3/solr:dev-task/14LTS_compatibility
```

> **Warning:** This branch is WIP. Do not use in production.

### CVE-2025-24814 Migration

Apache Solr 9.8.0+ disables loading `jar` files via `lib` directive in configsets. The `solr-typo3-plugin` must be moved from `/configsets/ext_solr_*/typo3lib/` to `/typo3lib/` at the Solr server root. Docker users: pull image v13.0.1+ -- the migration runs automatically.

## 3. Installation & Setup

### Composer

```bash
composer require apache-solr-for-typo3/solr
```

### DDEV Setup

The recommended local development setup uses the [ddev-typo3-solr](https://github.com/ddev/ddev-typo3-solr) addon:

```bash
ddev add-on get ddev/ddev-typo3-solr
ddev restart
```

Configure `.ddev/typo3-solr/config.yaml`:

```yaml
config: 'vendor/apache-solr-for-typo3/solr/Resources/Private/Solr/solr.xml'
typo3lib: 'vendor/apache-solr-for-typo3/solr/Resources/Private/Solr/typo3lib'
configsets:
  - name: 'ext_solr_13_1_0'
    path: 'vendor/apache-solr-for-typo3/solr/Resources/Private/Solr/configsets/ext_solr_13_1_0'
cores:
  - name: 'core_en'
    schema: 'english/schema.xml'
  - name: 'core_de'
    schema: 'german/schema.xml'
```

Auto-initialize cores on boot in `.ddev/config.yaml`:

```yaml
hooks:
  post-start:
    - exec-host: ddev solrctl apply
```

**Useful DDEV commands:**

| Command | Description |
|---------|-------------|
| `ddev solrctl apply` | Create cores from config |
| `ddev solrctl wipe` | Delete all cores |
| `ddev solr version` | Check Solr version |
| `ddev launch :8984` | Open Solr Admin UI |
| `ddev logs -s typo3-solr` | View Solr logs |

<!-- SCREENSHOT: ddev-solr-admin.png - DDEV Solr Admin at :8984 -->

### Docker (Production)

```yaml
services:
  solr:
    image: typo3solr/ext-solr:13.1
    ports:
      - "8983:8983"
    volumes:
      - solr-data:/var/solr
    restart: unless-stopped

volumes:
  solr-data:
    driver: local
```

The image ships default cores for all languages. Persistent data is stored at `/var/solr` (owned by UID 8983).

### Standalone Solr

Deploy the configset from EXT:solr into your Solr installation:

```bash
cp -r vendor/apache-solr-for-typo3/solr/Resources/Private/Solr/* $SOLR_INSTALL_DIR/server/solr/
```

Create cores via `core.properties` files or REST API:

```bash
curl -X POST http://localhost:8983/api/cores -H 'Content-Type: application/json' -d '{
  "create": {
    "name": "core_en",
    "configSet": "ext_solr_13_1_0",
    "schema": "english/schema.xml",
    "instanceDir": "cores/core_en",
    "dataDir": "/var/solr/data/core_en"
  }
}'
```

### Managed Hosting: Mittwald

Mittwald provides a managed Solr service via their container platform. Use the Terraform module:

```hcl
module "solr" {
  source         = "mittwald/solr/mittwald"
  solr_version   = "9"
  solr_core_name = "typo3"
  solr_heap      = "2g"
}
```

Access Solr at `http://solr:8983` inside the container. For local debugging:

```bash
mw container port-forward --port 8983
```

### Managed Hosting: hosted-solr.com

[hosted-solr.com](https://hosted-solr.com/en/) by dkd provides pre-configured Solr cores optimized for EXT:solr. Plans start at EUR 10/month (2 indexes, 4000 documents). After creating a core, configure it in your TYPO3 site config using the provided host, port, and path.

### TYPO3 Site Configuration

In `config/sites/<identifier>/config.yaml`:

```yaml
solr_enabled_read: true
solr_host_read: solr
solr_port_read: '8983'
solr_scheme_read: http
solr_path_read: /
solr_core_read: core_en
```

For DDEV, use the DDEV hostname and HTTPS port:

```yaml
solr_host_read: <project>.ddev.site
solr_port_read: '8984'
solr_scheme_read: https
```

<!-- SCREENSHOT: reports-module-solr.png - TYPO3 Reports module Solr status -->

### Environment Configuration (helhum/dotenv-connector)

Never hardcode Solr connection details. Use [helhum/dotenv-connector](https://github.com/helhum/dotenv-connector) for per-environment configuration:

```bash
composer require helhum/dotenv-connector
```

`.env` (gitignored):

```env
SOLR_HOST=solr
SOLR_PORT=8983
SOLR_SCHEME=http
SOLR_PATH=/
SOLR_CORE_EN=core_en
SOLR_CORE_DE=core_de
```

`.env.example` (committed to VCS):

```env
SOLR_HOST=solr
SOLR_PORT=8983
SOLR_SCHEME=http
SOLR_PATH=/
SOLR_CORE_EN=core_en
SOLR_CORE_DE=core_de
```

In `config/system/additional.php`, override site config values programmatically or use a post-processing approach. For simple setups, keep `.env` values and reference them in deployment scripts that generate site config YAML per environment.

## 4. EXT:tika -- When You Need It (and When Not)

EXT:tika integrates Apache Tika for metadata extraction, language detection, and text extraction from ~1200 file formats.

```mermaid
graph TD
    Question{"Do you index files?<br/>(PDF, DOCX, XLSX)"}
    Question -->|Yes| NeedTika[Install EXT:tika + EXT:solrfal]
    Question -->|No| SkipTika[Skip -- EXT:solr handles<br/>pages and records natively]
    NeedTika --> TikaServer["Run Tika Server 3.2.2+<br/>(Docker recommended)"]
```

**Three backends (choose one):**

| Backend | Recommended? | Setup |
|---------|-------------|-------|
| **Tika Server** | Yes | Standalone Docker container, newest Tika version |
| **Solr Cell** | Acceptable | Uses Tika built into Solr, no extra service needed |
| **Tika App** | Deprecated | Requires Java on webserver, do not use |

**When you NEED EXT:tika:**
- File indexing via EXT:solrfal (search inside PDFs, Word documents, etc.)
- Automatic FAL metadata enrichment (EXIF, XMP, document properties)
- Language detection on uploaded files

**When you DON'T need it:**
- Only indexing pages and structured records (news, events, products) via Index Queue
- EXT:solr handles page content and record fields natively without Tika

**Version:** EXT:tika 13.1. Requires Tika Server **3.2.2+** (CVE-2025-54988 fix).

See [SKILL-SOLRFAL.md](SKILL-SOLRFAL.md) for complete file indexing setup.

## 5. Configset & Schema

The configset defines how Solr processes and indexes text. EXT:solr ships configsets at `Resources/Private/Solr/configsets/ext_solr_13_1_0/`.

### Structure

```
ext_solr_13_1_0/
├── conf/
│   ├── solrconfig.xml
│   ├── english/
│   │   └── schema.xml
│   ├── german/
│   │   └── schema.xml
│   └── ... (other languages)
└── typo3lib/             # moved to server root since Solr 9.8
```

Each language has its own `schema.xml` with language-specific analyzers (stemmer, stop words).

### Dynamic Field Types

Use dynamic field suffixes to add custom fields without modifying the schema:

| Suffix | Type | Multi | Example |
|--------|------|-------|---------|
| `_stringS` | string (not analyzed) | No | `category_stringS` |
| `_stringM` | string (not analyzed) | Yes | `tags_stringM` |
| `_textS` | text (analyzed) | No | `description_textS` |
| `_textM` | text (analyzed) | Yes | `keywords_textM` |
| `_intS` | integer | No | `year_intS` |
| `_floatS` | float | No | `price_floatS` |
| `_dateS` | date | No | `published_dateS` |
| `_boolS` | boolean | No | `active_boolS` |

Full reference: [Dynamic Fields Appendix](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Appendix/DynamicFieldTypes.html)

### Site Hash Strategy

Since EXT:solr 13.0, the default site hash strategy changed from **domain-based** (deprecated) to **site-identifier-based**. Configure in Extension Settings:

- `siteHashStrategy = 1` (site-identifier, **default and recommended**)
- `siteHashStrategy = 0` (domain, deprecated, removed in 13.1.x+)

If upgrading from 12.x, you must re-index after switching strategies.

## 6. Index Queue Configuration

The Index Queue is the central mechanism for getting TYPO3 records into Solr.

```mermaid
graph LR
    subgraph monitoring [Record Monitoring]
        Edit[Record created/edited]
        Event[PSR-14 DataUpdate Event]
    end
    subgraph queue [Index Queue]
        Item["tx_solr_indexqueue_item"]
    end
    subgraph processing [Processing]
        Worker[Scheduler: Index Queue Worker]
        Solr[Solr Server]
    end
    Edit --> Event
    Event --> Item
    Worker -->|polls| Item
    Worker -->|POST document| Solr
```

### Pages

Pages are indexed out of the box. No additional configuration needed. The page indexer sends the rendered page content to Solr.

### Custom Records

Index any TYPO3 record table via TypoScript. Full example for EXT:news:

```typoscript
plugin.tx_solr.index.queue {
    news = 1
    news {
        type = tx_news_domain_model_news

        fields {
            abstract = teaser
            author = author
            authorEmail_stringS = author_email
            title = title

            content = SOLR_CONTENT
            content {
                cObject = COA
                cObject {
                    10 = TEXT
                    10 {
                        field = bodytext
                        noTrimWrap = || |
                    }
                }
            }

            category_stringM = SOLR_RELATION
            category_stringM {
                localField = categories
                multiValue = 1
            }

            keywords = SOLR_MULTIVALUE
            keywords {
                field = keywords
            }

            tags_stringM = SOLR_RELATION
            tags_stringM {
                localField = tags
                multiValue = 1
            }

            url = TEXT
            url {
                typolink.parameter = {$plugin.tx_news.settings.detailPid}
                typolink.additionalParams = &tx_news_pi1[controller]=News&tx_news_pi1[action]=detail&tx_news_pi1[news]={field:uid}
                typolink.additionalParams.insertData = 1
                typolink.returnLast = url
            }
        }

        attachments {
            fields = related_files
        }
    }
}
```

### Content Objects

| Object | Purpose |
|--------|---------|
| `SOLR_CONTENT` | Strips HTML/RTE from field content |
| `SOLR_RELATION` | Resolves relations (categories, tags, etc.), supports `multiValue = 1` |
| `SOLR_MULTIVALUE` | Splits a comma-separated field into multiple values |

### Records Outside Siteroot

To index records stored in sysfolders outside the site tree:

```typoscript
plugin.tx_solr.index.queue.news {
    additionalPageIds = 45,48
}
```

Enable monitoring in Extension Settings: "Enable tracking of records outside siteroot".

### Monitoring

EXT:solr detects record changes via PSR-14 events. Two modes:

- **Immediate** (default): changes are processed directly during the DataHandler operation
- **Delayed**: changes are queued in `tx_solr_eventqueue_item` and processed by a scheduler task ("Event Queue Worker")

Configure via Extension Settings: `monitoringType`.

<!-- SCREENSHOT: index-queue-tab.png - Backend module Index Queue tab -->

## 7. Search Configuration

### Essential Settings

```typoscript
plugin.tx_solr {
    enabled = 1
    search {
        targetPage = 42
        initializeWithEmptyQuery = 1
        showResultsOfInitialEmptyQuery = 0
        trustedFields = url
        keepExistingParametersForNewSearches = 1
    }
}
```

### Faceting

```typoscript
plugin.tx_solr.search {
    faceting = 1
    faceting {
        facets {
            contentType {
                label = Content Type
                field = type
            }
            category {
                label = Category
                field = category_stringM
            }
            year {
                label = Year
                field = year_intS
                type = queryGroup
                queryGroup {
                    2026 {
                        query = [2026-01-01T00:00:00Z TO 2026-12-31T23:59:59Z]
                    }
                    2025 {
                        query = [2025-01-01T00:00:00Z TO 2025-12-31T23:59:59Z]
                    }
                    2024 {
                        query = [2024-01-01T00:00:00Z TO 2024-12-31T23:59:59Z]
                    }
                }
            }
        }
    }
}
```

**Facet types:** `options` (default), `queryGroup`, `hierarchy`, `dateRange`, `numericRange`.

### Suggest / Autocomplete

```typoscript
plugin.tx_solr {
    suggest = 1
    suggest {
        numberOfSuggestions = 10
        suggestField = spell
        showTopResults = 1
        numberOfTopResults = 5
        additionalTopResultsFields = url,type
    }
}
```

The built-in suggest uses the devbridge/jQuery-Autocomplete library. For a jQuery-free approach, see [SKILL-FRONTEND.md](SKILL-FRONTEND.md).

### Sorting, Highlighting, Spellcheck

```typoscript
plugin.tx_solr.search {
    sorting = 1
    sorting {
        defaultOrder = asc
        options {
            relevance {
                field = relevance
                label = Relevance
            }
            title {
                field = sortTitle
                label = Title
            }
            created {
                field = created
                label = Date
            }
        }
    }

    results {
        resultsHighlighting = 1
        resultsHighlighting {
            fragmentSize = 200
            wrap = <mark>|</mark>
        }
    }

    spellchecking = 1
}
```

### Route Enhancers

SEO-friendly search URLs:

```yaml
routeEnhancers:
  SolrSearch:
    type: SolrFacetMaskAndCombineEnhancer
    extensionKey: solr
    solr:
      type: Extbase
      extension: Solr
      plugin: pi_results
      routes:
        - routePath: '/search/{q}'
          _controller: 'Search::results'
          _arguments:
            q: q
```

<!-- SCREENSHOT: frontend-search-facets.png - Search results with facets -->

## 8. Fluid Templates & Frontend

### Template Paths

```typoscript
plugin.tx_solr {
    view {
        templateRootPaths.10 = EXT:my_ext/Resources/Private/Templates/Solr/
        partialRootPaths.10 = EXT:my_ext/Resources/Private/Partials/Solr/
        layoutRootPaths.10 = EXT:my_ext/Resources/Private/Layouts/Solr/
    }
}
```

### Key Templates

| Template | Purpose |
|----------|---------|
| `Templates/Search/Results.html` | Main search results page |
| `Templates/Search/Form.html` | Search form |
| `Partials/Result/Document.html` | Single result item |
| `Partials/Facets/OptionsFacet.html` | Options facet rendering |
| `Partials/Facets/QueryGroupFacet.html` | Query group facet |

Copy default templates from EXT:solr to your extension path and modify them.

## 9. PSR-14 Events Reference

### Monitoring Events

| Event | Fired when |
|-------|-----------|
| `VersionSwappedEvent` | A version is swapped (workspace publish) |
| `RecordMovedEvent` | A record is moved |
| `RecordGarbageCheckEvent` | A garbage check is triggered |
| `RecordDeletedEvent` | A record is deleted |
| `PageMovedEvent` | A page is moved |
| `ContentElementDeletedEvent` | A content element is deleted |

### Data Update Processing Events

| Event | Fired when |
|-------|-----------|
| `ProcessingFinishedEvent` | Data update processing is complete |
| `DelayedProcessingQueuingFinishedEvent` | Update queued in event queue (delayed mode) |
| `DelayedProcessingFinishedEvent` | Delayed processing complete (scheduler) |

### Indexing Events

| Event | Fired when |
|-------|-----------|
| `BeforePageDocumentIsProcessedForIndexingEvent` | Before page document is processed (add extra documents) |
| `AfterPageDocumentIsCreatedForIndexingEvent` | After page document created (replace/substitute) |
| `BeforeDocumentIsProcessedForIndexingEvent` | Before non-page record document is processed |
| `BeforeDocumentsAreIndexedEvent` | Before documents are sent to Solr (add custom fields) |

### Other Events

| Event | Fired when |
|-------|-----------|
| `AfterFacetIsParsedEvent` | Facet component modification |
| `AfterUriIsProcessedEvent` | URI building in search context |
| `AfterSiteHashHasBeenDeterminedForSiteEvent` | Override calculated site hash |

### Example: Add Custom Fields to Documents (v14 Style)

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExt\EventListener;

use ApacheSolrForTypo3\Solr\Event\Indexing\BeforeDocumentsAreIndexedEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'my-ext/enrich-solr-documents')]
final class EnrichSolrDocuments
{
    public function __invoke(BeforeDocumentsAreIndexedEvent $event): void
    {
        foreach ($event->getDocuments() as $document) {
            $document->addField('custom_score_floatS', $this->calculateScore($document));
        }
    }

    private function calculateScore(\ApacheSolrForTypo3\Solr\System\Solr\Document\Document $document): float
    {
        return match ($document->getField('type')['value'] ?? '') {
            'pages' => 1.0,
            'tx_news_domain_model_news' => 0.8,
            default => 0.5,
        };
    }
}
```

**v13 fallback** (if `#[AsEventListener]` is not available): register in `Services.yaml`:

```yaml
MyVendor\MyExt\EventListener\EnrichSolrDocuments:
  tags:
    - name: event.listener
      identifier: 'my-ext/enrich-solr-documents'
```

## 10. Independent Indexer (Custom Data)

For external data or when the RecordIndexer is not sufficient:

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExt\Indexer;

use ApacheSolrForTypo3\Solr\ConnectionManager;
use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository;
use ApacheSolrForTypo3\Solr\System\Solr\Document\Document;

final class ExternalDataIndexer
{
    public function __construct(
        private readonly ConnectionManager $connectionManager,
        private readonly SiteRepository $siteRepository,
    ) {}

    public function index(array $rows, int $rootPageId = 1, int $language = 0): void
    {
        $site = $this->siteRepository->getSiteByRootPageId($rootPageId);
        $connection = $this->connectionManager->getConnectionByPageId($rootPageId, $language);
        $documents = [];

        foreach ($rows as $row) {
            $document = new Document();
            $document->setField('id', 'external_' . $row['uid']);
            $document->setField('variantId', 'external_' . $row['uid']);
            $document->setField('type', 'external_record');
            $document->setField('appKey', 'EXT:solr');
            $document->setField('access', ['r:0']);
            $document->setField('site', $site->getDomain());
            $document->setField('siteHash', $site->getSiteHash());
            $document->setField('uid', $row['uid']);
            $document->setField('pid', $rootPageId);
            $document->setField('title', $row['title']);
            $document->setField('content', $row['content']);
            $document->setField('url', $row['url']);

            $documents[] = $document;
        }

        $connection->getWriteService()->addDocuments($documents);
    }

    public function clearIndex(string $type = 'external_record'): void
    {
        $connections = $this->connectionManager->getAllConnections();
        foreach ($connections as $connection) {
            $connection->getWriteService()->deleteByType($type);
        }
    }
}
```

### CLI Command Wrapper

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExt\Command;

use MyVendor\MyExt\Indexer\ExternalDataIndexer;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(name: 'myext:index-external', description: 'Index external data into Solr')]
final class IndexExternalCommand extends Command
{
    public function __construct(
        private readonly ExternalDataIndexer $indexer,
    ) {
        parent::__construct();
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io = new SymfonyStyle($input, $output);
        $rows = $this->fetchExternalData();

        $io->info(sprintf('Indexing %d records...', count($rows)));
        $this->indexer->index($rows);
        $io->success('Done.');

        return Command::SUCCESS;
    }

    private function fetchExternalData(): array
    {
        // Implement your data fetching logic
        return [];
    }
}
```

## 11. LLM & Vector Search (Solr Server Native)

Apache Solr 9.8+ includes a native LLM module for semantic/vector search. This runs on the **Solr server side** -- EXT:solr queries the results.

```mermaid
graph LR
    subgraph indexing [Index Time]
        Text[Document text]
        URP["TextToVectorUpdateProcessor"]
        API1["Embedding API<br/>(OpenAI, Mistral, etc.)"]
        Vector1[Dense Vector]
        SolrIdx["Solr Index<br/>(DenseVectorField)"]
    end
    subgraph querying [Query Time]
        Query[User query text]
        QP["knn_text_to_vector parser"]
        API2["Embedding API"]
        Vector2[Query Vector]
        KNN["KNN Search"]
        Results[Ranked Results]
    end
    Text --> URP --> API1 --> Vector1 --> SolrIdx
    Query --> QP --> API2 --> Vector2 --> KNN --> Results
    SolrIdx -.->|similarity| KNN
```

### Prerequisites

- Apache Solr **9.8+** with the `llm` module enabled
- An external embedding API account (OpenAI, Mistral AI, Cohere, or HuggingFace)

### Schema: DenseVectorField

Add to your schema (or use a custom configset):

```xml
<fieldType name="knn_vector" class="solr.DenseVectorField"
           vectorDimension="1536"
           similarityFunction="cosine"
           knnAlgorithm="hnsw"/>

<field name="vector" type="knn_vector" indexed="true" stored="true"/>
```

`vectorDimension` must match your embedding model (e.g., OpenAI `text-embedding-3-small` = 1536 dimensions).

### Model Configuration

Register the query parser in `solrconfig.xml`:

```xml
<queryParser name="knn_text_to_vector"
             class="org.apache.solr.llm.textvectorisation.search.TextToVectorQParserPlugin"/>
```

Upload a model definition:

```bash
curl -XPUT 'http://localhost:8983/solr/core_en/schema/text-to-vector-model-store' \
  --data-binary @model.json -H 'Content-type:application/json'
```

`model.json` (OpenAI example):

```json
{
  "class": "dev.langchain4j.model.openai.OpenAiEmbeddingModel",
  "name": "openai-embed",
  "params": {
    "baseUrl": "https://api.openai.com/v1",
    "apiKey": "sk-...",
    "modelName": "text-embedding-3-small",
    "timeout": 60,
    "maxRetries": 3
  }
}
```

Supported providers: OpenAI, Mistral AI, Cohere, HuggingFace. See [LangChain4j Embedding Models](https://docs.langchain4j.dev/category/embedding-models) for all parameters.

### Indexing with Vectors

Add an update processor chain in `solrconfig.xml`:

```xml
<updateRequestProcessorChain name="vectorisation">
  <processor class="solr.llm.textvectorisation.update.processor.TextToVectorUpdateProcessorFactory">
    <str name="inputField">_text_</str>
    <str name="outputField">vector</str>
    <str name="model">openai-embed</str>
  </processor>
  <processor class="solr.RunUpdateProcessorFactory"/>
</updateRequestProcessorChain>
```

**Two-pass strategy** (recommended for production): index documents normally first, then enrich with vectors in a second pass to avoid blocking the indexing pipeline with slow API calls.

### Querying

**Semantic search** (text in, vector out):

```
?q={!knn_text_to_vector model=openai-embed f=vector topK=10}customer complaints handling
```

**Raw vector search:**

```
?q={!knn f=vector topK=10}[0.1, 0.2, 0.3, ...]
```

**Threshold search** (all documents above similarity):

```
?q={!vectorSimilarity f=vector minReturn=0.7}[0.1, 0.2, 0.3, ...]
```

**Hybrid search** (BM25 + vector re-ranking):

```
?q=customer complaints
&rq={!rerank reRankQuery=$rqq reRankDocs=50 reRankWeight=2}
&rqq={!knn_text_to_vector model=openai-embed f=vector topK=50}customer complaints
```

### Pre-Filtering

Combine vector search with traditional filters:

```
?q={!knn_text_to_vector model=openai-embed f=vector topK=10 preFilter=type:pages}search query
```

### EXT:solr Integration

EXT:solr 13.1 has initial vector search support. For custom integration, use a PSR-14 event listener on `BeforeDocumentsAreIndexedEvent` to add vector data to documents via your own embedding service call.

> **Note:** Full native vector search support in EXT:solr (Index Queue -> vector fields, Fluid templates for vector results) is still evolving. Monitor the [GitHub repository](https://github.com/TYPO3-Solr/ext-solr) for updates.

### Use Cases

| Use Case | Approach |
|----------|----------|
| **Smart site search** | `knn_text_to_vector` finds semantically similar content |
| **Similar content** | Given a document's vector, find K nearest neighbors |
| **Multilingual bridge** | Embeddings can match across languages |
| **FAQ matching** | Match user questions to FAQ answers despite different wording |
| **RAG preparation** | Use Solr as retrieval layer for LLM-powered Q&A |
| **File discovery** | Combined with solrfal + Tika: semantic search over documents |

## 12. Debugging & Troubleshooting

This section is the core reference for diagnosing and fixing EXT:solr issues. If you are new to EXT:solr, start with the step-by-step guide below before jumping to the reference sections.

### Step-by-Step Troubleshooting Guide (Beginner)

If search is not working and you have **no idea where to start**, follow these steps in order. Each step explains **what** you are checking, **where** to find it, and **what the result means**.

---

#### Step 1: Understand the Architecture

Before debugging, know how EXT:solr works. There are **four parts** that must all work:

```
┌─────────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  1. Connection   │───▶│  2. Indexing  │───▶│  3. Solr Core │───▶│  4. Frontend  │
│  TYPO3 ↔ Solr    │    │  Queue+Worker │    │  Documents   │    │  Search+Fluid │
└─────────────────┘    └──────────────┘    └──────────────┘    └──────────────┘
```

- **Connection**: TYPO3 must know where Solr is running (host, port, core name)
- **Indexing**: TYPO3 must send your content (pages, news, etc.) to Solr via the Index Queue and a Scheduler task
- **Solr Core**: The Solr server stores documents and makes them searchable
- **Frontend**: The search plugin sends queries to Solr and renders results via Fluid templates

A problem at **any** of these four parts breaks search. The steps below check each part from left to right.

---

#### Step 2: Is Solr Reachable? (Connection Check)

**Where:** TYPO3 Backend → Admin Tools → Reports → Status Report

**What you see:** A list of status checks. Look for entries about "Solr":
- **All green** ✅ = Solr is reachable, connection works → go to Step 3
- **Red/yellow** ⚠️ = Solr is not reachable → fix connection first

**If red -- things to check:**

Open your site configuration file `config/sites/<your-site>/config.yaml` and verify these settings:

```yaml
solr_enabled_read: true
solr_host_read: solr              # hostname where Solr runs
solr_port_read: '8983'            # port (8983 for standalone, 8984 for DDEV)
solr_scheme_read: http            # http or https (DDEV uses https)
solr_path_read: /                 # usually just /
solr_core_read: core_en           # must match the actual core name in Solr
```

**For DDEV users:**

```yaml
solr_host_read: <your-project>.ddev.site
solr_port_read: '8984'
solr_scheme_read: https
```

**Quick test from the terminal:**

```bash
# DDEV: test if Solr responds
ddev exec curl -s http://solr:8983/solr/core_en/admin/ping

# Expected: {"status":"OK"}
# If "Connection refused": Solr container is not running → ddev restart
```

**After fixing:** Always click "Initialize connection" in the EXT:solr backend module (Web → Search → Overview tab).

---

#### Step 3: Is Content in the Index Queue? (Indexing Check)

**Where:** TYPO3 Backend → Web → Search → "Index Queue" tab

**What you see:** A table showing record types (pages, news, etc.) with counts.

| What you see | What it means | What to do |
|-------------|---------------|------------|
| **Table is empty** | No content has been queued for Solr | You need to initialize the queue (see below) |
| **Items with count > 0, errors = 0** | Content is queued and indexed | Go to Step 4 |
| **Items with errors > 0** | Indexing failed for some records | Read the error messages (see below) |

**If the queue is empty:**

1. Check that EXT:solr TypoScript templates are included on the root page:
   - Backend → Web → Template module → root page → "Includes" tab
   - You must see "Search - Base Configuration" in the list
   - If it's missing, add it: click "Include static (from extensions)" and select "Search - Base Configuration (solr)"

2. Initialize the queue:
   - Go to Web → Search → "Index Queue" tab
   - Select the checkboxes for the record types you want to index (e.g., "pages")
   - Click "Queue selected content"
   - The table should now show a count

**If items have errors:**

The `errors` column tells you what went wrong. Common errors:

| Error message | What it means | Fix |
|---------------|---------------|-----|
| "Could not resolve host" | Solr server unreachable | Go back to Step 2 |
| "Document is missing mandatory uniqueKey field: id" | The document has no ID | Check your TypoScript field mapping |
| "OutOfMemoryError" | PHP or Solr ran out of memory | Increase `memory_limit` for the scheduler |
| "HTTP 404" | The Solr core doesn't exist | Check core name, run `ddev solrctl apply` |

---

#### Step 4: Have Documents Arrived in Solr? (Solr Check)

Even if the queue shows no errors, documents might not have been sent to Solr yet. The queue only fills the to-do list -- a **Scheduler task** actually sends them.

**Check if the Scheduler task exists:**
- Backend → System → Scheduler
- Look for a task called "Index Queue Worker" (class: `ApacheSolrForTypo3\Solr\Task\IndexQueueWorkerTask`)
- If it doesn't exist: create it (Add task → select "Index Queue Worker" → save)
- If it exists: check "Last execution" -- has it run recently?

**Run it manually now:**
- Click the "play" button next to the Index Queue Worker task

**Verify documents are in Solr:**

Open the Solr Admin UI:
- DDEV: run `ddev launch :8984` (opens in browser)
- Production: navigate to `http://your-solr-host:8983/solr/`

In Solr Admin UI:
1. Select your core from the dropdown (e.g., `core_en`)
2. Click "Query" in the left menu
3. Leave `q` as `*:*` (means "all documents")
4. Click "Execute Query"
5. Check `numFound` in the response:

| numFound | What it means | What to do |
|----------|---------------|------------|
| **0** | No documents at all | Scheduler task didn't run, or indexing failed silently → enable logging (see 12.0 Layer 2 below) |
| **> 0** | Documents exist in Solr | Go to Step 5 |

**Useful queries to run in Solr Admin:**

```
# How many documents per type?
q=*:*&rows=0&facet=true&facet.field=type

# Show 5 page documents with their key fields
q=*:*&fq=type:pages&rows=5&fl=uid,title,url,siteHash

# Search for a specific term
q=your search term&fl=uid,title,score
```

---

#### Step 5: Does the Frontend Show Results? (Frontend Check)

**Where:** Open the page with the search plugin in the frontend, type a search term you know exists in Solr.

| What you see | What it means | What to do |
|-------------|---------------|------------|
| **"Search is currently not available"** | Plugin can't connect to Solr | Go back to Step 2 -- connection issue from the frontend |
| **Search works, but 0 results** | Query runs but finds nothing | Check site hash, access field (see below) |
| **Results appear but wrong** | Documents found but wrong content/order | Check field mapping, boosting (see below) |
| **Results appear correctly** ✅ | Everything works! | Done |

**If 0 results even though Solr has documents:**

The most common cause is a **site hash mismatch**. EXT:solr adds a `siteHash` to every document to prevent cross-site contamination. If the hash during indexing differs from the hash during searching, no results appear.

Check in Solr Admin:
```
# What siteHash values exist in the index?
q=*:*&rows=0&facet=true&facet.field=siteHash
```

Compare with what EXT:solr expects: open Extension Settings (Admin Tools → Settings → Extension Configuration → solr) and check `siteHashStrategy`. Since EXT:solr 13.0, the default is **site-identifier** (not domain).

If the hashes don't match: **re-index** (Backend → Web → Search → Index Queue → clear and re-queue all).

**Other causes of 0 results:**

- `plugin.tx_solr.search.targetPage` points to wrong page → set it to the page UID containing the search plugin
- `plugin.tx_solr.enabled` is `0` somewhere in TypoScript → check for override
- Access restrictions: the `access` field on documents doesn't match the frontend user → test in Solr Admin with `q=*:*&fl=uid,title,access`

---

#### Step 6: Something Else is Wrong -- Enable Logging

If the steps above didn't solve it, **enable logging** to see exactly what EXT:solr does:

**1. Add TypoScript logging** (on the root page, dev/staging only):

```typoscript
plugin.tx_solr.logging {
    debugOutput = 1
    exceptions = 1
    indexing = 1
    query.rawGet = 1
    query.queryString = 1
    query.searchWords = 1
}
```

**2. Write logs to a file** (in `config/system/additional.php`):

```php
$GLOBALS['TYPO3_CONF_VARS']['LOG']['ApacheSolrForTypo3']['Solr']['writerConfiguration'] = [
    \Psr\Log\LogLevel::DEBUG => [
        \TYPO3\CMS\Core\Log\Writer\FileWriter::class => [
            'logFile' => \TYPO3\CMS\Core\Core\Environment::getVarPath() . '/log/solr.log',
        ],
    ],
];
```

**3. Watch the log file:**

```bash
# DDEV
ddev exec tail -f var/log/solr.log

# Then trigger the action: run the scheduler, or do a search in the frontend
```

**What to look for in the log:**

- `[ERROR]` lines → these tell you exactly what failed
- `Solr raw GET: http://...` → the exact URL sent to Solr (paste it in a browser to test)
- `Response: 0 results` → Solr answered but found nothing (site hash? access? wrong field?)

**4. Also check:** Admin Tools → Log module (filter by component "ApacheSolrForTypo3")

---

#### Step 7: Still Stuck? Escalation Path

If all six steps above didn't solve it:

1. **Ask in Slack:** [TYPO3 Slack #ext-solr](https://typo3.slack.com/messages/ext-solr/) -- the maintainers (dkd) are active here
2. **Search GitHub Issues:** [TYPO3-Solr/ext-solr/issues](https://github.com/TYPO3-Solr/ext-solr/issues) -- your problem might already be reported
3. **dkd support:** [Professional support](https://www.dkd.de/en/products/solr-enterprise-search/) -- paid support from the extension maintainers
4. **Collect info for a bug report:** Solr version, EXT:solr version, PHP version, TYPO3 version, error messages from the log, Solr Admin query results

---

### 12.0 How to Get Debug Data

Before you can fix anything, you need to **see** what EXT:solr is doing. There are six layers of debug data, from quick checks to deep inspection:

```mermaid
graph TD
    subgraph quick ["1. Quick Checks (no config needed)"]
        Reports["TYPO3 Reports Module"]
        Backend["EXT:solr Backend Module"]
        SolrUI["Solr Admin UI"]
    end
    subgraph config ["2. Enable Debug Output"]
        TSLog["TypoScript logging.*"]
        FileLog["FileWriter → var/log/solr.log"]
        DebugTools["EXT:solrdebugtools"]
    end
    subgraph deep ["3. Deep Inspection"]
        SolrDebug["Solr debugQuery=true"]
        DB["SQL on tx_solr_* tables"]
        CLI["CLI curl to Solr API"]
    end
    Reports --> TSLog
    Backend --> TSLog
    SolrUI --> SolrDebug
    TSLog --> FileLog
    FileLog --> DB
    DebugTools --> SolrDebug
```

#### Layer 1: Quick checks (zero configuration)

**TYPO3 Reports module** (Admin Tools -> Reports -> Status Report):
- Shows connection status, configset match, core availability per language
- If anything is red/yellow here, fix it before debugging further

**EXT:solr backend module** (Web -> Search):
- **Overview tab**: connection status per site and language
- **Index Queue tab**: record counts by type, error counts, last indexed timestamp
- **Index Fields tab**: all fields in the Solr schema with types and stored/indexed flags
- **Stop Words / Synonyms tabs**: current configuration on the Solr core

**Solr Admin UI** (DDEV: `ddev launch :8984`, Production: `http://host:8983/solr/`):
- **Dashboard**: JVM memory, Solr version, uptime
- **Core Selector** (dropdown): pick the core you want to inspect
- **Query tab**: run queries directly against the core
- **Schema Browser**: inspect field types, stored/indexed flags, sample values
- **Analysis tab**: paste text to see how Solr tokenizes and stems it

#### Layer 2: Enable TypoScript debug logging

Add to your **root page TypoScript setup** (dev/staging only):

```typoscript
plugin.tx_solr.logging {
    debugOutput = 1
    exceptions = 1

    indexing = 1
    indexing.indexQueueInitialization = 1
    indexing.pageIndexed = 1
    indexing.queue.news = 1

    query.rawGet = 1
    query.rawPost = 1
    query.queryString = 1
    query.searchWords = 1
}
```

| Setting | What it logs |
|---------|-------------|
| `debugOutput` | Writes debug info to the TYPO3 devlog |
| `exceptions` | Full exception stack traces from Solr communication |
| `indexing` | Every document sent to Solr during indexing |
| `indexing.indexQueueInitialization` | Which tables were initialized into the queue |
| `indexing.pageIndexed` | Each page after it was indexed (URL, fields, response) |
| `indexing.queue.<name>` | Per-config logging (e.g., `queue.news`, `queue.events`) |
| `query.rawGet` | The exact HTTP GET URL sent to Solr |
| `query.rawPost` | POST body sent to Solr (for complex queries) |
| `query.queryString` | The parsed Solr query string |
| `query.searchWords` | The user's search input after processing |

**Where does the output go?** By default, to the TYPO3 logging system. To get a file you can `tail -f`:

```php
// config/system/additional.php
$GLOBALS['TYPO3_CONF_VARS']['LOG']['ApacheSolrForTypo3']['Solr']['writerConfiguration'] = [
    \Psr\Log\LogLevel::DEBUG => [
        \TYPO3\CMS\Core\Log\Writer\FileWriter::class => [
            'logFile' => \TYPO3\CMS\Core\Core\Environment::getVarPath() . '/log/solr.log',
        ],
    ],
];
```

Then watch live:

```bash
# DDEV
ddev exec tail -f var/log/solr.log

# Standalone
tail -f /var/www/html/var/log/solr.log
```

**Also check the TYPO3 system log:** Admin Tools -> Log module, filter by component `ApacheSolrForTypo3`.

#### Layer 3: Solr debugQuery (score explanation)

In Solr Admin UI -> Query tab, add `debugQuery=true` to see **why** a document ranks where it does:

```
q=typo3 solr&debugQuery=true&fl=uid,title,score
```

The response includes a `debug` section:

```json
{
  "debug": {
    "rawquerystring": "typo3 solr",
    "querystring": "typo3 solr",
    "parsedquery": "(title:typo3 | content:typo3) (title:solr | content:solr)",
    "QParser": "ExtendedDismaxQParser",
    "explain": {
      "4f547484...": "\n7.256 = sum of:\n  3.628 = weight(title:typo3) ...\n  3.628 = weight(title:solr) ..."
    },
    "timing": {
      "time": 2.0,
      "prepare": { "time": 1.0 },
      "process": { "time": 1.0 }
    }
  }
}
```

Key fields in the debug response:

| Field | What it tells you |
|-------|-------------------|
| `parsedquery` | How Solr interpreted your search terms (which fields, operators) |
| `explain` | Per-document score breakdown: which fields matched, TF-IDF/BM25 weights |
| `timing` | Query execution time per phase (prepare, process) |
| `rawquerystring` | The original query before parsing |

**Reading the explain output:** Each line shows a score component. Higher = more relevant. Look for:
- Which **fields** contribute most (title vs content)
- **Boost** values being applied
- Whether **coord** (coordination) penalizes partial matches

#### Layer 4: EXT:solrdebugtools (funding extension)

[EXT:solrdebugtools](https://www.typo3-solr.com/) adds visual debugging to the frontend:

- **Debug\Query ViewHelper**: shows the Solr query, parsed query, and timing in the frontend
- **Explain display**: per-result score breakdown as pie chart
- **Query parameters**: see all parameters sent to Solr

Add to your Fluid template during development:

```html
<solrdebug:query resultSet="{resultSet}" />
```

This renders a debug panel directly in the search results page showing the full Solr request and response.

#### Layer 5: CLI curl to Solr API

Bypass TYPO3 entirely and talk to Solr directly:

```bash
# Ping (is Solr alive?)
ddev exec curl -s http://solr:8983/solr/core_en/admin/ping | python3 -m json.tool

# Total document count
ddev exec curl -s 'http://solr:8983/solr/core_en/select?q=*:*&rows=0' | python3 -m json.tool

# Search with debug
ddev exec curl -s 'http://solr:8983/solr/core_en/select?q=test&debugQuery=true&fl=uid,title,score&wt=json' | python3 -m json.tool

# Check a specific document by uid
ddev exec curl -s 'http://solr:8983/solr/core_en/select?q=uid:42&fl=*' | python3 -m json.tool

# List all field names in the index
ddev exec curl -s 'http://solr:8983/solr/core_en/admin/luke?numTerms=0&wt=json' | python3 -m json.tool | grep '"name"'

# Document count by type
ddev exec curl -s 'http://solr:8983/solr/core_en/select?q=*:*&rows=0&facet=true&facet.field=type' | python3 -m json.tool

# Check what siteHash values exist
ddev exec curl -s 'http://solr:8983/solr/core_en/select?q=*:*&rows=0&facet=true&facet.field=siteHash' | python3 -m json.tool

# Analyze how a term is tokenized (for field type "text")
ddev exec curl -s 'http://solr:8983/solr/core_en/analysis/field?analysis.fieldname=content&analysis.fieldvalue=TYPO3+Content+Management&wt=json' | python3 -m json.tool
```

**From host** (replace `ddev exec curl -s http://solr:8983` with `curl -sk https://<project>.ddev.site:8984`).

#### Layer 6: Database-level inspection

Query the TYPO3 database to see what EXT:solr thinks the state is:

```sql
-- How many items per type/config, and how many have errors?
SELECT item_type, indexing_configuration,
       COUNT(*) as total,
       SUM(CASE WHEN indexed > 0 THEN 1 ELSE 0 END) as indexed,
       SUM(CASE WHEN errors != '' THEN 1 ELSE 0 END) as errors,
       SUM(CASE WHEN changed > indexed THEN 1 ELSE 0 END) as stale
FROM tx_solr_indexqueue_item
GROUP BY item_type, indexing_configuration;

-- Show the actual error messages
SELECT uid, item_type, item_uid, LEFT(errors, 200) as error_preview
FROM tx_solr_indexqueue_item
WHERE errors != ''
ORDER BY uid DESC LIMIT 20;

-- When was the last successful indexing run?
SELECT MAX(FROM_UNIXTIME(indexed)) as last_indexed
FROM tx_solr_indexqueue_item
WHERE indexed > 0;

-- What is the site hash stored for a specific page?
SELECT uid, title, slug
FROM pages
WHERE uid = 1;
-- Then check: the site hash comes from the site identifier in config.yaml
```

#### Putting it all together: Debug workflow

```mermaid
graph TD
    Problem["Something's wrong"]
    Problem --> Reports["1. Check Reports module"]
    Reports -->|Red| Connection["Fix connection (12.2)"]
    Reports -->|Green| Queue["2. Check Index Queue in backend module"]
    Queue -->|Empty| Init["Initialize queue, check TypoScript"]
    Queue -->|Errors| SQL["3. Read error messages from DB"]
    Queue -->|OK| SolrAdmin["4. Query Solr Admin directly"]
    SolrAdmin -->|No docs| Scheduler["Run scheduler, check indexing log"]
    SolrAdmin -->|Docs exist| Frontend["5. Check frontend TypoScript"]
    Frontend -->|No results| TS["Enable query logging, check siteHash"]
    Frontend -->|Wrong results| Debug["6. debugQuery=true, check scoring"]
    Scheduler --> Log["Enable indexing logging, tail solr.log"]
```

### 12.1 Where to Start: Diagnostic Flowchart

```mermaid
graph TD
    Start["Problem with Solr?"]
    Start --> A{"Reports module:<br/>all green?"}
    A -->|No| Fix1["Fix connection config<br/>(see 12.2)"]
    A -->|Yes| B{"Index Queue:<br/>items present?"}
    B -->|No| Fix2["Initialize queue<br/>(see 12.3)"]
    B -->|Yes| C{"Items have<br/>errors?"}
    C -->|Yes| Fix3["Check error messages<br/>(see 12.3)"]
    C -->|No| D{"Solr Admin:<br/>documents exist?"}
    D -->|No| Fix4["Run scheduler worker<br/>(see 12.3)"]
    D -->|Yes| E{"Frontend shows<br/>results?"}
    E -->|No| Fix5["Check TypoScript,<br/>site hash (see 12.4)"]
    E -->|Yes| Done["Working!"]
```

**Step 1:** Check TYPO3 Reports module (Admin Tools -> Reports -> Status Report). All Solr-related checks must be green.

**Step 2:** Open Solr Admin UI. DDEV: `ddev launch :8984`. Production: `https://your-host:8983/solr/`.

**Step 3:** EXT:solr backend module -> "Index Queue" tab. Are items queued? Any errors?

<!-- SCREENSHOT: reports-module-solr.png - TYPO3 Reports module Solr status -->

### 12.2 Connection Problems

**"Search is currently not available"** -- full checklist:

1. Verify site config values: `solr_host_read`, `solr_port_read`, `solr_scheme_read`, `solr_path_read`
2. Click "Initialize connection" in the EXT:solr backend module after ANY config change
3. Test from CLI:
   ```bash
   # Inside DDEV container
   ddev exec curl -s http://solr:8983/solr/core_en/admin/ping
   # From host
   curl -sk https://<project>.ddev.site:8984/solr/core_en/admin/ping
   ```
4. **DDEV networking:** Inside the container, Solr is reachable at hostname `solr` on port `8983`. From the host, use `<project>.ddev.site:8984` with HTTPS.
5. **Firewall:** Ensure port 8983 (or 8984 for DDEV) is open
6. **HTTPS:** DDEV uses self-signed certificates. Use `solr_scheme_read: https` with DDEV.

### 12.3 Indexing Problems

#### Nothing in Index Queue

- **TypoScript not loaded:** Check the Template module on the root page. EXT:solr static templates must be included.
- **Queue not initialized:** Backend module -> "Index Queue" tab -> select record types -> click "Queue selected content"
- **Missing config:** `plugin.tx_solr.index.queue.[table] = 1` must exist for each table
- **Records outside siteroot:** Set `additionalPageIds` and enable "tracking of records outside siteroot" in Extension Settings

#### Items in Queue but Not Indexed

- **Scheduler not running:** A "Index Queue Worker" scheduler task must be configured and executing
- **Check errors:** Inspect the `errors` column:
  ```sql
  SELECT item_type, errors, COUNT(*) as cnt
  FROM tx_solr_indexqueue_item
  WHERE errors != ''
  GROUP BY item_type, errors;
  ```
- **Forced webroot:** If TYPO3 cannot auto-detect the web root (CLI context), configure "Forced webroot" in the scheduler task settings
- **Memory:** Large content can exceed PHP `memory_limit`. Increase for the scheduler process.

#### Items Indexed but Wrong/Missing Content

- **Check the actual document** in Solr Admin UI:
  ```
  q=*:*&fq=type:tx_news_domain_model_news&rows=5&fl=uid,title,content,url,siteHash
  ```
- **Field mapping:** Verify `plugin.tx_solr.index.queue.[table].fields` TypoScript matches your Solr schema fields
- **HTML in content:** Use `SOLR_CONTENT` to strip HTML from RTE fields
- **Relations empty:** Use `SOLR_RELATION` with `multiValue = 1` for category/tag fields

#### Re-indexing

1. **Soft re-index:** Backend module -> clear queue -> re-queue -> run scheduler task
2. **Force re-index:** Scheduler task "Force Re-Indexing of a site" for specific configurations
3. **Nuclear option** (DDEV only): `TRUNCATE tx_solr_indexqueue_item;` then re-initialize via backend module

### 12.4 Search / Query Problems

#### No Results in Frontend

```mermaid
graph LR
    Plugin["Search Plugin<br/>(Fluid)"] --> TS["TypoScript Config"]
    TS --> SolrQ["Solr Query<br/>(HTTP GET)"]
    SolrQ --> Response["Solr Response<br/>(JSON)"]
    Response --> Render["Fluid Rendering"]
```

- **targetPage:** `plugin.tx_solr.search.targetPage` must point to the page containing the search plugin
- **Disabled:** Check `plugin.tx_solr.enabled` is not set to `0`
- **Site hash mismatch:** Documents indexed with wrong site hash won't appear. Test in Solr Admin:
  ```
  q=YOUR_TERM&fq=siteHash:YOUR_EXPECTED_HASH
  ```
- **Access restrictions:** Documents have an `access` field. Ensure frontend user group matches.

#### Wrong Results / Bad Relevance

- **debugQuery:** In Solr Admin, run `q=YOUR_TERM&debugQuery=true` to see score breakdowns
- **EXT:solrdebugtools:** Install for Debug\Query ViewHelper, Explain functionality, and score pie charts
- **Boosting:** Configure field weights:
  ```typoscript
  plugin.tx_solr.search.query {
      boostFunction = recip(ms(NOW,created),3.16e-11,1,1)
      boostQuery = type:pages^2.0
  }
  ```

#### Facets Not Showing

- `plugin.tx_solr.search.faceting = 1` missing
- Field not indexed (check dynamic field suffix matches schema)
- `initializeWithEmptyQuery = 1` needed to show facets before any search
- Facet field has zero values in indexed documents

### 12.5 Logging Deep Dive

> For the full logging setup (TypoScript config, FileWriter, `tail -f`), see **12.0 Layer 2** above.

**Selective logging per Index Queue config:**

You can enable logging for just one config to reduce noise:

```typoscript
plugin.tx_solr.logging {
    indexing = 0
    indexing.queue.events = 1
}
```

This logs only the "events" queue processing, not "news" or "pages".

**What the indexing log contains per document:**

```
[DEBUG] Indexing document: tx_news_domain_model_news:42
  Fields: title=My Event, content=...(truncated), url=/events/my-event
  Solr response: {"responseHeader":{"status":0,"QTime":3}}
```

If indexing fails, you'll see the Solr error response:

```
[ERROR] Indexing failed for tx_news_domain_model_news:42
  Error: Document is missing mandatory uniqueKey field: id
```

**What the query log contains:**

```
[DEBUG] Solr query: q=test+search&fq=siteHash:abc123&fl=*,score
[DEBUG] Solr raw GET: http://solr:8983/solr/core_en/select?q=test+search&...
[DEBUG] Search words: ["test", "search"]
[DEBUG] Response: 15 results in 3ms
```

> **Warning:** Disable logging on production. Log data grows quickly and impacts performance. Remove the `plugin.tx_solr.logging` config and the `writerConfiguration` from `additional.php`.

### 12.6 Solr Admin UI as Debugging Tool

<!-- SCREENSHOT: solr-admin-query.png - Solr Admin query panel -->

> See **12.0 Layer 5** for CLI `curl` equivalents of all these queries.

Key diagnostic queries (run in Solr Admin -> Query tab):

| Query | Purpose |
|-------|---------|
| `q=*:*&rows=0` | Total document count |
| `q=*:*&fq=type:pages&rows=5&fl=uid,title,url,siteHash` | Check indexed pages |
| `q=*:*&fq=type:tx_news_domain_model_news&fl=uid,title,content` | Check custom records |
| `q=YOUR_TERM&debugQuery=true` | Score explanation (see 12.0 Layer 3 for reading the output) |
| `q=*:*&facet=true&facet.field=type&rows=0` | Document type distribution |
| `q=*:*&fq=siteHash:YOUR_HASH&rows=0` | Documents per site hash |

**Additional useful queries:**

```
# Find a specific record by UID and type
q=*:*&fq=uid:42&fq=type:tx_news_domain_model_news&fl=*

# Check which fields exist on a document (all fields)
q=*:*&rows=1&fl=*

# Compare what two configs indexed
q=*:*&fq=indexing_configuration:news&rows=0
q=*:*&fq=indexing_configuration:events&rows=0

# Find documents with empty content
q=*:*&fq=-content:[* TO *]&fl=uid,title,type

# Check the access field (frontend user restrictions)
q=*:*&fl=uid,title,access&rows=10
```

**Core Admin:** Reload a core after schema changes (Core Admin -> Reload button, or via API: `curl http://solr:8983/solr/admin/cores?action=RELOAD&core=core_en`).

**Analysis screen:** Test how text is tokenized and stemmed. In Solr Admin -> Analysis, select the field type (e.g., `text`), paste text in the "Field Value (Index)" box, and click "Analyse Values". This shows every processing step: char filters, tokenizer, token filters (lowercase, stop words, stemming).

<!-- SCREENSHOT: solr-analysis-screen.png - Solr Analysis screen -->

### 12.7 Database-Level Debugging

> See **12.0 Layer 6** for the complete SQL query cookbook.

Quick reference:

```sql
-- Overview: what's in the queue?
SELECT item_type, indexing_configuration,
       COUNT(*) as total,
       SUM(CASE WHEN errors != '' THEN 1 ELSE 0 END) as errors
FROM tx_solr_indexqueue_item
GROUP BY item_type, indexing_configuration;

-- What went wrong?
SELECT uid, item_type, item_uid, errors, changed, indexed
FROM tx_solr_indexqueue_item
WHERE errors != ''
LIMIT 20;

-- Event queue (delayed monitoring mode)
SELECT * FROM tx_solr_eventqueue_item
ORDER BY tstamp DESC LIMIT 20;

-- Items that need re-indexing
SELECT uid, item_type, item_uid, changed, indexed
FROM tx_solr_indexqueue_item
WHERE changed > indexed
LIMIT 20;

-- Reset errors to retry indexing
UPDATE tx_solr_indexqueue_item SET errors = '' WHERE errors != '';
```

### 12.8 Common Issues Quick Reference

| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| "Search is currently not available" | Solr connection misconfigured | Check site config, initialize connection |
| No results for any query | Site hash mismatch | Check `siteHashStrategy`, re-index |
| Empty Index Queue | TypoScript not loaded on root page | Include EXT:solr static templates |
| Queue items never indexed | Scheduler task not running | Create "Index Queue Worker" task |
| Items in queue with errors | Indexing failure (memory, URL) | Check `errors` column in DB |
| Facets not visible | `faceting = 0` or empty field | Enable faceting, check field has values |
| Suggest not working | `suggest = 0` or jQuery missing | Enable suggest, check JS includes |
| Wrong content in results | Field mapping incorrect | Fix TypoScript `fields` config |
| Access-restricted pages missing | `linkAccessRestrictedPages` not set | Add `config.typolinkLinkAccessRestrictedPages = 1` |
| URL generation fails for records | `detailPid` not configured | Set the correct detail page ID |
| Docker: permission denied on `/var/solr` | Volume ownership wrong | Ensure UID 8983 owns the volume |
| DDEV: port 8984 not reachable | Addon not installed or Solr down | `ddev add-on get ddev/ddev-typo3-solr && ddev restart` |
| Configset version mismatch | Wrong configset deployed | Redeploy matching `ext_solr_13_1_0` |
| Jar loading error (Solr 9.8+) | CVE-2025-24814 migration needed | Move `typo3lib/` to server root, or pull Docker 13.0.1+ |
| Tika: connection timeout | Tika Server not running or wrong port | Check Docker container, test connection in extension settings |
| Language core missing | No core for language | Create core with matching language schema |

## 13. Testing

### Functional Test Setup

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExt\Tests\Functional;

use ApacheSolrForTypo3\Solr\Tests\Integration\IntegrationTestCase;

final class CustomIndexingTest extends IntegrationTestCase
{
    protected array $testExtensionsToLoad = [
        'apache-solr-for-typo3/solr',
        'my-vendor/my-ext',
    ];

    public function testNewsRecordIsIndexed(): void
    {
        $this->importCSVDataSet(__DIR__ . '/Fixtures/news_records.csv');
        $this->indexQueue('news');

        $searchResult = $this->searchInSolr('test news title');
        self::assertGreaterThan(0, $searchResult->getResultCount());
    }
}
```

### Testing Custom Event Listeners

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExt\Tests\Unit\EventListener;

use ApacheSolrForTypo3\Solr\Event\Indexing\BeforeDocumentsAreIndexedEvent;
use ApacheSolrForTypo3\Solr\System\Solr\Document\Document;
use MyVendor\MyExt\EventListener\EnrichSolrDocuments;
use PHPUnit\Framework\TestCase;

final class EnrichSolrDocumentsTest extends TestCase
{
    public function testCustomScoreIsAdded(): void
    {
        $document = new Document();
        $document->setField('type', 'pages');

        $event = $this->createMock(BeforeDocumentsAreIndexedEvent::class);
        $event->method('getDocuments')->willReturn([$document]);

        $listener = new EnrichSolrDocuments();
        $listener($event);

        self::assertNotNull($document->getField('custom_score_floatS'));
    }
}
```

## 14. Best Practices

### Production Checklist

- [ ] Correct configset version deployed (`ext_solr_13_1_0`)
- [ ] Solr JVM memory configured (`SOLR_JAVA_MEM=-Xms512m -Xmx1g`)
- [ ] Solr NOT publicly accessible (firewall, reverse proxy)
- [ ] Scheduler task "Index Queue Worker" running regularly
- [ ] Logging disabled in production
- [ ] Solr data volume backed up
- [ ] CVE patches applied (Solr 9.8.0+ jar migration)
- [ ] `siteHashStrategy = 1` (site-identifier)

### Security

- Solr must **never** be publicly accessible. Use firewall rules or bind to `127.0.0.1`
- Use a reverse proxy (nginx/Apache) with authentication if remote access is needed
- Monitor [Apache Solr security advisories](https://solr.apache.org/security.html)
- Keep Tika Server updated (CVE-2025-54988: requires 3.2.2+)

### Performance

- **Commit strategy:** Use `autoSoftCommit` (1-5 seconds) for near-real-time, `autoCommit` (15-60 seconds) for durability
- **Cache warming:** Configure `newSearcher` and `firstSearcher` events in `solrconfig.xml`
- **Segment merging:** Monitor segment count in Solr Admin, consider `optimize` during off-peak

### Multi-Site / Multi-Language

- One Solr core per language (e.g., `core_en`, `core_de`) with language-specific schema
- Each TYPO3 site configures its own Solr connection in site config YAML
- Use `siteHash` to isolate documents per site

### Environment Configuration

Use `helhum/dotenv-connector` to manage Solr credentials per environment:

- `.env` file per environment (local, staging, production) with `SOLR_HOST`, `SOLR_PORT`, etc.
- Never hardcode connection details in site config YAML committed to VCS
- Commit `.env.example` with placeholder values

## 15. Database Structure

| Table | Purpose |
|-------|---------|
| `tx_solr_indexqueue_item` | Items queued for indexing (type, uid, errors, timestamps) |
| `tx_solr_indexqueue_indexing_property` | Additional indexing properties per queue item |
| `tx_solr_last_searches` | Recently executed search terms |
| `tx_solr_statistics` | Search statistics (terms, result counts, timestamps) |
| `tx_solr_eventqueue_item` | Event queue for delayed monitoring mode |

Key columns in `tx_solr_indexqueue_item`:

| Column | Type | Purpose |
|--------|------|---------|
| `item_type` | varchar | Table name (e.g., `pages`, `tx_news_domain_model_news`) |
| `item_uid` | int | Record UID |
| `indexing_configuration` | varchar | TypoScript configuration name |
| `changed` | int | Timestamp when record was last changed |
| `indexed` | int | Timestamp when record was last indexed |
| `errors` | text | Error message from last indexing attempt |

## FAQ / Common Recipes

### One Table, Two Index Configs (e.g. News + Events from EXT:news)

A common pattern: EXT:news is installed once but serves two purposes -- classic news articles and an event calendar (or product announcements, press releases, etc.). You want them as **separate facet values** in search.

**Step 1: Two Index Queue configurations for the same table**

Use `additionalWhereClause` to split records by category, type field, or sysfolder PID:

```typoscript
plugin.tx_solr.index.queue {

    # Config 1: Classic news articles
    news = 1
    news {
        type = tx_news_domain_model_news
        additionalWhereClause = categories.uid IN (1,2,3)

        fields {
            title = title
            content = SOLR_CONTENT
            content.cObject = COA
            content.cObject {
                10 = TEXT
                10.field = bodytext
            }
            category_stringM = SOLR_RELATION
            category_stringM {
                localField = categories
                multiValue = 1
            }
            url = TEXT
            url {
                typolink.parameter = {$plugin.tx_news.settings.detailPid}
                typolink.additionalParams = &tx_news_pi1[controller]=News&tx_news_pi1[action]=detail&tx_news_pi1[news]={field:uid}
                typolink.additionalParams.insertData = 1
                typolink.returnLast = url
            }
            # Custom field to distinguish in facets
            newstype_stringS = TEXT
            newstype_stringS.value = News
        }
    }

    # Config 2: Events (same table, different records)
    events = 1
    events {
        type = tx_news_domain_model_news
        additionalWhereClause = categories.uid IN (4,5,6)

        fields {
            title = title
            content = SOLR_CONTENT
            content.cObject = COA
            content.cObject {
                10 = TEXT
                10.field = bodytext
            }
            category_stringM = SOLR_RELATION
            category_stringM {
                localField = categories
                multiValue = 1
            }
            # Event-specific fields
            eventdate_dateS = datetime
            eventlocation_stringS = TEXT
            eventlocation_stringS {
                field = teaser
            }
            url = TEXT
            url {
                typolink.parameter = {$plugin.tx_news.settings.detailPid}
                typolink.additionalParams = &tx_news_pi1[controller]=News&tx_news_pi1[action]=detail&tx_news_pi1[news]={field:uid}
                typolink.additionalParams.insertData = 1
                typolink.returnLast = url
            }
            newstype_stringS = TEXT
            newstype_stringS.value = Event
        }
    }
}
```

**Alternative split strategies** (use only one):

| Strategy | `additionalWhereClause` | When to use |
|----------|------------------------|-------------|
| By category | `categories.uid IN (4,5,6)` | Different category trees for news vs events |
| By sysfolder | `pid IN (50,51)` | News and events stored in separate sysfolders |
| By type field | `tx_news_domain_model_news.type = 1` | EXT:news type field distinguishes record types |
| By tag | `tags.uid = 10` | Tagged with a specific tag |

**Step 2: Facet on the custom field**

```typoscript
plugin.tx_solr.search.faceting {
    facets {
        newstype {
            label = Content Type
            field = newstype_stringS
        }
    }
}
```

This shows "News (42)" and "Event (15)" as facet options.

**Step 3: Initialize both queue configs**

In the EXT:solr backend module -> Index Queue tab, both "news" and "events" appear as separate indexing configurations. Select both and click "Queue selected content".

**How it works internally:** Each config creates its own `tx_solr_indexqueue_item` entries with `indexing_configuration = 'news'` or `indexing_configuration = 'events'`. The Scheduler worker processes both. In Solr, the documents have `type = tx_news_domain_model_news` (the table name) but differ in the `newstype_stringS` field.

> **Tip:** If you want the Solr `type` field itself to differ (e.g., `type = news` vs `type = events`), you can override it in the fields config: `type = TEXT` / `type.value = events`. Be aware this affects other type-based filtering.

### Different Detail Page URLs per Config

When news and events have separate detail pages:

```typoscript
plugin.tx_solr.index.queue.news.fields.url {
    typolink.parameter = 42
    # ...
}

plugin.tx_solr.index.queue.events.fields.url {
    typolink.parameter = 55
    # ...
}
```

### Three or More Configs from One Table

The pattern scales -- you can create as many Index Queue configs as needed for the same table. Each gets its own name, `additionalWhereClause`, field mappings, and detail page URL.

## Related Skills

- **typo3-datahandler**: Database operations that trigger Solr indexing
- **typo3-seo**: SEO configuration that complements Solr search
- **ai-search-optimization**: AEO/GEO for AI search visibility
- **typo3-content-blocks**: Content Elements that can be indexed
- **typo3-testing**: Testing patterns for TYPO3 extensions

## References

### Official Documentation

- [EXT:solr Documentation](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/)
- [EXT:tika Documentation](https://docs.typo3.org/p/apache-solr-for-typo3/tika/main/en-us/)
- [Version Matrix](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Appendix/VersionMatrix.html)
- [Dynamic Fields Reference](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Appendix/DynamicFieldTypes.html)
- [tx_solr.search Reference](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Configuration/Reference/TxSolrSearch.html)
- [tx_solr.index Reference](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Configuration/Reference/TxSolrIndex.html)
- [tx_solr.suggest Reference](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Configuration/Reference/TxSolrSuggest.html)
- [Extension Settings](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Configuration/Reference/ExtensionSettings.html)
- [PSR-14 Events](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Development/Events.html)
- [Indexing (Development)](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Development/Indexing.html)
- [Index Queue](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Backend/IndexQueue.html)
- [Facets](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Frontend/Facets.html)
- [Autosuggest](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Frontend/Autosuggest.html)
- [AJAX Results](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Frontend/Ajax.html)
- [Customize Frontend](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Frontend/Customize.html)
- [Fluid Structure](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Frontend/Structure.html)
- [Scheduler Tasks](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/Backend/Scheduler.html)
- [FAQ](https://docs.typo3.org/p/apache-solr-for-typo3/solr/main/en-us/FAQ/Index.html)

### GitHub Repositories

- [EXT:solr](https://github.com/TYPO3-Solr/ext-solr)
- [EXT:tika](https://github.com/TYPO3-Solr/ext-tika)
- [TYPO3-Solr Organization](https://github.com/TYPO3-Solr)
- [v14 Development Branch](https://github.com/TYPO3-Solr/ext-solr/tree/task/14LTS_compatibility)
- [Docker Images](https://hub.docker.com/r/typo3solr/ext-solr/tags)

### Apache Solr Server

- [Reference Guide](https://solr.apache.org/guide/solr/latest/)
- [Dense Vector Search](https://solr.apache.org/guide/solr/latest/query-guide/dense-vector-search.html)
- [Text to Vector (LLM Module)](https://solr.apache.org/guide/solr/latest/query-guide/text-to-vector.html)
- [Solr Modules](https://solr.apache.org/guide/solr/latest/configuration-guide/solr-modules.html)
- [LLM API Javadoc](https://solr.apache.org/docs/9_8_0/modules/llm/index.html)
- [Tutorial: Vectors](https://solr.apache.org/guide/solr/latest/getting-started/tutorial-vectors.html)

### Commercial & Hosting

- [typo3-solr.com](https://www.typo3-solr.com/)
- [Changelog EXT:solr](https://typo3-solr.com/solr-for-typo3/versions/changelog-extsolr)
- [Changelog EXT:solrfal](https://typo3-solr.com/solr-for-typo3/versions/changelog-extsolrfal)
- [hosted-solr.com](https://hosted-solr.com/en/)
- [hosted-solr.com Pricing](https://hosted-solr.com/en/pricing/)
- [dkd Enterprise Support](https://www.dkd.de/en/products/solr-enterprise-search/)
- [dkd Workshops](https://www.dkd.de/en/services/workshops/)

### DDEV

- [ddev-typo3-solr Addon](https://github.com/ddev/ddev-typo3-solr)
- [DDEV Addons Directory](https://addons.ddev.com/addons/ddev/ddev-typo3-solr)

### Mittwald

- [Solr Documentation](https://developer.mittwald.de/docs/v2/platform/databases/solr/)

### Environment Config

- [helhum/dotenv-connector](https://github.com/helhum/dotenv-connector)
- [Packagist](https://packagist.org/packages/helhum/dotenv-connector)
- [TYPO3 Environment Configuration](https://docs.typo3.org/permalink/t3coreapi:environment-configuration)

### Community

- [TYPO3 Slack #ext-solr](https://typo3.slack.com/messages/ext-solr/)
- [GitHub Issues](https://github.com/TYPO3-Solr/ext-solr/issues)
- [TYPO3 Extensions (TER)](https://extensions.typo3.org/extension/solr)

### Blogs & Tutorials

- [Sease: Neural Search](https://sease.io/2022/01/apache-solr-neural-search.html)
- [Sease: KNN Benchmark](https://sease.io/2022/01/apache-solr-neural-search-knn-benchmark.html)
- [Sease: Semantic Search Text-to-Vector](https://sease.io/2025/07/semantic-search-text-to-vector-with-apache-solr.html)
- [Sease: SeededKnnVectorQuery (Solr 10)](https://sease.io/2026/02/lexically-accelerated-vector-search-seededknnvectorquery-support-in-apache-solr-10.html)
- [LangChain4j Embedding Models](https://docs.langchain4j.dev/category/embedding-models)

## Credits & Attribution

- **dkd Internet Service GmbH** for developing and maintaining EXT:solr, EXT:tika, and the TYPO3-Solr ecosystem
- **Apache Software Foundation** for Apache Solr and Apache Tika
- **b13** for maintaining the ddev-typo3-solr addon
- **Helmut Hummel (helhum)** for dotenv-connector
- **Mittwald** for managed Solr hosting documentation

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…