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
|
||||
{
|
||||
/**
|
||||
* Stores supported database drivers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supportedDrivers = array('orm', 'mongodb', 'propel');
|
||||
|
||||
private $configArray = array();
|
||||
|
@ -32,8 +37,12 @@ class Configuration implements ConfigurationInterface
|
|||
|
||||
$rootNode
|
||||
->children()
|
||||
->scalarNode('default_client')->end()
|
||||
->scalarNode('default_index')->end()
|
||||
->scalarNode('default_client')
|
||||
->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()
|
||||
->arrayNode('serializer')
|
||||
->treatNullLike(array())
|
||||
|
@ -105,6 +114,7 @@ class Configuration implements ConfigurationInterface
|
|||
->scalarNode('host')->end()
|
||||
->scalarNode('port')->end()
|
||||
->scalarNode('logger')
|
||||
->info('Set your own logger service for this client or disable the logger with false.')
|
||||
->defaultValue('fos_elastica.logger')
|
||||
->treatNullLike('fos_elastica.logger')
|
||||
->treatTrueLike('fos_elastica.logger')
|
||||
|
@ -134,11 +144,14 @@ class Configuration implements ConfigurationInterface
|
|||
->prototype('array')
|
||||
->performNoDeepMerging()
|
||||
->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('finder')
|
||||
->info('Defines an index wide finder that will search all types.')
|
||||
->treatNullLike(true)
|
||||
->defaultTrue()
|
||||
->defaultFalse()
|
||||
->end()
|
||||
->append($this->getTypePrototypeNode())
|
||||
->variableNode('settings')->defaultValue(array())->end()
|
||||
|
@ -157,6 +170,7 @@ class Configuration implements ConfigurationInterface
|
|||
{
|
||||
$builder = new TreeBuilder();
|
||||
$node = $builder->root('type_prototype');
|
||||
$node->info('Allows a prototype type definition that can be applied to all types.');
|
||||
|
||||
$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
|
||||
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.
|
||||
[![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)
|
||||
|
||||
Add FOSElasticaBundle to your application's `composer.json` file:
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"friendsofsymfony/elastica-bundle": "3.0.*@dev"
|
||||
}
|
||||
}
|
||||
```
|
||||
Documentation for FOSElasticaBundle is in `Resources/doc/index.md`
|
||||
|
||||
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
|
||||
$ php composer.phar update friendsofsymfony/elastica-bundle
|
||||
```
|
||||
[Read the documentation for 2.1.x](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/2.1.x/README.md)
|
||||
|
||||
You may rely on Composer to fetch the appropriate version of Elastica. Lastly,
|
||||
enable the bundle in your application kernel:
|
||||
Installation
|
||||
------------
|
||||
|
||||
```php
|
||||
// app/AppKernel.php
|
||||
Installation instructions can be found in the [documentation](https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/setup.md)
|
||||
|
||||
public function registerBundles()
|
||||
{
|
||||
$bundles = array(
|
||||
// ...
|
||||
new FOS\ElasticaBundle\FOSElasticaBundle(),
|
||||
);
|
||||
}
|
||||
```
|
||||
License
|
||||
-------
|
||||
|
||||
#### Elasticsearch
|
||||
This bundle is under the MIT license. See the complete license in the bundle:
|
||||
|
||||
Instructions for installing and deploying Elasticsearch may be found
|
||||
[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 }
|
||||
```
|
||||
Resources/meta/LICENSE
|
||||
|
|
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