Go to file
Jeremy Mikola 0391dff44e Configure Travis CI
This also changes the test suite and bootstrap to depend on packages installed by composer.
2012-03-06 20:33:50 -05:00
Command Remove mapping and setting registries, use the reseter to configure the indexes 2011-09-12 18:28:59 +02:00
Configuration Added configuring repository by annotation 2012-01-25 11:20:47 +00:00
DataCollector add logger explicit;cleanup;correct cs 2011-10-04 17:26:14 +02:00
DependencyInjection Added null_value to mapping configuration 2012-02-27 20:11:25 +00:00
Doctrine Hybrid finder results 2012-02-24 09:50:46 +11:00
Finder Hybrid finder results 2012-02-24 09:50:46 +11:00
Logger fix logQuery 2011-10-05 12:53:03 +02:00
Manager Renamed variable in RepositoryManager 2012-01-25 12:45:08 +00:00
Paginator Replace dependency to Zend\Paginator to dependency to Pagerfanta (optional) 2011-07-05 10:43:01 -07:00
Persister Cleaned up doctrine removal fix changes 2012-01-26 09:59:58 +00:00
Propel Merge pull request #51 from willdurand/propel-support 2012-01-12 16:01:33 +00:00
Provider Doctrine integration refactoring, adds realtime index updates 2011-06-07 11:16:19 -07:00
Resources Fixing issue with removing mapped Doctrine entities/documents 2012-01-25 19:51:10 +00:00
Tests Configure Travis CI 2012-03-06 20:33:50 -05:00
Transformer Merge remote-tracking branch 'origin/2.0' into 2.0 2012-02-27 20:12:50 +00:00
.gitignore Configure Travis CI 2012-03-06 20:33:50 -05:00
.travis.yml Configure Travis CI 2012-03-06 20:33:50 -05:00
Client.php Revert "Hide exceptions if elasticsearch is not running. This needs some fixing and cleaning up by someone smarter than myself." 2011-12-28 16:35:00 -05:00
composer.json Revise composer.json and require Symfony 2.0 explicitly 2012-03-03 22:46:34 -05:00
FOQElasticaBundle.php Merge pull request #58 from merk/index_transformer 2012-01-12 16:10:28 +00:00
HybridResult.php Hybrid finder results 2012-02-24 09:50:46 +11:00
IndexManager.php Give the index manager a default index 2011-04-12 15:15:48 -07:00
LICENSE Add MIT license 2012-03-03 22:45:28 -05:00
MappingRegistry.php Added test for MappingRegistry Populator and ModelToElasticaAutoTransformer. Extend Populator 2011-07-28 21:56:58 +02:00
phpunit.xml.dist Configure Travis CI 2012-03-06 20:33:50 -05:00
Populator.php Added test for MappingRegistry Populator and ModelToElasticaAutoTransformer. Extend Populator 2011-07-28 21:56:58 +02:00
README.md Fixed README formatting issue 2012-01-25 14:15:20 +00:00
Repository.php Adding manager that returns Doctrine style repositories 2011-11-12 21:10:00 +00:00
Reseter.php Remove mapping and setting registries, use the reseter to configure the indexes 2011-09-12 18:28:59 +02:00

Elastica integration in Symfony2

Installation

Install elasticsearch

http://www.elasticsearch.org/guide/reference/setup/installation.html

Install Elastica

Download

With submodule

git submodule add git://github.com/ruflin/Elastica vendor/elastica

With clone

git clone git://github.com/ruflin/Elastica vendor/elastica

Using the vendors script

Add the following lines to your deps file:

[Elastica]
    git=git://github.com/ruflin/Elastica.git
    target=elastica

Register autoloading

// app/autoload.php

$loader->registerPrefixes(array(
    ...
    'Elastica' => __DIR__.'/../vendor/elastica/lib',
));

Install ElasticaBundle

Use the master branch with Symfony2 master only, use the 2.0 branch with Symfony2.0.x releases.

Download

With submodule

git submodule add git://github.com/Exercise/FOQElasticaBundle vendor/bundles/FOQ/ElasticaBundle

With clone

git clone git://github.com/Exercise/FOQElasticaBundle vendor/bundles/FOQ/ElasticaBundle

With the vendors script

Add the following lines to your deps file:

[FOQElasticaBundle]
    git=git://github.com/Exercise/FOQElasticaBundle.git
    target=bundles/FOQ/ElasticaBundle

For the 2.0 branch for use with Symfony2.0.x releases add the following:

[FOQElasticaBundle]
    git=git://github.com/Exercise/FOQElasticaBundle.git
    target=bundles/FOQ/ElasticaBundle
    version=origin/2.0

Run the vendors script:

$ php bin/vendors install

Register autoloading

// app/autoload.php

$loader->registerNamespaces(array(
    ...
    'FOQ' => __DIR__.'/../vendor/bundles',
));

Register the bundle

// app/AppKernel.php

public function registerBundles()
{
    return array(
        // ...
        new FOQ\ElasticaBundle\FOQElasticaBundle(),
        // ...
    );
}

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
foq_elastica:
    clients:
        default: { host: localhost, port: 9200 }

