Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/phpunit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ jobs:

- name: 'Run phpunit tests'
run: |
vendor/bin/simple-phpunit --coverage-clover=tests/App/build/clover.xml
vendor/bin/phpunit --coverage-clover=tests/App/build/clover.xml

- name: Upload coverage results to Coveralls
uses: coverallsapp/github-action@v2
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
.phpunit.result.cache
.idea
/.php-cs-fixer.cache
/.phpstan-cache
/.phpstan-cache/
/.rector-cache/
/.phpunit.cache/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ This bundle is licensed under the MIT license. Please, see the complete license
```
composer install
docker compose up --detach --wait
vendor/bin/simple-phpunit
vendor/bin/phpunit
docker compose down --remove-orphans
```

Expand Down
22 changes: 11 additions & 11 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,22 @@
"symfony/http-kernel": "^5.4 || ^6.4",
"symfony/event-dispatcher-contracts": "^3.5",

"doctrine/annotations": "^1.2",
"doctrine/cache": "^1.4",
"elasticsearch/elasticsearch": "^8.0"
},
"require-dev": {
"symfony/stopwatch": "^5.4 || ^6.4",
"symfony/phpunit-bridge": "^5.4 || ^6.4",
"symfony/browser-kit": "^5.4 || ^6.4",
"symfony/dotenv": "^5.4 || ^6.4",
"doctrine/orm": "^2.6.3",

"monolog/monolog": "^2.0|^3.0",
"doctrine/orm": "^2.6.3",
"doctrine/annotations": "^1.2",
"knplabs/knp-paginator-bundle": "^4.0 || ^5.0",
"friendsofphp/php-cs-fixer": "^3.34",
"monolog/monolog": "^2.0|^3.0",

"phpunit/phpunit": "^10.5",
"php-coveralls/php-coveralls": "^2.1",
"jchook/phpunit-assert-throws": "^1.0",
"dms/phpunit-arraysubset-asserts": "^0.2.1",
"dms/phpunit-arraysubset-asserts": "^0.5.0",

"friendsofphp/php-cs-fixer": "^3.34",
"phpstan/phpstan": "^1.12",
"phpstan/phpstan-symfony": "^1.4",
"phpstan/phpstan-phpunit": "^1.4",
Expand All @@ -48,7 +47,8 @@
"suggest": {
"monolog/monolog": "Allows for client-level logging and tracing",
"knplabs/knp-paginator-bundle": "Allows for search results to be paginated",
"doctrine/orm": "Allows for using Doctrine as source for rebuilding indices"
"doctrine/orm": "Allows for using Doctrine as source for rebuilding indices",
"doctrine/annotations": "Allows for using annotations to configure the bundle, which is now deprecated"
},
"autoload": {
"psr-4": {
Expand All @@ -69,7 +69,7 @@
},
"scripts": {
"run-tests": [
"XDEBUG_MODE=coverage vendor/bin/simple-phpunit --coverage-text"
"XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text"
],
"check-code": [
"vendor/bin/phpstan",
Expand Down
11 changes: 6 additions & 5 deletions config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,19 @@ services:
arguments:
- '%sfes.entity_locations%'

Sineflow\ElasticsearchBundle\Mapping\DocumentParser:
Sineflow\ElasticsearchBundle\Mapping\DocumentAttributeParser:
arguments:
- '@annotation_reader'
- '@Sineflow\ElasticsearchBundle\Mapping\DocumentLocator'
- '%sfes.mlproperty.language_separator%'
- '%sfes.languages%'

Sineflow\ElasticsearchBundle\Mapping\DocumentMetadataCollector:
arguments:
- '%sfes.indices%'
- '@Sineflow\ElasticsearchBundle\Mapping\DocumentLocator'
- '@Sineflow\ElasticsearchBundle\Mapping\DocumentParser'
$indexManagers: '%sfes.indices%'
$documentLocator: '@Sineflow\ElasticsearchBundle\Mapping\DocumentLocator'
$annotationParser: '@?Sineflow\ElasticsearchBundle\Mapping\DocumentParser'
$attributeParser: '@Sineflow\ElasticsearchBundle\Mapping\DocumentAttributeParser'
$useAnnotations: '%sfes.use_annotations%'

Sineflow\ElasticsearchBundle\Subscriber\KnpPaginateQuerySubscriber:
arguments: ['@request_stack']
Expand Down
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ sineflow_elasticsearch:

languages: ['en', 'fr']

use_annotations: false

metadata_cache_pool: sfes.metadata_cache_pool

entity_locations:
Expand Down
13 changes: 6 additions & 7 deletions docs/crud.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ For all steps below we assume that there is an `App` entity location with the `P
use Sineflow\ElasticsearchBundle\Annotation as ES;
use Sineflow\ElasticsearchBundle\Document\AbstractDocument;

/**
* @ES\Document
*/
#[ES\Document]
class Product extends AbstractDocument
{
/**
* @ES\Property(type="text", name="title")
*/
public $title;
#[ES\Property(
name: 'title',
type: 'text',
)]
public ?string $title = null;
}

```
Expand Down
28 changes: 13 additions & 15 deletions docs/i18n.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,19 @@ sineflow_elasticsearch:
* Next, you need to declare your multilanguage field as such in your annotation:

```
/**
* @ES\Property(
* name="title",
* type="text",
* multilanguage=true,
* multilanguageDefaultOptions={
* "type":"text",
* "index":false
* },
* options={
* "analyzer":"{lang}_analyzer",
* }
* )
*/
public $title;
#[ES\Property(
name: 'title',
type: 'text',
multilanguage: true,
multilanguageDefaultOptions: [
'type' => 'text',
'index' => false,
],
options: [
'analyzer' => '{lang}_analyzer',
],
)]
public ?MLProperty $title = null;
```
> Note the use of **{lang}** in the analyzer declaration. It is going to be replaced by a respective language code at runtime.

Expand Down
182 changes: 182 additions & 0 deletions docs/mapping-annotations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Mapping

The Elasticsearch bundle requires document mapping definitions to create the correct index schema and be able to convert data to objects and vice versa - think Doctrine.

For this to work, you need `doctrine/annotations` installed in your project and `use_annotations` set to `true` in your configuration.

## Document class annotations

Elasticsearch index mappings are defined using annotations within document entity classes that implement DocumentInterface:
```php
<?php
namespace App\Document;

use Sineflow\ElasticsearchBundle\Document\AbstractDocument;
use Sineflow\ElasticsearchBundle\Annotation as ES;

/**
* @ES\Document
*/
class Product extends AbstractDocument
{
/**
* @var string
*
* @ES\Property(name="title", type="text")
*/
public $title;
}
```

> Make sure your document classes directly implement DocumentInterface or extend AbstractDocument.


### Document annotation

The class representing a document must be annotated as `@ES\Document`. The following properties are supported inside that annotation:

- `repositoryClass` Allows you to specify a specific repository class for this document. If not specified, the default repository class is used.
```
repositoryClass="App\Document\Repository\ProductRepository"
```

- `providerClass` Allows you to specify a specific data provider that will be used as data source when rebuilding the index. If not specified, the default self-provider is used, i.e the index is rebuilt from itself.
```
providerClass="App\Document\Provider\ProductProvider"
```

- `options` Allows to specify any type option supported by Elasticsearch, such as [\_all](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-all-field.html), [dynamic_templates](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html), [dynamic_date_formats](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-field-mapping.html#date-detection), etc.

### Property annotation

Each field within the document is specified using the `@ES\Property` annotation. The following properties are supported inside that annotation:

- `name` Specifies the name of the field (required).

- `multilanguage` A flag that specifies whether the field will be multilanguage. For more information, see [declaring multilanguage properties](#mlproperties).
```
multilanguage=true
```

- `objectName` When the field type is `object` or `nested`, this property must be specified, as it specifies which class defines the (nested) object.
```
objectName="App:ObjAlias"
```

- `multiple` Relevant only for `object` and `nested` fields. It specifies whether the field contains a single object or multiple ones.
```
multiple=true
```

- `options` An array of literal options, sent to Elasticsearch as they are. The only exception is with multilanguage properties, where further processing is applied.
```
options={
"analyzer":"my_special_analyzer",
"null_value":0
}
```

### <a name=mlproperties></a>Multilanguage properties

Sometimes, you may have a field that is available in more than one language. This is declared like this:

```
/**
* @ES\Property(
* name="name",
* type="text",
* multilanguage=true,
* multilanguageDefaultOptions={
* "type":"text",
* "index":false
* },
* options={
* "analyzer":"{lang}_analyzer",
* }
* )
*/
public $name;
```
> Note the use of `{lang}` placeholder in the options.

When you have a property definition like that, there will not be a field `name` in your index, but instead there will be `name-en`, `name-fr`, `name-de`, etc. where the suffixes are taken from the available languages in your application.
There will also be a field `name-default`, whose default mapping of `type:keyword;ignore_above:256` you can optionally override by specifying alternative `multilanguageDefaultOptions`.

You may also use the special `{lang}` placeholder in the options array, as often you would need to specify different analyzers, depending on the language. For more information on how that works, see [multilanguage support](i18n.md).

### Meta property annotations

#### @ES\Id

If you need to have access to the `_id` property of an Elasticsearch document you need to have a class property with this annotation.
This way, you can specify the `_id` when you create or update a document and you will also have that value populated in your object when you retrieve an existing document.

```php
use Sineflow\ElasticsearchBundle\Annotation as ES;

/**
* @ES\Document
*/
class Product
{
/**
* @var string
*
* @ES\Id
*/
public $id;
}
```
> Such property is already defined in `AbstractDocument`, so you can just extend it.

#### @ES\Score

You should have a property with this annotation, if you wish the matching `_score` of the document to be populated in it when searching.

```php
use Sineflow\ElasticsearchBundle\Annotation as ES;

/**
* @ES\Document
*/
class Product
{
/**
* @var float
*
* @ES\Score
*/
public $score;
}
```
> Such property is already defined in `AbstractDocument`, so you can just extend it.

## DocObject class annotation

Object classes are almost the same as document classes:

```php
<?php
namespace App\Document;

use Sineflow\ElasticsearchBundle\Document\ObjectInterface;
use Sineflow\ElasticsearchBundle\Annotation as ES;

/**
* @ES\DocObject
*/
class ObjAlias implements ObjectInterface
{
/**
* @var string
*
* @ES\Property(name="title", type="text")
*/
public $title;
}
```

The difference with document classes is that the class must implement `ObjectInterface` and be annotated as `@ES\DocObject`. The mapping of the object properties follows the same rules as the one for the document properties.


More info about mapping is in the [elasticsearch mapping documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html)
Loading