Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to Roadiz will be documented in this file.

## [2.7.2](https://github.com/roadiz/core-bundle-dev-app/compare/v2.7.1...v2.7.2) - 2026-02-23

### Bug Fixes

- add priority to login reset confirmation route - ([a6bfc27](https://github.com/roadiz/core-bundle-dev-app/commit/a6bfc27429fa2cf04074471ed8469cc138da27a1))

## [2.7.1](https://github.com/roadiz/core-bundle-dev-app/compare/v2.7.0...v2.7.1) - 2026-02-03

### Bug Fixes
Expand Down
54 changes: 27 additions & 27 deletions config/api_resources/nodes_sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,33 @@ resources:
description: |
Get available NodesSources archives (years and months) based on their `publishedAt` field

# api_nodes_sources_search:
# class: ApiPlatform\Metadata\GetCollection
# method: 'GET'
# uriTemplate: '/nodes_sources/search'
# controller: RZ\Roadiz\SolrBundle\Controller\NodesSourcesSearchController
# read: false
# normalizationContext:
# groups:
# - get
# - nodes_sources_base
# - nodes_sources_default
# - urls
# - tag_base
# - translation_base
# - document_display
# openapi:
# summary: Search NodesSources resources
# description: |
# Search all website NodesSources resources using **Solr** full-text search engine
# parameters:
# - type: string
# name: search
# in: query
# required: true
# description: Search pattern
# schema:
# type: string
api_nodes_sources_search:
class: ApiPlatform\Metadata\GetCollection
method: 'GET'
uriTemplate: '/nodes_sources/search'
controller: RZ\Roadiz\SolrBundle\Controller\NodesSourcesSearchController
read: false
normalizationContext:
groups:
- get
- nodes_sources_base
- nodes_sources_default
- urls
- tag_base
- translation_base
- document_display
openapi:
summary: Search NodesSources resources
description: |
Search all website NodesSources resources using **Solr** full-text search engine
parameters:
- type: string
name: search
in: query
required: true
description: Search pattern
schema:
type: string

# ApiPlatform\Metadata\Get:
# method: 'GET'
Expand Down
4 changes: 4 additions & 0 deletions config/packages/roadiz_solr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
roadiz_solr:
search:
fuzzy_proximity: 2
fuzzy_min_term_length: 3
13 changes: 13 additions & 0 deletions docs/developer/first-steps/use_apache_solr.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,23 @@ nelmio_solarium:
adapter_timeout: 5
```

Then configure fuzzy search options in `config/packages/roadiz_solr.yaml`:

```yaml
# config/packages/roadiz_solr.yaml
roadiz_solr:
search:
fuzzy_proximity: 2
fuzzy_min_term_length: 3
```

You can use Solr in 2 ways: as a core or as a collection:
- If you are using Solr as a single core, you can set the `SOLR_CORE_NAME` environment variable.
- If you are using _SolrCloud mode_, you can set the `SOLR_COLLECTION_NAME`

Fuzzy search options are configured in `roadiz_solr.search`.
For backward compatibility, `roadiz_core.solr.search` is still read as a fallback during migration.

::: info
When using _SolrCloud mode_ you will need to set the `SOLR_COLLECTION_NUM_SHARDS` and
`SOLR_COLLECTION_REPLICATION_FACTOR` variables to configure your collection and execute
Expand Down
13 changes: 13 additions & 0 deletions docs/developer/optional-bundles/solr-bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,23 @@ nelmio_solarium:
adapter_timeout: 5
```

Configure fuzzy search options in `config/packages/roadiz_solr.yaml`:

```yaml
# config/packages/roadiz_solr.yaml
roadiz_solr:
search:
fuzzy_proximity: 2
fuzzy_min_term_length: 3
```

::: tip
You can use Solr in 2 ways:
- **Standalone core**: Set the `core` parameter to `SOLR_CORE_NAME`
- **SolrCloud collection**: Set the `core` parameter to `SOLR_COLLECTION_NAME` and configure shards/replication

Fuzzy search options are configured in `roadiz_solr.search`.
For backward compatibility, `roadiz_core.solr.search` is still read as a fallback during migration.
:::

### API Platform Integration
Expand Down
1 change: 0 additions & 1 deletion lib/RoadizCoreBundle/config/packages/roadiz_core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,3 @@ roadiz_core:
domainName: '%env(string:VARNISH_DOMAIN)%'



2 changes: 1 addition & 1 deletion lib/RoadizCoreBundle/config/services.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
parameters:
roadiz_core.cms_version: '2.7.1'
roadiz_core.cms_version: '2.7.2'
roadiz_core.cms_version_prefix: 'main'
env(APP_NAMESPACE): "roadiz"
env(APP_VERSION): "0.1.0"
Expand Down
2 changes: 0 additions & 2 deletions lib/RoadizCoreBundle/src/Api/Model/WebResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

namespace RZ\Roadiz\CoreBundle\Api\Model;

use ApiPlatform\Metadata\ApiResource;

final class WebResponse implements WebResponseInterface, BlocksAwareWebResponseInterface, RealmsAwareWebResponseInterface
{
use WebResponseTrait;
Expand Down
14 changes: 14 additions & 0 deletions lib/RoadizCoreBundle/src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,20 @@ protected function addSolrNode(): ArrayNodeDefinition|NodeDefinition

$node->children()
->scalarNode('timeout')->defaultValue(3)->end()
->arrayNode('search')
->addDefaultsIfNotSet()
->children()
->integerNode('fuzzy_proximity')
->defaultValue(2)
->min(0)
->max(2)
->end()
->integerNode('fuzzy_min_term_length')
->defaultValue(3)
->min(0)
->end()
->end()
->end()
->arrayNode('endpoints')
->defaultValue([])
->useAttributeAsKey('name')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ public function load(array $configs, ContainerBuilder $container): void
$container->setParameter('roadiz_core.project_logo_url', $config['projectLogoUrl']);
$container->setParameter('roadiz_core.generated_class_namespace', $config['generatedClassNamespace']);
$container->setParameter('roadiz_core.generated_repository_namespace', $config['generatedRepositoryNamespace']);

/*
* Assets config
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@
namespace RZ\Roadiz\CoreBundle\Serializer\Normalizer;

use RZ\Roadiz\CoreBundle\Entity\NodesSources;
use RZ\Roadiz\CoreBundle\Serializer\Normalizer\AbstractPathNormalizer;

final class NodesSourcesTypeNormalizer extends AbstractPathNormalizer
{
/**
* @inheritDoc
*/
public function normalize(mixed $data, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
{
if (!$data instanceof NodesSources) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public function resetAction(Request $request, string $token): Response
path: '/rz-admin/login/reset/confirm',
name: 'loginResetConfirmPage',
methods: ['GET'],
priority: 2,
)]
public function confirmAction(): Response
{
Expand Down
12 changes: 12 additions & 0 deletions lib/RoadizSolrBundle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,22 @@ nelmio_solarium:
adapter_timeout: 5
```

Configure fuzzy search options in a dedicated `roadiz_solr` config file:
```yaml
# config/packages/roadiz_solr.yaml
roadiz_solr:
search:
fuzzy_proximity: 2
fuzzy_min_term_length: 3
```

You can use Solr Cloud with a collection instead of a core by setting the `SOLR_COLLECTION_NAME` environment variable and commenting the `core` line.
Then you will need to set the `SOLR_COLLECTION_NUM_SHARDS` and `SOLR_COLLECTION_REPLICATION_FACTOR` variables to configure your collection and execute
`solr:init` command to create the collection.

Fuzzy search options should now be configured in `roadiz_solr.search`.
For backward compatibility, `roadiz_core.solr.search` is still read as a fallback during migration.

#### Extending Solr configuration

If you want to add/remove fields and update filters you can add an event-subscriber to the `RZ\Roadiz\SolrBundle\Event\SolrInitializationEvent` event.
Expand Down
4 changes: 4 additions & 0 deletions lib/RoadizSolrBundle/config/packages/roadiz_solr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
roadiz_solr:
search:
fuzzy_proximity: 2
fuzzy_min_term_length: 3
2 changes: 2 additions & 0 deletions lib/RoadizSolrBundle/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ services:
$solrHostname: '%env(string:SOLR_HOST)%'
$solrPort: '%env(int:SOLR_PORT)%'
$solrSecure: '%env(bool:SOLR_SECURE)%'
$fuzzyProximity: '%roadiz_solr.search.fuzzy_proximity%'
$fuzzyMinTermLength: '%roadiz_solr.search.fuzzy_min_term_length%'

RZ\Roadiz\SolrBundle\:
resource: '../src/'
Expand Down
33 changes: 26 additions & 7 deletions lib/RoadizSolrBundle/src/AbstractSearchHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public function __construct(
protected readonly ObjectManager $em,
protected readonly LoggerInterface $searchEngineLogger,
protected readonly EventDispatcherInterface $eventDispatcher,
protected readonly int $fuzzyProximity,
protected readonly int $fuzzyMinTermLength,
) {
}

Expand Down Expand Up @@ -249,10 +251,10 @@ protected function getFormattedQuery(string $q): array
$fuzzyiedQuery = implode(' ', array_map(function (string $word) {
/*
* Do not fuzz short words: Solr crashes
* Proximity is set to 1 by default for single-words
* Proximity is configurable and can be disabled.
*/
if (\mb_strlen($word) > 3) {
return $this->escapeQuery($word).'~2';
if ($this->shouldFuzzify($word)) {
return $this->escapeQuery($word).$this->getFuzzySuffix();
}

return $this->escapeQuery($word);
Expand All @@ -264,7 +266,10 @@ protected function getFormattedQuery(string $q): array
/*
* Wildcard search for allowing autocomplete
*/
$wildcardQuery = $this->escapeQuery($q).'*~2';
$wildcardQuery = $this->escapeQuery($q).'*';
if ($this->shouldFuzzify($q)) {
$wildcardQuery .= $this->getFuzzySuffix();
}

return [$exactQuery, $fuzzyiedQuery, $wildcardQuery];
}
Expand Down Expand Up @@ -312,13 +317,27 @@ protected function buildHighlightingQuery(string $q): string
{
$q = trim($q);
$words = preg_split('#[\s,]+#', $q, -1, PREG_SPLIT_NO_EMPTY);
if (\is_array($words) && \count($words) > 1) {
if (!\is_array($words) || \count($words) > 1) {
return $this->escapeQuery($q);
}

$q = $this->escapeQuery($q);
$escapedQuery = $this->escapeQuery($q);
if (!$this->shouldFuzzify($q)) {
return $escapedQuery;
}

return $escapedQuery.$this->getFuzzySuffix();
}

private function shouldFuzzify(string $word): bool
{
return $this->fuzzyProximity > 0
&& \mb_strlen($word) >= $this->fuzzyMinTermLength;
}

return sprintf('%s~2', $q);
private function getFuzzySuffix(): string
{
return '~'.$this->fuzzyProximity;
}

protected function buildQueryFields(array &$args, bool $searchTags = true): string
Expand Down
39 changes: 39 additions & 0 deletions lib/RoadizSolrBundle/src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace RZ\Roadiz\SolrBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

final class Configuration implements ConfigurationInterface
{
#[\Override]
public function getConfigTreeBuilder(): TreeBuilder
{
$builder = new TreeBuilder('roadiz_solr');
$root = $builder->getRootNode();

$root
->addDefaultsIfNotSet()
->children()
->arrayNode('search')
->addDefaultsIfNotSet()
->children()
->integerNode('fuzzy_proximity')
->defaultValue(2)
->min(0)
->max(2)
->end()
->integerNode('fuzzy_min_term_length')
->defaultValue(3)
->min(0)
->end()
->end()
->end()
->end();

return $builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,48 @@ public function getAlias(): string
#[\Override]
public function load(array $configs, ContainerBuilder $container): void
{
$config = $this->processConfiguration(new Configuration(), $configs);

$loader = new YamlFileLoader($container, new FileLocator(dirname(__DIR__).'/../config'));
$loader->load('services.yaml');

$fuzzySearchConfig = $this->resolveFuzzySearchConfig($configs, $config, $container);
$container->setParameter('roadiz_solr.search.fuzzy_proximity', $fuzzySearchConfig['fuzzy_proximity']);
$container->setParameter('roadiz_solr.search.fuzzy_min_term_length', $fuzzySearchConfig['fuzzy_min_term_length']);
}

/**
* @return array{fuzzy_proximity: mixed, fuzzy_min_term_length: mixed}
*/
private function resolveFuzzySearchConfig(array $configs, array $config, ContainerBuilder $container): array
{
$hasRoadizSolrFuzzyProximity = false;
$hasRoadizSolrFuzzyMinTermLength = false;

foreach ($configs as $singleConfig) {
if (isset($singleConfig['search']['fuzzy_proximity'])) {
$hasRoadizSolrFuzzyProximity = true;
}
if (isset($singleConfig['search']['fuzzy_min_term_length'])) {
$hasRoadizSolrFuzzyMinTermLength = true;
}
}

$fuzzyProximity = $hasRoadizSolrFuzzyProximity ? $config['search']['fuzzy_proximity'] : null;
$fuzzyMinTermLength = $hasRoadizSolrFuzzyMinTermLength ? $config['search']['fuzzy_min_term_length'] : null;

foreach ($container->getExtensionConfig('roadiz_core') as $legacyConfig) {
if (null === $fuzzyProximity && isset($legacyConfig['solr']['search']['fuzzy_proximity'])) {
$fuzzyProximity = $legacyConfig['solr']['search']['fuzzy_proximity'];
}
if (null === $fuzzyMinTermLength && isset($legacyConfig['solr']['search']['fuzzy_min_term_length'])) {
$fuzzyMinTermLength = $legacyConfig['solr']['search']['fuzzy_min_term_length'];
}
}

return [
'fuzzy_proximity' => $fuzzyProximity ?? $config['search']['fuzzy_proximity'],
'fuzzy_min_term_length' => $fuzzyMinTermLength ?? $config['search']['fuzzy_min_term_length'],
];
}
}