Declare an index

Elasticsearch index is comparable to Doctrine entity manager. Most of the time, you will need only one.

foq_elastica:
    clients:
        default: { host: localhost, port: 9200 }
    indexes:
        website:
            client: default

Here we created a "website" index, that uses our "default" client.

Our index is now available as a service: foq_elastica.index.website. It is an instance of Elastica_Index.

Declare a type

Elasticsearch type is comparable to Doctrine entity repository.

foq_elastica:
    clients:
        default: { host: localhost, port: 9200 }
    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: foq_elastica.index.website.user. It is an instance of Elastica_Type.

Populate the types

php app/console foq: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 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.

foq_elastica:
    clients:
        default: { host: localhost, port: 9200 }
    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 paquets 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 provider configuration.

                    persistence:
                        driver: orm
                        model: Application\UserBundle\Entity\User
                        provider:
                            identifier: id

Manual provider

Create a service with the tag "foq_elastica.provider".

    <service id="acme.search_provider.user" class="Acme\UserBundle\Search\UserProvider">
        <tag name="foq_elastica.provider" />
        <argument type="service" id="foq_elastica.index.website.user" />
    </service>

Its class must implement FOQ\ElasticaBundle\Provider\ProviderInterface.

    <?php

    namespace Acme\UserBundle\Provider;

    use FOQ\ElasticaBundle\Provider\ProviderInterface;
    use Elastica_Type;

    class UserProvider implements ProviderInterface
    {
        protected $userType;

        public function __construct(Elastica_Type $userType)
        {
            $this->userType = $userType;
        }

        /**
         * Insert the repository objects in the type index
         *
         * @param Closure $loggerClosure
         */
        public function populate(Closure $loggerClosure)
        {
            $loggerClosure('Indexing users');

            $this->userType->addDocuments(array(
                array('username' => 'Bob')
            ));
        }
    }

You will find a more complete implementation example in src/FOQ/ElasticaBundle/Doctrine/AbstractProvider.php.

You can just use the index and type Elastica objects, provided as services, to perform searches.

/** var Elastica_Type */
$userType = $this->container->get('foq_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:

foq_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:

You can now use the foq_elastica.finder.website.user service:

/** var FOQ\ElasticaBundle\Finder\MappedFinder */
$finder = $container->get('foq_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!

/** var Pagerfanta\Pagerfanta */
$userPaginator = $finder->findPaginated('bob');
Index wide finder

You can also define a finder that will work on the entire index. Adjust your index configuration as per below:

foq_elastica:
    indexes:
        website:
            client: default
            finder:

You can now use the index wide finder service foq_elastica.finder.website:

/** var FOQ\ElasticaBundle\Finder\MappedFinder */
$finder = $container->get('foq_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 FOQ\ElasticaBundle\Manager\RepositoryManager */
$repositoryManager = $container->get('foq_elastica.manager.orm');

/** var FOQ\ElasticaBundle\Repository */
$repository = $repositoryManager->getRepository('UserBundle:User');

/** var array of Acme\UserBundle\Entity\User */
$users = $finder->find('bob');

You can also specify the full name of the entity instead of the shortcut syntax:

/** var FOQ\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 foq_elastica.manager. So the above example could be simplified to:

/** var FOQ\ElasticaBundle\Manager\RepositoryManager */
$repositoryManager = $container->get('foq_elastica.manager');

/** var FOQ\ElasticaBundle\Repository */
$repository = $repositoryManager->getRepository('UserBundle:User');

/** var array of Acme\UserBundle\Entity\User */
$users = $finder->find('bob');

If you use multiple drivers then you can choose which one is aliased to foq_elastica.manager using the default_manager parameter:

foq_elastica:
    default_manager: mongodb #defauults 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 FOQ\ElasticaBundle\Repository to have access to the finder:

<?php

namespace Acme\ElasticaBundle\SearchRepository;

use FOQ\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:

foq_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 FOQ\ElasticaBundle\Manager\RepositoryManager */
$repositoryManager = $container->get('foq_elastica.manager');

/** var FOQ\ElasticaBundle\Repository */
$repository = $repositoryManager->getRepository('UserBundle:User');

/** var array of Acme\UserBundle\Entity\User */
$users = $finder->findWithCustomQuery('bob');

Alternatively you can specify the custom repository using an annotation in the entity:

<?php

namespace Application\UserBundle\Entity;

use FOQ\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:

foq_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.

Advanced elasticsearch configuration

Any setting can be specified when declaring a type. For example, to enable a custom analyzer, you could write:

foq_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_Client 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 foq_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 FOQ\ElasticaBundle\Client as BaseClient;

class Client extends BaseClient
{
    public function request($path, $method, $data = array())
    {
        try {
            return parent::request($path, $method, $data);
        } catch (\Elastica_Exception_Abstract $e) {
            return new \Elastica_Response('{"took":0,"timed_out":false,"hits":{"total":0,"max_score":0,"hits":[]}}');
        }
    }
}