Refactor documentation
This commit is contained in:
parent
90022b0d0a
commit
5f8b8003d1
|
@ -8,6 +8,11 @@ use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||||
|
|
||||||
class Configuration implements ConfigurationInterface
|
class Configuration implements ConfigurationInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Stores supported database drivers.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $supportedDrivers = array('orm', 'mongodb', 'propel');
|
private $supportedDrivers = array('orm', 'mongodb', 'propel');
|
||||||
|
|
||||||
private $configArray = array();
|
private $configArray = array();
|
||||||
|
@ -32,8 +37,12 @@ class Configuration implements ConfigurationInterface
|
||||||
|
|
||||||
$rootNode
|
$rootNode
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('default_client')->end()
|
->scalarNode('default_client')
|
||||||
->scalarNode('default_index')->end()
|
->info('Defaults to the first client defined')
|
||||||
|
->end()
|
||||||
|
->scalarNode('default_index')
|
||||||
|
->info('Defaults to the first index defined')
|
||||||
|
->end()
|
||||||
->scalarNode('default_manager')->defaultValue('orm')->end()
|
->scalarNode('default_manager')->defaultValue('orm')->end()
|
||||||
->arrayNode('serializer')
|
->arrayNode('serializer')
|
||||||
->treatNullLike(array())
|
->treatNullLike(array())
|
||||||
|
@ -105,6 +114,7 @@ class Configuration implements ConfigurationInterface
|
||||||
->scalarNode('host')->end()
|
->scalarNode('host')->end()
|
||||||
->scalarNode('port')->end()
|
->scalarNode('port')->end()
|
||||||
->scalarNode('logger')
|
->scalarNode('logger')
|
||||||
|
->info('Set your own logger service for this client or disable the logger with false.')
|
||||||
->defaultValue('fos_elastica.logger')
|
->defaultValue('fos_elastica.logger')
|
||||||
->treatNullLike('fos_elastica.logger')
|
->treatNullLike('fos_elastica.logger')
|
||||||
->treatTrueLike('fos_elastica.logger')
|
->treatTrueLike('fos_elastica.logger')
|
||||||
|
@ -134,11 +144,14 @@ class Configuration implements ConfigurationInterface
|
||||||
->prototype('array')
|
->prototype('array')
|
||||||
->performNoDeepMerging()
|
->performNoDeepMerging()
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('index_name')->end()
|
->scalarNode('index_name')
|
||||||
|
->info('Defaults to the name of the index, but can be modified if the index name is different in ElasticSearch')
|
||||||
|
->end()
|
||||||
->scalarNode('client')->end()
|
->scalarNode('client')->end()
|
||||||
->scalarNode('finder')
|
->scalarNode('finder')
|
||||||
|
->info('Defines an index wide finder that will search all types.')
|
||||||
->treatNullLike(true)
|
->treatNullLike(true)
|
||||||
->defaultTrue()
|
->defaultFalse()
|
||||||
->end()
|
->end()
|
||||||
->append($this->getTypePrototypeNode())
|
->append($this->getTypePrototypeNode())
|
||||||
->variableNode('settings')->defaultValue(array())->end()
|
->variableNode('settings')->defaultValue(array())->end()
|
||||||
|
@ -157,6 +170,7 @@ class Configuration implements ConfigurationInterface
|
||||||
{
|
{
|
||||||
$builder = new TreeBuilder();
|
$builder = new TreeBuilder();
|
||||||
$node = $builder->root('type_prototype');
|
$node = $builder->root('type_prototype');
|
||||||
|
$node->info('Allows a prototype type definition that can be applied to all types.');
|
||||||
|
|
||||||
$this->applyTypeConfiguration($node);
|
$this->applyTypeConfiguration($node);
|
||||||
|
|
||||||
|
|
868
README.md
868
README.md
|
@ -1,862 +1,30 @@
|
||||||
[Elastica](https://github.com/ruflin/Elastica) integration in Symfony2
|
FOSElasticaBundle
|
||||||
|
=================
|
||||||
|
|
||||||
### Installation
|
This bundle provides integration with [ElasticSearch](http://www.elasticsearch.org) and [Elastica](https://github.com/ruflin/Elastica) with
|
||||||
|
Symfony2. Features include:
|
||||||
|
|
||||||
#### Bundle and Dependencies
|
- Features...
|
||||||
|
|
||||||
For Symfony 2.0.x projects, you must use a 1.x release of this bundle. Please
|
[![Build Status](https://secure.travis-ci.org/FriendsOfSymfony/FOSElasticaBundle.png?branch=master)](http://travis-ci.org/FriendsOfSymfony/FOSElasticaBundle) [![Total Downloads](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/downloads.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle) [![Latest Stable Version](https://poser.pugx.org/FriendsOfSymfony/elastica-bundle/v/stable.png)](https://packagist.org/packages/FriendsOfSymfony/elastica-bundle)
|
||||||
check the bundle
|
|
||||||
[tags](https://github.com/FriendsOfSymfony/FOSElasticaBundle/tags) or the
|
|
||||||
[Packagist](https://packagist.org/packages/friendsofsymfony/elastica-bundle)
|
|
||||||
page for information on Symfony and Elastica compatibility.
|
|
||||||
|
|
||||||
Add FOSElasticaBundle to your application's `composer.json` file:
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
```json
|
Documentation for FOSElasticaBundle is in `Resources/doc/index.md`
|
||||||
{
|
|
||||||
"require": {
|
|
||||||
"friendsofsymfony/elastica-bundle": "3.0.*@dev"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Install the bundle and its dependencies with the following command:
|
[Read the documentation for 3.0.x (master))](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/index.md)
|
||||||
|
|
||||||
```bash
|
[Read the documentation for 2.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/2.1.x/README.md)
|
||||||
$ php composer.phar update friendsofsymfony/elastica-bundle
|
|
||||||
```
|
|
||||||
|
|
||||||
You may rely on Composer to fetch the appropriate version of Elastica. Lastly,
|
Installation
|
||||||
enable the bundle in your application kernel:
|
------------
|
||||||
|
|
||||||
```php
|
Installation instructions can be found in the [documentation](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/setup.md)
|
||||||
// app/AppKernel.php
|
|
||||||
|
|
||||||
public function registerBundles()
|
License
|
||||||
{
|
-------
|
||||||
$bundles = array(
|
|
||||||
// ...
|
|
||||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Elasticsearch
|
This bundle is under the MIT license. See the complete license in the bundle:
|
||||||
|
|
||||||
Instructions for installing and deploying Elasticsearch may be found
|
Resources/meta/LICENSE
|
||||||
[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
|
|
||||||
|
|
||||||
### Basic configuration
|
|
||||||
|
|
||||||
#### Declare a client
|
|
||||||
|
|
||||||
Elasticsearch client is comparable to a database connection.
|
|
||||||
Most of the time, you will need only one.
|
|
||||||
|
|
||||||
#app/config/config.yml
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
|
|
||||||
|
|
||||||
#### Declare a serializer
|
|
||||||
|
|
||||||
Elastica can handle objects instead of data arrays if a serializer callable is configured
|
|
||||||
|
|
||||||
#app/config/config.yml
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
serializer:
|
|
||||||
callback_class: callback_class
|
|
||||||
serializer: serializer
|
|
||||||
|
|
||||||
``callback_class`` is the name of a class having a public method serialize($object) and should
|
|
||||||
extends from ``FOS\ElasticaBundle\Serializer\Callback``.
|
|
||||||
|
|
||||||
``serializer`` is the service id for the actual serializer, e.g. ``serializer`` if you're using
|
|
||||||
JMSSerializerBundle. If this is configured you can use ``\Elastica\Type::addObject`` instead of
|
|
||||||
``\Elastica\Type::addDocument`` to add data to the index. The bundle provides a default implementation
|
|
||||||
with a serializer service id 'serializer' that can be turned on by adding the following line to your config.
|
|
||||||
|
|
||||||
#app/config/config.yml
|
|
||||||
fos_elastica:
|
|
||||||
serializer: ~
|
|
||||||
|
|
||||||
#### Declare an index
|
|
||||||
|
|
||||||
Elasticsearch index is comparable to Doctrine entity manager.
|
|
||||||
Most of the time, you will need only one.
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
serializer:
|
|
||||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
|
||||||
serializer: serializer
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
|
|
||||||
Here we created a "website" index, that uses our "default" client.
|
|
||||||
|
|
||||||
Our index is now available as a service: `fos_elastica.index.website`. It is an instance of `\Elastica\Index`.
|
|
||||||
|
|
||||||
If you need to have different index name from the service name, for example,
|
|
||||||
in order to have different indexes for different environments then you can
|
|
||||||
use the ```index_name``` key to change the index name. The service name will
|
|
||||||
remain the same across the environments:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
index_name: website_qa
|
|
||||||
|
|
||||||
The service id will be `fos_elastica.index.website` but the underlying index name is website_qa.
|
|
||||||
|
|
||||||
#### Declare a type
|
|
||||||
|
|
||||||
Elasticsearch type is comparable to Doctrine entity repository.
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
serializer:
|
|
||||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
|
||||||
serializer: serializer
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
username: { boost: 5 }
|
|
||||||
firstName: { boost: 3 }
|
|
||||||
lastName: { boost: 3 }
|
|
||||||
aboutMe: ~
|
|
||||||
|
|
||||||
Our type is now available as a service: `fos_elastica.index.website.user`. It is an instance of `\Elastica\Type`.
|
|
||||||
|
|
||||||
### Declaring serializer groups
|
|
||||||
|
|
||||||
If you are using the JMSSerializerBundle for serializing objects passed to elastica you can define serializer groups
|
|
||||||
per type.
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
serializer:
|
|
||||||
callback_class: %classname%
|
|
||||||
serializer: serializer
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
username: { boost: 5 }
|
|
||||||
firstName: { boost: 3 }
|
|
||||||
lastName: { boost: 3 }
|
|
||||||
aboutMe:
|
|
||||||
serializer:
|
|
||||||
groups: [elastica, Default]
|
|
||||||
|
|
||||||
### Declaring parent field
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
serializer:
|
|
||||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
|
||||||
serializer: serializer
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
comment:
|
|
||||||
mappings:
|
|
||||||
date: { boost: 5 }
|
|
||||||
content: ~
|
|
||||||
_parent: { type: "post", property: "post", identifier: "id" }
|
|
||||||
|
|
||||||
The parent field declaration has the following values:
|
|
||||||
|
|
||||||
* `type`: The parent type.
|
|
||||||
* `property`: The property in the child entity where to look for the parent entity. It may be ignored if is equal to the parent type.
|
|
||||||
* `identifier`: The property in the parent entity which has the parent identifier. Defaults to `id`.
|
|
||||||
|
|
||||||
Note that to create a document with a parent, you need to call `setParent` on the document rather than setting a _parent field.
|
|
||||||
If you do this wrong, you will see a `RoutingMissingException` as elasticsearch does not know where to store a document that should have a parent but does not specify it.
|
|
||||||
|
|
||||||
### Declaring `nested` or `object`
|
|
||||||
|
|
||||||
Note that object can autodetect properties
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
serializer:
|
|
||||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
|
||||||
serializer: serializer
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
post:
|
|
||||||
mappings:
|
|
||||||
date: { boost: 5 }
|
|
||||||
title: { boost: 3 }
|
|
||||||
content: ~
|
|
||||||
comments:
|
|
||||||
type: "nested"
|
|
||||||
properties:
|
|
||||||
date: { boost: 5 }
|
|
||||||
content: ~
|
|
||||||
user:
|
|
||||||
type: "object"
|
|
||||||
approver:
|
|
||||||
type: "object"
|
|
||||||
properties:
|
|
||||||
date: { boost: 5 }
|
|
||||||
|
|
||||||
#### Doctrine ORM and `object` mappings
|
|
||||||
|
|
||||||
Objects operate in the same way as the nested results but they need to have associations set up in Doctrine ORM so that they can be referenced correctly when indexing.
|
|
||||||
|
|
||||||
If an "Entity was not found" error occurs while indexing, a null association has been discovered in the database. A custom Doctrine query must be used to utilize left joins instead of the default inner join.
|
|
||||||
|
|
||||||
### Populate the types
|
|
||||||
|
|
||||||
php app/console fos:elastica:populate
|
|
||||||
|
|
||||||
This command deletes and creates the declared indexes and types.
|
|
||||||
It applies the configured mappings to the types.
|
|
||||||
|
|
||||||
This command needs providers to insert new documents in the elasticsearch types.
|
|
||||||
There are 2 ways to create providers.
|
|
||||||
If your elasticsearch type matches a Doctrine repository or a Propel query, go for the persistence automatic provider.
|
|
||||||
Or, for complete flexibility, go for a manual provider.
|
|
||||||
|
|
||||||
#### Persistence automatic provider
|
|
||||||
|
|
||||||
If we want to index the entities from a Doctrine repository or a Propel query,
|
|
||||||
some configuration will let ElasticaBundle do it for us.
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
serializer:
|
|
||||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
|
||||||
serializer: serializer
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
username: { boost: 5 }
|
|
||||||
firstName: { boost: 3 }
|
|
||||||
# more mappings...
|
|
||||||
persistence:
|
|
||||||
driver: orm # orm, mongodb, propel are available
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider: ~
|
|
||||||
|
|
||||||
Three drivers are actually supported: orm, mongodb, and propel.
|
|
||||||
|
|
||||||
##### Use a custom Doctrine query builder
|
|
||||||
|
|
||||||
You can control which entities will be indexed by specifying a custom query builder method.
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider:
|
|
||||||
query_builder_method: createIsActiveQueryBuilder
|
|
||||||
|
|
||||||
Your repository must implement this method and return a Doctrine query builder.
|
|
||||||
|
|
||||||
> **Propel** doesn't support this feature yet.
|
|
||||||
|
|
||||||
##### Change the batch size
|
|
||||||
|
|
||||||
By default, ElasticaBundle will index documents by packets of 100.
|
|
||||||
You can change this value in the provider configuration.
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider:
|
|
||||||
batch_size: 100
|
|
||||||
|
|
||||||
##### Change the document identifier field
|
|
||||||
|
|
||||||
By default, ElasticaBundle will use the `id` field of your entities as the elasticsearch document identifier.
|
|
||||||
You can change this value in the persistence configuration.
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
identifier: id
|
|
||||||
|
|
||||||
#### Manual provider
|
|
||||||
|
|
||||||
Create a service with the tag "fos_elastica.provider" and attributes for the
|
|
||||||
index and type for which the service will provide.
|
|
||||||
|
|
||||||
<service id="acme.search_provider.user" class="Acme\UserBundle\Search\UserProvider">
|
|
||||||
<tag name="fos_elastica.provider" index="website" type="user" />
|
|
||||||
<argument type="service" id="fos_elastica.index.website.user" />
|
|
||||||
</service>
|
|
||||||
|
|
||||||
Its class must implement `FOS\ElasticaBundle\Provider\ProviderInterface`.
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Acme\UserBundle\Provider;
|
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
|
||||||
use Elastica\Type;
|
|
||||||
use Elastica\Document;
|
|
||||||
|
|
||||||
class UserProvider implements ProviderInterface
|
|
||||||
{
|
|
||||||
protected $userType;
|
|
||||||
|
|
||||||
public function __construct(Type $userType)
|
|
||||||
{
|
|
||||||
$this->userType = $userType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert the repository objects in the type index
|
|
||||||
*
|
|
||||||
* @param \Closure $loggerClosure
|
|
||||||
* @param array $options
|
|
||||||
*/
|
|
||||||
public function populate(\Closure $loggerClosure = null, array $options = array())
|
|
||||||
{
|
|
||||||
if ($loggerClosure) {
|
|
||||||
$loggerClosure('Indexing users');
|
|
||||||
}
|
|
||||||
|
|
||||||
$document = new Document();
|
|
||||||
$document->setData(array('username' => 'Bob'));
|
|
||||||
$this->userType->addDocuments(array($document));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
You will find a more complete implementation example in `src/FOS/ElasticaBundle/Doctrine/AbstractProvider.php`.
|
|
||||||
|
|
||||||
### Search
|
|
||||||
|
|
||||||
You can just use the index and type Elastica objects, provided as services, to perform searches.
|
|
||||||
|
|
||||||
/** var Elastica\Type */
|
|
||||||
$userType = $this->container->get('fos_elastica.index.website.user');
|
|
||||||
|
|
||||||
/** var Elastica\ResultSet */
|
|
||||||
$resultSet = $userType->search('bob');
|
|
||||||
|
|
||||||
#### Doctrine/Propel finder
|
|
||||||
|
|
||||||
If your elasticsearch type is bound to a Doctrine entity repository or a Propel query,
|
|
||||||
you can get your entities instead of Elastica results when you perform a search.
|
|
||||||
Declare that you want a Doctrine/Propel finder in your configuration:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
serializer:
|
|
||||||
callback_class: FOS\ElasticaBundle\Serializer\Callback
|
|
||||||
serializer: serializer
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
# your mappings
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider: ~
|
|
||||||
finder: ~
|
|
||||||
|
|
||||||
You can now use the `fos_elastica.finder.website.user` service:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Finder\TransformedFinder */
|
|
||||||
$finder = $container->get('fos_elastica.finder.website.user');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User */
|
|
||||||
$users = $finder->find('bob');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User limited to 10 results */
|
|
||||||
$users = $finder->find('bob', 10);
|
|
||||||
|
|
||||||
You can even get paginated results!
|
|
||||||
|
|
||||||
Pagerfanta:
|
|
||||||
|
|
||||||
/** var Pagerfanta\Pagerfanta */
|
|
||||||
$userPaginator = $finder->findPaginated('bob');
|
|
||||||
|
|
||||||
/** Number of results to be used for paging the results */
|
|
||||||
$countOfResults = $userPaginator->getNbResults();
|
|
||||||
|
|
||||||
Knp paginator:
|
|
||||||
|
|
||||||
$paginator = $this->get('knp_paginator');
|
|
||||||
$userPaginator = $paginator->paginate($finder->createPaginatorAdapter('bob'));
|
|
||||||
|
|
||||||
You can also get both the Elastica results and the entities together from the finder.
|
|
||||||
You can then access the score, highlights etc. from the Elastica\Result whilst
|
|
||||||
still also getting the entity.
|
|
||||||
|
|
||||||
/** var array of FOS\ElasticaBundle\HybridResult */
|
|
||||||
$hybridResults = $finder->findHybrid('bob');
|
|
||||||
foreach ($hybridResults as $hybridResult) {
|
|
||||||
|
|
||||||
/** var Acme\UserBundle\Entity\User */
|
|
||||||
$user = $hybridResult->getTransformed();
|
|
||||||
|
|
||||||
/** var Elastica\Result */
|
|
||||||
$result = $hybridResult->getResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
If you would like to access facets while using Pagerfanta they can be accessed through
|
|
||||||
the Adapter seen in the example below.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$query = new \Elastica\Query();
|
|
||||||
$facet = new \Elastica\Facet\Terms('tags');
|
|
||||||
$facet->setField('companyGroup');
|
|
||||||
$query->addFacet($facet);
|
|
||||||
|
|
||||||
$companies = $finder->findPaginated($query);
|
|
||||||
$companies->setMaxPerPage($params['limit']);
|
|
||||||
$companies->setCurrentPage($params['page']);
|
|
||||||
|
|
||||||
$facets = $companies->getAdapter()->getFacets());
|
|
||||||
```
|
|
||||||
|
|
||||||
##### Index wide finder
|
|
||||||
|
|
||||||
You can also define a finder that will work on the entire index. Adjust your index
|
|
||||||
configuration as per below:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
finder: ~
|
|
||||||
|
|
||||||
You can now use the index wide finder service `fos_elastica.finder.website`:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Finder\MappedFinder */
|
|
||||||
$finder = $container->get('fos_elastica.finder.website');
|
|
||||||
|
|
||||||
// Returns a mixed array of any objects mapped
|
|
||||||
$results = $finder->find('bob');
|
|
||||||
|
|
||||||
#### Repositories
|
|
||||||
|
|
||||||
As well as using the finder service for a particular Doctrine/Propel entity you
|
|
||||||
can use a manager service for each driver and get a repository for an entity to search
|
|
||||||
against. This allows you to use the same service rather than the particular finder. For
|
|
||||||
example:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
|
||||||
$repositoryManager = $container->get('fos_elastica.manager.orm');
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Repository */
|
|
||||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User */
|
|
||||||
$users = $repository->find('bob');
|
|
||||||
|
|
||||||
You can also specify the full name of the entity instead of the shortcut syntax:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Repository */
|
|
||||||
$repository = $repositoryManager->getRepository('Application\UserBundle\Entity\User');
|
|
||||||
|
|
||||||
> The **2.0** branch doesn't support using `UserBundle:User` style syntax and you must use the full name of the entity. .
|
|
||||||
|
|
||||||
##### Default Manager
|
|
||||||
|
|
||||||
If you are only using one driver then its manager service is automatically aliased
|
|
||||||
to `fos_elastica.manager`. So the above example could be simplified to:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
|
||||||
$repositoryManager = $container->get('fos_elastica.manager');
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Repository */
|
|
||||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User */
|
|
||||||
$users = $repository->find('bob');
|
|
||||||
|
|
||||||
If you use multiple drivers then you can choose which one is aliased to `fos_elastica.manager`
|
|
||||||
using the `default_manager` parameter:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
default_manager: mongodb #defaults to orm
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
#--
|
|
||||||
|
|
||||||
##### Custom Repositories
|
|
||||||
|
|
||||||
As well as the default repository you can create a custom repository for an entity and add
|
|
||||||
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
|
|
||||||
access to the finder:
|
|
||||||
|
|
||||||
```
|
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Acme\ElasticaBundle\SearchRepository;
|
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Repository;
|
|
||||||
|
|
||||||
class UserRepository extends Repository
|
|
||||||
{
|
|
||||||
public function findWithCustomQuery($searchText)
|
|
||||||
{
|
|
||||||
// build $query with Elastica objects
|
|
||||||
$this->find($query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
To use the custom repository specify it in the mapping for the entity:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
# your mappings
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
provider: ~
|
|
||||||
finder: ~
|
|
||||||
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
|
||||||
|
|
||||||
Then the custom queries will be available when using the repository returned from the manager:
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
|
||||||
$repositoryManager = $container->get('fos_elastica.manager');
|
|
||||||
|
|
||||||
/** var FOS\ElasticaBundle\Repository */
|
|
||||||
$repository = $repositoryManager->getRepository('UserBundle:User');
|
|
||||||
|
|
||||||
/** var array of Acme\UserBundle\Entity\User */
|
|
||||||
$users = $repository->findWithCustomQuery('bob');
|
|
||||||
|
|
||||||
Alternatively you can specify the custom repository using an annotation in the entity:
|
|
||||||
|
|
||||||
```
|
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Application\UserBundle\Entity;
|
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Configuration\Search;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Search(repositoryClass="Acme\ElasticaBundle\SearchRepository\UserRepository")
|
|
||||||
*/
|
|
||||||
class User
|
|
||||||
{
|
|
||||||
|
|
||||||
//---
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Realtime, selective index update
|
|
||||||
|
|
||||||
If you use the Doctrine integration, you can let ElasticaBundle update the indexes automatically
|
|
||||||
when an object is added, updated or removed. It uses Doctrine lifecycle events.
|
|
||||||
Declare that you want to update the index in real time:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
website:
|
|
||||||
client: default
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
# your mappings
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Application\UserBundle\Entity\User
|
|
||||||
listener: ~ # by default, listens to "insert", "update" and "delete"
|
|
||||||
|
|
||||||
Now the index is automatically updated each time the state of the bound Doctrine repository changes.
|
|
||||||
No need to repopulate the whole "user" index when a new `User` is created.
|
|
||||||
|
|
||||||
You can also choose to only listen for some of the events:
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
listener:
|
|
||||||
insert: true
|
|
||||||
update: false
|
|
||||||
delete: true
|
|
||||||
|
|
||||||
> **Propel** doesn't support this feature yet.
|
|
||||||
|
|
||||||
### Checking an entity method for listener
|
|
||||||
|
|
||||||
If you use listeners to update your index, you may need to validate your
|
|
||||||
entities before you index them (e.g. only index "public" entities). Typically,
|
|
||||||
you'll want the listener to be consistent with the provider's query criteria.
|
|
||||||
This may be achieved by using the `is_indexable_callback` config parameter:
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
listener:
|
|
||||||
is_indexable_callback: "isPublic"
|
|
||||||
|
|
||||||
If `is_indexable_callback` is a string and the entity has a method with the
|
|
||||||
specified name, the listener will only index entities for which the method
|
|
||||||
returns `true`. Additionally, you may provide a service and method name pair:
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
listener:
|
|
||||||
is_indexable_callback: [ "%custom_service_id%", "isIndexable" ]
|
|
||||||
|
|
||||||
In this case, the callback_class will be the `isIndexable()` method on the specified
|
|
||||||
service and the object being considered for indexing will be passed as the only
|
|
||||||
argument. This allows you to do more complex validation (e.g. ACL checks).
|
|
||||||
|
|
||||||
If you have the [Symfony ExpressionLanguage](https://github.com/symfony/expression-language) component installed, you can use expressions
|
|
||||||
to evaluate the callback:
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
listener:
|
|
||||||
is_indexable_callback: "user.isActive() && user.hasRole('ROLE_USER')"
|
|
||||||
|
|
||||||
As you might expect, new entities will only be indexed if the callback_class returns
|
|
||||||
`true`. Additionally, modified entities will be updated or removed from the
|
|
||||||
index depending on whether the callback_class returns `true` or `false`, respectively.
|
|
||||||
The delete listener disregards the callback_class.
|
|
||||||
|
|
||||||
> **Propel** doesn't support this feature yet.
|
|
||||||
|
|
||||||
### Ignoring missing index results
|
|
||||||
|
|
||||||
By default, FOSElasticaBundle will throw an exception if the results returned from
|
|
||||||
Elasticsearch are different from the results it finds from the chosen persistence
|
|
||||||
provider. This may pose problems for a large index where updates do not occur instantly
|
|
||||||
or another process has removed the results from your persistence provider without
|
|
||||||
updating Elasticsearch.
|
|
||||||
|
|
||||||
The error you're likely to see is something like:
|
|
||||||
'Cannot find corresponding Doctrine objects for all Elastica results.'
|
|
||||||
|
|
||||||
To solve this issue, each mapped object can be configured to ignore the missing results:
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
elastica_to_model_transformer:
|
|
||||||
ignore_missing: true
|
|
||||||
|
|
||||||
### Advanced elasticsearch configuration
|
|
||||||
|
|
||||||
Any setting can be specified when declaring a type. For example, to enable a custom analyzer, you could write:
|
|
||||||
|
|
||||||
fos_elastica:
|
|
||||||
indexes:
|
|
||||||
doc:
|
|
||||||
settings:
|
|
||||||
index:
|
|
||||||
analysis:
|
|
||||||
analyzer:
|
|
||||||
my_analyzer:
|
|
||||||
type: custom
|
|
||||||
tokenizer: lowercase
|
|
||||||
filter : [my_ngram]
|
|
||||||
filter:
|
|
||||||
my_ngram:
|
|
||||||
type: "nGram"
|
|
||||||
min_gram: 3
|
|
||||||
max_gram: 5
|
|
||||||
types:
|
|
||||||
blog:
|
|
||||||
mappings:
|
|
||||||
title: { boost: 8, analyzer: my_analyzer }
|
|
||||||
|
|
||||||
### Overriding the Client class to suppress exceptions
|
|
||||||
|
|
||||||
By default, exceptions from the Elastica client library will propagate through
|
|
||||||
the bundle's Client class. For instance, if the elasticsearch server is offline,
|
|
||||||
issuing a request will result in an `Elastica\Exception\Connection` being thrown.
|
|
||||||
Depending on your needs, it may be desirable to suppress these exceptions and
|
|
||||||
allow searches to fail silently.
|
|
||||||
|
|
||||||
One way to achieve this is to override the `fos_elastica.client.class` service
|
|
||||||
container parameter with a custom class. In the following example, we override
|
|
||||||
the `Client::request()` method and return the equivalent of an empty search
|
|
||||||
response if an exception occurred.
|
|
||||||
|
|
||||||
```
|
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Acme\ElasticaBundle;
|
|
||||||
|
|
||||||
use FOS\ElasticaBundle\Client as BaseClient;
|
|
||||||
|
|
||||||
use Elastica\Exception\ExceptionInterface;
|
|
||||||
use Elastica\Response;
|
|
||||||
|
|
||||||
class Client extends BaseClient
|
|
||||||
{
|
|
||||||
public function request($path, $method, $data = array())
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return parent::request($path, $method, $data);
|
|
||||||
} catch (ExceptionInterface $e) {
|
|
||||||
return new Response('{"took":0,"timed_out":false,"hits":{"total":0,"max_score":0,"hits":[]}}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Clients as Tagged Services
|
|
||||||
|
|
||||||
Clients will be tagged as `fos_elastica.client`, which makes it possible to
|
|
||||||
retrieve all clients from the service container and interact with them via a
|
|
||||||
compiler pass. See
|
|
||||||
[Working with Tagged Services](http://symfony.com/doc/current/components/dependency_injection/tags.html)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
### Example of Advanced Query
|
|
||||||
|
|
||||||
If you would like to perform more advanced queries, here is one example using
|
|
||||||
the snowball stemming algorithm.
|
|
||||||
|
|
||||||
It searches for Article entities using `title`, `tags`, and `categoryIds`.
|
|
||||||
Results must match at least one specified `categoryIds`, and should match the
|
|
||||||
`title` or `tags` criteria. Additionally, we define a snowball analyzer to
|
|
||||||
apply to queries against the `title` field.
|
|
||||||
|
|
||||||
```php
|
|
||||||
$finder = $this->container->get('fos_elastica.finder.website.article');
|
|
||||||
$boolQuery = new \Elastica\Query\Bool();
|
|
||||||
|
|
||||||
$fieldQuery = new \Elastica\Query\Text();
|
|
||||||
$fieldQuery->setFieldQuery('title', 'I am a title string');
|
|
||||||
$fieldQuery->setFieldParam('title', 'analyzer', 'my_analyzer');
|
|
||||||
$boolQuery->addShould($fieldQuery);
|
|
||||||
|
|
||||||
$tagsQuery = new \Elastica\Query\Terms();
|
|
||||||
$tagsQuery->setTerms('tags', array('tag1', 'tag2'));
|
|
||||||
$boolQuery->addShould($tagsQuery);
|
|
||||||
|
|
||||||
$categoryQuery = new \Elastica\Query\Terms();
|
|
||||||
$categoryQuery->setTerms('categoryIds', array('1', '2', '3'));
|
|
||||||
$boolQuery->addMust($categoryQuery);
|
|
||||||
|
|
||||||
$data = $finder->find($boolQuery);
|
|
||||||
```
|
|
||||||
|
|
||||||
Configuration:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
site:
|
|
||||||
settings:
|
|
||||||
index:
|
|
||||||
analysis:
|
|
||||||
analyzer:
|
|
||||||
my_analyzer:
|
|
||||||
type: snowball
|
|
||||||
language: English
|
|
||||||
types:
|
|
||||||
article:
|
|
||||||
mappings:
|
|
||||||
title: { boost: 10, analyzer: my_analyzer }
|
|
||||||
tags:
|
|
||||||
categoryIds:
|
|
||||||
persistence:
|
|
||||||
driver: orm
|
|
||||||
model: Acme\DemoBundle\Entity\Article
|
|
||||||
provider:
|
|
||||||
finder:
|
|
||||||
```
|
|
||||||
|
|
||||||
### Filtering Results and Executing a Default Query
|
|
||||||
|
|
||||||
If may want to omit certain results from a query, filtering can be more
|
|
||||||
performant than a basic query because the filter results can be cached. In turn,
|
|
||||||
the query is run against only a subset of the results. A common use case for
|
|
||||||
filtering would be if your data has fields that indicate whether records are
|
|
||||||
"active" or "inactive". The following example illustrates how to issue such a
|
|
||||||
query with Elastica:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$query = new \Elastica\Query\QueryString($queryString);
|
|
||||||
$term = new \Elastica\Filter\Term(array('active' => true));
|
|
||||||
|
|
||||||
$filteredQuery = new \Elastica\Query\Filtered($query, $term);
|
|
||||||
$results = $this->container->get('fos_elastica.finder.index.type')->find($filteredQuery);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Date format example
|
|
||||||
|
|
||||||
If you want to specify a [date format](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-date-format.html):
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
site:
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
mappings:
|
|
||||||
username: { type: string }
|
|
||||||
lastlogin: { type: date, format: basic_date_time }
|
|
||||||
birthday: { type: date, format: "yyyy-MM-dd" }
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Dynamic templates
|
|
||||||
|
|
||||||
Dynamic templates allow to define mapping templates that will be
|
|
||||||
applied when dynamic introduction of fields / objects happens.
|
|
||||||
|
|
||||||
[Documentation](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-root-object-type.html#_dynamic_templates)
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
fos_elastica:
|
|
||||||
clients:
|
|
||||||
default: { host: localhost, port: 9200 }
|
|
||||||
indexes:
|
|
||||||
site:
|
|
||||||
types:
|
|
||||||
user:
|
|
||||||
dynamic_templates:
|
|
||||||
my_template_1:
|
|
||||||
match: apples_*
|
|
||||||
mapping:
|
|
||||||
type: float
|
|
||||||
my_template_2:
|
|
||||||
match: *
|
|
||||||
match_mapping_type: string
|
|
||||||
mapping:
|
|
||||||
type: string
|
|
||||||
index: not_analyzed
|
|
||||||
mappings:
|
|
||||||
username: { type: string }
|
|
||||||
```
|
|
||||||
|
|
72
Resources/doc/cookbook/custom-repositories.md
Normal file
72
Resources/doc/cookbook/custom-repositories.md
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
##### Custom Repositories
|
||||||
|
|
||||||
|
As well as the default repository you can create a custom repository for an entity and add
|
||||||
|
methods for particular searches. These need to extend `FOS\ElasticaBundle\Repository` to have
|
||||||
|
access to the finder:
|
||||||
|
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Acme\ElasticaBundle\SearchRepository;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Repository;
|
||||||
|
|
||||||
|
class UserRepository extends Repository
|
||||||
|
{
|
||||||
|
public function findWithCustomQuery($searchText)
|
||||||
|
{
|
||||||
|
// build $query with Elastica objects
|
||||||
|
$this->find($query);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To use the custom repository specify it in the mapping for the entity:
|
||||||
|
|
||||||
|
fos_elastica:
|
||||||
|
clients:
|
||||||
|
default: { host: localhost, port: 9200 }
|
||||||
|
indexes:
|
||||||
|
website:
|
||||||
|
client: default
|
||||||
|
types:
|
||||||
|
user:
|
||||||
|
mappings:
|
||||||
|
# your mappings
|
||||||
|
persistence:
|
||||||
|
driver: orm
|
||||||
|
model: Application\UserBundle\Entity\User
|
||||||
|
provider: ~
|
||||||
|
finder: ~
|
||||||
|
repository: Acme\ElasticaBundle\SearchRepository\UserRepository
|
||||||
|
|
||||||
|
Then the custom queries will be available when using the repository returned from the manager:
|
||||||
|
|
||||||
|
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||||
|
$repositoryManager = $container->get('fos_elastica.manager');
|
||||||
|
|
||||||
|
/** var FOS\ElasticaBundle\Repository */
|
||||||
|
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||||
|
|
||||||
|
/** var array of Acme\UserBundle\Entity\User */
|
||||||
|
$users = $repository->findWithCustomQuery('bob');
|
||||||
|
|
||||||
|
Alternatively you can specify the custom repository using an annotation in the entity:
|
||||||
|
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Application\UserBundle\Entity;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Configuration\Search;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Search(repositoryClass="Acme\ElasticaBundle\SearchRepository\UserRepository")
|
||||||
|
*/
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
56
Resources/doc/cookbook/manual-provider.md
Normal file
56
Resources/doc/cookbook/manual-provider.md
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
Manual provider
|
||||||
|
===============
|
||||||
|
|
||||||
|
Create a service with the tag "fos_elastica.provider" and attributes for the
|
||||||
|
index and type for which the service will provide.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# app/config/config.yml
|
||||||
|
services:
|
||||||
|
acme.search_provider.user:
|
||||||
|
class: Acme\UserBundle\Search\UserProvider
|
||||||
|
arguments:
|
||||||
|
- @fos_elastica.index.website.user
|
||||||
|
tags:
|
||||||
|
- { name: fos_elastica.provider, index: website, type: user }
|
||||||
|
```
|
||||||
|
|
||||||
|
Its class must implement `FOS\ElasticaBundle\Provider\ProviderInterface`.
|
||||||
|
|
||||||
|
```php
|
||||||
|
|
||||||
|
namespace Acme\UserBundle\Provider;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Provider\ProviderInterface;
|
||||||
|
use Elastica\Type;
|
||||||
|
use Elastica\Document;
|
||||||
|
|
||||||
|
class UserProvider implements ProviderInterface
|
||||||
|
{
|
||||||
|
protected $userType;
|
||||||
|
|
||||||
|
public function __construct(Type $userType)
|
||||||
|
{
|
||||||
|
$this->userType = $userType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the repository objects in the type index
|
||||||
|
*
|
||||||
|
* @param \Closure $loggerClosure
|
||||||
|
* @param array $options
|
||||||
|
*/
|
||||||
|
public function populate(\Closure $loggerClosure = null, array $options = array())
|
||||||
|
{
|
||||||
|
if ($loggerClosure) {
|
||||||
|
$loggerClosure('Indexing users');
|
||||||
|
}
|
||||||
|
|
||||||
|
$document = new Document();
|
||||||
|
$document->setData(array('username' => 'Bob'));
|
||||||
|
$this->userType->addDocuments(array($document));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You will find a more complete implementation example in `src/FOS/ElasticaBundle/Doctrine/AbstractProvider.php`.
|
36
Resources/doc/cookbook/suppress-server-errors.md
Normal file
36
Resources/doc/cookbook/suppress-server-errors.md
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
Suppressing Server Errors
|
||||||
|
========================
|
||||||
|
|
||||||
|
By default, exceptions from the Elastica client library will propagate through
|
||||||
|
the bundle's Client class. For instance, if the Elasticsearch server is offline,
|
||||||
|
issuing a request will result in an `Elastica\Exception\Connection` being thrown.
|
||||||
|
Depending on your needs, it may be desirable to suppress these exceptions and
|
||||||
|
allow searches to fail silently.
|
||||||
|
|
||||||
|
One way to achieve this is to override the `fos_elastica.client.class` service
|
||||||
|
container parameter with a custom class. In the following example, we override
|
||||||
|
the `Client::request()` method and return the equivalent of an empty search
|
||||||
|
response if an exception occurred.
|
||||||
|
|
||||||
|
```
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Acme\ElasticaBundle;
|
||||||
|
|
||||||
|
use FOS\ElasticaBundle\Client as BaseClient;
|
||||||
|
|
||||||
|
use Elastica\Exception\ExceptionInterface;
|
||||||
|
use Elastica\Response;
|
||||||
|
|
||||||
|
class Client extends BaseClient
|
||||||
|
{
|
||||||
|
public function request($path, $method, $data = array())
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return parent::request($path, $method, $data);
|
||||||
|
} catch (ExceptionInterface $e) {
|
||||||
|
return new Response('{"took":0,"timed_out":false,"hits":{"total":0,"max_score":0,"hits":[]}}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
14
Resources/doc/index.md
Normal file
14
Resources/doc/index.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
FOSElasticaBundle Documentation
|
||||||
|
===============================
|
||||||
|
|
||||||
|
Available documentation for FOSElasticaBundle:
|
||||||
|
|
||||||
|
* [Setup](setup.md)
|
||||||
|
* [Usage](usage.md)
|
||||||
|
* [Using a Serializer](serializer.md)
|
||||||
|
* [Types](types.md)
|
||||||
|
|
||||||
|
Cookbook Entries
|
||||||
|
|
||||||
|
* [Custom Repositories](cookbook/custom-repositories.md)
|
||||||
|
* [Suppressing server errors](cookbook/suppress-server-errors.md)
|
39
Resources/doc/serializer.md
Normal file
39
Resources/doc/serializer.md
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
Using a Serializer in FOSElasticaBundle
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
FOSElasticaBundle supports using a Serializer component to serialize your objects to JSON
|
||||||
|
which will be sent directly to the Elasticsearch server. Combined with automatic mapping
|
||||||
|
it means types do not have to be mapped.
|
||||||
|
|
||||||
|
A) Install and declare the serializer
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Follow the installation instructions for [JMSSerializerBundle](http://jmsyst.com/bundles/JMSSerializerBundle).
|
||||||
|
|
||||||
|
Enable the serializer configuration for the bundle:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
#app/config/config.yml
|
||||||
|
fos_elastica:
|
||||||
|
serializer: ~
|
||||||
|
```
|
||||||
|
|
||||||
|
The default configuration that comes with FOSElasticaBundle supports both the JMS Serializer
|
||||||
|
and the Symfony Serializer. If JMSSerializerBundle is installed, additional support for
|
||||||
|
serialization groups and versions are added to the bundle.
|
||||||
|
|
||||||
|
B) Set up each defined type to support serialization
|
||||||
|
----------------------------------------------------
|
||||||
|
|
||||||
|
A type does not need to have mappings defined when using a serializer. An example configuration
|
||||||
|
for a type in this case:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
search:
|
||||||
|
types:
|
||||||
|
user:
|
||||||
|
serializer:
|
||||||
|
groups: [elastica, Default]
|
||||||
|
```
|
144
Resources/doc/setup.md
Normal file
144
Resources/doc/setup.md
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
Step 1: Setting up the bundle
|
||||||
|
=============================
|
||||||
|
|
||||||
|
A) Install FOSElasticaBundle
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
FOSElasticaBundle is installed using [Composer](https://getcomposer.org).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ php composer.phar require friendsofsymfony/elastica-bundle "3.0.*"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Elasticsearch
|
||||||
|
|
||||||
|
Instructions for installing and deploying Elasticsearch may be found
|
||||||
|
[here](http://www.elasticsearch.org/guide/reference/setup/installation/).
|
||||||
|
|
||||||
|
|
||||||
|
B) Enable FOSElasticaBundle
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Enable FOSElasticaBundle in your AppKernel:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
// app/AppKernel.php
|
||||||
|
|
||||||
|
public function registerBundles()
|
||||||
|
{
|
||||||
|
$bundles = array(
|
||||||
|
// ...
|
||||||
|
new FOS\RestBundle\FOSRestBundle(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
C) Basic Bundle Configuration
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The basic minimal configuration for FOSElasticaBundle is one client with one Elasticsearch
|
||||||
|
index. In almost all cases, an application will only need a single index. An index can
|
||||||
|
be considered comparable to a Doctrine Entity Manager, where the index will hold multiple
|
||||||
|
type definitions.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
#app/config/config.yml
|
||||||
|
fos_elastica:
|
||||||
|
clients:
|
||||||
|
default: { host: localhost, port: 9200 }
|
||||||
|
indexes:
|
||||||
|
search: ~
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, an Elastica index (an instance of `Elastica\Index`) is available as a
|
||||||
|
service with the key `fos_elastica.index.search`.
|
||||||
|
|
||||||
|
If the Elasticsearch index name needs to be different to the service name in your
|
||||||
|
application, for example, renaming the search index based on different environments.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
#app/config/config.yml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
search:
|
||||||
|
index_name: search_dev
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, the service `fos_elastica.index.search` will be using an Elasticsearch
|
||||||
|
index of search_dev.
|
||||||
|
|
||||||
|
D) Defining index types
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
By default, FOSElasticaBundle requires each type that is to be indexed to be mapped.
|
||||||
|
It is possible to use a serializer to avoid this requirement. To use a serializer, see
|
||||||
|
the [serializer documentation](serializer.md)
|
||||||
|
|
||||||
|
An Elasticsearch type needs to be defined with each field of a related PHP object that
|
||||||
|
will end up being indexed.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
search:
|
||||||
|
types:
|
||||||
|
user:
|
||||||
|
mappings:
|
||||||
|
username: ~
|
||||||
|
firstName: ~
|
||||||
|
lastName: ~
|
||||||
|
email: ~
|
||||||
|
```
|
||||||
|
|
||||||
|
Each defined type is made available as a service, and in this case the service key is
|
||||||
|
`fos_elastica.index.search.user` and is an instance of `Elastica\Type`.
|
||||||
|
|
||||||
|
FOSElasticaBundle requires a provider for each type that will notify when an object
|
||||||
|
that maps to a type has been modified. The bundle ships with support for Doctrine and
|
||||||
|
Propel objects.
|
||||||
|
|
||||||
|
Below is an example for the Doctrine ORM. For additional information regarding
|
||||||
|
integration with Doctrine or Propel or how to create a custom provider, see
|
||||||
|
[type-providers.md].
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
user:
|
||||||
|
mappings:
|
||||||
|
username: ~
|
||||||
|
firstName: ~
|
||||||
|
lastName: ~
|
||||||
|
email: ~
|
||||||
|
persistence:
|
||||||
|
# the driver can be orm, mongodb or propel
|
||||||
|
# listener and finder are not supported by
|
||||||
|
# propel and should be removed
|
||||||
|
driver: orm
|
||||||
|
model: Acme\ApplicationBundle\Entity\User
|
||||||
|
provider: ~
|
||||||
|
listener: ~
|
||||||
|
finder: ~
|
||||||
|
```
|
||||||
|
|
||||||
|
There are a significant number of options available for types, that can be
|
||||||
|
[found here](types.md)
|
||||||
|
|
||||||
|
E) Populating the Elasticsearch index
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
When using the providers and listeners that come with the bundle, any new or modified
|
||||||
|
object will be indexed automatically. In some cases, where the database is modified
|
||||||
|
externally, the Elasticsearch index must be updated manually. This can be achieved by
|
||||||
|
running the console command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ php app/console fos:elastica:populate
|
||||||
|
```
|
||||||
|
|
||||||
|
The command will also create all indexes and types defined if they do not already exist
|
||||||
|
on the Elasticsearch server.
|
||||||
|
|
||||||
|
F) Usage
|
||||||
|
--------
|
||||||
|
|
||||||
|
Usage documentation for the bundle is available [here](usage.md)
|
264
Resources/doc/types.md
Normal file
264
Resources/doc/types.md
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
Type configuration
|
||||||
|
==================
|
||||||
|
|
||||||
|
Handling missing results with FOSElasticaBundle
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
By default, FOSElasticaBundle will throw an exception if the results returned from
|
||||||
|
Elasticsearch are different from the results it finds from the chosen persistence
|
||||||
|
provider. This may pose problems for a large index where updates do not occur instantly
|
||||||
|
or another process has removed the results from your persistence provider without
|
||||||
|
updating Elasticsearch.
|
||||||
|
|
||||||
|
The error you're likely to see is something like:
|
||||||
|
'Cannot find corresponding Doctrine objects for all Elastica results.'
|
||||||
|
|
||||||
|
To solve this issue, each type can be configured to ignore the missing results:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
user:
|
||||||
|
persistence:
|
||||||
|
elastica_to_model_transformer:
|
||||||
|
ignore_missing: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Dynamic templates
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Dynamic templates allow to define mapping templates that will be
|
||||||
|
applied when dynamic introduction of fields / objects happens.
|
||||||
|
|
||||||
|
[Documentation](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-root-object-type.html#_dynamic_templates)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
site:
|
||||||
|
types:
|
||||||
|
user:
|
||||||
|
dynamic_templates:
|
||||||
|
my_template_1:
|
||||||
|
match: apples_*
|
||||||
|
mapping:
|
||||||
|
type: float
|
||||||
|
my_template_2:
|
||||||
|
match: *
|
||||||
|
match_mapping_type: string
|
||||||
|
mapping:
|
||||||
|
type: string
|
||||||
|
index: not_analyzed
|
||||||
|
mappings:
|
||||||
|
username: { type: string }
|
||||||
|
```
|
||||||
|
|
||||||
|
Nested objects in FOSElasticaBundle
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
Note that object can autodetect properties
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
website:
|
||||||
|
types:
|
||||||
|
post:
|
||||||
|
mappings:
|
||||||
|
date: { boost: 5 }
|
||||||
|
title: { boost: 3 }
|
||||||
|
content: ~
|
||||||
|
comments:
|
||||||
|
type: "nested"
|
||||||
|
properties:
|
||||||
|
date: { boost: 5 }
|
||||||
|
content: ~
|
||||||
|
user:
|
||||||
|
type: "object"
|
||||||
|
approver:
|
||||||
|
type: "object"
|
||||||
|
properties:
|
||||||
|
date: { boost: 5 }
|
||||||
|
```
|
||||||
|
|
||||||
|
Parent fields
|
||||||
|
-------------
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
website:
|
||||||
|
types:
|
||||||
|
comment:
|
||||||
|
mappings:
|
||||||
|
date: { boost: 5 }
|
||||||
|
content: ~
|
||||||
|
_parent:
|
||||||
|
type: "post"
|
||||||
|
property: "post"
|
||||||
|
identifier: "id"
|
||||||
|
```
|
||||||
|
|
||||||
|
The parent field declaration has the following values:
|
||||||
|
|
||||||
|
* `type`: The parent type.
|
||||||
|
* `property`: The property in the child entity where to look for the parent entity. It may be ignored if is equal to
|
||||||
|
the parent type.
|
||||||
|
* `identifier`: The property in the parent entity which has the parent identifier. Defaults to `id`.
|
||||||
|
|
||||||
|
Note that to create a document with a parent, you need to call `setParent` on the document rather than setting a
|
||||||
|
_parent field. If you do this wrong, you will see a `RoutingMissingException` as Elasticsearch does not know where
|
||||||
|
to store a document that should have a parent but does not specify it.
|
||||||
|
|
||||||
|
Date format example
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
If you want to specify a [date format](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-date-format.html):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
user:
|
||||||
|
mappings:
|
||||||
|
username: { type: string }
|
||||||
|
lastlogin: { type: date, format: basic_date_time }
|
||||||
|
birthday: { type: date, format: "yyyy-MM-dd" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom settings
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Any setting can be specified when declaring a type. For example, to enable a custom
|
||||||
|
analyzer, you could write:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
indexes:
|
||||||
|
search:
|
||||||
|
settings:
|
||||||
|
index:
|
||||||
|
analysis:
|
||||||
|
analyzer:
|
||||||
|
my_analyzer:
|
||||||
|
type: custom
|
||||||
|
tokenizer: lowercase
|
||||||
|
filter : [my_ngram]
|
||||||
|
filter:
|
||||||
|
my_ngram:
|
||||||
|
type: "nGram"
|
||||||
|
min_gram: 3
|
||||||
|
max_gram: 5
|
||||||
|
types:
|
||||||
|
blog:
|
||||||
|
mappings:
|
||||||
|
title: { boost: 8, analyzer: my_analyzer }
|
||||||
|
```
|
||||||
|
|
||||||
|
Provider Configuration
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
### Specifying a custom query builder for populating indexes
|
||||||
|
|
||||||
|
When populating an index, it may be required to use a different query builder method
|
||||||
|
to define which entities should be queried.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
user:
|
||||||
|
persistence:
|
||||||
|
provider:
|
||||||
|
query_builder_method: createIsActiveQueryBuilder
|
||||||
|
```
|
||||||
|
|
||||||
|
### Populating batch size
|
||||||
|
|
||||||
|
By default, ElasticaBundle will index documents by packets of 100.
|
||||||
|
You can change this value in the provider configuration.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
user:
|
||||||
|
persistence:
|
||||||
|
provider:
|
||||||
|
batch_size: 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Changing the document identifier
|
||||||
|
|
||||||
|
By default, ElasticaBundle will use the `id` field of your entities as
|
||||||
|
the Elasticsearch document identifier. You can change this value in the
|
||||||
|
persistence configuration.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
user:
|
||||||
|
persistence:
|
||||||
|
identifier: searchId
|
||||||
|
```
|
||||||
|
|
||||||
|
Listener Configuration
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
### Realtime, selective index update
|
||||||
|
|
||||||
|
If you use the Doctrine integration, you can let ElasticaBundle update the indexes automatically
|
||||||
|
when an object is added, updated or removed. It uses Doctrine lifecycle events.
|
||||||
|
Declare that you want to update the index in real time:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
user:
|
||||||
|
persistence:
|
||||||
|
driver: orm
|
||||||
|
model: Application\UserBundle\Entity\User
|
||||||
|
listener: ~ # by default, listens to "insert", "update" and "delete"
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the index is automatically updated each time the state of the bound Doctrine repository changes.
|
||||||
|
No need to repopulate the whole "user" index when a new `User` is created.
|
||||||
|
|
||||||
|
You can also choose to only listen for some of the events:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
persistence:
|
||||||
|
listener:
|
||||||
|
insert: true
|
||||||
|
update: false
|
||||||
|
delete: true
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Propel** doesn't support this feature yet.
|
||||||
|
|
||||||
|
### Checking an entity method for listener
|
||||||
|
|
||||||
|
If you use listeners to update your index, you may need to validate your
|
||||||
|
entities before you index them (e.g. only index "public" entities). Typically,
|
||||||
|
you'll want the listener to be consistent with the provider's query criteria.
|
||||||
|
This may be achieved by using the `is_indexable_callback` config parameter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
persistence:
|
||||||
|
listener:
|
||||||
|
is_indexable_callback: "isPublic"
|
||||||
|
```
|
||||||
|
|
||||||
|
If `is_indexable_callback` is a string and the entity has a method with the
|
||||||
|
specified name, the listener will only index entities for which the method
|
||||||
|
returns `true`. Additionally, you may provide a service and method name pair:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
persistence:
|
||||||
|
listener:
|
||||||
|
is_indexable_callback: [ "%custom_service_id%", "isIndexable" ]
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, the callback_class will be the `isIndexable()` method on the specified
|
||||||
|
service and the object being considered for indexing will be passed as the only
|
||||||
|
argument. This allows you to do more complex validation (e.g. ACL checks).
|
||||||
|
|
||||||
|
If you have the [Symfony ExpressionLanguage](https://github.com/symfony/expression-language)
|
||||||
|
component installed, you can use expressions to evaluate the callback:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
persistence:
|
||||||
|
listener:
|
||||||
|
is_indexable_callback: "user.isActive() && user.hasRole('ROLE_USER')"
|
||||||
|
```
|
||||||
|
|
||||||
|
As you might expect, new entities will only be indexed if the callback_class returns
|
||||||
|
`true`. Additionally, modified entities will be updated or removed from the
|
||||||
|
index depending on whether the callback_class returns `true` or `false`, respectively.
|
||||||
|
The delete listener disregards the callback_class.
|
||||||
|
|
||||||
|
> **Propel** doesn't support this feature yet.
|
177
Resources/doc/usage.md
Normal file
177
Resources/doc/usage.md
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
FOSElasticaBundle Usage
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Basic Searching with a Finder
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The most useful searching method is to use a finder defined by the type configuration.
|
||||||
|
A finder will return results that have been hydrated by the configured persistence backend,
|
||||||
|
allowing you to use relationships of returned entities. For more information about
|
||||||
|
configuration options for this kind of searching, please see the [types](types.md)
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$finder = $this->container->get('fos_elastica.finder.search.user');
|
||||||
|
|
||||||
|
// Option 1. Returns all users who have example.net in any of their mapped fields
|
||||||
|
$results = $finder->find('example.net');
|
||||||
|
|
||||||
|
// Option 2. Returns a set of hybrid results that contain all Elasticsearch results
|
||||||
|
// and their transformed counterparts. Each result is an instance of a HybridResult
|
||||||
|
$results = $finder->findHybrid('example.net');
|
||||||
|
|
||||||
|
// Option 3a. Pagerfanta'd resultset
|
||||||
|
/** var Pagerfanta\Pagerfanta */
|
||||||
|
$userPaginator = $finder->findPaginated('bob');
|
||||||
|
$countOfResults = $userPaginator->getNbResults();
|
||||||
|
|
||||||
|
// Option 3b. KnpPaginator resultset
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Faceted Searching
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
When searching with facets, the facets can be retrieved when using the paginated
|
||||||
|
methods on the finder.
|
||||||
|
|
||||||
|
```php
|
||||||
|
$query = new \Elastica\Query();
|
||||||
|
$facet = new \Elastica\Facet\Terms('tags');
|
||||||
|
$facet->setField('companyGroup');
|
||||||
|
$query->addFacet($facet);
|
||||||
|
|
||||||
|
$companies = $finder->findPaginated($query);
|
||||||
|
$companies->setMaxPerPage($params['limit']);
|
||||||
|
$companies->setCurrentPage($params['page']);
|
||||||
|
|
||||||
|
$facets = $companies->getAdapter()->getFacets());
|
||||||
|
```
|
||||||
|
|
||||||
|
Searching the entire index
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
You can also define a finder that will work on the entire index. Adjust your index
|
||||||
|
configuration as per below:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
website:
|
||||||
|
finder: ~
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now use the index wide finder service `fos_elastica.finder.website`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
/** var FOS\ElasticaBundle\Finder\MappedFinder */
|
||||||
|
$finder = $container->get('fos_elastica.finder.website');
|
||||||
|
|
||||||
|
// Returns a mixed array of any objects mapped
|
||||||
|
$results = $finder->find('bob');
|
||||||
|
```
|
||||||
|
|
||||||
|
Type Repositories
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
In the case where you need many different methods for different searching terms, it
|
||||||
|
may be better to separate methods for each type into their own dedicated repository
|
||||||
|
classes, just like Doctrine ORM's EntityRepository classes.
|
||||||
|
|
||||||
|
The manager class that handles repositories has a service key of `fos_elastica.manager`.
|
||||||
|
The manager will default to handling ORM entities, and the configuration must be changed
|
||||||
|
for MongoDB users.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
default_manager: mongodb
|
||||||
|
```
|
||||||
|
|
||||||
|
An example for using a repository:
|
||||||
|
|
||||||
|
```php
|
||||||
|
/** var FOS\ElasticaBundle\Manager\RepositoryManager */
|
||||||
|
$repositoryManager = $container->get('fos_elastica.manager');
|
||||||
|
|
||||||
|
/** var FOS\ElasticaBundle\Repository */
|
||||||
|
$repository = $repositoryManager->getRepository('UserBundle:User');
|
||||||
|
|
||||||
|
/** var array of Acme\UserBundle\Entity\User */
|
||||||
|
$users = $repository->find('bob');
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information about customising repositories, see the cookbook entry
|
||||||
|
[Custom Repositories](custom-repositories.md).
|
||||||
|
|
||||||
|
Using a custom query builder method for transforming results
|
||||||
|
------------------------------------------------------------
|
||||||
|
|
||||||
|
When returning results from Elasticsearch to be transformed by the bundle, the default
|
||||||
|
`createQueryBuilder` method on each objects Repository class will be called. In many
|
||||||
|
circumstances this is not ideal and you'd prefer to use a different method to join in
|
||||||
|
any entity relations that are required on the page that will be displaying the results.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
user:
|
||||||
|
elastica_to_model_transformer:
|
||||||
|
query_builder_method: createSearchQueryBuilder
|
||||||
|
```
|
||||||
|
|
||||||
|
Advanced Searching Example
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
If you would like to perform more advanced queries, here is one example using
|
||||||
|
the snowball stemming algorithm.
|
||||||
|
|
||||||
|
It searches for Article entities using `title`, `tags`, and `categoryIds`.
|
||||||
|
Results must match at least one specified `categoryIds`, and should match the
|
||||||
|
`title` or `tags` criteria. Additionally, we define a snowball analyzer to
|
||||||
|
apply to queries against the `title` field.
|
||||||
|
|
||||||
|
Assuming a type is configured as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fos_elastica:
|
||||||
|
indexes:
|
||||||
|
site:
|
||||||
|
settings:
|
||||||
|
index:
|
||||||
|
analysis:
|
||||||
|
analyzer:
|
||||||
|
my_analyzer:
|
||||||
|
type: snowball
|
||||||
|
language: English
|
||||||
|
types:
|
||||||
|
article:
|
||||||
|
mappings:
|
||||||
|
title: { boost: 10, analyzer: my_analyzer }
|
||||||
|
tags:
|
||||||
|
categoryIds:
|
||||||
|
persistence:
|
||||||
|
driver: orm
|
||||||
|
model: Acme\DemoBundle\Entity\Article
|
||||||
|
provider: ~
|
||||||
|
finder: ~
|
||||||
|
```
|
||||||
|
|
||||||
|
The following code will execute a search against the Elasticsearch server:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$finder = $this->container->get('fos_elastica.finder.site.article');
|
||||||
|
$boolQuery = new \Elastica\Query\Bool();
|
||||||
|
|
||||||
|
$fieldQuery = new \Elastica\Query\Text();
|
||||||
|
$fieldQuery->setFieldQuery('title', 'I am a title string');
|
||||||
|
$fieldQuery->setFieldParam('title', 'analyzer', 'my_analyzer');
|
||||||
|
$boolQuery->addShould($fieldQuery);
|
||||||
|
|
||||||
|
$tagsQuery = new \Elastica\Query\Terms();
|
||||||
|
$tagsQuery->setTerms('tags', array('tag1', 'tag2'));
|
||||||
|
$boolQuery->addShould($tagsQuery);
|
||||||
|
|
||||||
|
$categoryQuery = new \Elastica\Query\Terms();
|
||||||
|
$categoryQuery->setTerms('categoryIds', array('1', '2', '3'));
|
||||||
|
$boolQuery->addMust($categoryQuery);
|
||||||
|
|
||||||
|
$data = $finder->find($boolQuery);
|
||||||
|
```
|
Loading…
Reference in a new issue